从父iOS访问容器视图控制器
在iOS6中,我注意到了新的容器视图,但不确定如何从包含视图访问它的控制器 情景: 我想从包含容器视图的视图控制器访问Alert view controller中的标签从父iOS访问容器视图控制器,ios,objective-c,swift,uiviewcontroller,uicontainerview,Ios,Objective C,Swift,Uiviewcontroller,Uicontainerview,在iOS6中,我注意到了新的容器视图,但不确定如何从包含视图访问它的控制器 情景: 我想从包含容器视图的视图控制器访问Alert view controller中的标签 它们之间有一个分段,我可以使用它吗?是的,您可以使用该分段访问子视图控制器(及其视图和子视图)。使用序列图像板中的属性检查器为序列提供标识符(例如警报视图\u embed)。然后让父视图控制器(包含容器视图的控制器)实现如下方法: - (void) prepareForSegue:(UIStoryboardSegue *)se
它们之间有一个分段,我可以使用它吗?是的,您可以使用该分段访问子视图控制器(及其视图和子视图)。使用序列图像板中的属性检查器为序列提供标识符(例如
警报视图\u 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...
}
}
只需使用
self.childViewControllers.lastObject
(假设您只有一个子对象,否则请使用objectAtIndex:
) self.childViewControllers
在您需要来自父级的控制时更具相关性。例如,如果子控制器是一个表视图,并且您希望通过点击按钮或父视图控制器上的任何其他事件强制重新加载它或更改属性,则可以通过访问ChildViewController的实例而不是通过prepareForSegue来实现。两者都有不同的应用方式。用于快速编程
你可以这样写
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
}
}
- (IBAction)showDetail:(UIButton *)sender {
DetailViewController *detailVc = [self.childViewControllers firstObject];
detailVc.lable.text = sender.titleLabel.text;
}
}
prepareforsgue
方法可以工作,但它依赖于segue标识符魔术字符串。也许有更好的办法
如果你知道你要找的VC的类别,你可以通过一个计算属性非常巧妙地做到这一点:
var camperVan: CamperVanViewController? {
return childViewControllers.flatMap({ $0 as? CamperVanViewController }).first
// This works because `flatMap` removes nils
}
var jobSummaryViewController: JobSummaryViewController {
get {
let ctrl = childViewControllers.first(where: { $0 is JobSummaryViewController })
return ctrl as! JobSummaryViewController
}
}
这依赖于childViewControllers
。虽然我同意依赖第一个类可能很脆弱,但命名您所寻找的类似乎很可靠。我使用的代码如下:
- (IBAction)showCartItems:(id)sender{
ListOfCartItemsViewController *listOfItemsVC=[self.storyboard instantiateViewControllerWithIdentifier:@"ListOfCartItemsViewController"];
[self addChildViewController:listOfItemsVC];
}
你可以这样写
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
}
}
- (IBAction)showDetail:(UIButton *)sender {
DetailViewController *detailVc = [self.childViewControllers firstObject];
detailVc.lable.text = sender.titleLabel.text;
}
}
使用计算属性更新Swift 3的答案:
var camperVan: CamperVanViewController? {
return childViewControllers.flatMap({ $0 as? CamperVanViewController }).first
// This works because `flatMap` removes nils
}
var jobSummaryViewController: JobSummaryViewController {
get {
let ctrl = childViewControllers.first(where: { $0 is JobSummaryViewController })
return ctrl as! JobSummaryViewController
}
}
这只会迭代子项列表,直到它到达第一个匹配项。如果有人正在寻找Swift 3.0 viewController1、viewController2等都可以访问
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")
}
在视图控制器的类型上使用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
}
}
有了泛型,你可以做一些甜蜜的事情。以下是对数组的扩展:
extension Array {
func firstMatchingType<Type>() -> Type? {
return first(where: { $0 is Type }) as? Type
}
}
我们不是在继续吗?我是否遗漏了什么…?是的,当第二个视图控制器成为第一个视图控制器的子视图时,会出现一个嵌入序列。prepareforsgue:在发生这种情况之前调用。您可以利用此机会将数据传递给子级,或存储对子级的引用以供以后使用。另请参见,当视图加载时,“第二个视图控制器成为第一个视图控制器的子视图”是否正确?这现在更有意义了,谢谢。我现在不支持我的项目,但稍后将进行测试。确切地说,它是在viewDidLoad之前调用的。当达到viewDidLoad时,父级和子级已经连接,父级中的[self ChildViewController]将返回所有子级控制器的数组(请参见下面rdelmar的回答)。我要对建议的解决方案添加一条警告:访问(子级)时要非常小心目标视图控制器的视图属性:在某些情况下,这会导致在那里调用其viewDidLoad。我建议事先设置任何所需的segue数据,以便viewDidLoad可以安全启动。@RaphaelOliveira,不一定。如果在一个视图中有多个ChildController,这将是首选方法。它允许您同时协调多个容器。prepareForSegue只引用它所作用的单子控制器实例。@Fydo,处理“prepare for segue”上的所有多个容器有什么问题吗?如果(恐怖!)您决定从情节提要切换或不使用seques等,那么您必须挖掘代码进行更改等。这是我通常的方法,但是现在它对我来说崩溃了,因为我正在访问
childViewControllers
“太快了”if语句中segueName之后的问号有什么用?“如果segueName?”返回childViewControllers.filter{$0是CamperVanViewController}。第一个
在一行中我做了childViewControllers.flatMap({$0作为?CamperVanViewController})。第一个
,我认为更好一点,这是一个非常好的解决方案,如果你想多次访问视图控制器,这是没有希望的-没有特别的原因,你可能只有一个特定的类。这正是标识符存在的原因。只要遵循标准公式。。。不要只过滤第一个元素。只需先使用(其中:)
ChildViewController。首先(其中:{$0是CamperVanViewController})
在这里详细解释,对于现代容器视图: