7/04/2014

Note about distanceFromLocation: of CLLocation

CLLocation
- (CLLocationDistance)distanceFromLocation:(const CLLocation *)location;

When argument is nil, this method returns -1.
There is no document about this behavior and no definition about -1.
(Maybe it means invalid distance. ex. CLLocationInvalidDistance = -1 in internal)


CLLocation *location = [[CLLocation alloc] initWithLatitude:37.785834 longitude:-122.406417];
CLLocation *nilLocation = nil;
CLLocationDistance distance;

// distance from nil to location
distance = [location distanceFromLocation:nilLocation];
NSLog(@"distance %f", distance);
// -1.000000

// message send to nil object, results is nil
distance = [nilLocation distanceFromLocation:location];
NSLog(@"distance %f", distance);
// -0.000000


CLLocation の distanceFromLocation: メソッドの引数に nil を渡すと-1が返ってくる。
この挙動はドキュメントには載っていなくて、-1 が何を意味するかも定義されてない。
定義は無いけどたぶん CLLocationInvalidDistance = -1 とか内部的にはなってるんじゃないかな。

Equivalence of CLRegion

To see how the management of CLRegion by CLLocationManager,
I wrote below codes.
I found that CLRegion(s) that have same identifier isEqual.

*1
{
    // different parameters, same identifiers
    CLCircularRegion *region1 = [[CLCircularRegion allocinitWithCenter:CLLocationCoordinate2DMake(1020)
                                                                  radius:30
                                                              identifier:@"test"];
    CLCircularRegion *region2 = [[CLCircularRegion allocinitWithCenter:CLLocationCoordinate2DMake(4050)
                                                                  radius:60
                                                              identifier:@"test"];
    NSLog(@"regions are equal = %d", [region1 isEqual:region2]);
    // regions are equal = 1
}
{
    // same parameters, different identifiers
    CLCircularRegion *region1 = [[CLCircularRegion allocinitWithCenter:CLLocationCoordinate2DMake(1020)
                                                                  radius:30
                                                              identifier:@"test"];
    CLCircularRegion *region2 = [[CLCircularRegion allocinitWithCenter:CLLocationCoordinate2DMake(1020)
                                                                  radius:30
                                                              identifier:@"tes"];
    NSLog(@"regions are equal = %d", [region1 isEqual:region2]);
    // regions are equal = 0
}


So codes that remove a region has same identifier from mutable array or set are below. See simple way.

*2
    // remove region with same identifier from array
    NSArray *regions;
    CLRegion *regionToRemove;

    NSMutableArray *mArray = regions.mutableCopy;
    // normal way
    for (CLRegion *region in regions) {
        if ([region.identifier isEqualToString:regionToRemove.identifier]) {
            [mArray removeObject:region];
        }
    }
    
    // simple way
    [mArray removeObject:regionToRemove];

Conversely you may need to be careful about this behavior in certain situation.


CLLocationManager による CLRegion の管理の仕方を見てもしやと思いテストコードを書いてみると、同じ identifier の CLRegion は isEqual という事が分かった。
*1

という事で、Mutableな配列やセットから同じ identifier の region を削除するには単にこう書けばいい。simple wayの方を参照。
*2

逆に言うと、Mutableな配列から region を削除する時はこの挙動を気を付けなければならないケースがあるかも知れない。

Behavior of the monitoring region by CLLocationManager

In document

- (void)startMonitoringForRegion:(CLRegion *)region
If an existing region with the same identifier is already being monitored by the application, the old region is replaced by the new one. The regions you add using this method are shared by all location manager objects in your application and stored in the monitoredRegions property.
CLLocationManager manage regions with not equivalence of instance but equivalence of identifier.
And it's surprise for me that sharing regions with instances.


ドキュメントによれば
同じ identifier の region の監視を開始すると、古いものが新しいもので置き換えられる。
インスタンスの同一性ではなく、identifier の同値性で管理されるらしい。
また、監視を開始した region はアプリ内の全ての CLLocationManager で共有されて monitoredRegions プロパティに保存される。
インスタンス間で監視中の region が共有されるのは意外だった。

6/28/2010

AVFoundation.framework might be included in Mac OS 10.7

AVFoundation.framework in iOS4 has AVBase.h file as follow

/*
File:  AVBase.h

Framework:  AVFoundation

Copyright 2010 Apple Inc. All rights reserved.

 */

#import

// Pre-10.7, weak import
#ifndef __AVAILABILITY_INTERNAL__MAC_10_7
#define __AVAILABILITY_INTERNAL__MAC_10_7 __AVAILABILITY_INTERNAL_WEAK_IMPORT
#endif

Apple makes efforts to the maintenance of API related to the sound caused by AVFoundation, and I think this is a strongly possibility.


iOS4 の AVFoundation.framework あたりを見ていたら 10.7 に AVFoundation が搭載されることを示唆する記述を発見。AVBase.h 以外のところにも記述はある。AVFoundation は iPhoneOS 上でサウンド関係の API として整備されてきており、これが現実になる可能性は高いだろう。

5/15/2010

CALayer setting in initWithFrame:

I try to make custom view that is high performance and animatable. I set layer in custom view's initialize method initWithFrame:

- (id)initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
  CALayer *layer = [CALayer layer];
  CGColorRef color = CGColorCreateGenericGray(0.0, 1.0);
  layer.backgroundColor = color;
  CGColorRelease(color);
  [self setLayer:layer];
  [self setWantsLayer:YES];
  }
    return self;
}

but, layer doesn't see why.
In some CALayer's sample codes, layer setting is done in awakeFromNib.

- (void)awakeFromNib {
    NSLog(@"%d", [self wantsLayer]);
    NSLog(@"%@", [[self layer] description]);
}

I check as above, layer is exist but wantsLayer method return NO.
Layer now appears that properly implemented as follows.

- (void)awakeFromNib {
    [self setWantsLayer:YES];
}

When custom view is made from code, not nib, awakeFromNib method doesn't call but setWantsLayer: work properly. I looked for document about timing of setWantsLayer: to NSView but could not find. Anyone know?


CALayer を使って高速かつアニメーション可能なカスタムビューを作ろうと思って、カスタムビューの初期化メソッド initWithFrame: でレイヤーを設定してみた。

- (id)initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
  CALayer *layer = [CALayer layer];
  CGColorRef color = CGColorCreateGenericGray(0.01.0);
  layer.backgroundColor = color;
  CGColorRelease(color);
  [self setLayer:layer];
  [self setWantsLayer:YES];
  }
    return self;
}

