如何使用AutoLayout实现UIView的上滑动画?

9
这是我想要实现的效果: enter image description here 我想要执行一个向上滑动的动画,用户可以将UIView2向上滑动,而UIView2会在屏幕上停留一半。
我知道如何通过UIButton操作以模态方式呈现UIViewControler,例如:
[self presentViewController:newController animated:NO completion:nil];

我知道您需要这种类型的代码来制作动画:

我也知道您需要这种类型的代码来制作动画:

[UIView animateWithDuration:0.5f animations:^{    [self.customView layoutIfNeeded];
} completion:^(BOOL finished) {

}];

然而,我不确定如何在UIView中实现这一点,特别是因为我在项目中使用AutoLayout,并使用约束轻松支持多个屏幕大小。

如何在不破坏AutoLayout和已设置的约束的情况下实现此目标?

谢谢

1个回答

18

我通常在这种情况下会采取以下步骤。

给你的场景添加两个约束条件。一个是让 UIView2 对齐到 UIView1 的底部,另一个是让它对齐到 UIView1 的中心(你需要在视图之间进行 ctrl+drag 操作以适当添加约束条件)。这些约束条件最初会相互冲突,但这没关系。

为你的视图控制器添加 NSLayoutConstraintsIBOutlet,并将我们创建的两个约束条件分配给这些 IBOutlet

将初始条件的约束优先级设置为 999(即对齐到底部的约束优先级应为 999)。将目标约束条件的约束优先级设置为 998(即对齐到中心的约束优先级为 998)。你现在会看到这些约束条件不再冲突了。这是因为优先级高的约束条件会覆盖优先级低的约束条件。

你可能已经看出了这一点。所以当你想要在约束条件之间动画 UIView2 时,交换优先级并进行动画!

代码:

@interface MyViewController ()
    @property (nonatomic, weak) IBOutlet NSLayoutConstraint* constraint0;
    @property (nonatomic, weak) IBOutlet NSLayoutConstraint* constraint1;
@end

- (void)someMethodWhereIWantToAnimate
{
    NSInteger temp = self.constraint0.priority;
    self.constraint0.priority = self.constraint1.priority;
    self.constraint1.priority = temp;

    [UIView animateWithDuration:0.3 animations:^{
        // Simplest is to use the main ViewController's view
        [self.view layoutIfNeeded];
    }];
}

为了启用平移并使用它来启动您的动画,请向您的视图控制器添加一个Pan Gesture Recognizer。从UIView2到Pan Gesture Recognizer进行Ctrl+拖动,并将其设置为gestureRecognizer。现在,当您在UIView2上拖动时,您将能够接收平移事件。
添加一个IBAction来处理平移:
- (IBAction)onPan:(id)sender
{
}

从平移手势识别器(Ctrl+drag)拖动到视图控制器,并设置onPan:为发送的动作。

sender实际上是平移手势识别器本身。因此,我们将能够填写该方法以处理平移,并使UIView2跟随用户的手指移动,然后在他们松开时启动动画。

让我们开始编写代码:

- (IBAction)onPan:(id)sender
{
    UIPanGestureRecognizer* recognizer = (UIPanGestureRecognizer*)sender;
    CGPoint translation = [recognizer translationInView:self.view];
    NSLog(@"State: (%d) Translation in view: (%f, %f)", recognizer.state, translation.x, translation.y);
}

如果你运行这段代码,并在UIView2上拖动手指,你会看到类似下面的输出:
State: (1) Translation in view: (0.000000, -2.500000)
State: (2) Translation in view: (0.500000, -7.500000)
State: (2) Translation in view: (0.500000, -7.500000)
State: (2) Translation in view: (1.500000, -12.000000)
State: (2) Translation in view: (2.500000, -16.500000)
State: (2) Translation in view: (2.500000, -19.500000)
State: (2) Translation in view: (2.500000, -24.500000)
State: (2) Translation in view: (2.500000, -25.000000)
State: (2) Translation in view: (2.500000, -25.500000)
State: (2) Translation in view: (2.500000, -27.000000)
State: (2) Translation in view: (2.500000, -29.500000)
State: (2) Translation in view: (2.500000, -31.000000)
State: (2) Translation in view: (2.500000, -31.500000)
State: (3) Translation in view: (2.500000, -31.500000)

注意值不断增加。我们需要的是增量。注意日志中的状态。状态(1)是拖动开始。状态(2)是拖动更改。状态(3)是拖动结束。利用这些信息,我们可以计算增量。
在您的视图控制器中添加一个CGPoint属性,并将UIView2也添加为IBOutlet:
@property (nonatomic) CGPoint lastTranslation;
@property (nonatomic, weak) IBOutlet UIView* uiView2;

最后,让我们填写 pan 方法的最终表单:
- (IBAction)onPan:(id)sender
{
    UIPanGestureRecognizer* recognizer = (UIPanGestureRecognizer*)sender;
    CGPoint delta;
    switch(recognizer.state) {
        case UIGestureRecognizerStateBegan:
            // On state begin: delta is the translation. Store it in our member for later user.
            self.lastTranslation = delta = [recognizer translationInView:self.view];
            break;
        case UIGestureRecognizerStateChanged:
            // On state changed: calculate the difference between the translation and our
            // previous translation.
            delta = CGPointApplyAffineTransform([recognizer translationInView:self.view], CGAffineTransformMakeTranslation(-self.lastTranslation.x, -self.lastTranslation.y));
            self.lastTranslation = [recognizer translationInView:self.view];
            break;
        case UIGestureRecognizerStateEnded:
            // On state ended: Let's just do the constraint animation.
            [self someMethodWhereIWantToAnimate];
            break;
        default:
            break;
    }
    delta.x = 0; // Forces only vertical drag on the UIView2.
    self.uiView2.center = CGPointApplyAffineTransform(self.uiView2.center, CGAffineTransformMakeTranslation(delta.x, delta.y)); // Move our uiView2 based on the delta.
    NSLog(@"State: (%d) Translation in view: (%f, %f)", recognizer.state, delta.x, delta.y);
}

这应该可以帮助你迈出重要的一步。根据手势的 velocityInView: 方法,您可能想微调约束上的UIView动画,但我会将其留作练习。


我假设constraint1是垂直居中的约束条件?也许我在阅读时错过了,那么constraint0对应哪个约束条件呢?底部? - Pangu
是的,constraint0 将是对齐到底部的约束。Constraint1 将是中心垂直约束。@盘古 - Sandy Chapman
UIView2部分超出了UIView1的窗口,而不是完全在UIView1内。这个事实会不会也造成自己的约束冲突呢? - Pangu
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Sandy Chapman
为了实现这个功能,我会在Interface Builder中的视图控制器中添加滑动手势识别器。然后添加一个IBAction来处理当向上滑动被识别时的情况。接下来,你只需要将Swipe Gesture Recognizer连接到那个IBAction即可。你的IBAction可以调用someMethodWhereIWantToAnimate方法,从而使你的视图进行动画。我会再添加一个滑动手势识别器来处理向下滑动的情况。我还会将其中一个设置为禁用状态,并在手势执行时切换启用状态。如果你需要更详细的示例,请告诉我,我会尽量在明天更新我的答案。 - Sandy Chapman
显示剩余3条评论

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