从容器视图中的视图控制器内部,如何访问包含容器的视图控制器?

52

这很棘手,但我有一个包含容器视图(我正在使用故事板)的视图控制器(vc1)。在该容器视图中有一个导航控制器和一个根视图控制器(vc2)。

如何从vc2内部访问vc1?

或者,如何将vc1传递给vc2?(请注意,我正在使用故事板。)


完全解释在这里!! https://dev59.com/KGAg5IYBdhLWcg3wm7_6#23403979 - Fattie
7个回答

51

您可以在Vc1中使用prepareForSegue方法,因为当ContainerViewController成为子级时发生嵌入式segue。您可以将self作为obj传递或存储对子级的引用以供将来使用。

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    NSString * segueName = segue.identifier;
    if ([segueName isEqualToString: @"embedseg"]) {
        UINavigationController * navViewController = (UINavigationController *) [segue destinationViewController];
        Vc2 *detail=[navViewController viewControllers][0];
        Vc2.parentController=self;
    }
}

编辑:代码修复了一个小问题


@Firula,这行代码 UINavigationController * navViewController = (UINavigationController *) [segue destinationViewController]; 并不会初始化任何东西,它只是一个指向已经初始化的控制器的指针。 - Bonnie
1
首先,在故事板中为连接容器视图和其第一个视图控制器的segue(链接)命名。我将其命名为“toContainer”。然后在包含容器视图的视图控制器中添加此方法
  • (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([segue.identifier isEqualToString: @"toContainer"]) { UINavigationController *navViewController = (UINavigationController *) [segue destinationViewController]; UIViewController *vc2 = [navViewController viewControllers][0]; } } vc2是我想要引用的控制器。
- Mark Bridges
注意:destinationViewController已经是我的容器嵌入目标UIViewController。 - BananaAcid
这对我非常有帮助,谢谢。还有一个相关的问题:为什么我的子视图控制器的ViewDidLoad方法会在父视图控制器之前被调用?我在容器的设置上搞错了什么吗? - Chucky
但是如果我从视图控制器vc2有另外两个segue,那么我该如何从它们中访问vc1? - Jenita _Alice4Real
显示剩余2条评论

18

要在子视图控制器中访问父视图控制器,您必须重写didMoveToParentViewController

- (void)didMoveToParentViewController:(UIViewController *)parent {
    [super didMoveToParentViewController:parent];

    //Use parent
}

在Xcode中,通过Command+单击此方法可以获取更多信息:

这两个方法是为容器子类调用的公共方法,用于在子控制器之间进行转换。如果被重写,必须确保调用super以执行父类操作。在这两个方法中,当子级从其父级中移除时,父级参数为nil;否则等于新的父视图控制器。

addChildViewController:将在添加子级之前调用[child willMoveToParentViewController:self],但它不会调用didMoveToParentViewController:。预期容器视图控制器子类在完成到新子级的过渡后或在没有过渡的情况下立即在addChildViewController:之后调用该方法。同样,removeFromParentViewController:在删除子级之前不会调用[self willMoveToParentViewController:nil]。这也是容器子类的责任。容器子类通常会定义一个方法,首先调用addChildViewController:,然后执行一次过渡,将新子级的视图添加到其父级的视图层次结构中,最后调用didMoveToParentViewController:。同样地,子类通常会定义一个方法,通过首先调用[child willMoveToParentViewController:nil]来以相反的方式删除子级。


10

你可以使用与Bonnie相同的方法来进行委托。以下是如何操作:

在您的containerViews ViewController中:

class ContainerViewViewController: UIViewController {
   //viewDidLoad and other methods

   var delegate: ContainerViewControllerProtocol?

   @IBAction func someButtonTouched(sender: AnyObject) { 
    self.delegate?.someDelegateMethod() //call this anywhere
   }

}

protocol ContainerViewControllerProtocol {
    func someDelegateMethod()
}

在您的父视图控制器中:

class ParentViewController: UIViewController, ContainerViewControllerProtocol {
   //viewDidLoad and other methods

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "filterEmbedSegue" {
            let containerViewViewController = segue.destinationViewController as ContainerViewViewController

            containerViewViewController.delegate = self
        }
    }

    func someDelegateMethod() {
        //do your thing
    }
}

10

使用属性parentViewController时,作为self.parentViewController


8
在我的情况下,这并没有起作用,因为self.parentViewController为空。我在storyboard中添加了containerView,然后将关联的视图控制器类设置为我的特定子类。我期望parentViewController被设置为包含容器视图的VC,但是这并没有自动设置。我不得不使用上面提到的-prepareForSegue解决方案。 - Craig Watkinson
在 Swift 3 中,属性是 "self.parent"。 - Travis M.

2

谢谢Bonnie告诉我该怎么做。确实,准备segue的方法是正确的方法。

我在这里只是澄清代码和步骤。

首先,在故事板中将容器视图与其第一个视图控制器连接的segue(链接)命名。我把我的命名为“toContainer”。

然后,在包含容器视图的视图控制器中添加此方法。

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString: @"toContainer"]) {
        UINavigationController *navViewController = (UINavigationController *) [segue destinationViewController];
        UIViewController *vc2 = [navViewController viewControllers][0];
    }
}

所以vc2是我想要引用的控制器。

这对我有用,如果你的第一个视图控制器不是导航控制器,那么在prepareForSegue中你的方法会稍有不同。


3
你本可以将我的答案标记为正确,并在评论中添加你的解释以获得更好的澄清。 - Bonnie
马克,你需要将 Firula 或 Bonnie 的答案标记为正确的,并且可以编辑你的问题并将这个有用的信息放入问题中。 - Fattie

1

1)在VC2上公开一个属性,用于传递对VC1的引用

//VC2.h
#import "VC1.h"

@interface VC2 : NSObject
@property (strong, nonatomic) VC1 *parent;
@end

2)在VC1中,设置segue的标识符为"ToVC2"后,在prepareForSegue方法中将self传递到VC2中公开的属性中。然后像这样传递引用:

//VC1.m
@implementation VC1 
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:@"ToVC2"]) {
    VC2 *vc2 = segue.destinationViewController;
    vc2.parent = self;
}
}

0

Swift - 另一种方法是在父UIViewController(vc1)中创建对子/子视图UIViewController(vc2)的引用,并在vc2中创建对vc1的引用。 在父级(vc1)viewDidLoad() 中分配引用,示例如下。

父UIViewController vc1:

      class vc1: UIViewController {

          @IBOutlet weak var parentLabel: UILabel!
          var childVc2: vc2?;

           overide func viewDidLoad() {
               super.viewDidLoad();
               // Use childViewControllers[0] without type/class verification only 
               // when adding a single child UIViewController 
               childVc2 = self.childViewControllers[0] as? vc2;
               childVc2?.parentVc1 = self
           }
      }

子UIViewController vc2:

      class vc2: UIViewCortoller {
          var parentVc1: vc1?;

          // At this point child and parent UIViewControllers are loaded and 
          // child views can be accessed
          override func viewWillAppear(_ animated: Bool) {
             parentVc1?.parentLabel.text = "Parent label can be edited from child";
          }
      } 

在Storyboard中,记得在Identity Inspector中将父级UIViewContoller类设置为vc1,将子级UIViewContoller类设置为vc2。从vc1 UIViewController的Container View中Ctrl+拖动到vc2 UIViewController,并选择嵌入。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接