iOS:如何将动画应用于新的自适应布局约束(高度)

127

我以前从未与自动布局约束条件一起工作过。我正在开发一个小型的新应用程序,注意到 NIB 视图默认使用自动布局。因此,我想利用这个机会来尝试并理解苹果对此的意图。

第一个挑战:

我需要调整一个MKMapView的大小,并希望将其动画到新位置。如果按照之前的方式进行:

[UIView animateWithDuration:1.2f
     animations:^{
         CGRect theFrame = worldView.frame;
         CGRect newFrame = CGRectMake(theFrame.origin.x, theFrame.origin.y, theFrame.size.width, theFrame.size.height - 170);
         worldView.frame = newFrame;
}];

如果一个相邻视图被更新(在我的情况下是通过[myUISegmentedControl setTitle:newTitle forSegmentAtIndex:0]更新了UISegmentedControl的标题),那么MKMapView会“捕捉”回其原始高度。

因此,我认为我想要做的是改变MKMapView的约束条件,将其从等于父视图的高度改为相对于它曾经覆盖的UISegmentedControl顶部的约束: V:[MKMapView]-(16)-[UISegmentedControl]

我想要的是MKMapView的高度缩短,以便露出地图视图下面的一些控件。为此,我认为需要将约束条件从固定的全尺寸视图更改为底部约束到UISegmentedControl的顶部的视图之一...而且我希望它在视图缩小时以动画形式出现。

如何实现这个过程?

编辑 - 尽管视图底部立即向上移动了170,但这个动画没有执行

    [UIView animateWithDuration:1.2f
         animations:^{
             self.nibMapViewConstraint.constant = -170;

    }];

并且 nibMapViewConstraint 在 IB 中与底部垂直间距约束相连。


1
我知道你可以轻松地更改[UIView animateWithDuration..]块中约束的常量值来实现高度变化的动画。你需要为该约束创建一个IBOutlet,并在xib中将其连接起来,或者如果是在代码中创建它,则保留对它的引用(或者循环遍历所有约束以寻找它)。不确定如何实现relatedBy更改的动画,但我已经读到过应该仅更改约束的常量而不是其他值(对于其他值,请创建新的约束)。 - yuf
嗯,我本以为我可以做到,但是它没有动画。它成功地改变了,并且在动画块中,但却没有动画!?! - Meltemi
在这里找到了答案:https://dev59.com/B2cs5IYBdhLWcg3wWCpB - Meltemi
1
不要忘记 [view layoutIfNeeded],那也是我的问题哈哈。这就是解决我的问题的相同问题。 - yuf
可能是重复的问题,参考如何对约束变化进行动画处理? - Senseful
6个回答

183

更新约束条件后:

[UIView animateWithDuration:0.5 animations:^{[self.view layoutIfNeeded];}];

用包含视图的引用替换self.view


4
可以适用于视图本身,但是与该视图有约束关系的其他视图会立即移动。我该怎么办?谢谢。 - dietbacon
7
如果需要,在这些视图上也需要调用布局。但是问题在于这样会使视图刷新的过程也带上动画效果,无法正常工作。如何只为其他视图动态调整约束并实现动画效果? - ngb
3
注意:如果使用UIViewAnimationOptionBeginFromCurrentState选项,布局约束会在动画之前设置! - Robert
14
不要逐个调用每个视图的 layoutIfNeeded 方法,只需调用 [[self.view superview] layoutIfNeeded]; - Flying_Banana
2
@ngb 你需要在动画块内部更改约束常量。这样,约束随着动画的变化而变化,你就可以继续使用UIViewAnimationOptionBeginFromCurrentState - Eran Goldin

87

这对我很有效(适用于iOS7和iOS8+)。点击您想要调整的自动布局约束(在界面构建器中,例如顶部约束)。接下来将其设置为IBOutlet;

@property (strong, nonatomic) IBOutlet NSLayoutConstraint *topConstraint;

向上动画;

    self.topConstraint.constant = -100;    
    [self.viewToAnimate setNeedsUpdateConstraints]; 
    [UIView animateWithDuration:1.5 animations:^{
        [self.viewToAnimate layoutIfNeeded]; 
    }];

返回到原始位置进行动画

    self.topConstraint.constant = 0;    
    [self.viewToAnimate setNeedsUpdateConstraints];  
    [UIView animateWithDuration:1.5 animations:^{
        [self.viewToAnimate layoutIfNeeded];
    }];

2
哇!!这真的很有效!这个答案让我大开眼界。非常感谢你!-- Erik - Erik van der Neut
2
@ErikvanderNeut 对我来说也是如此,很高兴它有帮助。从现在开始,我将通过动画约束而不是帧位置进行动画处理。 - DevC
1
[containerView layoutIfNeeded]; 确保在执行 animateWithDuration 块之前,所有挂起的布局操作都已完成。 - ingconti

11

苹果官方有一份非常好的教程,讲解了如何在自动布局中使用动画。请参考 链接 并找到名为“Auto layout by example”的视频。其中介绍了一些关于自动布局的有趣内容,最后一部分是关于如何使用动画的。


3

我已经制作了一个小的演示程序,可以在这里找到。它展示了如何在一个非常简单的例子中更改和动画化自动布局约束。只需查看DemoViewController.m即可。


1
大多数人使用自动布局来布置视图上的项目,并修改布局约束以创建动画。
一种简单的方法是在Storyboard中创建要动画的UIView,然后创建一个隐藏的UIView,将其放在您想要UIView结束的位置。您可以使用Xcode中的预览确保两个UIView位于您想要的位置。之后,隐藏结束UIView并交换布局约束。
如果您不想自己编写代码,还有一个用于交换布局约束的podfile叫做SBP。 这里有一个教程

0

在编程中,你不需要使用更多的 IBOutlet reference 约束,而是可以直接访问或更新已经应用于任何视图上的约束。这些约束可以通过编程或者界面构建器直接应用。为此,你可以使用 KVConstraintExtensionsMaster 库。该库还管理着 NSLayoutConstraint 的累积行为。

要在容器视图上添加高度约束,可以:

 CGFloat height = 200;
 [self.containerView applyHeightConstrain:height];

通过动画更新containerView的高度约束

[self.containerView accessAppliedConstraintByAttribute:NSLayoutAttributeHeight completion:^(NSLayoutConstraint *expectedConstraint){
        if (expectedConstraint) {
            expectedConstraint.constant = 100;

            /* for the animation */ 
            [self.containerView  updateModifyConstraintsWithAnimation:NULL];
      }
    }];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接