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



我有一个根视图,使用自动布局,用户可以通过交互式向下滑动来关闭它,通过调整视图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中也看到了这一点(。

如果要为呈现的视图控制器实现自定义转换,请尝试使用 UIViewController 类的 transitioningDelegate 属性。我过去多次使用过这个(虽然不是与手势交互(,并且从未在使用自动布局的子视图时遇到过问题。下面是一些可能对您有所帮助的代码。我从这个Github项目中借用了大部分。我还在根视图的左侧添加了一个子视图,其中包含您指定的约束,并在 iPhone X 模拟器上对其进行了测试。似乎没有任何问题。

@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