web-dev-qa-db-ja.com

ストーリーボード内で子View Controllerを親View Controllerにリンクする

Storyboardで子View ControllerをカスタムContainer View Controllerに関連付けることはできますか?

子View ControllerをTab View Controllerにリンクでき、1つのView ControllerをNavigation Controllerにリンクできます。

子VCを受け入れるには、コンテナVCに対して何を行う必要がありますか?

57

カレブとマットの答えのコンボのようなものとして、私はやった:

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"cpdc_check_embed"]) {
        self.checkVC = segue.destinationViewController;
    }
}

...ここで、checkVCはコンテナコントローラのプロパティです。

@property (weak,nonatomic) PXPCheckViewController * checkVC;

embedセグエのStoryboard IDを必要なものに設定するだけです(この場合、cpdc_check_embed):

check embed screen in Xcode

...そして-prepareForSegue:sender:の識別子を確認します。

まだアウトレットではありませんが、マット(IMHO)よりもクリーンで、カレブよりも具体的です。

enter image description here

75
Ben Mosher

ストーリーボードは、組み込みのコンテナービューコントローラーを非常にうまく処理し、セグエを子/ルートビューコントローラーに表示して、関係が明確に表示されるようにします。また、子と親のView Controllerがどのように異なるシーンに分離されるかは素晴らしいことです。

自分のプロジェクトでこの効果を達成したい場合、完璧ではないが非常に簡単なトリックがあります。この例では、「左」と「右」の2つのタブのみを持つTab Bar Controllerのように動作するコンテナビューコントローラがあるとします。親のビューコントローラーを表すシーンがあり、2つの別々のシーンが「左」の子ビューコントローラーと「右」の子ビューコントローラーの両方を表します。

不可能ではありますが、コンテナビューコントローラから異なるシーンの子にIBOutletsを作成し、コンテナビューコントローラが表示されたら、親/子関係をルールは IViewController documentation を説明しました。 「左」および「右」の子View Controllerへの参照があれば、問題なく関係を設定できます。

この参照問題の標準的な解決策は、Objectアウトレットをコンテナビューコントローラのシーンにドラッグし、そのクラスタイプを子ビューコントローラクラスのインスタンスとして指定することにより、子ビューコントローラへの参照を作成することです。

ただし、Appleのビルトインコンテナのような異なるシーンで子供を分離するために、別のトリックを使用します。まず、コンテナクラスContainerViewControllerで次のプロパティが宣言されているとします。

@property (nonatomic, strong, readwrite) UIViewController *leftViewController;
@property (nonatomic, strong, readwrite) UIViewController *rightViewController;

ストーリーボードで、「左」View Controllerを表すシーンを選択します。属性インスペクターで、View Controllerのidentifierプロパティを"cvc_leftViewController"に設定します(「cvc_」はContainerViewControllerを指しますが、実際には識別子は任意です)。右側のView Controllerのシーンにも同じことを行い、その識別子を"cvc_rightViewController"に設定します。

次のコードをContainerViewControllerviewDidLoadメソッドに挿入します。

if (self.storyboard) {
    _leftViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"cvc_leftViewController"];
    _rightViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"cvc_rightViewController"];
}

ContainerViewControllerがストーリーボードからロードされると、それぞれのシーンから「左」および「右」のビューコントローラーを取得し、そのプロパティを介してそれらへの参照を設定します。子View Controllerインスタンスを制御できるようになったので、親/子関係を好きなように設定できます。適切に行う方法については、 IViewController documentation を参照してください。

このトリックは完璧ではなく、多くの注意事項がありますが、注意すればプロジェクトでうまく機能させることができます。

編集:これは完全に不要で何の意味もありませんが、本当に本当にストーリーボードにコンテナから子ビューへの接続を表示させたい場合Appleのビルトインコンテナと同様に、上記の方法を使用して、コンテナシーンと子シーンの間にセグエを直接設定し、それらのセグエを実行しないでください。これですべてが正常に動作し、きれいになります。

15
Matt

Storyboardで子View ControllerをカスタムContainer View Controllerに関連付けることはできますか?

