Objective-C
Xcode
iOS
Xcode6
iOS8

Unwind SegueがiOS8でうまくいかないのを無理矢理回避してみた(8.1ではfix済らしい)

More than 3 years have passed since last update.

背景

iOS7までは生き生きと動いていたunwind segueが、なぜかiOS8の端末だと動かなくなった。
もしかしたらXcode6のせいなのかもしれないけれど、とりあえず動いてくれないことにはとても困る。
ってことでなんとかして最小の手間でこれを正常化できないか試してみた。

どうやらバグだったようです[2015/2/9 追記]

コメントで寄せられたリンクによれば、8.0のバグだったようですね。
Unwind Segue not working in iOS 8

Apple has FIXED this bug in iOS 8.1

なので、この投稿はどうしても残る8.0への対応に使いたい場合にのみ参考にしてください。(ほぼ不要物ですね^^;

いろいろ調べてみた

どこで止まっているか

遷移元にあるはずのUnwind Segue用のメソッドが呼ばれない。storyboardでは確かに選べたはずなのに、なぜか呼び出されない。
twitterで検索してみたら「トップのViewControllerにしか戻れなくなってる」って話もあった。試しにより上位のController(UINavigationControllerとか)にそのメソッド置いてみたら、そっちはたしかに呼び出された。
https://twitter.com/tmasu/status/509617575391555584

そもそもprepareForSegueも呼ばれない。

調べてみたらprepareForSegueの前段階でcanPerformUnwindSegueActionというのがあって、こいつがきっとNOを返してるんだと思った。

canPerformUnwindSegueActionでYES返してみたらiOS7も死んだ

そこで、canPerformUnwindSegueActionでYESを返すようにしてみた。ところがなぜかこうすると今度はiOS7ですらprepareForSegueで[segue destinationViewController]が遷移先の自分自身になってしまい、正常に動かなくなってしまった・・・

たどり着いた解決法

iOS7:segueをそのまま使う
iOS8:canPerformUnwindSegueActionでsegueを止めて、手動でデータセット+dismiss

とりあえずこれで動くっぽい。修正量も少ないし、何より遷移元はなにかメソッドを公開しておくだけで良いので後々動作が正常化した場合にも奇麗に戻しやすい。

汚いサンプルコード

遷移先(閉じたい)ViewController.mで
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    // ここは普通の実装
    HOGEHOGEViewController *controller = [segue destinationViewController];
    [controller setXXX:XXX];
}

- (BOOL)canPerformUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender
{
    // iOS8未満ならsuperクラスのメソッドを叩く。(なぜか普通にreturn YES;すると正しく動かない。)
    if ([[[UIDevice currentDevice] systemVersion] compare:@"8.0" options:NSNumericSearch] == NSOrderedAscending){
        return [super canPerformUnwindSegueAction:action fromViewController:fromViewController withSender:sender];
    }else{
        // iOS8の場合なぜかUnwind Segueが動かないため、手動でデータをセットしてmodalを閉じてunwindの処理を呼び出す
        UIViewController *destination = (HOGEHOGEViewController *)[(UINavigationController *)[self presentingViewController] topViewController];
        if ([destination isKindOfClass:[HOGEHOGEViewController class]]) {
            [destination setXXX:XXX];
            [self dismissViewControllerAnimated:YES completion:^{
                // 予めUnwind Segue用のメソッドを.hに記載して公開しておく
                // もちろん本来は必要ない。同様の処理をする別メソッドに変えても良いだろう
                [destination unwindXXX:nil];
            }];
        }
        // NOを返してsegueによる本来の画面遷移はさせない
        return NO;
    }
}
遷移元(呼び出した方。データ受け取りたい方)ViewController.hで
/// 本来は必要ないUnwind Segue用のメソッドを外部から呼べるようにするための追記
- (IBAction)unwindXXX:nil:(UIStoryboardSegue*)segue;

最後に

たぶんiOS8のバグ・・・なのかな?
今後速やかに正常化される事を祈ります。

バグじゃネェよ!とか、これじゃ動かないよ!とか、情報持っている方教えてください。よろしくお願いしますm(__)m

来週まで放置して、ツッコミがなければたぶんいいんだなーってことでこの修正でいまのプロジェクトにプルリク出すかな。

気になったんだけど

Unwind Segueって「あんわいんど」なのか「あんうぃんど」なのか。