问题引入
最近在项目中,遇到一个问题:
实现类似于设置功能的简单应用,方法是中在storyboard中,基于UITableView 建立一个静态tableViewCell。基于这个静态cell直接在storyboard上拖拽到下一级Controller, 方法为push。
问题现象:ios8以上系统没有问题。到ios7系统,出现crash. 最后用下边博文的方法解决。
转载分享给大家,下面是转载原文 :
---------------------------------
问题:
iOS7 相比iOS8及以后,在页面进入或者退出的时候,有一定延迟
比如push到子页面的时候,如果viewwillappear 还没有完成,就直接再进入下一个页面,就会crash
场景1:
举例场景:用户查看自己的地址列表时,如果地址列表为空,产品希望我们APP自动跳转到新建地址的页面
这个常规的做法是在viewdidload里面request address list,在response里面,判断如果空的话,push到新建address的页面
做出来,iOS8.9都ok,但是在iOS7上就会直接crash
类似很多场景(push/pop操作未完成,就继续push/pop),如果大家crash,能看到“Can't add self as subview”
类似这种,就是其中之一的场景。
解决方案:
这种的主流解决方案是:在viewwillappear的时候再执行相关动作。
我这边自己用过的一个方式是使用了个开源库:UIViewController+APSafeTransition
插一段代码,其实就是建了个父类在所有pop或者push的接口做了统一处理
问题:
A->B, Apush 到B,但实际页面还是A,但是导航左上角是个返回按钮(push后自带的返回上一页的backButton),然后整个页面点击无任何响应.
场景2:
使用了tabbrviewcontroller, 有4个tab(tabA,tabB .... tabD)
然后从2个tab,进入了子页面N多层,最后来到页面M,然后点击M页面的提交button后,希望回到第2个界面B。
需要达到的效果是,也可能从任何一个tab进入页面M,但都需要进入界面B
实现方式:
[self.navigationController popToRootViewControllerAnimated:NO];
然后发个通知,让tabbarViewController设置select为tabB,然后pushViewController到界面B
解决方案:
可能很多童鞋都没看懂,我说了什么,总结下就是popToRootViewController之后,在rooview还没出现viewappear的时候,执行pushViewController,操作,iOS7会出现页面错乱
但是这个又和上一个问题不太一样,因为之前已经使用了SafeTransition,不应该出这种问题的。
后来想了下,这个是appdelegate接受Notification 的,而Notification无法保证在主线程(理论上来说,Notification是线程安全的,同时post 和receive是在一个线程里面,但是在这里出了错误,说明的确有问题),于是在pushviewcontroller的地方增加为主线程,于是问题解决。
备注:这个没有更新完,待补充,具体我需要查看下实际所在的线程,确认在iOS7上的情况,但目前的解决方案是如此。谨记之
dispatch_async(dispatch_get_main_queue(), ^{
[currentViewController PushViewController:viewController animation:animation];
});
文/pingpong_龘(简书作者)
原文链接:http://www.jianshu.com/p/b49a87093ac4
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
源码
直接将这个分类的源文件,加入项目即可,利用了runtime机制将一部分生命周期函数替换成安全的方式。
//
// UINavigationController+APSafeTransition.h
//
// Created by hanamichi on 16/1/4.
// Copyright © 2016年 hanamichi. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
@interface UINavigationController () <UINavigationControllerDelegate>
@property (nonatomic, assign) BOOL viewTransitionInProgress;
@end
@implementation UINavigationController (APSafeTransition)
+ (void)load {
method_exchangeImplementations(class_getInstanceMethod(self, @selector(pushViewController:animated:)),
class_getInstanceMethod(self, @selector(safePushViewController:animated:)));
method_exchangeImplementations(class_getInstanceMethod(self, @selector(popViewControllerAnimated:)),
class_getInstanceMethod(self, @selector(safePopViewControllerAnimated:)));
method_exchangeImplementations(class_getInstanceMethod(self, @selector(popToRootViewControllerAnimated:)),
class_getInstanceMethod(self, @selector(safePopToRootViewControllerAnimated:)));
method_exchangeImplementations(class_getInstanceMethod(self, @selector(popToViewController:animated:)),
class_getInstanceMethod(self, @selector(safePopToViewController:animated:)));
}
#pragma mark - setter & getter
- (void)setViewTransitionInProgress:(BOOL)property {
NSNumber *number = [NSNumber numberWithBool:property];
objc_setAssociatedObject(self, @selector(viewTransitionInProgress), number, OBJC_ASSOCIATION_RETAIN);
}
- (BOOL)viewTransitionInProgress {
NSNumber *number = objc_getAssociatedObject(self, @selector(viewTransitionInProgress));
return [number boolValue];
}
#pragma mark - Intercept Pop, Push, PopToRootVC
- (NSArray *)safePopToRootViewControllerAnimated:(BOOL)animated {
if (self.viewTransitionInProgress) return nil;
if (animated) {
self.viewTransitionInProgress = YES;
}
NSArray *viewControllers = [self safePopToRootViewControllerAnimated:animated];
if (viewControllers.count == 0) {
self.viewTransitionInProgress = NO;
}
return viewControllers;
}
- (NSArray *)safePopToViewController:(UIViewController *)viewController animated:(BOOL)animated {
if (self.viewTransitionInProgress) return nil;
if (animated){
self.viewTransitionInProgress = YES;
}
NSArray *viewControllers = [self safePopToViewController:viewController animated:animated];
if (viewControllers.count == 0) {
self.viewTransitionInProgress = NO;
}
return viewControllers;
}
- (UIViewController *)safePopViewControllerAnimated:(BOOL)animated {
if (self.viewTransitionInProgress) return nil;
if (animated) {
self.viewTransitionInProgress = YES;
}
UIViewController *viewController = [self safePopViewControllerAnimated:animated];
if (viewController == nil) {
self.viewTransitionInProgress = NO;
}
return viewController;
}
- (void)safePushViewController:(UIViewController *)viewController animated:(BOOL)animated {
if (self.viewTransitionInProgress == NO) {
[self safePushViewController:viewController animated:animated];
if (animated) {
self.viewTransitionInProgress = YES;
}
}
}
@end
@implementation UIViewController (APSafeTransitionLock)
+ (void)load {
Method m1;
Method m2;
m1 = class_getInstanceMethod(self, @selector(sofaViewDidAppear:));
m2 = class_getInstanceMethod(self, @selector(viewDidAppear:));
method_exchangeImplementations(m1, m2);
}
- (void)sofaViewDidAppear:(BOOL)animated {
self.navigationController.viewTransitionInProgress = NO;
[self sofaViewDidAppear:animated];
}
@end
转载请注明原文地址: https://ju.6miu.com/read-1260728.html