親iOSからContainer View Controllerにアクセスする


203

iOS6では新しいコンテナービューに気づきましたが、コンテナービューからコントローラーにアクセスする方法がよくわかりません。

シナリオ:

例

コンテナービューを格納しているビューコントローラーからアラートビューコントローラーのラベルにアクセスしたい。

それらの間にセグエがあります、それを使用できますか?


ここで、最新のコンテナービューについて詳しく説明します。stackoverflow.com
a

回答:


362

はい、セグエを使用して子ビューコントローラー(およびそのビューとサブビュー)にアクセスできます。alertview_embedストーリーボードの属性インスペクタを使用して、セグエに識別子(など)を付けます。次に、親ビューコントローラー(コンテナービューを格納するコントローラー)に次のようなメソッドを実装させます。

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
   NSString * segueName = segue.identifier;
   if ([segueName isEqualToString: @"alertview_embed"]) {
       AlertViewController * childViewController = (AlertViewController *) [segue destinationViewController];
       AlertView * alertView = childViewController.view;
       // do something with the AlertView's subviews here...
   }
}

1
私たちは偽装していませんか?ここに何か欠けていますか...?
アダムウェイト

25
はい、2番目のビューコントローラーが最初のビューコントローラーの子になったときに発生する埋め込みセグエがあります。prepareForSegue:これが発生する直前に呼び出されます。この機会を使用して、子にデータを渡したり、後で使用するために子への参照を保存したりできます。developer.apple.com/library/ios/#documentation/uikit/reference/…
Peter E

1
そうですね、ビューが読み込まれると、「2番目のビューコントローラーは最初のビューコントローラーの子になりますか?」これは、今ではもっと理にかなっています、ありがとう。私は現在プロジェクトに参加していませんが、後でテストします
アダムウェイト

1
正確には、viewDidLoadの前に呼び出されます。viewDidLoadに到達するまでに、親と子は接続されており、親の[self childViewControllers]はすべての子コントローラーの配列を返します(以下のrdelmarの回答を参照)。
Peter E

2
提案されたソリューションに注意点を1つ追加します:(子)宛先ビューコントローラーのビュープロパティにアクセスするときは十分に注意してください:状況によっては、そこにそのviewDidLoadが呼び出され、その後、必要なセグエデータを事前にセットアップすることをお勧めしますこれにより、viewDidLoadを安全に起動できます。
AlwaysLearning

56

あなたはそれで簡単にそれを行うことができますself.childViewControllers.lastObject(あなたはあなたが1人の子しか持っていないと仮定し、そうでなければを使用しますobjectAtIndex:


1
@RaphaelOliveira、必ずしもそうではありません。1つのビューに複数のchildControllerがある場合、これが推奨されるアプローチです。複数のコンテナを同時に調整できます。prepareForSegueは、動作している単一の子コントローラインスタンスへの参照のみを持っています。
Fydo 2014年

2
@Fydo、そして「セグエの準備」で複数のコンテナーのすべてを処理する際の問題は何ですか?
LayGonzález14年

1
どのような場合(!恐怖)あなたはストーリーボードからスイッチに決めるかは、その後、あなたは、コードのメーク変化など掘るする必要があるなど、sequesを使用する
トム・アンデルセン

2
これは私のいつものアプローチですが、私はアクセスしておりますので、それが今の私のためにクラッシュしchildViewControllers、「早すぎる」を
Mazyod

24

Swiftプログラミング用

あなたはこのように書くことができます

var containerViewController: ExampleViewController?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    // you can set this name in 'segue.embed' in storyboard
    if segue.identifier == "checkinPopupIdentifierInStoryBoard" {
        let connectContainerViewController = segue.destinationViewController as ExampleViewController
        containerViewController = connectContainerViewController
    }
}

ifステートメントのsegueNameの後の疑問符の使用は何ですか?「segueNameの場合」
牧師の

19

このprepareForSegueアプローチは機能しますが、セグエ識別子のマジックストリングに依存しています。たぶんもっと良い方法があるでしょう。

