ナビゲーションの戻るボタンのタイトルを消して矢印だけにする

戻るボタンのタイトルを消して左矢印「<」だけにするにはどうすれば良いか?実は前の画面と繋がっているため、前の画面で空のnavigationItemを割り当てておかなくてはなりません。

// 遷移元VC
- (void)viewDidLoad {
    [super viewDidLoad];
    self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
}

こうして空ボタンを作ってから遷移すると戻るボタンのタイトルが消えるんですが、遷移元が複数ある場合、そのすべてに割り当てないといけません。

タイトルを消したいVC側だけで実現するには、遷移先で遷移元を取得して空ボタンを割り当て、戻る際に解放すると上手くいきます。iOS8, 9, 10, 11で確認済み。

※iOS 11になってから空文字を割り当てなくても[UIBarButtonItem new];でOKになりました。

// 遷移先VC
- (void)hideBackBarButtonTitle {
    if ([self isMovingToParentViewController]) {
        UIViewController *previousViewController = self.navigationController.viewControllers[self.navigationController.viewControllers.count - 2];
        previousViewController.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
    }
}

- (void)showBackBarButtonTitle {
    if ([self isMovingFromParentViewController]) {
        self.navigationController.topViewController.navigationItem.backBarButtonItem = nil;
    }
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self hideBackBarButtonTitle];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [self showBackBarButtonTitle];
}

自前で矢印の画像を割り当てた場合もタイトルを消せますが、左端からのエッジスワイプが無効にされます。

NSKeyedUnarchiverを使った正しい復元方法

NSKeyedArchiverで圧縮(シリアライズ)したコードは復元できない場合があるらしく、例外処理を挟んでいないとクラッシュするかもしれません。Appleのドキュメント「NSKeyedUnarchiver」のunarchiveObjectWithDataには注意書きがあります。

Discussion
This method raises an NSInvalidArchiveOperationException if data is not a valid archive.

と、言う事はアーカイブした後で復元できるかチェックしておけば良いとも考えたんですが、どうやら、iOSやアプリのアップデートにより、復元できない場合がある模様。アーカイブのチェックは、復元の時だけ例外処理を挟んでおくのが良さそうです。

NSData *archiveData = [[NSUserDefaults standardUserDefaults] objectForKey:@"ArchiveKey"];
if (archiveData != nil) {
    NSArray *array;
    @try {
        array = [NSKeyedUnarchiver unarchiveObjectWithData:archiveData];
    }
    @catch (NSException *exception) {
        [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"ArchiveKey"];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
}

例えば、こんな感じでユーザーデフォルトにアーカイブDataを保存していて、例外をキャッチした時には、ゴミDataを取り除いておきます。

Appleのドキュメント「Archives and Serializations Programming Guide」のArchiveに例外についての詳しい説明があります。型が一致しない場合に例外が発生するようです。

Type Coercions
NSKeyedUnarchiver supports limited type coercion. A value encoded as any type of integer, be it a standard int or an explicit 32-bit or 64-bit integer, can be decoded using any of the integer decode methods. Likewise, a value encoded as a float or double can be decoded as either a float or a double value. When decoding a double value as a float, though, the decoded value loses precision. If an encoded value is too large to fit within the coerced decoded type, the decoding method throws an NSRangeException. Further, when trying to coerce a value to an incompatible type, such as decoding an int as a float, the decoding method throws an NSInvalidUnarchiveOperationException.