程序化 NSLayoutConstraint 动画问题

Posted

技术标签:

【中文标题】程序化 NSLayoutConstraint 动画问题【英文标题】:Programatic NSLayoutConstraint animation issues 【发布时间】:2013-05-21 04:24:24 【问题描述】:

所以我正在尝试为一些布局约束设置动画并且运气不佳,我只是什么也没看到,然后我收到一条错误消息,说明它如何不能同时满足所有约束等。

我正在使用一个名为 PullableView 的类,其动画使用的是旧样式 [UIView commitAnimation],因此我将其子类化并添加到我的代码中,我认为这将用于动画约束...没有这样的运气并尝试对其进行动画处理甚至让它做很多事情都被证明是困难的,我只是得到“无法同时满足约束”。问题是我对这个约束业务相当陌生,所以我不知道从哪里开始。

这是另一个错误几乎相同,但对于 centerY。

"<NSLayoutConstraint:0x7c8a3a0 StyledPullableView:0x905a9b0.centerX == UIView:0x9054680.centerX>", "<NSLayoutConstraint:0x7c6b480 StyledPullableView:0x905a9b0.centerX == UIView:0x9054680.centerX + 128>"

在调用这个之前我当然会[pullRightView setTranslatesAutoresizingMaskIntoConstraints:NO];

任何帮助表示赞赏。

- (void)setOpenedForConstraints:(BOOL)op animated:(BOOL)anim

    opened = op;

    if (anim)
            
        NSLayoutConstraint *constraintX = [NSLayoutConstraint constraintWithItem:self
                                                                               attribute:NSLayoutAttributeCenterX
                                                                               relatedBy:NSLayoutRelationEqual
                                                                                  toItem:_parentView
                                                                               attribute:NSLayoutAttributeCenterX
                                                                              multiplier:1
                                                                                constant:0];

        NSLayoutConstraint *constraintY = [NSLayoutConstraint constraintWithItem:self
                                                                               attribute:NSLayoutAttributeCenterY
                                                                               relatedBy:NSLayoutRelationEqual
                                                                                  toItem:_parentView
                                                                               attribute:NSLayoutAttributeCenterY
                                                                              multiplier:1
                                                                                constant:0];

        [_parentView addConstraint:constraintX];
        [_parentView addConstraint:constraintY];

        constraintX.constant = opened ? self.openedCenter.x : self.closedCenter.x;
        constraintY.constant = opened ? self.openedCenter.y : self.closedCenter.y;
    

    if (anim)
    

        // For the duration of the animation, no further interaction with the view is permitted
        dragRecognizer.enabled = NO;
        tapRecognizer.enabled = NO;

        //[UIView commitAnimations];

        [UIView animateWithDuration:animationDuration
                         animations:^
         
             [self layoutIfNeeded];
         ];

    
    else
    

        if ([delegate respondsToSelector:@selector(pullableView:didChangeState:)])
        
            [delegate pullableView:self didChangeState:opened];
        
    

【问题讨论】:

【参考方案1】:

一些想法:

    您正在这里创建新的约束。通常,您将调整现有约束的constant。或者,如有必要,您将删除旧约束并添加一个新约束。

    更改现有约束时,有两种方法。一种是遍历视图的约束,找到有问题的约束,然后为此调整constant。另一种方法(当视图是您在 Interface Builder 中添加的视图时更容易)是在 Interface Builder 中为约束权创建一个IBOutlet。然后你就可以制作动画了。

    This answer 在关注另一个问题的同时,展示了为约束创建 IBOutlet 并为其值变化设置动画的过程。

    如果您必须遍历约束以寻找约束(例如,从您可能使用可视格式语言创建的整个集合中挑选一个约束),那么 replaceLeadingAndTopWithCenterConstraints 在 @ 987654322@ 可能是一个有用的示例,说明如何在现有视图上找到约束。这个特殊的例子是删除一个约束并添加一个新的约束,但是你可以很容易地找到现有的约束,调整它的常数。


我不完全确定您要使用的 UX,但假设您有一个面板要滑到屏幕左侧:

    第一个问题是你定义了什么约束:在这个例子中,我定义了四个约束,三个到超级视图(即顶部、底部和左侧,但 不是右边)和宽度限制。如果你在 IB 中这样做,它可能会很挑剔(例如,在添加宽度约束之前,你不能删除正确的约束)。只需确保完全定义了约束,但至少要做到这一点。

    然后我可以通过更改左侧约束的constant 来为面板滑出屏幕设置动画:

    - (IBAction)touchUpInsideButton:(id)sender
    
        self.offscreen = !self.offscreen;
    
        [UIView animateWithDuration:0.5
                         animations:^
                             self.panelLeftConstraint.constant = ([self isOffscreen] ? -kHowMuchToSlideOffToLeft : 0.0);
                             [self.view layoutIfNeeded];
                         ];
    
    

    您现在可以看到为什么确保没有定义正确的约束很重要。因为如果我在定义了宽度约束右约束的同时调整了左约束,那么它将变得无法满足。通过确保在开始时对约束进行了最低限度的定义(尽管定义明确),我可以更改左侧约束,并且视图现在可以按照我想要的方式进行动画处理。

【讨论】:

感谢您的回答,我将深入研究这些...我还在添加新约束之前删除了所有约束...虽然我在 init 函数中执行... @Genhain 我认为更改约束比添加和删除要容易得多。并且首先要非常小心您为视图定义的约束。例如,如果我要将它向左滑动,我会用顶部、底部、左侧和宽度(但不是右侧!)来定义它,然后我可以通过更改左侧约束的常量来设置动画.请参阅修订后的答案。我必须承认,我无法从您的代码示例中解读出您想要的 UX,因此,如果您澄清您要完成的工作,我可能会提供更多帮助。 很好的答案,但第一个列表中的第 2 点是错误的。只有 layoutIfNeeded 必须在动画块内。 @jrturton 非常正确。谢谢。我总是对自己重新应用约束的事情感到紧张,但你是对的,layoutIfNeeded 正确捕获了这一点,我已经删除了这一点。 添加 [self.view layoutIfNeeded];在动画块内修复了明显不起作用的动画。

以上是关于程序化 NSLayoutConstraint 动画问题的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式创建的动画 NSLayoutConstraint

如何在 Swift 中为 NSLayoutConstraint 设置动画?

iOS7中的NSLayoutConstraint动画持续时间

对于 NSLayoutConstraint 不执行动画的处理:

更改 NSLayoutConstraint 时的 SWIFT 动画

NSLayoutConstraint 不使用 UIView.animate 进行动画处理