将子视图控制器添加到当前视图控制器

21

我正在尝试使用下面的代码将一个子视图控制器以编程方式添加到从故事板创建的当前视图控制器中:

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle: nil];
LogInTutorialViewController *lvc = [[LogInTutorialViewController alloc] init];
lvc = (LogInTutorialViewController *)[storyboard instantiateViewControllerWithIdentifier:@"LogInTutorialViewControllerID"];
[self displayContentController:lvc];

- (void) displayContentController: (LogInTutorialViewController*) content;
{

    //add as childViewController
    [self addChildViewController:content];
    [content didMoveToParentViewController:self];
    [content.view setFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)];
    [self.view addSubview:content.view];

}

看起来至少在模拟器上显示了视图,但是在控制台中我得到了很多错误:

 <Error>: CGContextSaveGState: invalid context 0x0. This is a serious error. This application, or a library it uses, is using an invalid context  and is thereby contributing to an overall degradation of system stability and reliability. This notice is a courtesy: please fix this problem. It will become a fatal error in an upcoming update.

还有相同的描述但是不同的错误:

CGContextSetLineWidth、CGContextSetLineJoin、CGContextSetLineCap、CGContextSetMiterLimit、CGContextSetFlatness、CGContextAddPath、CGContextDrawPath、CGContextRestoreGState,所有这些错误都被记录了两次。

有人知道我做错了什么吗?

我也读了一些帖子,在一些帖子中建议在传递数据之前分配和初始化视图控制器,我也尝试过了,但没有任何运气。


你不需要这一行代码 LogInTutorialViewController *lvc = [[LogInTutorialViewController alloc] init];,因为你是从故事板实例化的。至于错误,请查看下面的答案,我认为你的顺序不正确。 - GoodSp33d
是的,我知道但我只是按照一篇帖子建议尝试了一下。 - Laur Stefan
4个回答

29

为什么不尝试使用这段代码来添加视图,我认为这个简单易用。

self.loginView = [self.storyboard instantiateViewControllerWithIdentifier:@"LOGIN"];
[self addChildViewController:self.loginView];
[self.loginView.view setFrame:CGRectMake(0.0f, 0.0f, self.contentView.frame.size.width, self.contentView.frame.size.height)];
[self.contentView addSubview:self.loginView.view];
[self.loginView didMoveToParentViewController:self]; 

想了解更多信息,请查看此链接


让我困惑的部分是必须明确将原点设置为零。这个[self.loginView.view setFrame:self.contentView.frame]不能正确地在我的视图中设置它,因为我的contentView有一个UINavigationBar,所以contentView.frame.origin.y是64,从而将self.loginView向下推。谢谢! - Josh Valdivieso
这个回答有15个以上的赞同票,而被接受的回答只有13个赞同票。因此我认为这个回答应该是被接受的回答。 :) - Shahzaib Maqbool

8

Swift实现解决方案(本文所述为Swift 4):

//load the view controller and add as child
storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
loginVC = storyboard.instantiateViewController(withIdentifier: "LOGIN")
addChildViewController(loginVC)

//make sure that the child view controller's view is the right size
loginVC.view.frame = contentView.bounds
contentView.addSubview(loginVC.view)

//you must call this at the end per Apple's documentation
loginVC.didMove(toParentViewController: self)

注:

  • Storyboard名称为“Main”。
  • 故事板中视图控制器的标识符命名为“LOGIN”。
  • 使用故事板创建加载视图控制器,但同样可以通过编程实现。只需确保在尝试访问视图的框架之前将视图加载到内存中,否则会崩溃(例如以模态方式呈现视图控制器)。

1
谢谢,这个解决方案在Swift 4中非常好用。 - user2737948

7

didMoveToParentViewController必须是最后一个执行的方法。


当我到达以下代码行时,所有错误都会被记录:[content.view setFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)]; - Laur Stefan
先尝试添加子视图,然后再设置其框架。 - kirander
我现在怀疑你的问题与子视图控制器视图内部有关,而不是父子关系。 - kirander
尝试了下面的代码: [self addChildViewController:content]; [self.view addSubview:content.view]; [content didMoveToParentViewController:self]; [content.view setFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)]; 但是我仍然在didMoveToParent行上得到了错误日志。 - Laur Stefan
LogInTutorialViewController视图里面有什么?看起来好像有一些绘图问题。 - kirander
几个UIImageView、UITextField和UIButton - Laur Stefan

0

你也可以创建一个扩展来添加和移除UIViewController

extension UIViewController {
    func addChildViewControllerWithView(_ childViewController: UIViewController, toView view: UIView? = nil) {
        let view: UIView = view ?? self.view
        childViewController.removeFromParent()
        childViewController.willMove(toParent: self)
        addChild(childViewController)
        childViewController.didMove(toParent: self)
        childViewController.view.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(childViewController.view)
        view.addConstraints([
            NSLayoutConstraint(item: childViewController.view!, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1, constant: 0),
            NSLayoutConstraint(item: childViewController.view!, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0),
            NSLayoutConstraint(item: childViewController.view!, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 0),
            NSLayoutConstraint(item: childViewController.view!, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: 0)
        ])
        view.layoutIfNeeded()
    }

    func removeChildViewController(_ childViewController: UIViewController) {
        childViewController.removeFromParent()
        childViewController.willMove(toParent: nil)
        childViewController.removeFromParent()
        childViewController.didMove(toParent: nil)
        childViewController.view.removeFromSuperview()
        view.layoutIfNeeded()
    }
}

无论何时您想在`viewDidLoad()`方法中添加 `UIViewController`,都需要调用 `addChildViewControllerWithView(someVC)`。

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