最近、分けあってObjective-Cを学び始めました。
ここは最初に躓いたり、あるいは今後どっかで使えそうだなーと思ったものをただひたすらメモしていくところです。
※Xcode関連は別にまとめたほうがいい気がしたので別記事にしました。
##ライフサイクル
iOSアプリは以下のライフサイクルでデリゲートメソッドが呼ばれる。
本来はもう少し細かい分岐処理とかあるけど、大まかには以下の順番。
(なにがどのタイミングで存在するか、みたいなのは未調査)
UIApplicationDelegate | 最初のViewController |
---|---|
application:willFinishLaunchingWithOptions: | -- |
application:shouldRestoreApplicationState: | -- |
application:didDecodeRestorableStateWithCorder: | -- |
application:didFinishLaunchingWithOptions: | loadView |
-- | viewDidLoad |
-- | viewWillAppear: |
applicationWillEnterForeground: | viewWillLayoutSubviews |
applicationDidBecomeActive: | viewDidLayoutSubviews |
-- | viewDidAppear: |
ちなみに、バックグラウンドから復帰した場合はViewController側は呼ばれないので注意。
Storyboardを使った場合の起動シーケンスは以下。
- アプリケーションの起動
- Storyboardファイルが読み込まれる
- ファイル内オブジェクトのインスタンス化と初期化
- Storyboardで設定されている属性を各インスタンスに反映
- 各インスタンスのawakeFromNibが呼ばれる
- アプリケーション起動完了
##色々なチェック
###マルチタスク対応か確認
UIDevice *device = [UIDevice currentDevice];BOOL backgroundSupported = NO;
if ([device respondsToSelector:@selector(isMultitaskingSupported)])
{ backgroundSupported = device.multitaskingSupported;
}
###subviewをチェック
インスタンス変数に操作したいViewを保持しておかなくても、subviewをたどって処理をしてみる。
ほんとはもっといい方法があるのかもしれないけど、とりあえず。
for (UIView *view in self.view.subviews)
{
if ([view isKindOfClass:[Hoge class]])
{
//該当クラスのviewなら処理
}
}
subviewはまさにsubviews
というプロパティでアクセスできる。
それをfor inで回して、さらに取り出したviewが目的のクラスかどうかをチェックしている。
##@ディレクティブ
色々記事を見ていると古いものから新しいものまであるけど、最近のiOSでは「@ディレクティブ」でのショートカットが増えてきた印象?
- @"text" … NSString型のリテラル
- @5 … NSNumber型のリテラル
- @[…] … NSArray型のリテラル
- @{@"key": @"value"} … NSDictionary型のリテラル
##便利ツールとかもろもろメモ
開発に必要なツールとかも大事なので、そのへんもメモしていく。
-
Cocoapods
iOSやMacアプリを作る際に便利なライブラリ管理ツール。gemやnpm、Home brewみたいな感じかな? 紹介記事はこちら
実際に使うには、npmのpackage.jsonのように設定ファイル(Podfile)を生成して、その中に色々インストールしたいライブラリ名やバージョンを書くみたい。
platform:ios
pod 'CocoaLumberjack'
- Xxx-Prefix.pch
全ファイル共通で読み込まれるファイル。全体で共通の設定などをここに書いておくと個別にヘッダファイルを指定しなくてもいいので便利。
##動的に色を設定する
色を設定するには、UIColor
クラスを用いるよう。
hoge.backgroundColor = [UIColor redColor]; //赤を設定
//RGBAからカラーを生成
UIColor *color = [UIColor colorWithRed:0.0 green:0.5 blue:0.8 alpha:1.0];
##よく使いそうなUILabelの設定メモ
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 150, 50)];
label.text = @"テキスト";
label.font = [UIFont systemFontOfSize:16];
label.textColor = [UIColor blackColor];
label.shadowColor = [UIColor whiteColor];
label.shadowOffset = CGSizeMake(1, 1);
###ぼけた影のあるUILabelを作る
ちなみにこのshadow***
はぼかしのついた影は作れないらしいので、ぼけた影を付けたい場合はUILabelを継承したカスタムクラスを作らないとならないぽい。
@interface ShadowLabel : UILabel
@end
@implementation ShadowLabel
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetShadow(context, CGSizeMake(2, -2), 5);
[super drawRect:rect];
CGContextRestoreGState(context);
}
@end
drawRect:
メソッドは描画を担当するメソッド。それをオーバーライドすることで対応。
(内部実装追っていないけど、これを見るにCGContextでレンダリングを行っているぽい?)
親クラスのdrawRect:
メソッドを実行する前にCGContextの状態をごにょごにょすることで割り込みする、というイメージかな。
これを応用すれば色々できそう。
##ランタイムな調査
###動的にクラスの調査をする
Objective-Cはどうやらだいぶ柔軟な言語のよう。
JSでやっていたような、引数チェックみたいなことができる。
クラスに関する情報はclass
メソッドから取得することができる。
- (void)method:(id)object
{
//引数object(インスタンス)からクラスオブジェクトを取得する
Class cls;
cls = [object class];
//クラス自体からクラスオブジェクトを取得する
Class stringCls;
stringCls = [NSString class]; //クラス・メソッドとしてもclassがある
//文字列からクラスオブジェクトを取得する
Class myClass;
myClass = NSClassFromString(@"MySuperClass");
}
###プロトコルに準拠しているか調査
- (void)method:(id)object
{
Class cls;
cls = [object class];
if ([cls conformsToProtocol:@protocol(HogeProtocol)]) {
//do something.
}
}
###クラス・継承のチェック
JSでいうところのinstanceof
やisPrototypeOf
的な感じだと思う。
- (void) method:(id)object
{
//objectがNSStringクラスを継承しているかチェック
if ([object isKindOfClass:[NSString class]]) {
//NSStringを継承
}
//objectがまさに特定のクラスかをチェック
if ([object isMemberOfClass:[NSString class]]) {
//NSStringクラスそのもの
}
}
###AppDelegateの参照を得る
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[delegate doSomething];
##NSLog関連
###CGRect, CGSize, CGPointをNSLogする
これら3つは構造体のため、id型の出力であるNSLog(@"%@", hoge)
は使えない。
そのため、NSStringFromCG****()
関数を利用して出力する。(***はそれぞれRectなどが入る)
例)
NSLog(@"%@", NSStringFromCGRect(hoge);
##アプリのアイコン設定でハマる
ハマったと言えるほど時間はかからなかったけれど、いちおうメモw
アプリのアイコンをPhotoshopで作って設定したら、なんだかこんなエラーが。
"None of the input catalogs contained a matching app icon set named "AppIcon"".
検索してみると、StackOverflowに解決策がありました。
どうやらPhotoshopから書きだしたときにpngがおかしくなってる模様。
ImageOptimとかのツールを使って変換することで、エラーなく使用することが出来た。
##ブロック構文
JavaScriptではおなじみの関数を渡す書式。
C#とかではラムダ式。それをObjective-Cで実現する仕組み。
^(仮引数) {
//実行する処理
};
戻り値 (^変数名)(引数) = ^(仮引数) {
//実行する処理
};
実際のコードだと以下。
^(int x) {
return x * x;
};
int (^hoge)(int) = ^(int x) {
return x * x;
};
NSLog(@"int is %d", hoge(5));
###__block修飾子
追記:__block
修飾子についてコメントをもらいました。変数を上書きする必要がある場合のみ、宣言が必要です。(読み込みはできる)
通常、ブロック構文内では外部で宣言された変数に書き込みできない。
そのため、書き込みしたい変数に__block
修飾子をつけると書き込むことができる。
__block int i = 0;
void (^printInt)(void) = ^(void) {
//書き込む必要がある場合のみ、`__block` 修飾子が必要。
//i = 5;
NSLog(@"%d", i);
};
printInt() //=> 0;
###ブロックの型を定義する
typedefを使用することで、ブロック構文も定義を行うことができる。
typedef BOOL (^someCheck)(int) = ^(int x);
someCheck check1 = ^(int x) {
if (x < 0) {
return YES;
}
else {
return NO;
}
};
###ブロック構文を利用したTips
StackOverflowで見かけた手法。
NSDictionaryを使ったもの。
typedef void (^Callback)();
NSString *callbackType = @"callback1";
NSDictionary *d = @{
@"callback1":
^{
NSLog(@"invoked callback1");
},
@"callback2":
^{
NSLog(@"invoked callback2");
}
};
((Callback)d[callbackType])();
JSで書くと多分こんな感じのこと。
var callbackType = 'callback1';
var d = {
"callback1": function () {
alert('invoked callback1");
},
"callback2": function () {
alert('invoked callback2");
}
};
d[callbackType]();
##delegateやイベントなど細々まとめ
###UIBarButtonItemのアクション
UIBarButtonItemは、UIButtonのaddTarget:action:forControlEvents:
でのdelegate設定がなく、以下のようにsetAction
とsetTarget
を利用する必要があった。
UIBarButtonItem *btn = [[UIBarButtonItem alloc] init];
[btn setTarget:self];
[btn setAction:@selector(action)];
##値の取得
###ステータスバーのframe
以下の方法で取得できる。
ただし、回転方向を考慮していないので横向きの場合はheight
が幅の数値を返すので、回転しているかどうかを考慮してプログラムしないとおかしなことになるので注意。
CGRect frame = [UIApplication sharedApplication].statusBarFrame;
##参考にした記事まとめ