从父级iOS访问Container View Controller


203

在iOS6中,我注意到了新的Container View,但不确定如何从包含的视图访问它的控制器。

场景:

例

我想从容纳容器视图的视图控制器访问“警报”视图控制器中的标签。

他们之间有关系,我可以使用吗?


对于现代容器视图,此处
对此有

Answers:


362

是的,您可以使用segue来访问子视图控制器(及其视图和子视图)。alertview_embed使用情节提要中的“属性”检查器为segue赋予一个标识符(例如)。然后让父视图控制器(一个容纳容器视图的控制器)实现如下方法:

- (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
是的,当第二个视图控制器成为第一个视图控制器的子代时,会发生嵌入事件。prepareForSegue:在发生这种情况之前被调用。您可以利用此机会将数据传递给孩子,或存储对孩子的引用以供以后使用。又见developer.apple.com/library/ios/#documentation/uikit/reference/...
彼得Ë

1
是的,加载视图时,“第二个视图控制器成为第一个视图控制器的子代”吗?谢谢,这现在变得更有意义了。我现在不在我的项目中,但稍后将进行测试
Adam Waite 2012年

1
确切地说,它在viewDidLoad之前调用。到viewDidLoad到达时,父级和子级已连接,并且父级中的[self childViewControllers]将返回所有子级控制器的数组(请参见下面的rdelmar答案)。
Peter E

2
我会在建议的解决方案中添加一个警告:在访问(子)目标视图控制器的view属性时要非常小心:在某些情况下,这将导致在此处调用其viewDidLoad。我建议事先设置任何需要的segue数据以便viewDidLoad可以安全触发。
AlwaysLearning 2012年

56

您可以轻松地做到这一点self.childViewControllers.lastObject(假设您只有一个孩子,否则使用objectAtIndex:)。


1
@RaphaelOliveira,不一定。如果在单个视图中有多个childController,则这是首选方法。它使您可以一次协调多个容器。prepareForSegue仅引用其作用于的单个子控制器实例。
Fydo 2014年

2
@Fydo,如何处理“准备封存”上的所有多个容器有什么问题?
LayGonzález2014年

1
如果(恐怖!)您决定从情节提要切换或不使用定语等,那么该怎么办?您必须挖掘代码进行更改等。–
Tom Andersen

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?”
牧师

18

prepareForSegue方法有效,但是它依赖于segue标识符魔术字符串。也许有更好的方法。

如果知道要使用的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在一个班轮中
亚当·怀特

1
从那以后childViewControllers.flatMap({ $0 as? CamperVanViewController }).first,我做得更好,因为它可以转换并摆脱任何钉子。
SimplGy

如果您想多次访问该视图控制器,那么这是一个非常好的解决方案
Gabriel Goncalves

这是没有希望的-没有特别的原因,您可能只有一个特定的班级。这就是为什么存在标识符的原因。只需遵循标准公式即可... stackoverflow.com/a/23403979/294884
Fattie

不要只过滤第一个元素。只是使用first(where:)childViewControllers.first(where: { $0 is CamperVanViewController })
亚历山大-恢复莫妮卡

9

使用计算的属性更新的Swift 3答案:

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

这只会迭代子级列表,直到到达第一个匹配项。


8

self.childViewControllers 当您需要父母的控制权时,它更为相关。例如,如果子控制器是一个表视图,并且您想通过按钮点击或父视图控制器上的任何其他事件来强制重新加载它或更改属性,则可以通过访问ChildViewController的实例而不是prepareForSegue来实现。两者都有不同的应用方式。


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

使用泛型,您可以做一些甜蜜的事情。这是Array的扩展:

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;  
    }  
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.