cdts/xdts-ios 3/TreeHole/CYHResetCode/CYH/QMUIKit/QMUIMainFrame/QMUINavigationController.m

656 lines
36 KiB
Mathematica
Raw Normal View History

2023-07-27 09:20:00 +08:00
/**
* Tencent is pleased to support the open source community by making QMUI_iOS available.
* Copyright (C) 2016-2021 THL A29 Limited, a Tencent company. All rights reserved.
* Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
//
// QMUINavigationController.m
// qmui
//
// Created by QMUI Team on 14-6-24.
//
#import "QMUINavigationController.h"
#import "QMUICore.h"
#import "QMUINavigationTitleView.h"
#import "QMUICommonViewController.h"
#import "UIViewController+QMUI.h"
#import "UINavigationController+QMUI.h"
#import "UIView+QMUI.h"
#import "UINavigationItem+QMUI.h"
#import "UINavigationController+QMUI.h"
#import "QMUILog.h"
#import "QMUIMultipleDelegates.h"
#import "QMUIWeakObjectContainer.h"
#import <AVKit/AVKit.h>
@protocol QMUI_viewWillAppearNotifyDelegate <NSObject>
- (void)qmui_viewControllerDidInvokeViewWillAppear:(UIViewController *)viewController;
@end
@interface _QMUINavigationControllerDelegator : NSObject <QMUINavigationControllerDelegate>
@property(nonatomic, weak) QMUINavigationController *navigationController;
@end
@interface QMUINavigationController () <UIGestureRecognizerDelegate, QMUI_viewWillAppearNotifyDelegate>
@property(nonatomic, strong) _QMUINavigationControllerDelegator *delegator;
/// push/pop push/pop
/// getter PreventConcurrentNavigationControllerTransitions
@property(nonatomic, assign) BOOL isViewControllerTransiting;
/// popcontroller
@property(nonatomic, weak) UIViewController *viewControllerPopping;
@end
@interface UIViewController (QMUINavigationControllerTransition)
@property(nonatomic, weak) id<QMUI_viewWillAppearNotifyDelegate> qmui_viewWillAppearNotifyDelegate;
@end
@implementation UIViewController (QMUINavigationControllerTransition)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
OverrideImplementation([UIViewController class], @selector(viewWillAppear:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UIViewController *selfObject, BOOL firstArgv) {
// call super
void (*originSelectorIMP)(id, SEL, BOOL);
originSelectorIMP = (void (*)(id, SEL, BOOL))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, firstArgv);
if ([selfObject.qmui_viewWillAppearNotifyDelegate respondsToSelector:@selector(qmui_viewControllerDidInvokeViewWillAppear:)]) {
[selfObject.qmui_viewWillAppearNotifyDelegate qmui_viewControllerDidInvokeViewWillAppear:selfObject];
}
};
});
OverrideImplementation([UIViewController class], @selector(viewDidAppear:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UIViewController *selfObject, BOOL firstArgv) {
// call super
void (*originSelectorIMP)(id, SEL, BOOL);
originSelectorIMP = (void (*)(id, SEL, BOOL))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, firstArgv);
if ([selfObject.navigationController.viewControllers containsObject:selfObject] && [selfObject.navigationController isKindOfClass:[QMUINavigationController class]]) {
((QMUINavigationController *)selfObject.navigationController).isViewControllerTransiting = NO;
}
selfObject.qmui_poppingByInteractivePopGestureRecognizer = NO;
selfObject.qmui_willAppearByInteractivePopGestureRecognizer = NO;
};
});
OverrideImplementation([UIViewController class], @selector(viewDidDisappear:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UIViewController *selfObject, BOOL firstArgv) {
// call super
void (*originSelectorIMP)(id, SEL, BOOL);
originSelectorIMP = (void (*)(id, SEL, BOOL))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, firstArgv);
selfObject.qmui_poppingByInteractivePopGestureRecognizer = NO;
selfObject.qmui_willAppearByInteractivePopGestureRecognizer = NO;
};
});
});
}
static char kAssociatedObjectKey_qmui_viewWillAppearNotifyDelegate;
- (void)setQmui_viewWillAppearNotifyDelegate:(id<QMUI_viewWillAppearNotifyDelegate>)qmui_viewWillAppearNotifyDelegate {
objc_setAssociatedObject(self, &kAssociatedObjectKey_qmui_viewWillAppearNotifyDelegate, [[QMUIWeakObjectContainer alloc] initWithObject:qmui_viewWillAppearNotifyDelegate], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id<QMUI_viewWillAppearNotifyDelegate>)qmui_viewWillAppearNotifyDelegate {
QMUIWeakObjectContainer *weakContainer = objc_getAssociatedObject(self, &kAssociatedObjectKey_qmui_viewWillAppearNotifyDelegate);
if (weakContainer.isQMUIWeakObjectContainer) {
id notifyDelegate = [weakContainer object];
return notifyDelegate;
}
return nil;
}
@end
@implementation QMUINavigationController
#pragma mark - &&
- (void)qmui_didInitialize {
[super qmui_didInitialize];
self.qmui_multipleDelegatesEnabled = YES;
self.delegator = [[_QMUINavigationControllerDelegator alloc] init];
self.delegator.navigationController = self;
self.delegate = self.delegator;
BeginIgnoreDeprecatedWarning
[self didInitialize];
EndIgnoreDeprecatedWarning
}
- (void)didInitialize {
}
- (void)dealloc {
self.delegate = nil;
}
- (void)viewDidLoad {
[super viewDidLoad];
// addTarget
[self.interactivePopGestureRecognizer addTarget:self action:@selector(handleInteractivePopGestureRecognizer:)];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self willShowViewController:self.topViewController animated:animated];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self didShowViewController:self.topViewController animated:animated];
}
- (UIViewController *)popViewControllerAnimated:(BOOL)animated {
if (self.viewControllers.count < 2) {
// 1 viewController viewController popViewControllerAnimated: willPop / didPop
return [super popViewControllerAnimated:animated];
}
UIViewController *viewController = [self topViewController];
self.viewControllerPopping = viewController;
if (animated) {
self.viewControllerPopping.qmui_viewWillAppearNotifyDelegate = self;
self.isViewControllerTransiting = YES;
}
if ([viewController respondsToSelector:@selector(willPopInNavigationControllerWithAnimated:)]) {
[((UIViewController<QMUINavigationControllerTransitionDelegate> *)viewController) willPopInNavigationControllerWithAnimated:animated];
}
// QMUILog(@"NavigationItem", @"call popViewControllerAnimated:%@, current viewControllers = %@", StringFromBOOL(animated), self.viewControllers);
viewController = [super popViewControllerAnimated:animated];
// QMUILog(@"NavigationItem", @"pop viewController: %@", viewController);
if ([viewController respondsToSelector:@selector(didPopInNavigationControllerWithAnimated:)]) {
[((UIViewController<QMUINavigationControllerTransitionDelegate> *)viewController) didPopInNavigationControllerWithAnimated:animated];
}
return viewController;
}
- (NSArray<UIViewController *> *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated {
if (!viewController || self.topViewController == viewController) {
// pop viewController super return
return [super popToViewController:viewController animated:animated];
}
self.viewControllerPopping = self.topViewController;
if (animated) {
self.viewControllerPopping.qmui_viewWillAppearNotifyDelegate = self;
self.isViewControllerTransiting = YES;
}
// will pop
for (NSInteger i = self.viewControllers.count - 1; i > 0; i--) {
UIViewController *viewControllerPopping = self.viewControllers[i];
if (viewControllerPopping == viewController) {
break;
}
if ([viewControllerPopping respondsToSelector:@selector(willPopInNavigationControllerWithAnimated:)]) {
BOOL animatedArgument = i == self.viewControllers.count - 1 ? animated : NO;// viewController animated viewController pop
[((UIViewController<QMUINavigationControllerTransitionDelegate> *)viewControllerPopping) willPopInNavigationControllerWithAnimated:animatedArgument];
}
}
NSArray<UIViewController *> *poppedViewControllers = [super popToViewController:viewController animated:animated];
// did pop
for (NSInteger i = poppedViewControllers.count - 1; i >= 0; i--) {
UIViewController *viewControllerPopped = poppedViewControllers[i];
if ([viewControllerPopped respondsToSelector:@selector(didPopInNavigationControllerWithAnimated:)]) {
BOOL animatedArgument = i == poppedViewControllers.count - 1 ? animated : NO;// viewController animated viewController pop
[((UIViewController<QMUINavigationControllerTransitionDelegate> *)viewControllerPopped) didPopInNavigationControllerWithAnimated:animatedArgument];
}
}
return poppedViewControllers;
}
- (NSArray<UIViewController *> *)popToRootViewControllerAnimated:(BOOL)animated {
// tabBarItem 使 item popToRootViewControllerAnimated: rootViewController
if (self.topViewController == self.qmui_rootViewController) {
return nil;
}
self.viewControllerPopping = self.topViewController;
if (animated) {
self.viewControllerPopping.qmui_viewWillAppearNotifyDelegate = self;
self.isViewControllerTransiting = YES;
}
// will pop
for (NSInteger i = self.viewControllers.count - 1; i > 0; i--) {
UIViewController *viewControllerPopping = self.viewControllers[i];
if ([viewControllerPopping respondsToSelector:@selector(willPopInNavigationControllerWithAnimated:)]) {
BOOL animatedArgument = i == self.viewControllers.count - 1 ? animated : NO;// viewController animated viewController pop
[((UIViewController<QMUINavigationControllerTransitionDelegate> *)viewControllerPopping) willPopInNavigationControllerWithAnimated:animatedArgument];
}
}
NSArray<UIViewController *> * poppedViewControllers = [super popToRootViewControllerAnimated:animated];
// did pop
for (NSInteger i = poppedViewControllers.count - 1; i >= 0; i--) {
UIViewController *viewControllerPopped = poppedViewControllers[i];
if ([viewControllerPopped respondsToSelector:@selector(didPopInNavigationControllerWithAnimated:)]) {
BOOL animatedArgument = i == poppedViewControllers.count - 1 ? animated : NO;// viewController animated viewController pop
[((UIViewController<QMUINavigationControllerTransitionDelegate> *)viewControllerPopped) didPopInNavigationControllerWithAnimated:animatedArgument];
}
}
return poppedViewControllers;
}
- (void)setViewControllers:(NSArray<UIViewController *> *)viewControllers animated:(BOOL)animated {
UIViewController *topViewController = self.topViewController;
// will pop
NSMutableArray<UIViewController *> *viewControllersPopping = self.viewControllers.mutableCopy;
[viewControllersPopping removeObjectsInArray:viewControllers];
[viewControllersPopping enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(UIViewController * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj respondsToSelector:@selector(willPopInNavigationControllerWithAnimated:)]) {
BOOL animatedArgument = obj == topViewController ? animated : NO;// viewController animated viewController pop
[((UIViewController<QMUINavigationControllerTransitionDelegate> *)obj) willPopInNavigationControllerWithAnimated:animatedArgument];
}
}];
// setViewControllers pushViewController
[viewControllers enumerateObjectsUsingBlock:^(UIViewController * _Nonnull viewController, NSUInteger idx, BOOL * _Nonnull stop) {
[self updateBackItemTitleWithCurrentViewController:viewController nextViewController:idx + 1 < viewControllers.count ? viewControllers[idx + 1] : nil];
}];
[super setViewControllers:viewControllers animated:animated];
// did pop
[viewControllersPopping enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(UIViewController * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj respondsToSelector:@selector(didPopInNavigationControllerWithAnimated:)]) {
BOOL animatedArgument = obj == topViewController ? animated : NO;// viewController animated viewController pop
[((UIViewController<QMUINavigationControllerTransitionDelegate> *)obj) didPopInNavigationControllerWithAnimated:animatedArgument];
}
}];
// topViewController
if (topViewController == viewControllers.lastObject) {
if ([topViewController respondsToSelector:@selector(viewControllerKeepingAppearWhenSetViewControllersWithAnimated:)]) {
[((UIViewController<QMUINavigationControllerTransitionDelegate> *)topViewController) viewControllerKeepingAppearWhenSetViewControllersWithAnimated:animated];
}
}
}
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
if (!viewController) return;
if (self.isViewControllerTransiting && animated) {
QMUILogWarn(NSStringFromClass(self.class), @"%@, 上一次界面切换的动画尚未结束就试图进行新的 push 操作,为了避免产生 bug将本次 push 改为非动画形式。\n%s, isViewControllerTransiting = %@, viewController = %@, self.viewControllers = %@", NSStringFromClass(self.class), __func__, StringFromBOOL(self.isViewControllerTransiting), viewController, self.viewControllers);
animated = NO;
}
if (self.isViewLoaded) {
if (self.view.window) {
// self.view.window UINavigationController prenset vc nav tabBar tab pushViewController navigationController:didShowViewController:animated: delegate isViewControllerTransiting
// https://github.com/Tencent/QMUI_iOS/issues/261
if (animated) {
self.isViewControllerTransiting = YES;
}
} else {
QMUILogWarn(NSStringFromClass(self.class), @"push 的时候 navigationController 不可见(例如上面盖着一个 prenset vc或者切到别的 tab可能导致一些 UINavigationControllerDelegate 不会被调用");
}
}
// push
[self updateBackItemTitleWithCurrentViewController:self.topViewController nextViewController:viewController];
[super pushViewController:viewController animated:animated];
// push push push
// https://github.com/Tencent/QMUI_iOS/issues/426
if (![self.viewControllers containsObject:viewController]) {
self.isViewControllerTransiting = NO;
}
}
- (void)updateBackItemTitleWithCurrentViewController:(UIViewController *)currentViewController nextViewController:(UIViewController *)nextViewController {
if (!currentViewController) return;
// viewController NeedsBackBarButtonItemTitle
UIViewController<QMUINavigationControllerAppearanceDelegate> *vc = (UIViewController<QMUINavigationControllerAppearanceDelegate> *)nextViewController;
if ([vc respondsToSelector:@selector(qmui_backBarButtonItemTitleWithPreviousViewController:)]) {
NSString *title = [vc qmui_backBarButtonItemTitleWithPreviousViewController:currentViewController];
currentViewController.navigationItem.backBarButtonItem = title ? [[UIBarButtonItem alloc] initWithTitle:title style:UIBarButtonItemStylePlain target:nil action:NULL] : nil;
return;
}
//
if (QMUICMIActivated && !NeedsBackBarButtonItemTitle) {
if (@available(iOS 14.0, *)) {
// API iOS 14 viewController title
currentViewController.navigationItem.backButtonDisplayMode = UINavigationItemBackButtonDisplayModeMinimal;
return;
}
// backBarButtonItem
if (!currentViewController.navigationItem.backBarButtonItem) {
currentViewController.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:NULL];
}
}
}
#pragma mark -
- (BOOL)isViewControllerTransiting {
// 使 isViewControllerTransiting NO
if (!PreventConcurrentNavigationControllerTransitions) {
return NO;
}
return _isViewControllerTransiting;
}
//
- (void)handleInteractivePopGestureRecognizer:(UIScreenEdgePanGestureRecognizer *)gestureRecognizer {
UIGestureRecognizerState state = gestureRecognizer.state;
UIViewController<QMUINavigationControllerTransitionDelegate> *viewControllerWillDisappear = [self.transitionCoordinator viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController<QMUINavigationControllerTransitionDelegate> *viewControllerWillAppear = [self.transitionCoordinator viewControllerForKey:UITransitionContextToViewControllerKey];
viewControllerWillDisappear.qmui_poppingByInteractivePopGestureRecognizer = YES;
viewControllerWillDisappear.qmui_willAppearByInteractivePopGestureRecognizer = NO;
viewControllerWillAppear.qmui_poppingByInteractivePopGestureRecognizer = NO;
viewControllerWillAppear.qmui_willAppearByInteractivePopGestureRecognizer = YES;
if (state == UIGestureRecognizerStateBegan) {
// UIGestureRecognizerStateBegan viewWillAppear: viewWillAppear: viewWillAppear: dispatch viewWillAppear: Runloop
dispatch_async(dispatch_get_main_queue(), ^{
viewControllerWillDisappear.qmui_navigationControllerPopGestureRecognizerChanging = YES;
viewControllerWillAppear.qmui_navigationControllerPopGestureRecognizerChanging = YES;
});
} else if (state > UIGestureRecognizerStateChanged) {
viewControllerWillDisappear.qmui_navigationControllerPopGestureRecognizerChanging = NO;
viewControllerWillAppear.qmui_navigationControllerPopGestureRecognizerChanging = NO;
}
if (state == UIGestureRecognizerStateEnded) {
if (self.transitionCoordinator.cancelled) {
QMUILog(NSStringFromClass(self.class), @"手势返回放弃了");
UIViewController<QMUINavigationControllerTransitionDelegate> *temp = viewControllerWillDisappear;
viewControllerWillDisappear = viewControllerWillAppear;
viewControllerWillAppear = temp;
} else {
QMUILog(NSStringFromClass(self.class), @"执行手势返回");
}
}
if ([viewControllerWillDisappear respondsToSelector:@selector(navigationController:poppingByInteractiveGestureRecognizer:isCancelled:viewControllerWillDisappear:viewControllerWillAppear:)]) {
[((UIViewController<QMUINavigationControllerTransitionDelegate> *)viewControllerWillDisappear) navigationController:self poppingByInteractiveGestureRecognizer:gestureRecognizer isCancelled:self.transitionCoordinator.cancelled viewControllerWillDisappear:viewControllerWillDisappear viewControllerWillAppear:viewControllerWillAppear];
}
if ([viewControllerWillAppear respondsToSelector:@selector(navigationController:poppingByInteractiveGestureRecognizer:isCancelled:viewControllerWillDisappear:viewControllerWillAppear:)]) {
[((UIViewController<QMUINavigationControllerTransitionDelegate> *)viewControllerWillAppear) navigationController:self poppingByInteractiveGestureRecognizer:gestureRecognizer isCancelled:self.transitionCoordinator.cancelled viewControllerWillDisappear:viewControllerWillDisappear viewControllerWillAppear:viewControllerWillAppear];
}
BeginIgnoreDeprecatedWarning
if ([viewControllerWillDisappear respondsToSelector:@selector(navigationController:poppingByInteractiveGestureRecognizer:viewControllerWillDisappear:viewControllerWillAppear:)]) {
[((UIViewController<QMUINavigationControllerTransitionDelegate> *)viewControllerWillDisappear) navigationController:self poppingByInteractiveGestureRecognizer:gestureRecognizer viewControllerWillDisappear:viewControllerWillDisappear viewControllerWillAppear:viewControllerWillAppear];
}
if ([viewControllerWillAppear respondsToSelector:@selector(navigationController:poppingByInteractiveGestureRecognizer:viewControllerWillDisappear:viewControllerWillAppear:)]) {
[((UIViewController<QMUINavigationControllerTransitionDelegate> *)viewControllerWillAppear) navigationController:self poppingByInteractiveGestureRecognizer:gestureRecognizer viewControllerWillDisappear:viewControllerWillDisappear viewControllerWillAppear:viewControllerWillAppear];
}
EndIgnoreDeprecatedWarning
}
- (void)qmui_viewControllerDidInvokeViewWillAppear:(UIViewController *)viewController {
viewController.qmui_viewWillAppearNotifyDelegate = nil;
[self.delegator navigationController:self willShowViewController:self.viewControllerPopping animated:YES];
self.viewControllerPopping = nil;
self.isViewControllerTransiting = NO;
}
#pragma mark - StatusBar
- (UIViewController *)childViewControllerIfSearching:(UIViewController *)childViewController customBlock:(BOOL (^)(UIViewController *vc))hasCustomizedStatusBarBlock {
UIViewController *presentedViewController = childViewController.presentedViewController;
// 3. viewControllers vc definesPresentationContext = YES present vc UISearchController self presentedViewController 1 present vc statusBar present vc
if (!presentedViewController.beingDismissed && presentedViewController && presentedViewController != self.presentedViewController && hasCustomizedStatusBarBlock(presentedViewController)) {
return [self childViewControllerIfSearching:childViewController.presentedViewController customBlock:hasCustomizedStatusBarBlock];
}
// 4. dismiss iOS 13 present UISearchController 退 statusBar childViewController dismiss vc self.topViewController present
if (childViewController.beingDismissed) {
return [self childViewControllerIfSearching:self.topViewController customBlock:hasCustomizedStatusBarBlock];
}
return childViewController;
}
// hasCustomizedStatusBarBlock vc hidden/style
- (UIViewController *)childViewControllerForStatusBarWithCustomBlock:(BOOL (^)(UIViewController *vc))hasCustomizedStatusBarBlock {
// 1. modal present modal present vc definesPresentationContext UISearchController
UIViewController *childViewController = self.visibleViewController;
// 2. modal present UINavigationController self.visibleViewController UINavigationController.topViewController UINavigationController beingDismissed
if (childViewController.navigationController && (self.presentedViewController == childViewController.navigationController)) {
childViewController = childViewController.navigationController;
}
childViewController = [self childViewControllerIfSearching:childViewController customBlock:hasCustomizedStatusBarBlock];
if (QMUICMIActivated) {
if (hasCustomizedStatusBarBlock(childViewController)) {
return childViewController;
}
return nil;
}
return childViewController;
}
- (UIViewController *)childViewControllerForStatusBarHidden {
return [self childViewControllerForStatusBarWithCustomBlock:^BOOL(UIViewController *vc) {
return vc.qmui_prefersStatusBarHiddenBlock || [vc qmui_hasOverrideUIKitMethod:@selector(prefersStatusBarHidden)];
}];
}
- (UIViewController *)childViewControllerForStatusBarStyle {
return [self childViewControllerForStatusBarWithCustomBlock:^BOOL(UIViewController *vc) {
return vc.qmui_preferredStatusBarStyleBlock || [vc qmui_hasOverrideUIKitMethod:@selector(preferredStatusBarStyle)];
}];
}
- (UIStatusBarStyle)preferredStatusBarStyle {
// -[UIViewController childViewControllerForStatusBarStyle] nil vc preferredStatusBarStyle nil self preferredStatusBarStyle iOS 13 present UISearchController 便 childViewControllerForStatusBarStyle vc -[self preferredStatusBarStyle]
UIViewController *childViewController = [self childViewControllerForStatusBarStyle];
if (childViewController) {
return [childViewController preferredStatusBarStyle];
}
if (QMUICMIActivated) {
return DefaultStatusBarStyle;
}
return [super preferredStatusBarStyle];
}
#pragma mark -
- (BOOL)shouldAutorotate {
return [self.visibleViewController qmui_hasOverrideUIKitMethod:_cmd] ? [self.visibleViewController shouldAutorotate] : YES;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
// fix UIAlertController:supportedInterfaceOrientations was invoked recursively!
// crash in iOS 9 and show log in iOS 10 and later
// https://github.com/Tencent/QMUI_iOS/issues/502
// https://github.com/Tencent/QMUI_iOS/issues/632
UIViewController *visibleViewController = self.visibleViewController;
if (!visibleViewController || visibleViewController.isBeingDismissed || [visibleViewController isKindOfClass:UIAlertController.class]) {
visibleViewController = self.topViewController;
}
return [visibleViewController qmui_hasOverrideUIKitMethod:_cmd] ? [visibleViewController supportedInterfaceOrientations] : SupportedOrientationMask;
}
#pragma mark - HomeIndicator
- (UIViewController *)childViewControllerForHomeIndicatorAutoHidden {
return self.topViewController;
}
@end
@implementation QMUINavigationController (UISubclassingHooks)
- (void)willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
//
}
- (void)didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
//
}
@end
@implementation _QMUINavigationControllerDelegator
#pragma mark - <UINavigationControllerDelegate>
- (void)navigationController:(QMUINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
[navigationController willShowViewController:viewController animated:animated];
}
- (void)navigationController:(QMUINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
navigationController.viewControllerPopping = nil;
[navigationController didShowViewController:viewController animated:animated];
}
@end
// Category
// https://github.com/Tencent/QMUI_iOS/issues/1130
@interface UINavigationItem (QMUIBackBarButtonItemTitle)
@property(nonatomic, strong) UIBarButtonItem *qmuibbbt_backItem;
@end
@implementation UINavigationItem (QMUIBackBarButtonItemTitle)
QMUISynthesizeIdStrongProperty(qmuibbbt_backItem, setQmuibbbt_backItem);
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
OverrideImplementation([UINavigationItem class], @selector(setBackBarButtonItem:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UINavigationItem *selfObject, UIBarButtonItem *backBarButtonItem) {
UINavigationBar *navigationBar = selfObject.qmui_navigationBar;
UINavigationController *navigationController = selfObject.qmui_navigationController;
if (navigationController) {
if ([navigationBar.items containsObject:selfObject]
&& (navigationBar.topItem != selfObject || navigationController.qmui_isPushing || navigationController.qmui_isPopping)
&& (!selfObject.qmuibbbt_backItem || selfObject.qmuibbbt_backItem != backBarButtonItem)) {
// vc backBarButtonItem使 qmui_backBarButtonItemTitleWithPreviousViewController:
UIViewController *currentViewController = nil;
UIViewController *nextViewController = nil;
NSInteger indexForChildViewController = [navigationBar.items indexOfObject:selfObject] + 1;
if (indexForChildViewController < navigationController.viewControllers.count) {
nextViewController = navigationController.viewControllers[indexForChildViewController];
currentViewController = navigationController.viewControllers[indexForChildViewController - 1];
} else if (navigationController.qmui_isPopping) {
// UINavigationController pop navigationBar.items pop navigationController.viewControllers pop transitionCoordinator pop
nextViewController = [navigationController.transitionCoordinator viewControllerForKey:UITransitionContextFromViewControllerKey];
currentViewController = [navigationController.transitionCoordinator viewControllerForKey:UITransitionContextToViewControllerKey];
}
if ([nextViewController respondsToSelector:@selector(qmui_backBarButtonItemTitleWithPreviousViewController:)]) {
QMUIAssert(!!currentViewController, @"UINavigationItem (QMUIBackBarButtonItemTitle)", @"currentViewController 和 nextViewController 必须同时存在");
selfObject.qmuibbbt_backItem = backBarButtonItem;
return;
} else if (!nextViewController) {
QMUILogWarn(@"UINavigationItem (QMUIBackBarButtonItemTitle)", @"当前界面理应存在子界面但获取不到qmui_isPopping = %@, navigationBar.items = %@", StringFromBOOL(navigationController.qmui_isPopping), navigationBar.items);
}
}
}
if (selfObject.qmuibbbt_backItem) {
selfObject.qmuibbbt_backItem = nil;
}
// call super
void (*originSelectorIMP)(id, SEL, UIBarButtonItem *);
originSelectorIMP = (void (*)(id, SEL, UIBarButtonItem *))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, backBarButtonItem);
};
});
OverrideImplementation([UIViewController class], @selector(viewDidAppear:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UIViewController *selfObject, BOOL firstArgv) {
// setBackBarButtonItem
if (selfObject.navigationItem.qmuibbbt_backItem) {
selfObject.navigationItem.backBarButtonItem = selfObject.navigationItem.qmuibbbt_backItem;
}
// call super
void (*originSelectorIMP)(id, SEL, BOOL);
originSelectorIMP = (void (*)(id, SEL, BOOL))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, firstArgv);
};
});
});
}
@end
@implementation QMUINavigationTitleView (QMUINavigationController)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// title titleView titleView
OverrideImplementation([UINavigationItem class], @selector(setTitleView:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UINavigationItem *selfObject, QMUINavigationTitleView *titleView) {
// call super
void (*originSelectorIMP)(id, SEL, UIView *);
originSelectorIMP = (void (*)(id, SEL, UIView *))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, titleView);
if ([titleView isKindOfClass:QMUINavigationTitleView.class]) {
if ([selfObject.qmui_viewController respondsToSelector:@selector(qmui_titleViewTintColor)]) {
titleView.tintColor = ((id<QMUINavigationControllerDelegate>)selfObject.qmui_viewController).qmui_titleViewTintColor;
}
}
};
});
});
}
@end