简体   繁体   中英

How to size and position an embedded UIViewController using Auto Layout constraints?

Here is what I wanna do: My storyboard contains a UIViewController with just a few controlls. It's height is less than half a screen. For simplicity, let's call it the DataViewController . The DataViewController is embedded inside other UIViewControllers through a " Container View ".

Although " Container View " displays the DataViewController , it still needs to have an explicit height set. Otherwise interface builder complains about ambiguous constraints.

Now, how can I tell " Container View " that it's size should be what's required by DataViewController ? Ie without setting a hard coded, explicit height in Interface Builder (which, I fear, would break the layout if the font size changes)?

Or in other words: How do you size/position embedded UIViewControllers in Interface Builder?

If you want the childViewContainer to be responsible for its width and height, here is a way to do it :

  • Set width and height constraints in your Parent View Controller(s) and select remove at build time. They are just here so interface builder stops complaining about missing constraints. 在此处输入图片说明

  • (You can) Change your Child View Controller simulated size from fixed to freeform in your storyboard size inspector tab.

在此处输入图片说明

  • Here is the trick : In your Parent View Controller(s) viewDidLoad disable translatesAutoresizingMaskIntoConstraints on the first subviews, and simply redefine by yourself the containerView constraints to its subview - root view of your Child View Controller - like bellow :

Objective-C

- (void)viewDidLoad {
    [super viewDidLoad];
    self.containerView.subviews[0].translatesAutoresizingMaskIntoConstraints = NO;
}

#pragma mark - Constraints

- (void)updateViewConstraints {
    [super updateViewConstraints];
    [self initConstraints];
}

- (void)initConstraints {
    if (!self.didSetConstraints) {
        self.didSetConstraints = YES;

        self.containerView.subviews[0].translatesAutoresizingMaskIntoConstraints = NO;

        NSDictionary *views = @{@"subview" : self.containerView.subviews[0]};

        [self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[subview]|" options:0 metrics:nil views:views]];
        [self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[subview]|" options:0 metrics:nil views:views]];
    }
}

Swift

override func viewDidLoad() {
    super.viewDidLoad()
    containerView.subviews[0].translatesAutoresizingMaskIntoConstraints = false
}

// Mark: Constraints

override func updateViewConstraints() {
    super.updateViewConstraints()
    initConstraints()
}

func initConstraints() {
    if !didSetConstraints {
        didSetConstraints = true

        containerView.subviews[0].translatesAutoresizingMaskIntoConstraints = false

        let views = ["subview": containerView.subviews[0]]

        containerView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[subview]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
        containerView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[subview]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
    }
}

Now your child View Controller (linked to the container view) is fully responsible for its content size. Thus, you must set constraints that let the root view calculates its size to satisfies the constraints it holds.

If you are embedding with a segue, just do:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let viewController = segue.destination as? EmbeddedViewController {
      viewController.view.translatesAutoresizingMaskIntoConstraints = false
    }
}

in your content view controller set the preferredContentSize (In your example DataViewController ) like the following

override func viewDidLoad() {
    super.viewDidLoad()
    preferredContentSize = CGSize(width: 400, height: 200)
}

When the child gets added to the parentViewController, the parentViewController gets notified about the prefered size. Following function gets called on the parent

func preferredContentSizeDidChange(forChildContentContainer container: UIContentContainer)

In this method you can then change the container to the prefered size of the child.
For example change the height to the prefered height of the child/content

override func preferredContentSizeDidChange(forChildContentContainer container: UIContentContainer) {
    // heightConstraint is a IBOutlet to your NSLayoutConstraint you want to adapt to height of your content
    heigtConstraint.constant = container.preferredContentSize.height
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM