cdts/xdts-ios 3/TreeHole/CYHResetCode/CYH/QMUIKit/UIKitExtensions/UINavigationBar+QMUI.m

371 lines
24 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.
*/
//
// UINavigationBar+QMUI.m
// QMUIKit
//
// Created by QMUI Team on 2018/O/8.
//
#import "UINavigationBar+QMUI.h"
#import "QMUICore.h"
#import "NSObject+QMUI.h"
#import "UIView+QMUI.h"
#import "NSArray+QMUI.h"
#import "UINavigationItem+QMUI.h"
NSString *const kShouldFixTitleViewBugKey = @"kShouldFixTitleViewBugKey";
@implementation UINavigationBar (QMUI)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// [UIKit Bug] Xcode 14 App iOS 16.0
// https://github.com/Tencent/QMUI_iOS/issues/1457
//#ifdef IOS16_SDK_ALLOWED Xcode 13 Xcode
if (@available(iOS 16.0, *)) {
if (@available(iOS 16.1, *)) {
// iOS 16.1
} else {
OverrideImplementation([UINavigationItem class], @selector(setTitleView:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UINavigationItem *selfObject, UIView *firstArgv) {
// call super
void (*originSelectorIMP)(id, SEL, UIView *);
originSelectorIMP = (void (*)(id, SEL, UIView *))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, firstArgv);
if (!firstArgv) return;
UINavigationBar *navigationBar = selfObject.qmui_navigationBar;
[navigationBar qmuinb_fixTitleViewLayoutInIOS16];
};
});
OverrideImplementation([UINavigationBar class], @selector(pushNavigationItem:animated:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UINavigationBar *selfObject, UINavigationItem *navigationItem, BOOL animated) {
if (!animated && !selfObject.topItem.titleView && navigationItem.titleView) {
[selfObject qmuinb_fixTitleViewLayoutInIOS16];
}
// call super
void (*originSelectorIMP)(id, SEL, UINavigationItem *, BOOL);
originSelectorIMP = (void (*)(id, SEL, UINavigationItem *, BOOL))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, navigationItem, animated);
};
});
OverrideImplementation([UINavigationBar class], @selector(setItems:animated:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UINavigationBar *selfObject, NSArray<UINavigationItem *> *items, BOOL animated) {
if (!animated && !selfObject.topItem.titleView && items.lastObject.titleView) {
[selfObject qmuinb_fixTitleViewLayoutInIOS16];
}
// call super
void (*originSelectorIMP)(id, SEL, NSArray<UINavigationItem *> *, BOOL);
originSelectorIMP = (void (*)(id, SEL, NSArray<UINavigationItem *> *, BOOL))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, items, animated);
};
});
}
}
//#endif
// [UIKit Bug] iOS 12 leftBarButtonItem title pop title leftBarButtonItem
// https://github.com/Tencent/QMUI_iOS/issues/1217
// _UITAMICAdaptorView
Class adaptorClass = NSClassFromString([NSString qmui_stringByConcat:@"_", @"UITAMIC", @"Adaptor", @"View", nil]);
// -[_UINavigationBarContentView didAddSubview:]
OverrideImplementation(NSClassFromString([NSString qmui_stringByConcat:@"_", @"UINavigationBar", @"ContentView", nil]), @selector(didAddSubview:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UIView *selfObject, UIView *firstArgv) {
// call super
void (*originSelectorIMP)(id, SEL, UIView *);
originSelectorIMP = (void (*)(id, SEL, UIView *))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, firstArgv);
if ([firstArgv isKindOfClass:adaptorClass] || [firstArgv isKindOfClass:UILabel.class]) {
firstArgv.qmui_frameWillChangeBlock = ^CGRect(__kindof UIView * _Nonnull view, CGRect followingFrame) {
if ([view qmui_getBoundObjectForKey:kShouldFixTitleViewBugKey]) {
followingFrame = [[view qmui_getBoundObjectForKey:kShouldFixTitleViewBugKey] CGRectValue];
}
return followingFrame;
};
}
};
});
void (^boundTitleViewMinXBlock)(UINavigationBar *, BOOL) = ^void(UINavigationBar *navigationBar, BOOL cleanup) {
if (!navigationBar.topItem.leftBarButtonItem) return;
UIView *titleView = nil;
UIView *adapterView = navigationBar.topItem.titleView.superview;
if ([adapterView isKindOfClass:adaptorClass]) {
titleView = adapterView;
} else {
titleView = [navigationBar.qmui_contentView.subviews qmui_filterWithBlock:^BOOL(__kindof UIView * _Nonnull item) {
return [item isKindOfClass:UILabel.class];
}].firstObject;
}
if (!titleView) return;
if (cleanup) {
[titleView qmui_bindObject:nil forKey:kShouldFixTitleViewBugKey];
} else if (CGRectGetWidth(titleView.frame) > CGRectGetWidth(navigationBar.bounds) / 2) {
[titleView qmui_bindObject:[NSValue valueWithCGRect:titleView.frame] forKey:kShouldFixTitleViewBugKey];
}
};
// // - [UINavigationBar _popNavigationItemWithTransition:]
// - (id) _popNavigationItemWithTransition:(int)arg1; (0x1a15513a0)
OverrideImplementation([UINavigationBar class], NSSelectorFromString([NSString qmui_stringByConcat:@"_", @"popNavigationItem", @"With", @"Transition:", nil]), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^id(UINavigationBar *selfObject, NSInteger firstArgv) {
boundTitleViewMinXBlock(selfObject, NO);
// call super
id (*originSelectorIMP)(id, SEL, NSInteger);
originSelectorIMP = (id (*)(id, SEL, NSInteger))originalIMPProvider();
id result = originSelectorIMP(selfObject, originCMD, firstArgv);
return result;
};
});
// - (void) _completePopOperationAnimated:(BOOL)arg1 transitionAssistant:(id)arg2; (0x1a1551668)
OverrideImplementation([UINavigationBar class], NSSelectorFromString([NSString qmui_stringByConcat:@"_", @"complete", @"PopOperationAnimated:", @"transitionAssistant:", nil]), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UINavigationBar *selfObject, BOOL firstArgv, id secondArgv) {
// call super
void (*originSelectorIMP)(id, SEL, BOOL, id);
originSelectorIMP = (void (*)(id, SEL, BOOL, id))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, firstArgv, secondArgv);
boundTitleViewMinXBlock(selfObject, YES);
};
});
// iOS 12 UINavigationBar iOS 13
// iOS 13 iOS 1314 QMUI iOS 15 使 @available iOS 15 iOS 13 QMUIConfiguration.m
// QMUIConfiguration appearance standardAppearance UINavigationBar window UINavigationBarAppearance standardAppearance UINavigationBar standardAppearance moveToWindow appearance window
#ifdef IOS15_SDK_ALLOWED
if (@available(iOS 15.0, *)) {
void (^syncAppearance)(UINavigationBar *, void(^barActionBlock)(UINavigationBarAppearance *appearance)) = ^void(UINavigationBar *navigationBar, void(^barActionBlock)(UINavigationBarAppearance *appearance)) {
if (!barActionBlock) return;
// navigationBar.standardAppearance UIAppearance issue
// https://github.com/Tencent/QMUI_iOS/issues/1437
UINavigationBarAppearance *appearance = navigationBar.standardAppearance;
barActionBlock(appearance);
navigationBar.standardAppearance = appearance;
if (QMUICMIActivated && NavBarUsesStandardAppearanceOnly) {
navigationBar.scrollEdgeAppearance = appearance;
}
};
OverrideImplementation([UINavigationBar class], @selector(setBarTintColor:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UINavigationBar *selfObject, UIColor *barTintColor) {
// call super
void (*originSelectorIMP)(id, SEL, UIColor *);
originSelectorIMP = (void (*)(id, SEL, UIColor *))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, barTintColor);
syncAppearance(selfObject, ^void(UINavigationBarAppearance *appearance) {
appearance.backgroundColor = barTintColor;
});
};
});
OverrideImplementation([UINavigationBar class], @selector(barTintColor), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^UIColor *(UINavigationBar *selfObject) {
return selfObject.standardAppearance.backgroundColor;
};
});
OverrideImplementation([UINavigationBar class], @selector(setBackgroundImage:forBarPosition:barMetrics:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UINavigationBar *selfObject, UIImage *image, UIBarPosition barPosition, UIBarMetrics barMetrics) {
// call super
void (*originSelectorIMP)(id, SEL, UIImage *, UIBarPosition, UIBarMetrics);
originSelectorIMP = (void (*)(id, SEL, UIImage *, UIBarPosition, UIBarMetrics))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, image, barPosition, barMetrics);
syncAppearance(selfObject, ^void(UINavigationBarAppearance *appearance) {
appearance.backgroundImage = image;
});
};
});
OverrideImplementation([UINavigationBar class], @selector(backgroundImageForBarPosition:barMetrics:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^UIImage *(UINavigationBar *selfObject, UIBarPosition firstArgv, UIBarMetrics secondArgv) {
return selfObject.standardAppearance.backgroundImage;
};
});
OverrideImplementation([UINavigationBar class], @selector(setShadowImage:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UINavigationBar *selfObject, UIImage *shadowImage) {
// call super
void (*originSelectorIMP)(id, SEL, UIImage *);
originSelectorIMP = (void (*)(id, SEL, UIImage *))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, shadowImage);
syncAppearance(selfObject, ^void(UINavigationBarAppearance *appearance) {
appearance.shadowImage = shadowImage;
});
};
});
OverrideImplementation([UINavigationBar class], @selector(shadowImage), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^UIImage *(UINavigationBar *selfObject) {
return selfObject.standardAppearance.shadowImage;
};
});
OverrideImplementation([UINavigationBar class], @selector(setBarStyle:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UINavigationBar *selfObject, UIBarStyle barStyle) {
// call super
void (*originSelectorIMP)(id, SEL, UIBarStyle);
originSelectorIMP = (void (*)(id, SEL, UIBarStyle))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, barStyle);
syncAppearance(selfObject, ^void(UINavigationBarAppearance *appearance) {
appearance.backgroundEffect = [UIBlurEffect effectWithStyle:barStyle == UIBarStyleDefault ? UIBlurEffectStyleSystemChromeMaterialLight : UIBlurEffectStyleSystemChromeMaterialDark];
});
};
});
// iOS 15
// OverrideImplementation([UINavigationBar class], @selector(barStyle), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
// return ^UIBarStyle(UINavigationBar *selfObject) {
//
// if (@available(iOS 15.0, *)) {
// return ???;
// }
//
//
// // call super
// UIBarStyle (*originSelectorIMP)(id, SEL);
// originSelectorIMP = (UIBarStyle (*)(id, SEL))originalIMPProvider();
// UIBarStyle result = originSelectorIMP(selfObject, originCMD);
//
// return result;
// };
// });
OverrideImplementation([UINavigationBar class], @selector(setTitleTextAttributes:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UINavigationBar *selfObject, NSDictionary<NSAttributedStringKey, id> *titleTextAttributes) {
// call super
void (*originSelectorIMP)(id, SEL, NSDictionary<NSAttributedStringKey, id> *);
originSelectorIMP = (void (*)(id, SEL, NSDictionary<NSAttributedStringKey, id> *))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, titleTextAttributes);
syncAppearance(selfObject, ^void(UINavigationBarAppearance *appearance) {
appearance.titleTextAttributes = titleTextAttributes;
});
};
});
}
if (@available(iOS 15.0, *)) {
if (!QMUICMIActivated) return;
if (!(NavBarRemoveBackgroundEffectAutomatically || TabBarRemoveBackgroundEffectAutomatically || ToolBarRemoveBackgroundEffectAutomatically)
&& !(NavBarUsesStandardAppearanceOnly || TabBarUsesStandardAppearanceOnly || ToolBarUsesStandardAppearanceOnly)) return;
// - [_UIBarBackground updateBackground]
OverrideImplementation(NSClassFromString(@"_UIBarBackground"), NSSelectorFromString(@"updateBackground"), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UIView *selfObject) {
// call super
void (*originSelectorIMP)(id, SEL);
originSelectorIMP = (void (*)(id, SEL))originalIMPProvider();
originSelectorIMP(selfObject, originCMD);
if (!selfObject.superview) return;
if (!NavBarRemoveBackgroundEffectAutomatically && !NavBarUsesStandardAppearanceOnly && [selfObject.superview isKindOfClass:UINavigationBar.class]) return;
if (!TabBarRemoveBackgroundEffectAutomatically && !TabBarUsesStandardAppearanceOnly && [selfObject.superview isKindOfClass:UITabBar.class]) return;
if (!ToolBarRemoveBackgroundEffectAutomatically && !ToolBarUsesStandardAppearanceOnly && [selfObject.superview isKindOfClass:UIToolbar.class]) return;
UIImageView *backgroundImageView1 = [selfObject valueForKey:@"_colorAndImageView1"];
UIImageView *backgroundImageView2 = [selfObject valueForKey:@"_colorAndImageView2"];
UIVisualEffectView *backgroundEffectView1 = [selfObject valueForKey:@"_effectView1"];
UIVisualEffectView *backgroundEffectView2 = [selfObject valueForKey:@"_effectView2"];
// iOS 14 backgroundImage barTintColor view
// iOS 15 backgroundImagebackgroundColorbackgroundEffect _colorAndImageView iOS 14 _colorAndImageView backgroundImage
if (NavBarRemoveBackgroundEffectAutomatically || TabBarRemoveBackgroundEffectAutomatically || ToolBarRemoveBackgroundEffectAutomatically) {
BOOL hasBackgroundImage1 = backgroundImageView1 && backgroundImageView1.superview && !backgroundImageView1.hidden && backgroundImageView1.image;
BOOL hasBackgroundImage2 = backgroundImageView2 && backgroundImageView2.superview && !backgroundImageView2.hidden && backgroundImageView2.image;
BOOL shouldHideEffectView = hasBackgroundImage1 || hasBackgroundImage2;
if (shouldHideEffectView) {
backgroundEffectView1.hidden = YES;
backgroundEffectView2.hidden = YES;
} else {
// backgroundImage nil effectView iOS 15 effectView 2 contentScrollView updateBackground ...
}
}
// 4.4.0 scrollEdgeAppearance view standard scrollEdge view 4.4.4 view
if (NavBarUsesStandardAppearanceOnly || TabBarUsesStandardAppearanceOnly || ToolBarUsesStandardAppearanceOnly) {
backgroundImageView2.hidden = YES;
backgroundEffectView2.hidden = YES;
}
};
});
// UIAppearance bar bar
// https://github.com/Tencent/QMUI_iOS/issues/1451
// - [UINavigationBar setStandardAppearance:]
OverrideImplementation([UINavigationBar class], @selector(setStandardAppearance:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UINavigationBar *selfObject, UINavigationBarAppearance * firstArgv) {
// call super
void (*originSelectorIMP)(id, SEL, UINavigationBarAppearance *);
originSelectorIMP = (void (*)(id, SEL, UINavigationBarAppearance *))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, firstArgv);
// UINavigationController navigationBar new bar superview UILayoutContainerView
if ([NSStringFromClass(selfObject.superview.class) hasPrefix:@"UILayoutContainer"] && !selfObject.window) {
QMUIAssert(NO, @"UINavigationBar (QMUI)", @"试图在 UINavigationBar 尚未添加到 window 上时就修改它的样式,可能导致 UINavigationBar 的样式无法与全局保持一致。");
}
};
});
}
#endif
});
}
- (UIView *)qmui_contentView {
return [self valueForKeyPath:@"visualProvider.contentView"];
}
- (void)qmuinb_fixTitleViewLayoutInIOS16 {
// _UINavigationBarTitleControl
Class titleControlClass = NSClassFromString([NSString qmui_stringByConcat:@"_", @"UINavigationBar", @"TitleControl", nil]);
UIView *titleControl = [self.qmui_contentView.subviews qmui_filterWithBlock:^BOOL(__kindof UIView * _Nonnull item) {
return [item isKindOfClass:titleControlClass];
}].firstObject;
titleControl.qmui_frameWillChangeBlock = ^CGRect(__kindof UIView * _Nonnull view, CGRect followingFrame) {
followingFrame = CGRectSetY(followingFrame, CGRectGetMinYVerticallyCenterInParentRect(view.superview.bounds, followingFrame));
return followingFrame;
};
}
@end