ここで求めているのは、あるシーンのView Controllerを別のシーンのView Controllerのコンセントに接続する方法だと思います。おそらく、ストーリーボードの機械には、ストーリーボードのすべてのシーンが同時にロードされるわけではないため、それが可能だとは思わない。

あるシーンから次のシーンにセグエするときに、あるView Controllerから別のView Controllerに情報を渡したいので、おそらくこれを求めているでしょう。ストーリーボードで作業しているときにこれを行う方法は、-prepareForSegue:sender:セグエの影響を受ける一方または両方のView Controller。 segueパラメーターで提供されるUIStoryboardSegueオブジェクトには、sourceViewControllerおよびdestinationViewControllerプロパティ、およびidentifierプロパティがあります。これらのプロパティを使用して、View Controller間でデータを転送しようとしているセグエを識別できます。

Ray Wenderlichのブログには、ストーリーボードを使用するのに役立つ素敵な2部構成のチュートリアルがあります。

  • パート1 ストーリーボードプロジェクトの設定、シーンの追加、セグエの作成について説明します。

  • パート2 セグエを使用してシーン間を遷移することを処理します(上記のprepareForSequeメソッドを含む)。

iOS 5では、同じシーンで複数のView Controllerをアクティブにすることができます(ただし、1つは引き続き担当する必要があります)。そのため、ストーリーボードの1つのシーンに複数のコントローラーが含まれる場合があります。アウトレットを使用してこれらのコントローラーを相互に接続し、IBで行ったのと同じ方法でこれらの接続を構成できます。同じシーンでコントローラーから別のコントローラーにドラッグします。通常のコンセントリストが開いて、接続するコンセントを選択できます。

7
Caleb

あるシーンで複数のコントローラーを使用するための鍵は、IBのオブジェクトリストにある不思議なオブジェクトを使用して、他のビューコントローラーを表し、そのコンセントを接続することです。

この回答 iOS 5でストーリーボードを使用してカスタムView Controllerコンテナを作成する方法 が役立つと思います。答えは、非常に役立つ実用的なサンプルアプリも提供します。

5
Matt__C

@Benの(それ以外の場合は合理的な)答えの問題は、ネストの1つのレベルでのみ機能することです。さらに、以降のすべてのVC=をカスタマイズして、prepareForSegueにネストされたView Controllerを保存する必要があります。

これを解決するために、NSObjectベースのインデックスの探索に多くの時間を費やしました。これをストーリーボードに追加し、シーンにバインドすると、グローバルインデックスにその親VCタイプとrestoreIdに基づきます。これは機能します/機能しますが、最終的には手間がかかり、視覚的にバインドし、プログラムで検索するという2段階のプロセスが必要です。

私にとって、最も簡単で最も一般的な解決策は、View Controllerの階層をゆっくりと下降させることです

私の簡単なテストプロジェクトでは、viewDidLoadに次の行を追加しました。

_    self.left.data = [
        "Zombie ipsum reversus ab viral inferno, nam rick grimes malum cerebro.",
        "De carne lumbering animata corpora quaeritis." ]
_

ここで、leftは次のように定義されます。

_lazy var left:CollectionViewController = { [unowned self] in
    return self.childViewControllerWithId("Left") as! CollectionViewController }()
_

childViewControllerWithIdは次のように定義されます:

_extension UIViewController {
    func childViewControllerWithId(rid:String) -> UIViewController? {

        // check immediate child controllers
        for vc in self.childViewControllers as! [UIViewController] {
            if vc.restorationIdentifier == rid { return vc }
        }

        // check nested controllers
        for vc in self.childViewControllers as! [UIViewController] {
            if let vc = vc.childViewControllerWithId(rid) {
                return vc
            }
        }

        assert(false, "check your assumptions")
        return nil
    }
}
_

必要に応じて、タイプに基づいて他のfindバリアントを実行できることに注意してください。また、上記の場合、ストーリーボードファイルで復元IDを定義する必要があることに注意してください。同じView Controllerのインスタンスを繰り返していない場合は、typeを使用する方が簡単です。

願わくば明らかなことを述べるために、prepareForSegueを実装する必要も、遅延読み込みを使用する必要もありません。find(...)を呼び出すだけです。

4
Chris Conover