今天在做开发的时候,忽然发现在视图的viewWillAppear:方法中添加: [self.tableView reloadData]; 不起作用,viewWillAppear:这个方法根本没有调用 后来发现原来用了 UINavigationController后,viewWillAppear方法是没有效果的,要用UINavigationControllerDelegate的– navigationController
方案 使用 继承,在父类的 viewWillAppear 中写入相关的代码即可,如果是新项目自然是可以的。 使用代码注入 就是传说中的 Runtime - Method Swizzling。 方法 Method viewWillAppear11= class_getInstanceMethod([self class], @selector(viewWillAppear:)); //需要替换成 能够输出日志的viewWillAppear Method logViewWillAppear11 = class_getInstanceMethod([self class], @selector( ), method_getTypeEncoding(viewWillAppear11)); } else{ method_exchangeImplementations(viewWillAppear11 ]的存在你会发现,其它的VC中还是会执行它自己viewWillAppear 的类容,因为你拦截并换的只是它父类中的viewWillAppear而不是它本身的viewWillAppear。
ifdef DEBUG static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //原本的viewWillAppear 方法 Method viewWillAppear11= class_getInstanceMethod([self class], @selector(viewWillAppear:)) ; //需要替换成 能够输出日志的viewWillAppear Method logViewWillAppear11 = class_getInstanceMethod([self ), method_getTypeEncoding(viewWillAppear11)); } else{ method_exchangeImplementations (viewWillAppear11, logViewWillAppear11); } }); #endif } - (void)logViewWillAppear:(BOOL
1.在摸个界面完全禁用IQKeyboard - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; IQKeyboardManager sharedManager] setEnable:YES]; } 2.在摸个界面完全禁用IQKeyboard 的 Toolbar (完成、切换箭头) - (void)viewWillAppear :(BOOL)animated { [super viewWillAppear:animated]; //TODO: 页面appear 禁用 [[IQKeyboardManager IQPreviousNextDisplayModeAlwaysHide, 隐藏切换箭头 IQPreviousNextDisplayModeAlwaysShow, 一直显示切换箭头(不受输入框个数限制) }; - (void)viewWillAppear :(BOOL)animated { [super viewWillAppear:animated]; //TODO: 页面appear 禁用 [IQKeyboardManager
[RuntimeViewController runtimeLog]; } - (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear :animated]; NSLog(@"viewWillAppear_原生的"); } - (void)viewWillDisappear:(BOOL)animated{ [super :(BOOL)animated{ NSLog(@"viewWillAppear_ExchangeMethod1"); [self wp_viewWillAppear:animated]; :(BOOL)animated{ NSLog(@"viewWillAppear_ExchangeMethod2"); [self wp_viewWillAppear:animated]; 验证交换后方法同名结果 控制台日志: viewWillAppear_原生的 此日志说明:多个category同时交换同一个方法,且交换后的方法名称也相同,结果等同于没有交换。
和生命周期几个相关的方法 - (void)viewDidLoad { [super viewDidLoad]; NSLog(@"FirstVC viewDidLoad"); } -(void)viewWillAppear :(BOOL)animated { [super viewWillAppear]; NSLog(@"FirstVC viewWillAppear"); } - (void)didReceiveMemoryWarning 28.835 SIMAlbum[33599:524075] FirstVC viewDidLoad 2016-03-24 10:31:28.835 SIMAlbum[33599:524075] FirstVC viewWillAppear 17.626 SIMAlbum[35103:546098] FirstVC viewDidLoad 2016-03-24 10:55:17.658 SIMAlbum[35103:546098] FirstVC viewWillAppear 注意到其中的viewWillLayoutSubviews和viewDidLayoutSubviews,调用情况视具体的viewDidLoad和viewWillAppear等方法中的代码而定。
Aspects hook的过程 在没有hook之前,ViewController的SEL与IMP关系如下 最初的viewWillAppear: 指向了_objc_msgForward 增加了aspects_viewWillAppear :,指向最初的viewWillAppear:的IMP 最初的forwardInvocation:指向了Aspect提供的一个C方法 __ASPECTS_ARE_BEING_CALLED__ 动态增加了 __aspects_forwardInvocation:, 指向最初的forwardInvocation:的IMP 然后,我们再来看看hook后,一个viewWillAppear:的实际调用顺序: 1 .object收到selector(viewWillAppear:)的消息 2.找到对应的IMP:_objc_msgForward,执行后触发消息转发机制。 :方法让其指向_objc_msgForward, 4.动态添加aspects_viewWillAppear:指向最初的viewWillAppear:实现 B)Hook实例的方法 Aspects支持只hook
viewDidLoad() { super.viewDidLoad() print("viewDidLoad") } override func viewWillAppear (_ animated: Bool) { super.viewWillAppear(animated) print("viewWillAppear") } super.updateViewConstraints() print("updateViewConstraints") } } 打印结果 ---- loadView viewDidLoad viewWillAppear
navLine = backgroundView.subviews.firstObject; } return _navLine; } 2.单个页面设置隐藏显示 - (void)viewWillAppear :(BOOL)animated { [super viewWillAppear:animated]; self.navLine.hidden = YES; } - (void)viewWillDisappear whiteColor] }]; 设置导航栏透明 - (void)viewWillAppear :(BOOL)animated { [super viewWillAppear:animated]; [self.navigationController.navigationBar
友盟统计中要求在每个页面的viewWillAppear和viewWillDisappear方法中添加友盟统计的方法: - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:YES]; [MobClick beginLogPageView:@"DiscoverController"]; } - (void) class_getInstanceMethod(self, @selector(statisticsViewWillAppear:)); m2 = class_getInstanceMethod(self, @selector(viewWillAppear load类方法会在每个页面中被调用,在运行时,viewWillAppear 方法会被statisticsViewWillAppear替换,viewWillDisappear会被statisticsViewWillDisappear 替换,且每个页面的viewWillAppear、viewWillDisappear方法仍然有效。
:(BOOL)animated { [self xxx_viewWillAppear:animated]; NSLog(@"xxx_viewWillAppear: %@" 比如上面我们调用-xxx_viewWillAppear:,因为-xxx_viewWillAppear: 和-viewWillAppear:的实现部分互换后,其实执行的时候,并不会执行上面的这个实现,而是调用 -viewWillAppear:的内部实现。 还是写段代码说明吧: - (void)viewWillAppear:(BOOL)animated { NSLog(@"这是原来的方法"); } - (void)xxx_viewWillAppear } 假如上面这俩方法用method swizzling 替换后,我们调用-xxx_viewWillAppear:会打印这是原来的方法;而调用-viewWillAppear:会打印xxx_viewWillAppear
问题: 如果按照以上方法来做,代码层面会出现类似以下情景: /*viewControllerOne*/ @implemention ViewControllerOne() - (void)viewWillAppear { [super viewWillAppear]; [MITStat statForEventID:@1 message:@"ViewControllerOne viewWillAppear "]; } @end /* viewControllerTwo*/ @implemention ViewControllerTwo() - (void)viewWillAppear{ [super viewWillAppear]; [MITStat statForEventID:@2 message:@"ViewControllerTwo viewWillAppear"]; } @end
现在,UIViewController 或其子类的实例对象在调用 viewWillAppear: 的时候会有 log 的输出。 调用 _cmd 下面代码在正常情况下会出现循环: ~~~{objective-c} - (void)xxx_viewWillAppear:(BOOL)animated { [self xxx_viewWillAppear :animated]; NSLog(@"viewWillAppear: %@", NSStringFromClass([self class])); } ~~~ 然而在交换了方法实现后就不会出现循环了。 在交换了方法的实现后,xxx_viewWillAppear:方法的实现已经被替换为了 UIViewController -viewWillAppear:的原生实现,所以这里并不是在递归调用。 由于 xxx_viewWillAppear: 这个方法的实现已经被替换为了 viewWillAppear: 的实现,所以,当我们在这个方法中再调用 viewWillAppear: 时便会造成递归循环。
onceToken, ^{ Class class = [self class]; SEL originalSelector = @selector(viewWillAppear :); SEL swizzledSelector = @selector(xxx_viewWillAppear:); Method originalMethod originalMethod, swizzledMethod); } }); } #pragma mark - Method Swizzling - (void)xxx_viewWillAppear :(BOOL)animated { [self xxx_viewWillAppear:animated]; NSLog(@"viewWillAppear: %@", self); } 所以我们在替换的方法里一定要调用替换的方法(如上面例子中的[self xxx_viewWillAppear:animated]),因为替换的方法已经替换了原有的实现,所以不是递归调用,如果继续调用原生的实现则会出现递归循环
四个方法如下: //将要显示,一定要调用super - (void)viewWillAppear:(BOOL)animated; // Called when the view is about to 2.1 viewWillAppear 当view即将被显示时调用,此时superview 为nil,也就是说这个是controller还不知道superview是谁。 下面做一个小测试,打印在不同方法执行的过程中,界面的长宽属性如何,结果如下: //viewDidLoad w:320.000000 h:568.000000 //viewWillAppear w: 320.000000 h:568.000000 //viewDidAppear w:414.000000 h:672.000000 有没有发现在viewWillAppear、viewDidAppear iOS7新增加了导航控制器侧滑手势,当触发侧滑返回时,会调用系统的viewWillDisappear:方法,取消侧滑返回时又会调用viewWillAppear:方法。
增加了新的生命周期函数viewIsAppearing(),调用时机介于viewWillAppear()与viewDidAppear()之间,并且兼容到 iOS 13。 super.viewDidLoad() print(#function) } // MARK: view即将显示 override func viewWillAppear (_ animated: Bool) { super.viewWillAppear(animated) print(#function) } // MARK
only a view controller in the tab bar controller's list of view controllers can be selected以及crash堆栈里有viewWillAppear 结合这个猜测,当vc被复用到新的tabbarVC时,加了一段代码让新的tabbarVC不添加到window,从而旧的tabbar继续触发viewWillAppear,问题可以复现。 当旧的tabbarVC触发viewWillAppear的时候,复用vc的parentVC已经变成新的tabbarVC(截图为nil是因为新的tabbarVC被释放了),但是没被复用的另外一个vc的parentVC only a view controller in the tab bar controller's list of view controllers can be selected 问题解决 方案1:在viewWillAppear
delegate; appDelegate.restrictRotation = restriction; } 在进入页面时允许屏幕旋转,并设置旋转的方向,代码如下 - (void)viewWillAppear :(BOOL)animated { [super viewWillAppear:animated]; [self restrictRotation:YES]; NSNumber 但是,新的问题出现了:当B控制器返回A时,A控制器页变也为横屏(需要将手机转向才能恢复) 解决办法很简单: 在A控制器的-(void)viewWillAppear:(BOOL)animated方法中添加
导航栏显示就比较顺滑 [self.navigationController setNavigationBarHidden:YES animated:YES]; 所以,做法是: A页面: - (void)viewWillAppear :(BOOL)animated { [super viewWillAppear:animated]; [self.navigationController setNavigationBarHidden :YES animated:YES]; } B页面: - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated :(BOOL)animated { [super viewWillAppear:animated]; [self.navigationController setNavigationBarHidden :NO animated:YES]; } B页面: // 在页面将要出现时,记录原始侧滑手势代理对象,并将手势代理设置为当前页面 - (void)viewWillAppear:(BOOL)animated
在平时的开发中会遇到下面两种场景: 有些特殊页面需要判断是通过push/pop 或 模态化的方式进入/退出; 页面出现/消失的时机; UIViewController出现 override func viewWillAppear (_ animated: Bool) { super.viewWillAppear(animated) if isBeingPresented || isMovingToParent