我有一个根视图,使用自动布局,用户可以通过交互式向下滑动来解除,通过平移手势识别器来调整视图的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中也看到了这一点)。
答案 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