Use autolayout to set dynamic UIView to match container view

前端 未结 2 1784
暖寄归人
暖寄归人 2020-12-13 06:32

I have a UIView in IB that has another UIView within it, which I am using as a container view. In code, I create three different views and then animate the appropriate one

相关标签:
2条回答
  • 2020-12-13 06:55

    In the problem you state "my new subviews are not scaled to match the container view" - but from the description I think they are - the problem is that your container view has no fixed size.

    I think that if you set your container view to have a fixed width and height that should do it, you may also need to call

    [self.containerView layoutSubviews]
    

    to force a resize after updating the constraints.

    I'd also suggest you swap to using the text based formatting, you could swap out your lines above for something like "H:|[newSubview]|" and "V:|[newSubview]|"

    0 讨论(0)
  • 2020-12-13 07:10

    A couple of thoughts:

    1. This basically looks fine, other than some variable name weirdness: Your local variable, containerView is equal to self.subView, but all of your other lines refer to a different variable, a class property, self.containerView. Did you omit a line where you set that containerView class property? But when I fixed that, your code worked fine for me.

    2. Make sure you're not trying to look at the frame immediately after setting the constraints, as the change will not yet be reflected in the frame settings. You can do a [containerView layoutIfNeeded]; if you want to force it to relayout everything based upon the constraints. Also, if you want to confirm frame settings, it's better to defer looking at those values until after viewDidAppear (i.e. viewDidLoad is too early in the view construction process).

    3. A minor tweak on your code (and unrelated to your problem), but when I'm setting the constraints within a container view, I often will set not only NSLayoutAttributeTop and NSLayoutAttributeLeading, like you did, but also NSLayoutAttributeBottom and NSLayoutAttributeTrailing (rather than NSLayoutAttributeWidth and NSLayoutAttributeHeight). It accomplishes the same thing as your code, but when using non-zero values, it is a fraction more intuitive.

      Anyway, I just did the following code, permutation of yours, and it works fine:

      - (IBAction)didTouchUpInsideAddView:(id)sender
      {
          UIView *containerView = self.containerView;
          UIView *newSubview = [[UIView alloc] initWithFrame:CGRectZero]; // initializing with CGRectZero so we can see it change
          newSubview.translatesAutoresizingMaskIntoConstraints = NO;
          newSubview.backgroundColor = [UIColor lightGrayColor];
          [containerView addSubview:newSubview];
      
          [containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview
                                                                    attribute:NSLayoutAttributeTop
                                                                    relatedBy:NSLayoutRelationEqual
                                                                       toItem:containerView
                                                                    attribute:NSLayoutAttributeTop
                                                                   multiplier:1.0
                                                                     constant:0.0]];
      
          [containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview
                                                                    attribute:NSLayoutAttributeLeading
                                                                    relatedBy:NSLayoutRelationEqual
                                                                       toItem:containerView
                                                                    attribute:NSLayoutAttributeLeading
                                                                   multiplier:1.0
                                                                     constant:0.0]];
      
          [containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview
                                                                    attribute:NSLayoutAttributeBottom
                                                                    relatedBy:NSLayoutRelationEqual
                                                                       toItem:containerView
                                                                    attribute:NSLayoutAttributeBottom
                                                                   multiplier:1.0
                                                                     constant:0.0]];
      
          [containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview
                                                                    attribute:NSLayoutAttributeTrailing
                                                                    relatedBy:NSLayoutRelationEqual
                                                                       toItem:containerView
                                                                    attribute:NSLayoutAttributeTrailing
                                                                   multiplier:1.0
                                                                     constant:0.0]];
      
          // the frame is still `CGRectZero` at this point
      
          NSLog(@"newSubview.frame before = %@", NSStringFromCGRect(newSubview.frame));
      
          // if not doing this in `viewDidLoad` (e.g. you're doing this in
          // `viewDidAppear` or later), you can force `layoutIfNeeded` if you want
          // to look at `frame` values. Generally you don't need to do this unless
          // manually inspecting `frame` values or when changing constraints in a
          // `animations` block of `animateWithDuration`.
      
          [containerView layoutIfNeeded];
      
          // everything is ok here
      
          NSLog(@"containerView.bounds after = %@", NSStringFromCGRect(containerView.bounds));
          NSLog(@"newSubview.frame after = %@", NSStringFromCGRect(newSubview.frame));
      }
      
    4. You can simplify that code a little using visual format language, e.g.:

      NSDictionary *views = NSDictionaryOfVariableBindings(newSubview);
      
      [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[newSubview]|"
                                                                            options:0
                                                                            metrics:nil
                                                                              views:views]];
      [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[newSubview]|"
                                                                            options:0
                                                                            metrics:nil
                                                                              views:views]];
      

      I find it easier to get the constraints right using visual format language. A little less error-prone (for me, at least). There are, though, some constraints that cannot be represented in visual format language, in which case I fall back to the syntax you outline.

    5. In your revised question, you show us an init method for your subview, which does another addSubview. You have to set constraints there, too. Bottom line, wherever you do addSubview, you have to set your constraints, e.g.

      - (id)init
      {
          if(self = [super init])
          {
              NSArray *nibArray = [[NSBundle mainBundle]loadNibNamed:@"MySubview" owner:self options:nil];
              UIView *subview = [nibArray objectAtIndex:0];
              subview.translatesAutoresizingMaskIntoConstraints = NO;
      
              [self addSubview:subview];
      
              NSDictionary *views = NSDictionaryOfVariableBindings(subview);
              [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[subview]|"
                                                                           options:0
                                                                           metrics:nil
                                                                             views:views]];
              [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[subview]|"
                                                                           options:0
                                                                           metrics:nil
                                                                             views:views]];
          }
          return self;
      }
      
    0 讨论(0)
提交回复
热议问题