Autolayout约束和子视图控制器

时间:2013-07-29 16:04:47

标签: objective-c autolayout

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

所以在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
//

所以基本上发生的事情是在应用父控制器约束之前在ChildViewController上调用updateViewConstraints,实际上self.view.frame == CGRectZero与我在{loadView方法中指定的完全相同1}}。

所有观点的

ChildViewController都设置为translatesAutoresizingMaskIntoConstraints

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

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

ChildViewController

5 个答案:

答案 0 :(得分: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];

将子控制器的视图添加到父控制器的视图时,可以定义所需的约束。

答案 1 :(得分: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]);
    }
    
  4. 也许这会揭示出你的观点是(或不是)布局他们的子视图的时候。

答案 2 :(得分:2)

您可以在调用addSubview后立即添加约束:不要忘记将translatesAutoresizingMaskIntoConstraints设置为false

以下是使用约束添加和隐藏子视图控制器的代码片段(受apple guide启发)

<强> 显示

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()
    }

答案 3 :(得分:1)

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

答案 4 :(得分:0)

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

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()
   }
   
}