这是一个例子.我创建并添加一个视图到我当前的视图并设置一些约束.我正在保存前导和后沿约束,所以我可以稍后更改它们的常量.
- (MyView*)createMyView { MyView* myView = [[MyView alloc init]; [myView setTranslatesAutoresizingMaskIntoConstraints:NO]; [self.mainView addSubview:myView]; NSDictionary* views = @{@"myView" : myView}; NSLayoutConstraint* leading = [NSLayoutConstraint constraintWithItem:myView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1.0f constant:0]; NSLayoutConstraint* trailing = [NSLayoutConstraint constraintWithItem:myView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTrailing multiplier:1.0f constant:0]; myView.leadingConstraint = leading; myView.trailingConstraint = trailing; [self.view addConstraints:@[leading,trailing]]; NSString* vflConstraints = @"V:|[myView]|"; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vflConstraints options:NSLayoutFormatAlignAllCenterX metrics:nil views:views]]; return myView; }
当视图上的滑动手势触发时,我会根据滑动的方向创建一个或另一侧的新视图.然后我更新约束和动画,以便新的传入视图将旧视图“推”出视图.
- (void)transitionToCameraView:(MyView*)newCameraView swipeDirection:(UISwipeGestureRecognizerDirection)swipeDirection { // Set new view off screen before adding to parent view float width = self.myView.frame.size.width; float height = self.myView.frame.size.height; float initialX = (swipeDirection == UISwipeGestureRecognizerDirectionLeft) ? width : -width; float finalX = (swipeDirection == UISwipeGestureRecognizerDirectionLeft) ? -width : width; float y = 0; [newView setFrame:CGRectMake(initialX,y,width,height)]; [self.mainView addSubview:newView]; self.myView.leadingConstraint.constant = finalX; self.myView.trailingConstraint.constant = finalX; // Slide old view out and new view in [UIView animateWithDuration:0.4f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction animations:^ { [newView setFrame:self.myView.frame]; [self.myView layoutIfNeeded]; } completion:^(BOOL finished) { [self.myView removeFromSuperview]; self.myView = newView; }]; }
滑动方向为UISwipeGestureRecognizerDirectionLeft时,此方法正常,但是当它是UISwipeGestureRecognizerDirectionRight时,异常会被抛出
self.myView.leadingConstraint.constant = finalX;
如果我检查方向并以相反的顺序更新约束,一切都很好,并且不会抛出异常:
if(swipeDirection == UISwipeGestureRecognizerDirectionLeft) { self.myView.leadingConstraint.constant = finalX; self.myView.trailingConstraint.constant = finalX; } else { self.myView.trailingConstraint.constant = finalX; self.myView.leadingConstraint.constant = finalX; }
它对我有意义为什么异常被抛出,但似乎更改多个约束参数是我们应该能够做的事情并让自动布局引擎知道我们将要做到这一点,而不是立即试图满足约束和呕吐.
有没有办法告诉自动布局引擎我正在对多个约束进行更改,然后告诉它在我完成后再次运行,或者我是不是这样做了?当我完成更改时,当所有事情都变得很好时,让我更新约束的顺序对我来说似乎真的很愚蠢.
编辑
经过进一步调查,我发现订单重要的根本原因是myView添加了一个子视图,该子视图有一个约束,指明它的前沿距离myView的前沿是10pt.如果我对新常量进行调整和调整以解决此问题,则不再存在约束冲突,也不存在异常.
self.myView.leadingConstraint.constant = finalX+25; // Just arbitrary number that happened to work self.myView.trailingConstraint.constant = finalX;
出现问题的原因是从左向右滑动时的新约束会将myView折叠为宽度为0.显然,在宽度为0的视图中没有足够的空间来向子视图添加10pt前沿约束.相反,以相反的顺序更改常量,首先通过增加后沿来扩大视图,然后通过减小前沿再次将其缩小.
更有理由告诉自动布局系统我们将进行多项更改.
解决方法
除此之外,考虑它的更好方法可能是有两个必须同步更新的约束表明你使用了错误的约束.你可以对前沿有约束,然后使约束使myView的宽度等于self.view的宽度.这样,您只需调整前导约束的常量,因此两个边都会移动.
此外,您正在使用-setFrame :,这是一个禁止自动布局.你应该为约束设置动画.您应该在newView上设置约束,使其前沿或后缘(视情况而定)等于myView的相邻边缘,其宽度等于self.view的宽度.然后,在动画块中,更改约束的常量,确定myView(以及间接地,newView)的位置以将它们一起滑动.在完成处理程序中,在newView上安装一个约束以保持其前沿与self.view的重合,因为它是新的myView.
BOOL left = (swipeDirection == UISwipeGestureRecognizerDirectionLeft); float width = self.myView.frame.size.width; float finalX = left ? -width : width; NSString* layout = left ? @"[myView][newView(==mainView)]" : @"[newView(==mainView)][myView]"; NSDictionary* views = NSDictionaryOfVariableBindings(newView,myView,mainView); [self.mainView addSubview:newView]; [self.mainView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:layout options:0 metrics:nil views:views]]; [self.mainView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|newView|" options:0 metrics:nil views:views]]; [self.mainView layoutIfNeeded]; // Slide old view out and new view in [UIView animateWithDuration:0.4f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction animations:^ { self.myView.leadingConstraint.constant = finalX; [self.myView layoutIfNeeded]; } completion:^(BOOL finished) { [self.myView removeFromSuperview]; self.myView = newView; NSLayoutConstraint* leading = [NSLayoutConstraint constraintWithItem:newView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1.0f constant:0]; self.myView.leadingConstraint = leading; [self.mainView addConstraint:leading]; }];
编辑更直接地回答这个问题:不,公共API中没有办法批量约束更新,以便引擎不会一次一个地检查它们,因为它们是通过设置它们的常量来添加或变异的.