UITabBar आइटम्स iOS 12.1 पर बैक नेविगेशन पर कूदते हैं
swift (8)
मेरे पास एक मास्टर स्क्रीन पर
UITabBarController
साथ एक iOS ऐप है, जिसमें
hidesBottomBarWhenPushed = true
को सेट करने के साथ
UITabBarController
छिपाते हुए एक विस्तार स्क्रीन
hidesBottomBarWhenPushed = true
।
जब
UITabBarController
मास्टर स्क्रीन पर वापस जा रहा है तो एक अजीब "कूद" करता है जैसा कि इस GIF पर दिखाया गया है:
यह केवल iOS 12.1 पर होता है, 12.0 या 11.x पर नहीं।
एक iOS 12.1 बग की तरह लगता है, क्योंकि मैंने इस व्यवहार के साथ FB मैसेंजर जैसे अन्य एप्लिकेशन को देखा, लेकिन मैं सोच रहा था कि क्या इसके लिए किसी तरह का वर्कअराउंड है?
Apple ने अब iOS 12.1.1 में तय कर दिया है
आप इस के साथ कुछ iOS 12 विध्वंस के लिए ओवरराइड
- (UIEdgeInsets)safeAreaInsets
विधि को ओवरराइड कर सकते हैं:
- (UIEdgeInsets)safeAreaInsets {
UIEdgeInsets insets = [super safeAreaInsets];
CGFloat h = CGRectGetHeight(self.frame);
if (insets.bottom >= h) {
insets.bottom = [self.window safeAreaInsets].bottom;
}
return insets;
}
इस समस्या को ठीक करने के दो तरीके हैं, पहला, आपके UITabBarController में, isranslucent = झूठा सेट करना:
[[UITabBar appearance] setTranslucent:NO];
ईमानदारी से, अगर पहला समाधान आपके issur को ठीक नहीं करता है, तो इस तरह से प्रयास करें:
यहाँ उद्देश्य-सी कोड है
// .h
@interface CYLTabBar : UITabBar
@end
// .m
#import "CYLTabBar.h"
CG_INLINE BOOL
OverrideImplementation(Class targetClass, SEL targetSelector, id (^implementationBlock)(Class originClass, SEL originCMD, IMP originIMP)) {
Method originMethod = class_getInstanceMethod(targetClass, targetSelector);
if (!originMethod) {
return NO;
}
IMP originIMP = method_getImplementation(originMethod);
method_setImplementation(originMethod, imp_implementationWithBlock(implementationBlock(targetClass, targetSelector, originIMP)));
return YES;
}
@implementation CYLTabBar
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (@available(iOS 12.1, *)) {
OverrideImplementation(NSClassFromString(@"UITabBarButton"), @selector(setFrame:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP originIMP) {
return ^(UIView *selfObject, CGRect firstArgv) {
if ([selfObject isKindOfClass:originClass]) {
if (!CGRectIsEmpty(selfObject.frame) && CGRectIsEmpty(firstArgv)) {
return;
}
}
// call super
void (*originSelectorIMP)(id, SEL, CGRect);
originSelectorIMP = (void (*)(id, SEL, CGRect))originIMP;
originSelectorIMP(selfObject, originCMD, firstArgv);
};
});
}
});
}
@end
अधिक जानकारी: https://github.com/ChenYilong/CYLTabBarController/commit/2c741c8bffd47763ad2fca198202946a2a63c4fc
मुझे लगता है कि यह Apple का बग है लेकिन आप इसे एक हॉट फ़िक्स के रूप में आज़मा सकते हैं: बस निम्नलिखित कोड के साथ अपने टैबबार के लिए एक क्लास बनाएं:
import UIKit
class FixedTabBar: UITabBar {
var itemFrames = [CGRect]()
var tabBarItems = [UIView]()
override func layoutSubviews() {
super.layoutSubviews()
if itemFrames.isEmpty, let UITabBarButtonClass = NSClassFromString("UITabBarButton") as? NSObject.Type {
tabBarItems = subviews.filter({$0.isKind(of: UITabBarButtonClass)})
tabBarItems.forEach({itemFrames.append($0.frame)})
}
if !itemFrames.isEmpty, !tabBarItems.isEmpty, itemFrames.count == items?.count {
tabBarItems.enumerated().forEach({$0.element.frame = itemFrames[$0.offset]})
}
}
}
मैं ठीक उसी मुद्दे का सामना कर रहा था, जहां ऐप को प्रति टैब एक नेविगेशन नियंत्रक के साथ आर्किटेक्चर किया गया था।
मुझे इसे ठीक करने के लिए सबसे आसान गैर-हैकॉई रास्ता मिला, जो
UITabBarController
अंदर
UITabBarController
को रखना था और व्यक्तिगत
UINavigationController
हटाना था।
पहले:
-> UINavigationController -> UIViewController
-> UINavigationController -> UIViewController
UITabBarController -> UINavigationController -> UIViewController
-> UINavigationController -> UIViewController
-> UINavigationController -> UIViewController
बाद:
-> UIViewController
-> UIViewController
UINavigationController -> UITabBarController -> UIViewController
-> UIViewController
-> UIViewController
बाहरी
UINavigationController
का उपयोग करके, आपको नेविगेशन स्टैक पर व्यू कंट्रोलर को धक्का देते समय
UITabBar
को छिपाने की आवश्यकता नहीं है।
चेतावनी:
एकमात्र मुद्दा जो मुझे अब तक मिला है, वह यह है कि प्रत्येक
UIViewController
पर शीर्षक या दाएं / बाएं बार बटन आइटम सेट करने का समान प्रभाव नहीं होता है।
इस समस्या को दूर करने के लिए, मैंने
UITabBarControllerDelegate
माध्यम से परिवर्तन लागू किए जब दृश्य
UIViewController
बदल गया है।
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
guard let topItem = self.navigationController?.navigationBar.topItem else { return }
precondition(self.navigationController == viewController.navigationController, "Navigation controllers do not match. The following changes might result in unexpected behaviour.")
topItem.title = viewController.title
topItem.titleView = viewController.navigationItem.titleView
topItem.leftBarButtonItem = viewController.navigationItem.leftBarButtonItem
topItem.rightBarButtonItem = viewController.navigationItem.rightBarButtonItem
}
ध्यान दें कि मैंने किसी भी मामले को पकड़ने के लिए एक
preconditionFailure
को जोड़ा है जब नेविगेशन आर्किटेक्चर को संशोधित किया गया है
यदि आप अभी भी अपने टैब बार को पारभासी रखना चाहते हैं, तो आपको
UITabBar
से उप-
safeAreaInsets
और संपत्ति को
safeAreaInsets
class MyTabBar: UITabBar {
private var safeInsets = UIEdgeInsets.zero
@available(iOS 11.0, *)
override var safeAreaInsets: UIEdgeInsets {
set {
if newValue != UIEdgeInsets.zero {
safeInsets = newValue
}
}
get {
return safeInsets
}
}
}
यह विचार है कि सिस्टम को
zero
इनसेट सेट करने की अनुमति नहीं है, इसलिए टैब बार कूद नहीं पाएगा।
यहाँ स्विफ्ट कोड है
extension UIApplication {
open override var next: UIResponder? {
// Called before applicationDidFinishLaunching
SwizzlingHelper.enableInjection()
return super.next
}
}
वर्ग SwizzlingHelper {
static func enableInjection() {
DispatchQueue.once(token: "com.SwizzlingInjection") {
//what to need inject
UITabbarButtonInjection.inject()
}
} अधिक जानकारी https://github.com/tonySwiftDev/UITabbar-fixIOS12.1Bug
@ElonChan
के विचार के लिए धन्यवाद, मैंने अभी C इनलाइन फंक्शन को OC स्टैटिक मेथड में बदल दिया है, क्योंकि मैं इस
overrideImplementation
@ElonChan
बहुत ज्यादा इस्तेमाल नहीं करूंगा।
और यह भी, इस स्निपेट को अब iPhoneX में समायोजित किया गया था।
static CGFloat const kIPhoneXTabbarButtonErrorHeight = 33;
static CGFloat const kIPhoneXTabbarButtonHeight = 48;
@implementation FixedTabBar
typedef void(^NewTabBarButtonFrameSetter)(UIView *, CGRect);
typedef NewTabBarButtonFrameSetter (^ImpBlock)(Class originClass, SEL originCMD, IMP originIMP);
+ (BOOL)overrideImplementationWithTargetClass:(Class)targetClass targetSelector:(SEL)targetSelector implementBlock:(ImpBlock)implementationBlock {
Method originMethod = class_getInstanceMethod(targetClass, targetSelector);
if (!originMethod) {
return NO;
}
IMP originIMP = method_getImplementation(originMethod);
method_setImplementation(originMethod, imp_implementationWithBlock(implementationBlock(targetClass, targetSelector, originIMP)));
return YES;
}
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (@available(iOS 12.1, *)) {
[self overrideImplementationWithTargetClass:NSClassFromString(@"UITabBarButton")
targetSelector:@selector(setFrame:)
implementBlock:^NewTabBarButtonFrameSetter(__unsafe_unretained Class originClass, SEL originCMD, IMP originIMP) {
return ^(UIView *selfObject, CGRect firstArgv) {
if ([selfObject isKindOfClass:originClass]) {
if (!CGRectIsEmpty(selfObject.frame) && CGRectIsEmpty(firstArgv)) {
return;
}
if (firstArgv.size.height == kIPhoneXTabbarButtonErrorHeight) {
firstArgv.size.height = kIPhoneXTabbarButtonHeight;
}
}
void (*originSelectorIMP)(id, SEL, CGRect);
originSelectorIMP = (void (*)(id, SEL, CGRect))originIMP;
originSelectorIMP(selfObject, originCMD, firstArgv);
};
}];
}
});
}
@end