AutoLayout,约束和animation
假设我有这个UIView
:
与这些相对的限制:
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *leftMarginConstraint; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *topMarginConstraint; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *widthConstraint; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *heightConstraint;
好,现在让我们假设当用户点击那个UIButton
,button应该移动到视图的右下angular。 我们可以很容易地使用两个约束来定义button和底部布局指南之间的底部空间,以及从button和视图右边缘的右边空间(尾部空间)。
问题是UIButton
已经有两个约束(left / top)和两个约束来定义它的宽度和高度,所以我们不能添加两个新约束,因为它们会与其他约束冲突。
一个animation场景的简单而普遍的情况,但是却造成了一些问题。 想法?
编辑
当用户点击UIButton
,我需要button:
- 将其标题更改为“第二”
- 等待1秒,然后移动到右下angular(移除顶部和左侧边距约束,并添加底部和右侧边距约束)
- 将其标题更改为“第三”
- 等待1秒,然后移到右上angular(移除底部边距约束并添加顶部边距约束)
我真的很想使用这个混乱的代码吗?
@implementation ViewController { NSLayoutConstraint *_topMarginConstraint; NSLayoutConstraint *_leftMarginConstraint; NSLayoutConstraint *_bottomMarginConstraint; NSLayoutConstraint *_rightMarginConstraint; } - (IBAction)buttonPressed:(id)sender { UIButton *button = sender; // 1. [sender setTitle:@"Second" forState:UIControlStateNormal]; // 2. double delayInSeconds = 1.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); dispatch_after(popTime, dispatch_get_main_queue(), ^(void) { [button removeConstraints:@[self.leftMarginConstraint, self.topMarginConstraint]]; _bottomMarginConstraint = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeBottom relatedBy:0 toItem:button attribute:NSLayoutAttributeBottom multiplier:1 constant:20]; [self.view addConstraint:_bottomMarginConstraint]; _rightMarginConstraint = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeRight relatedBy:0 toItem:button attribute:NSLayoutAttributeRight multiplier:1 constant:20]; [self.view addConstraint:_rightMarginConstraint]; [UIView animateWithDuration:1 animations:^{ [self.view layoutIfNeeded]; } completion:^(BOOL finished) { // 3. [sender setTitle:@"Third" forState:UIControlStateNormal]; // 4. double delayInSeconds = 1.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); dispatch_after(popTime, dispatch_get_main_queue(), ^(void) { [button removeConstraint:_bottomMarginConstraint]; _topMarginConstraint = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeTop relatedBy:0 toItem:button attribute:NSLayoutAttributeTop multiplier:1 constant:20]; [UIView animateWithDuration:1 animations:^{ [self.view layoutIfNeeded]; }]; }); }]; }); }
严重? :d
删除左边和上边的约束,添加底部和右边的约束,然后调用animation块中的layoutIfNeededanimation到新的位置。 第二个方法中的代码move2可以embedded到move1的完成块中,但是如果在两个单独的方法中保持这两个方法(它需要添加另一个属性bottomCon),我发现它更容易阅读:
- (IBAction)move1:(UIButton *)sender { [sender setTitle:@"Second" forState:UIControlStateNormal]; [sender layoutIfNeeded]; [self.view removeConstraints:@[self.leftCon,self.topCon]]; self.bottomCon = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeBottom relatedBy:0 toItem:self.button attribute:NSLayoutAttributeBottom multiplier:1 constant:20]; [self.view addConstraint:self.bottomCon]; [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeRight relatedBy:0 toItem:self.button attribute:NSLayoutAttributeRight multiplier:1 constant:20]]; [UIView animateWithDuration:1 delay:1.0 options:0 animations:^{ [self.view layoutIfNeeded]; } completion:^(BOOL finished) { [sender setTitle:@"Third" forState:UIControlStateNormal]; [self move2]; }]; } -(void)move2 { [self.view removeConstraint:self.bottomCon]; [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeTop relatedBy:0 toItem:self.button attribute:NSLayoutAttributeTop multiplier:1 constant:-20]]; [UIView animateWithDuration:1 delay:1.0 options:0 animations:^{ [self.view layoutIfNeeded]; } completion:nil]; }
对于animation,你需要改变左边和上边的约束的常量,而不是增加新的约束。 如下:
self.mBtnTopConstraint.constant = yourval1; self.mBtmLeftConstraint.constant = yourval2; [yourbtn setNeedsUpdateConstraints]; [UIView animateWithDuration:0.5 animations:^{ [yourbtn layoutIfNeeded]; } completion:^(BOOL isFinished){ }];
希望这可以帮助。
您可以使用所需的结束约束来创build占位符UIView,并使用占位符UIView约束来交换原始约束。
这样它就保持了故事板的布局约束,并防止你的代码混乱。 它还为您提供了在xcode的预览部分查看animation效果的附加效果。
如果使用cocoapod“SBP”来做到这一点,只需几行。 这是一个教程 。