0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Swift】IBOutlet接続したプロパティのdidset内に直接遷移を実装すると画面が表示されない理由

Last updated at Posted at 2021-05-08

結論

プロパティオブザーバーを利用して画面遷移を行う際は、イベントコントロールで正しく行えるようにする必要がある。直接遷移をさせずに、addTargetメソッドなどを用いる。

理由

IBOutletが接続されているViewに遷移されると、イニシャライズされて変数にIB部品のインスタンスが入ります。その際にプロパティオブザーバーとしてのdidsetが実行されるが、このdidset内にNavigationControllerによる別の画面への遷移を直接実装してしまうと、画面が遷移されてきたにも関わらず、画面を遷移するという状態になるため、意図している形には正しく遷移されない、もしくはエラーが生じる。

このエラーはaddTargetメソッドでイベントコントロールを図ることで解決できる。実行のタイミングを.touchUpInside、つまり枠内をタッチするイベントのような形にすることで、didset内に記述されていてもインスタンス生成時に実行されなくなる。

そして、実際の遷移はaddTargetメソッドでセレクタをし、objc属性のfunctionとしてNavigationControllerによる遷移を実装するということになる。

didset内に遷移を直接実装した場合


final class ViewController: UIViewController {

    @IBOutlet private weak var reStartButton: UIButton! {
      didSet {              
        RootViewContorller.root.reStart()
      }
    }
}

//RootViewController.swift
final class RootViewContorller {
    
    static let root: RootViewContorller = .init()
    private init(){}

    func reStart(){
        UserDefaults.standard.isLogined = [true, false].randomElement()!
        showInitialView(window: window)
    }
}

この場合は次のようなDebugが表示される

2021-05-08 10:33:25.508263+0900 RootViewControllerTest[13610:776213] Unbalanced calls to begin/end appearance transitions for < RootViewControllerTest.SecondViewContorller: 0x7fd8bb524200 >.

メッセージにあるように、遷移画面を起動/終了時の呼び出しが不安定ということ。遷移しても、即座に別の画面に遷移するような状態にあり、結果的に画面が表示されないためこのようなメッセージが表示されている。

didset内でaddTargetを利用した場合


final class ViewController: UIViewController {

    @IBOutlet private weak var reStartButton: UIButton! {
      didSet {              
        reStartButton.addTarget(self, action: #selector(tapReStartButton(_:)), for: .touchUpInside)
      }
    }

    @objc private func tapReStartButton(_ sender: UIResponder) {
      RootViewContorller.root.reStart()
    }
}

//RootViewController.swift
final class RootViewContorller {
    
    static let root: RootViewContorller = .init()
    private init(){}

    func reStart(){
        UserDefaults.standard.isLogined = [true, false].randomElement()!
        showInitialView(window: window)
    }
}

この記述だと、プロパティオブザーバーとしてdidset内はインスタンス生成に実行されるが、addTargetメソッドは.touchUpInsideというIBパーツの枠内をタッチした場合に実行されるような形になっているため、遷移は実行さレナい。addTargetでは遷移の中身はactionパラメータのセレクターとして指定され、objc属性以下に書かれる。

addTargetメソッドは別のQiitaにまとめています。

まとめ

プロパティオブザーバーを利用して画面遷移を行う際は、イベントコントロールで正しく行えるようにする必要がある。直接遷移をさせずに、addTargetメソッドなどを用いる。

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?