でもこれだとなぜかレイヤーが表示されない。
CALayer のサンプルコードを見るとレイヤーの設定は awakeFromNib 内で行われていた。

- (void)awakeFromNib {
    NSLog(@"%d", [self wantsLayer]);
    NSLog(@"%@", [[self layerdescription]);
}

として確認してみると、レイヤーは設定されているが wantsLayer が NO を返しているようだ。
以下のように実装することでちゃんとレイヤーが表示されるようになった。

- (void)awakeFromNib {
    [self setWantsLayer:YES];
}

カスタムビューが nib でなくてコードから生成される場合には awakeFromNib は呼ばれないが setWantsLayer: は正しく効く。この NSView への setWantsLayer: のタイミングについてドキュメントを探しけど見つけられなかった。正しい解答が得られないまま使うのは気持ち悪いけどとりあえず。

3/09/2010

Automate to delete private header of own framework

When you build application using own framework, class header file does exist in package.
It's no matter for public framework, but off course we don't want to open class header rerated to activation or authorization.
After build application, you need delete

MyApp.app/Contents/Frameworks/MyKit.framework/PrivateHeaders
or
MyApp.app/Contents/Frameworks/MyKit.framework/Versions/A/PrivateHeaders

It's troublesome.
In Xcode, select your target and
Project > New Build Phase > New Run Script Build Phase
or control click target and select
Add > New Build Phase > New Run Script Build Phase
from context.
Write script like follow and you can perfectly automate this work

cd "${TARGET_BUILD_DIR}/${FULL_PRODUCT_NAME}/Contents/Frameworks"
rm -rf *.framework/PrivateHeaders
rm -rf *.framework/Versions/*/PrivateHeaders

It saves much time.


自作フレームワークを組み込んだアプリケーションをビルドする時、パッケージの中にヘッダファイルが残ってしまう。
公開したいフレームワークの場合はいいんだけど、アクティベートとかオーソライズといったものに関連したフレームワークのヘッダなんてもちろん公開したくない。
アプリケーションをビルドしたあと、ビルドされたアプリ内の

MyApp.app/Contents/Frameworks/MyKit.framework/PrivateHeaders

MyApp.app/Contents/Frameworks/MyKit.framework/Versions/A/PrivateHeaders

あたりのファイルを削除する必要があるけど、アプリケーションのビルドの都度これらを Finder 上で削除するのは非常に面倒。
Xcode のメニューで
プロジェクト > 新規ビルドフェーズ > 新規スクリプトを実行
もしくはターゲットを右クリックしてコンテクストメニューから
追加 > 新規ビルドフェーズ > 新規スクリプトを実行
を選択し以下のようなスクリプトを入力することでこの作業を完全自動化できる。

cd "${TARGET_BUILD_DIR}/${FULL_PRODUCT_NAME}/Contents/Frameworks"
rm -rf *.framework/PrivateHeaders
rm -rf *.framework/Versions/*/PrivateHeaders

めちゃくちゃ時間の節約になる。

3/04/2010

UIImagePickerController doesn't work in certain cases

The following are the codes that use UIImagePickerController with UIImagePickerControllerSourceTypeCamera.
This code is written in subclass of UIViewController and "self" is UIViewController.

if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
  UIImagePickerController *controller = [[UIImagePickerController alloc] init];
  controller.sourceType = UIImagePickerControllerSourceTypeCamera;
  [self presentModalViewController:controller animated:NO];
  [controller release];
}

This code doesn't work at -viewDidLoad and -viewWillAppear:
At -viewDidAppear:, that works properly.

For example, in AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  //insert initialize code here, not work
  [window addSubview:viewController.view];
  [window makeKeyAndVisible];
  //insert initialize code here, work

  return YES;
}

初期化した UIImagePickerController を View がオフスクリーン状態の ViewController を利用して表示しようとすると正しく動作しない。UIImagePickerControllerSourceTypePhotoLibrary でも試してみたけど同じく動作しない。View がオンスクリーンになっている ViewController を利用して表示しないといけないみたい。

spec iPhone 3GS OS 3.1.3