目的のVCのクラスがわかっている場合は、計算されたプロパティを使用してこれを非常にきちんと行うことができます。

var camperVan: CamperVanViewController? {
  return childViewControllers.flatMap({ $0 as? CamperVanViewController }).first
  // This works because `flatMap` removes nils
}

これはに依存していchildViewControllersます。最初のものに頼るのは壊れやすいかもしれないと私は同意しますが、求めるクラスに名前を付けると、これは非常に堅実に見えます。


3
return childViewControllers.filter { $0 is CamperVanViewController }.firstワンライナー
アダムウェイト2016年

1
私はそれをやっchildViewControllers.flatMap({ $0 as? CamperVanViewController }).firstてきて、キャストしてネイルを取り除くので、少し良いと思っています。
SimplGy 2016年

アクセスしたい場合、これは本当に良い解決策であるビューコントローラに複数の時間
ガブリエルGoncalvesの

これは絶望的です。その特定のクラスを1つだけ持つ特別な理由はありません。これこそが、識別子が存在する理由です。標準の式に従ってください... stackoverflow.com/a/23403979/294884
Fattie

最初の要素を取得するためだけにフィルタリングしないでください。ただ使うfirst(where:)childViewControllers.first(where: { $0 is CamperVanViewController })
アレクサンダー-モニカの復活2017年

9

計算されたプロパティを使用したSwift 3の更新された回答:

var jobSummaryViewController: JobSummaryViewController {
    get {
        let ctrl = childViewControllers.first(where: { $0 is JobSummaryViewController })
        return ctrl as! JobSummaryViewController
    }
}

これは、最初の一致に達するまで子のリストを反復するだけです。


8

self.childViewControllers 親からの制御が必要な場合に、より適切です。たとえば、子コントローラーがテーブルビューであり、それを強制的に再読み込みしたり、ボタンタップや親ビューコントローラーの他のイベントを介してプロパティを変更したりする場合は、prepareForSegueではなく、ChildViewControllerのインスタンスにアクセスして行うことができます。どちらにも、さまざまな方法でアプリケーションがあります。


2

ビューコントローラのタイプにSwiftのswitchステートメントを使用する別の方法があります:

override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
  switch segue.destination
  {
    case let aViewController as AViewController:
      self.aViewController = aViewController
    case let bViewController as BViewController:
      self.bViewController = bViewController
    default:
      return
  }
}

1

私は次のようなコードを使用します:

- (IBAction)showCartItems:(id)sender{ 
  ListOfCartItemsViewController *listOfItemsVC=[self.storyboard instantiateViewControllerWithIdentifier:@"ListOfCartItemsViewController"];
  [self addChildViewController:listOfItemsVC];
 }

1

誰かがSwift 3.0を探している場合、

これでviewController1viewController2などにアクセスできるようになります。

let viewController1 : OneViewController!
let viewController2 : TwoViewController!

// Safety handling of optional String
if let identifier: String = segue.identifier {

    switch identifier {

    case "segueName1":
        viewController1 = segue.destination as! OneViewController
        break

    case "segueName2":
        viewController2 = segue.destination as! TwoViewController
        break

    // ... More cases can be inserted here ...

    default:
        // A new segue is added in the storyboard but not yet including in this switch
        print("A case missing for segue identifier: \(identifier)")
        break
    }

} else {
    // Either the segue or the identifier is inaccessible 
    print("WARNING: identifier in segue is not accessible")
}

1

ジェネリックを使用すると、いくつかの甘いことができます。以下は配列の拡張です:

extension Array {
    func firstMatchingType<Type>() -> Type? {
        return first(where: { $0 is Type }) as? Type
    }
}

次に、viewControllerでこれを行うことができます。

var viewControllerInContainer: YourViewControllerClass? {
    return childViewControllers.firstMatchingType()!
}

0

あなたはこのように書くことができます

- (IBAction)showDetail:(UIButton *)sender {  
            DetailViewController *detailVc = [self.childViewControllers firstObject];  
        detailVc.lable.text = sender.titleLabel.text;  
    }  
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.