如何在平移动画期间暂停布局更新?

时间:2018-04-13 21:29:54

标签: ios autolayout

我有一个根视图,使用自动布局,用户可以通过交互式向下滑动来解除,通过平移手势识别器来调整视图的frame.origin属性。顶部前角有一个视图,具有以下约束:

Top = Safe Area
Height == 50
Trailing >= Safe Area + 8
Leading = Safe Area + 16

origin.y不是0.0时,顶部的安全区域显然会发生变化,视图会跳起来。我试图避免这种行为。我尝试应用仿射变换而不是操纵原点,但这并没有什么不同。

在动画进行过程中,有没有办法暂停或冻结自动布局约束评估?

我在运行iOS 11.3的iPhone SE模拟器中进行测试(我在11.2中也看到了这一点)。

1 个答案:

答案 0 :(得分:0)

如果要为呈现的视图控制器实现自定义转换,请尝试使用transitioningDelegate类的UIViewController属性。我过去曾多次使用过这种情况(虽然没有用手势交互),并且使用AutoLayout从未出现过子视图问题。这里有一些可能会帮助你的代码。我从this Github project借了大部分。我还添加了一个子视图,其中包含您在根视图左上角指定的约束,并在iPhone X Simulator上进行了测试。似乎没有任何问题。

@interface TransitionInteractor: UIPercentDrivenInteractiveTransition

@property (nonatomic, assign) BOOL hasStarted;
@property (nonatomic, assign) BOOL shouldFinish;

@end

然后在符合UIViewControllerAnimatedTransitioning协议的类中,实现以下方法:

@interface DismissAnimator : NSObject <UIViewControllerAnimatedTransitioning>

- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext {
    return 0.4f;
}

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
    UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    if (!fromVC || !toVC)
        return;

    [transitionContext.containerView insertSubview:toVC.view belowSubview:fromVC.view];

    CGRect screenBounds = [UIScreen mainScreen].bounds;
    CGFloat height = CGRectGetHeight(screenBounds);
    CGPoint bottomLeftCorner = CGPoint(x: 0, y: screenBounds.height)
    CGRect finalFrame = CGRectMake(0.0f, height, CGRectGetWidth(screenBounds), height);

    NSTimeInterval duration = [self transitionDuration:transitionContext];
    [UIView animateWithDuration:duration animations:^{
        fromVC.view.frame = finalFrame;
    } completion:^(BOOL finished) {
        [transitionContext completeTransition:!transitionContext.transitionWasCancelled];
    }];
}

在视图控制器文件中:

@interface PresentedViewController

@property (nonatomic, strong) TransitionInteractor *interactor;

@end

@implementation PresentedViewController

- (void)handlePanGesture:(UIPanGestureRecognizer *)sender {
    CGFloat percentThreshold = 0.3f;

    // convert y-position to downward pull progress (percentage)
    CGPoint translation = [sender translationInView:self];
    CGFloat verticalMovement = translation.y / [UIScreen mainScreen].bounds.size.height;
    CGFloat downwardMovement = fmaxf(verticalMovement, 0.0f);
    CGFloat progress = fminf(downwardMovement, 1.0f);
    CGPoint velocity = [sender velocityInView:self];

    switch (sender.state) {
        case UIGestureRecognizerStateBegan:
            self.interactor.hasStarted = YES;
            [self.vc dismissViewControllerAnimated:YES completion:nil];
            break;
        case UIGestureRecognizerStateChanged:
            self.interactor.shouldFinish = (velocity.y > 1000) ? YES : (progress > percentThreshold);
            [self.interactor updateInteractiveTransition:progress];
            break;
        case UIGestureRecognizerStateCancelled:
            self.interactor.hasStarted = NO;
            [self.interactor cancelInteractiveTransition];
            break;
        case UIGestureRecognizerStateEnded:
            self.interactor.hasStarted = NO;
            self.interactor.shouldFinish ? [self.interactor finishInteractiveTransition] : [self.interactor cancelInteractiveTransition];
            break;
        default:
            break;
    }
}

@end

在呈现此视图控制器之前,在呈现视图控制器中设置转换委托。实现委托方法如下:

@interface PresentingViewController () <UIViewControllerTransitioningDelegate>

@property (nonatomic, strong) TransitionInteractor *interactor;

@end

@implementation PresentingViewController

- (TransitionInteractor *)interactor {
    if (!_interactor) {
        _interactor = [TransitionInteractor new];
    }
    return _interactor;
}

#pragma mark - Navigation

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.destinationViewController isKindOfClass:[PresentedViewController class]]) {
        PresentedViewController *destination = [segue destinationViewController];
        destination.transitioningDelegate = self;
        destination.interactor = self.interactor;
    }
}

#pragma mark - UIViewControllerTransitioningDelegate

- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
    return [DismissAnimator new];
}

- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator {
    return self.interactor;
}

@end