Adding View Programatically With Auto Layout Gives 'NSGenericException', reason: 'Unable to install constraint on view

后端 未结 3 1557
暖寄归人
暖寄归人 2021-02-09 18:37

I am adding a view as a subview using [self.view addSubview:myView]. This works fine in portrait mode. However, it doesn\'t work at all in landscape. How do I add l

相关标签:
3条回答
  • 2021-02-09 18:59

    There are few things about Auto layouts. When ever you add layout constraints make sure it is not ambiguous. Ambiguous layout would result in undefined behaviour in your display. So good idea is to use IB which will never allow you to create a ambiguous layout, but you got to go through all the constraints to make sure they are valid.

    If you want to do it programatically I would suggest you to use Visual language.

    It will be helpful to go though these tips before using layout.

    0 讨论(0)
  • A couple of observations:

    1. Your constraint references a toItem of self.view.superview. I assume you meant self.view.

    2. You're adding the constraint to _preView, but you should add it to self.view (if you make the above change; if not, you'd use self.view.superview). You always add the constraint to the nearest shared parent.

    3. For the views you're creating programmatically, make sure to set translatesAutoresizingMaskIntoConstraints to NO.

      Thus:

      _preView.translatesAutoresizingMaskIntoConstraints = NO;
      [self.view addSubview:_preView];
      NSLayoutConstraint *myConstraint = [NSLayoutConstraint constraintWithItem:_preView
                                                                      attribute:NSLayoutAttributeBottom
                                                                      relatedBy:NSLayoutRelationEqual
                                                                         toItem:self.view
                                                                      attribute:NSLayoutAttributeBottom
                                                                     multiplier:1.0
                                                                       constant:-239];
      [self.view addConstraint:myConstraint];
      

    Chatting to you offline, two final observations:

    1. Your constraints were ambiguous. In the future, you can identify that by running the app in your debugger, hitting the pause button while the app is running (enter image description here) and then at the (lldb) prompt, you can enter

      po [[UIWindow keyWindow] _autolayoutTrace]

      ambiguous layout

      If you see AMBIGUOUS LAYOUT, then your constraints are not fully qualified (and thus you'll get unpredictable behavior). If you add the missing constraints, you should be able to eliminate this warning.

    2. If you want to animate constraint based views, you animate the changing of constant properties of the constraints, not by changing frame properties yourself. For example:

          // create subview
      
          UIView *subview = [[UIView alloc] init];
          subview.backgroundColor = [UIColor lightGrayColor];
          subview.translatesAutoresizingMaskIntoConstraints = NO;
          [self.view addSubview:subview];
      
          // create dictionary for VFL commands
      
          NSDictionary *views = @{@"subview" : subview, @"superview" : self.view};
      
          // add horizontal constraints
      
          [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[subview]|" options:0 metrics:nil views:views]];
      
          // set the height of the offscreen subview to be the same as its superview
      
          [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[subview(==superview)]" options:0 metrics:nil views:views]];
      
          // set the location of the subview to be just off screen below the current view
      
          NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:self.view.bounds.size.height];
          [self.view addConstraint:constraint];
      
          // then in two seconds, animate this subview back on-screen (i.e. change the top constraint `constant` to zero)
      
          double delayInSeconds = 2.0;
          dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
          dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
              constraint.constant = 0.0;
              [UIView animateWithDuration:1.0
                               animations:^{
                                   [self.view layoutIfNeeded];
                               }];
          });
      
    0 讨论(0)
  • 2021-02-09 19:10

    From your code above, there are 2 issues. 1. The constraint should be added to the parentview (self.view or self.view.superview as appropriate). 2. The items which are part of the myConstraint should be present in the view hierarchy to which you add your constraints.

    My suggestion would be to check if your myConstraint can be formed with _preView and self.view , add the _preView to self.view as a subview and then add the myConstraint to self.view.

    Also, the constraints should ideally be placed in -(void)updateConstraints method in your view (if you have a custom view) and you should call [self setNeedsUpdateConstraints]; in your view whenever you want the updateConstraints to be called on your view (after initializing your view, after rotation etc). You won't be calling updateConstraints directly.

    0 讨论(0)
提交回复
热议问题