效果图
技术点
1.当滑动时的上一界面view是怎么出来的
2.每一个控制器界面的导航栏颜色为什么不同,pop回来还不会变
github地址https://github.com/lsmakethebest/LSNavigationBarTransition
具体实现
思路就是重写父类的push pop方法在push时对屏幕截图,滑动屏幕时显示截的图,pop时移除截图,多次push利用栈的原理管理截图
1.首先写个UINavigationController分类
load方法是在程序第一次启动时,该类加载到内存中会调用,在此交换方法
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
LSSwizzleInstanceMethod([self class],
@selector(viewDidLoad),
@selector(ls_navigaionControllerViewDidLoad));
LSSwizzleInstanceMethod([self class],
@selector(pushViewController:animated:),
@selector(ls_pushViewController:animated:));
LSSwizzleInstanceMethod([self class],
@selector(popViewControllerAnimated:),
@selector(ls_popViewControllerAnimated:));
LSSwizzleInstanceMethod([self class],
@selector(popToViewController:animated:),
@selector(ls_popToViewController:animated:));
LSSwizzleInstanceMethod([self class],
@selector(popToRootViewControllerAnimated:),
@selector(ls_popToRootViewControllerAnimated:));
});
}
方法交换内部实现,不懂可以参考前几篇文章runtime学习
void LSSwizzleInstanceMethod(Class class, SEL originalSelector, SEL swizzledSelector)
{
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL didAddMethod = class_addMethod(class,
originalSelector,method_getImplementation(swizzledMethod),method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
}
else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
交换完方法内部实现,因为系统自带个边缘手势禁用,添加拖拽手势
- (void)ls_navigaionControllerViewDidLoad
{
[self ls_navigaionControllerViewDidLoad];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
UIGestureRecognizer* gesture = self.interactivePopGestureRecognizer;
gesture.enabled = NO;
UIPanGestureRecognizer* pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
[self.view addGestureRecognizer:pan];
pan.delegate = self;
});
}
滑动时移动navigationController.view的frame同时移动截图的frame
结束时判断比例是还原位置还是pop
- (void)handleGesture:(UIScreenEdgePanGestureRecognizer*)recognizer
{
CGFloat x = [recognizer translationInView:recognizer.view].x;
if (x < 0) {
self.frontControllerView.transform = CGAffineTransformIdentity;
self.view.transform = CGAffineTransformIdentity;
self.frontControllerView.scaleAlpha=0;
return;
}
self.topViewController.view.userInteractionEnabled = NO;
switch (recognizer.state) {
case UIGestureRecognizerStateBegan: {
if (self.frontControllerView == nil) {
LSImageView* imageView = [[LSImageView alloc] init];
imageView.backgroundColor = [UIColor whiteColor];
imageView.image = [[LSStack sharedInstance] getTop];
CGRect frame = self.view.window.bounds;
frame.origin.x = -LSScreenWidth * LSRightScale;
imageView.frame = frame;
[self.view.window insertSubview:imageView belowSubview:self.view];
self.frontControllerView = imageView;
}
break;
}
case UIGestureRecognizerStateChanged: {
;
self.frontControllerView.transform = CGAffineTransformMakeTranslation(x * LSRightScale, 0);
self.view.transform = CGAffineTransformMakeTranslation(x, 0);
self.frontControllerView.scaleAlpha=x/LSScreenWidth;
break;
}
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateEnded: {
//打开用户交互
self.topViewController.view.userInteractionEnabled = YES;
if (x >= self.view.frame.size.width / Scale) {
[UIView animateWithDuration:0.2 animations:^{
self.frontControllerView.transform = CGAffineTransformMakeTranslation(LSScreenWidth * LSRightScale, 0);
self.frontControllerView.scaleAlpha=1;
self.view.transform = CGAffineTransformMakeTranslation(LSScreenWidth, 0);
}
completion:^(BOOL finished) {
[self.frontControllerView removeFromSuperview];
self.frontControllerView = nil;
self.view.transform = CGAffineTransformIdentity;
[self popViewControllerAnimated:NO];
}];
}
else {
[UIView animateWithDuration:0.2 animations:^{
self.frontControllerView.transform = CGAffineTransformIdentity;
self.frontControllerView.scaleAlpha=0;
self.view.transform = CGAffineTransformIdentity;
}];
}
} break;
default:
break;
}
}
系统导航栏默认有个背景_backgroundView负责存放背景image等
我们可以把它隐藏,在控制器将要显示时在navigationbar上添加一个和他类似的view,这样每一个界面都有这样一个view即使pop回去导航栏颜色就不会变了,因为显示的是自己添加的而不是系统的
交换方法
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
LSSwizzleInstanceMethod([self class],
@selector(viewDidLoad),
@selector(ls_viewDidLoad));
LSSwizzleInstanceMethod([self class],
@selector(viewWillLayoutSubviews),
@selector(ls_viewWillLayoutSubviews));
});
}
- (void)ls_viewDidLoad
{
[self ls_viewDidLoad];
//此处设置全局状态栏背景 其他控制器想修改UINavigationBar背景色可以在viewDidLoad里修改,不会影响其他控制器UINavigationBar背景色
[self.navigationController.navigationBar setBackgroundImage:[[UIColor colorWithRed:0.671 green:0.184 blue:1.000 alpha:1.000] imageWithColor] forBarMetrics:UIBarMetricsDefault];
}
- (void)ls_viewWillLayoutSubviews
{
if (self.navigationController.viewControllers.count) {
if (!self.ls_navigationBar) {
[self ls_addNavigationBarIfNeed];
self.ls_prefersNavigationBarBackgroundViewHidden = YES;
}
}
[self ls_viewWillLayoutSubviews];
}
#import <UIKit/UIKit.h>
@interface UIViewController (LSNavigationBarTransition)
@property (nonatomic, strong) UINavigationBar *ls_navigationBar;
@property (nonatomic, assign) BOOL ls_prefersNavigationBarBackgroundViewHidden;
-(void)ls_addNavigationBarIfNeed;
@end
- (void)setLs_navigationBar:(UINavigationBar*)ls_navigationBar
{
objc_setAssociatedObject(self, @selector(ls_navigationBar), ls_navigationBar, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)ls_prefersNavigationBarBackgroundViewHidden
{
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
显示时添加自定义的view
- (void)ls_viewWillLayoutSubviews
{
if (self.navigationController.viewControllers.count) {
if (!self.ls_navigationBar) {
[self ls_addNavigationBarIfNeed];
self.ls_prefersNavigationBarBackgroundViewHidden = YES;//隐藏系统的
}
}
[self ls_viewWillLayoutSubviews];
}
- (BOOL)ls_prefersNavigationBarBackgroundViewHidden
{
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (void)setLs_prefersNavigationBarBackgroundViewHidden:(BOOL)hidden
{
[[self.navigationController.navigationBar valueForKey:@"_backgroundView"]
setHidden:hidden];
objc_setAssociatedObject(self, @selector(ls_prefersNavigationBarBackgroundViewHidden), @(hidden), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)ls_addNavigationBarIfNeed
{
if (!self.view.window) {
return;
}
if (!self.navigationController.navigationBar) {
return;
}
UIView* backgroundView = [self.navigationController.navigationBar valueForKey:@"_backgroundView"];
CGRect rect = [backgroundView.superview convertRect:backgroundView.frame toView:self.view];
UINavigationBar* bar = [[UINavigationBar alloc] initWithFrame:rect];
bar.barStyle = self.navigationController.navigationBar.barStyle;
if (bar.translucent != self.navigationController.navigationBar.translucent) {
bar.translucent = self.navigationController.navigationBar.translucent;
}
bar.barTintColor = self.navigationController.navigationBar.barTintColor;
[bar setBackgroundImage:[self.navigationController.navigationBar backgroundImageForBarMetrics:UIBarMetricsDefault] forBarMetrics:UIBarMetricsDefault];
bar.shadowImage = self.navigationController.navigationBar.shadowImage;
bar.backgroundColor = self.navigationController.navigationBar.backgroundColor;
bar.translucent = self.navigationController.navigationBar.translucent;
self.ls_navigationBar = bar;
if (!self.navigationController.navigationBarHidden) {
[self.view addSubview:self.ls_navigationBar];
}
}