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




uitabbar