在看到一网友提的问题 iOS 自带 “信息” APP 的导航栏动态变化功能?, 自己好奇去了解下。
PS : 在 5s 和 5 的模拟器上看不出效果,得 6 以上的模拟器才可以。
注意仔细看上面导航栏的高度的变化,不注意还真不知道。
一、最简单粗暴的实现
// 注意实现
- (IBAction)goTestVC:(id)sender {
TestViewController *testVC = [[TestViewController alloc] init];
self.navigationController.delegate = self;
[self.navigationController pushViewController:testVC animated:YES];
}
- (nullable id )navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC {
if (operation == UINavigationControllerOperationPush) {
[UIView animateWithDuration:0.5 animations:^{
CGRect rect = CGRectMake(toVC.navigationController.navigationBar.frame.origin.x, toVC.navigationController.navigationBar.frame.origin.y, toVC.navigationController.navigationBar.frame.size.width, 64);
toVC.navigationController.navigationBar.frame = rect;
} completion:^(BOOL finished) {}];
}
else if (operation == UINavigationControllerOperationPop) {
[UIView animateWithDuration:0.5 animations:^{
CGRect rect = CGRectMake(fromVC.navigationController.navigationBar.frame.origin.x, fromVC.navigationController.navigationBar.frame.origin.y, fromVC.navigationController.navigationBar.frame.size.width, 44);
fromVC.navigationController.navigationBar.frame = rect;
} completion:^(BOOL finished) {}];
}
return nil;
}
上述实现,可以直接变换其高度,但是有点生硬,而且没有了向右滑动取消的效果。
二、稍微改良下的实现,加上手势
UIPercentDrivenInteractiveTransition
的运用
@property (nonatomic, strong) UIPercentDrivenInteractiveTransition* interactionController;
经典的添加手势
- (void)addPanGesture {
UIPanGestureRecognizer* panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
[self.navigationController.view addGestureRecognizer:panRecognizer];
}
- (void)pan:(UIPanGestureRecognizer*)recognizer {
UIView* view = self.navigationController.view;
switch (recognizer.state) {
case UIGestureRecognizerStateBegan:
{
CGPoint location = [recognizer locationInView:view];
if (location.x < CGRectGetMidX(view.bounds) && self.navigationController.viewControllers.count > 1) {
self.interactionController = [UIPercentDrivenInteractiveTransition new];
[self.navigationController popViewControllerAnimated:YES];
}
}
break;
case UIGestureRecognizerStateChanged:
{
CGPoint translation = [recognizer translationInView:view];
CGFloat d = fabs(translation.x / CGRectGetWidth(view.bounds));
[self.interactionController updateInteractiveTransition:d];
}
break;
case UIGestureRecognizerStateEnded:
{
if ([recognizer velocityInView:view].x > 0) {
[self.interactionController finishInteractiveTransition];
} else {
[self.interactionController cancelInteractiveTransition];
}
self.interactionController = nil;
}
break;
default:
break;
}
}
代理的实现
- (id)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id)animationController {
return self.interactionController;
}
但是目前仍然有一个问题,右边滑动时,太快,没有信息的那个慢慢滑动效果。
三、发现一个很奇怪的问题
自定义 UIViewControllerAnimatedTransitioning
居然出现卡顿
- (nullable id )navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC {
if (operation == UINavigationControllerOperationPush || operation == UINavigationControllerOperationPop) {
self.animator.operation = operation;
return self.animator;
}
return nil;
}
#import
@interface TestAnimator : NSObject
@property (nonatomic, assign) UINavigationControllerOperation operation;
@end
#import "TestAnimator.h"
@implementation TestAnimator
- (NSTimeInterval)transitionDuration:(id )transitionContext {
return 0.5;
}
- (void)animateTransition:(id)transitionContext {
UIViewController* toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController* fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
[[transitionContext containerView] addSubview:toVC.view];
[[transitionContext containerView]bringSubviewToFront:fromVC.view];
switch (self.operation) {
case UINavigationControllerOperationPush:
{
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
CGRect rect = CGRectMake(toVC.navigationController.navigationBar.frame.origin.x, toVC.navigationController.navigationBar.frame.origin.y, toVC.navigationController.navigationBar.frame.size.width, 64);
toVC.navigationController.navigationBar.frame = rect;
} completion:^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
break;
case UINavigationControllerOperationPop:
{
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
CGRect rect = CGRectMake(fromVC.navigationController.navigationBar.frame.origin.x, fromVC.navigationController.navigationBar.frame.origin.y, fromVC.navigationController.navigationBar.frame.size.width, 44);
fromVC.navigationController.navigationBar.frame = rect;
} completion:^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
break;
default:
break;
}
}
说白了就是高度出现卡顿的。
- 用自定义时: 高度变化出现卡段,不符合要求;右边滑动缓慢,符合要求。
- 直接在代理写时: 高度有个缓慢动态变化,符合要求;右边滑动太快,不符合要求。
此时就在想既能高度木有卡段,右边滑动又可以慢下来呢?
暂时一下子不知道如何将 POP 的动画 和 最后完成的结合起来:
- 采取方法一,我需要找一个结合点
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
CGRect rect = CGRectMake(fromVC.navigationController.navigationBar.frame.origin.x, fromVC.navigationController.navigationBar.frame.origin.y, fromVC.navigationController.navigationBar.frame.size.width, 44);
fromVC.navigationController.navigationBar.frame = rect;
} completion:^(BOOL finished) {
// 这个地方需要处理
}];
- 采取方法二,那个过渡动画不知道再哪
暂时先采取方法一,毕竟只缺少一个慢慢滑动的效果缺陷,总体来说还能接受。
当然有朋友有方法解决这个问题,非常欢迎告知。