UINavigationController/フリックで親画面に戻らないようにする

iOS7では、UINavigationControllerで子画面に進んだあと、子画面を左から右にスワイプすると子画面がpopされます親画面に戻ります。この機能は以下のコードで無効化できます。

if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
    self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}

How to disable back swipe gesture in UINavigationController on iOS 7

iOSアプリ申請時に『The network connection was lost』エラーが出る場合の対処法

iOSアプリ申請時に「The network connection was lost」と表示されました。ネットワークは大丈夫そうなので、Appleの申請サーバが調子わるいのかなと思っていたのですが、ぐぐってみた結果、以下の様な情報が出てきたので、Xcodeを再起動したところ、エラーは解決しました。Xcodeの不具合っぽいですね。

iOSアプリ申請時に『The network connection was lost』エラーが出る場合の対処法

intとlongとNSInteger

iPhoneアプリにおいて、長らく、intとlongとNSIntegerは4バイトで同じサイズだった。iPhone5Sが登場し、64ビットCPUになったので、それぞれの型がどのように違うのか確認してみた。64ビット整数型を使う機会はあまり多くないかもしれないが、得点がインフレするゲームでは(約21億点 もしくは 43億点)32ビット環境と64ビット環境で動作が異なるので注意が必要だ。

int a = 1;
long b = 1;
NSInteger c = a;
   
NSLog(@"%lu %lu %lu", sizeof(a), sizeof(b), sizeof(c));

//32ビット環境 iPhone Retina(3.5inch)
2013-11-16 21:18:39.355 NSInteger[4024:70b] 4 4 4

//64ビット環境 iPhone Retina(4-inch 64bit)
2013-11-16 21:14:25.935 NSInteger[3962:70b] 4 8 8

undeclared selector警告を取り除く

プロトコルやヘッダ宣言されていないセレクターを使おうとするとUndeclared selectorの警告がでます。これを防ぐには、プロトコルやヘッダで宣言すればよいのですが、面倒だったりそうすることが相応しくない場合もあります。そういった場合には、@selector(someMethod)の代わりに、sel_registerName(“someMethod”)と記述すると、警告が発生しないようになります。ただし、この方法を使うと、実行時までセレクタ名があっているか検証されなくなるので、誤植や、呼び出し間違いの危険性がありますのでご注意ください。

SEL sel1 = @selector(someMethod);
SEL sel2 = sel_registerName("someMethod"); //@"someMethod"ではなく、"someMethod"

stackoverflow: How to get rid of the ‘undeclared selector’ warning

iOS Developer Library: Objective-C Runtime Reference

iOS7でのレビューページへのリンク

iOS7では、iOS6までの方法では、AppStoreのレビューページに飛ばなくなっている。面倒だが、iOS6向けとiOS7向けでURLを切り替えるしかなさそうだ。

参考:iOS7でアプリからappstoreのレビューページにリンクする

#define APP_ID @"XXXXXXXXX"
NSString* url = ([[UIDevice currentDevice].systemVersion floatValue] >= 7.0)?
    // iOS7以降
    [NSString stringWithFormat:@"itms-apps://itunes.apple.com/app/id%@",APP_ID]:
    // iOS6以前
    [NSString stringWithFormat:@"itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=%@&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software",APP_ID];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];

Block構文(クロージャー)

Block構文は、iOS4.0で導入された構文。関数をオブジェクトとして扱うことができる。Block内にstaticな変数を使うと、内部状態を持つBlockを作ることができる。Block内からBlock外の変数を参照することはできるが、変更はできない。Block外の変数を変更するには、ブロック外の変数の宣言に__blockを指定する必要がある。

カウンター

    int (^counter)() = ^(){
        static int i = 1;
        return i++;
    };
   
    NSLog(@"%d", counter());//1
    NSLog(@"%d", counter());//2
    NSLog(@"%d", counter());//3
//引数が無い場合は、()を省略できる。
    int (^counter2)() = ^{
        static int i = 1;
        return i++;
    };

数値の合計を計算

    int (^sum)(int) = ^(int num){
        static int value = 0;
        value += num;
        return value;
    };
    NSLog(@"%d", sum(3));//3
    NSLog(@"%d", sum(5));//8
    NSLog(@"%d", sum(10));//18

文字列を連結

    NSString * (^appender)(NSString *) = ^(NSString * str){
        static NSString *_str = @"";
        _str = [_str stringByAppendingString:str];
        return _str;
    };
   
    NSLog(@"%@", appender(@"test"));//test
    NSLog(@"%@", appender(@"test"));//testtest
    NSLog(@"%@", appender(@"test"));//testtesttest

外部の変数も参照できる。変数はクロージャ生成時に複製されるため、もとの変数の値が変わっても影響されない。

    int i = 3;
    int (^add3)(int) = ^(int num){
        return i + num;
    };
   
    NSLog(@"%d", add3(5));//8
   
    i = 4;
   
    NSLog(@"%d", add3(5));//8

通常、外部の変数は変更できない。__block指定子が付いている変数はBlockの中から変更できる。

    int j = 1;
    __block int k = 1;
    void (^inc)() = ^() {
        j = 2;//エラーになりコンパイルできない
        k = 2;//問題なく実行できる。
    };
    inc();

UILongPressがイベントを2重送信する

UILongPressGestureRecognizerは、長押し開始時と、長押し終了時の2回、イベントを送信する。長押し判定の秒数を1秒に指定して、3秒間の長押しをした場合、押し始めて1秒で長押し開始、離した瞬間(3秒の時点)に長押し終了イベントを発生させる。どちらか片方でしかイベントを実行したくない場合は以下のようにする。

-  (void)handleLongPress:(UILongPressGestureRecognizer*)sender {
    if (sender.state == UIGestureRecognizerStateEnded) {
      NSLog(@"UIGestureRecognizerStateEnded");
    //Do Whatever You want on End of Gesture
     }
    else if (sender.state == UIGestureRecognizerStateBegan){
       NSLog(@"UIGestureRecognizerStateBegan.");
   //Do Whatever You want on Began of Gesture
     }
  }

StackOverFlow:UILongPressGestureRecognizer gets called twice when pressing down

iPhoneアプリ開発に関する情報を公開中 ソースコードもあるよ。