5

假设我有这个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该按钮应移动到视图的右下角。我们可以轻松地使用两个约束来定义按钮和底部布局指南之间的底部空间以及按钮和视图右边缘之间的右侧空间(尾随空间)。

问题在于UIButton已经有两个约束(左/上)和两个定义其宽度和高度的约束,因此我们不能添加两个新约束,因为它们会与其他约束冲突。

动画场景的简单常见情况,但它给我带来了一些问题。想法?

编辑

当用户点击 时UIButton,我需要该按钮:

  1. 将其标题更改为“第二”
  2. 等待 1 秒并移动到右下角(删除顶部和左侧边距约束并添加底部和右边距约束)
  3. 将其标题更改为“第三”
  4. 等待 1 秒并移动到右上角(移除下边距约束并添加上边距约束)

我真的一定要使用这个凌乱的代码吗?

@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

4

3 回答 3

11

删除左侧和顶部约束,添加底部和右侧约束,然后在动画块中调用 layoutIfNeeded 以动画到新位置。第二种方法 move2 中的代码可以嵌入到 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];
}
于 2013-11-11T05:20:49.210 回答
1

对于动画,您需要更改左侧和顶部约束的常量,而不是添加新约束。如下:

 self.mBtnTopConstraint.constant = yourval1;
    self.mBtmLeftConstraint.constant = yourval2;
    [yourbtn setNeedsUpdateConstraints];

    [UIView animateWithDuration:0.5 animations:^{

            [yourbtn layoutIfNeeded];

        } completion:^(BOOL isFinished){
        }];

希望这可以帮助。

于 2013-11-11T05:23:29.647 回答
-1

您可以使用所需的结束约束制作占位符 UIView,并将原始约束与占位符 UIView 约束交换。

这样,它可以保持故事板中的布局约束,并防止您的代码混乱。它还为您提供了在 xcode 的预览部分中查看动画最终结果的附加效果。

如果使用 cocoapod “SBP” 只需几行即可。 这是一个教程

于 2015-11-15T21:50:15.803 回答