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