8

我有两个视图控制器,父级和子级。

因此,在该viewDidLoad方法中,我执行以下操作:

ChildViewController* childViewController = [[ChildViewController alloc] init];

[self addChildViewController:childViewController];

// ChildViewController sets his own constraints in viewDidLoad
[self.view addSubview:childViewController.view];

[childViewController didMoveToParentViewController:self];

//
// setup constraints to expand childViewController.view to 
// fill the size of parent view controller
//

所以基本上发生的事情是updateViewConstraints在应用父控制器约束之前在 ChildViewController 上调用的,所以实际上和我在自定义方法中self.view.frame == CGRectZero指定的完全一样。loadViewChildViewController

translatesAutoresizingMaskIntoConstraints全部设置NO为所有视图。

在这种情况下设置约束的正确方法是什么,以便ChildViewController在父级之后更新他的约束?

来自两个控制器的当前日志非常令人沮丧,我不明白如何在 viewWillLayoutSubviews 之前调用 updateViewConstraints:

App[47933:c07] ChildViewController::updateViewConstraints. RECT: {{0, 0}, {0, 0}}
App[47933:c07] ParentViewController::updateViewConstraints
App[47933:c07] ChildViewController:viewWillLayoutSubviews. RECT: {{0, 0}, {984, 454}}
App[47933:c07] ChildViewController:viewDidLayoutSubviews. RECT: {{0, 0}, {984, 454}}
4

5 回答 5

7

您可以在调用 addSubview: 后立即添加约束,不要忘记设置translatesAutoresizingMaskIntoConstraintsfalse

这是添加和隐藏带有约束的子视图控制器的代码片段(受苹果指南的启发)

展示

private func display(contentController content : UIViewController)
    {
        self.addChildViewController(content)
        content.view.translatesAutoresizingMaskIntoConstraints = false
        self.containerView.addSubview(content.view)
        content.didMove(toParentViewController: self)

        containerView.addConstraints([
            NSLayoutConstraint(item: content.view, attribute: .top, relatedBy: .equal, toItem: containerView, attribute: .top, multiplier: 1, constant: 0),
            NSLayoutConstraint(item: content.view, attribute: .bottom, relatedBy: .equal, toItem: containerView, attribute: .bottom, multiplier: 1, constant: 0),
            NSLayoutConstraint(item: content.view, attribute: .leading, relatedBy: .equal, toItem: containerView, attribute: .leading, multiplier: 1, constant: 0),
            NSLayoutConstraint(item: content.view, attribute: .trailing, relatedBy: .equal, toItem: containerView, attribute: .trailing, multiplier: 1, constant: 0)
            ])
    }

隐藏

private func hide(contentController content : UIViewController)
    {
        content.willMove(toParentViewController: nil)
        content.view.removeFromSuperview()
        content.removeFromParentViewController()
    }
于 2018-03-14T09:37:52.717 回答
5

我有类似的情况,我将子控制器添加到可见控制器(弹出窗口)。

我在界面生成器中定义了子视图控制器。它的 viewDidLoad 方法只是调用 setTranslatesAutoresizingMaskIntoConstraints:NO

然后我在子控制器上定义这个方法,它接受一个 UIViewController 参数,它是父控制器。此方法将自身添加到给定的父视图控制器并定义自己的约束:

- (void) addPopupToController:(UIViewController *)parent {
    UIView *view = [self view];
    [self willMoveToParentViewController:parent];
    [parent addChildViewController:self];
    [parent.view addSubview:view];
    [self didMoveToParentViewController:parent];

    NSArray *horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[view]-0-|"
                                                                             options:0
                                                                             metrics:nil
                                                                               views:NSDictionaryOfVariableBindings(view)];
    [parent.view addConstraints:horizontalConstraints];

    NSArray *verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[view]-0-|"
                                                                           options:0
                                                                           metrics:nil
                                                                             views:NSDictionaryOfVariableBindings(view)];
    [parent.view addConstraints:verticalConstraints];
}

然后在父 UIViewController (已经显示的那个)中,当我想显示我的子弹出视图控制器时,它调用:

PopUpNotificationViewController *popup = [[self storyboard] instantiateViewControllerWithIdentifier:@"NotificationPopup"];
[popup addPopupToController:self];

您可以在将子控制器的视图添加到父控制器的视图时定义您想要的任何约束。

于 2014-01-23T12:49:47.657 回答
2
  1. 我认为该layoutIfNeeded方法只有在您之前调用过setNeedsLayout. 如果您setNeedsLayout改用,系统将知道在适当的时间进行更新。尝试在您的代码中更改它。

  2. 当您向视图添加约束时,该视图应自动再次布局其子视图以考虑新约束。setNeedsLayout除非在添加约束后发生了变化,否则不需要调用。您是否将约束添加到视图中,如果是这样:您是否将它们添加到正确的视图中?

  3. 您可以尝试的一件事是子类UIView化,以便在ChildViewController.viewParentViewController.view执行新布局时看到日志消息:

    -(void) layoutSubviews {
        [super layoutSubviews];
        NSLog(@"layoutSubviews was called on the following view: %@", [view description]);
    }
    

也许这会揭示您的视图何时(或不是)布局它们的子视图。

于 2013-07-29T16:30:03.247 回答
2

根据以下链接,我将约束创建移动到viewWillLayoutSubviews,这是正确设置视图边界的地方。我觉得这个答案错过了为什么子视图控制器的 updateViewConstraints 在父视图控制器之前调用的解释,或者它可能只是我的代码中的一些错误,但这个解决方法解决了这个问题......

于 2013-07-29T22:04:13.757 回答
1

将添加和删除子控制器的代码放在扩展中很方便。使用示例:

override func viewDidLoad() {
   super.viewDidLoad()
   let childController = YourCustomController()
   add(childController)
}

代码本身:

extension UIViewController {
   
   func add(_ controller: UIViewController) {
      addChild(controller)
      view.addSubview(controller.view)
      
      controller.view.translatesAutoresizingMaskIntoConstraints = false
      NSLayoutConstraint.activate([
         controller.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
         controller.view.topAnchor.constraint(equalTo: view.topAnchor),
         controller.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
         controller.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
      ])
      
      controller.didMove(toParent: self)
   }
   
   func remove() {
      guard parent != nil else {
         return
      }

      willMove(toParent: nil)
      view.removeFromSuperview()
      removeFromParent()
   }
   
}
于 2020-09-19T15:14:36.530 回答