ios - guidelines - uitabbarcontroller delegate




Gli oggetti UITabBar saltano sulla navigazione posteriore su iOS 12.1 (8)

Apple ha risolto il problema con iOS 12.1.1

Ho un'app iOS con UITabBarController su una schermata principale, navigando verso una schermata dei dettagli che nasconde UITabBarController con l'impostazione hidesBottomBarWhenPushed = true .

Quando torni alla schermata principale, UITabBarController fa uno strano "salto" come mostrato in questa GIF:

Questo succede solo su iOS 12.1 , non su 12.0 o 11.x.

Sembra un bug di iOS 12.1, perché ho notato altre app come FB Messenger con questo comportamento, ma mi chiedevo, c'è qualche soluzione per questo?


Ecco una soluzione in grado di gestire la rotazione e gli elementi della barra delle schede aggiunti o rimossi:

class FixedTabBar: UITabBar {

    var buttonFrames: [CGRect] = []
    var size: CGSize = .zero

    override func layoutSubviews() {
        super.layoutSubviews()

        if UIDevice.current.systemVersion >= "12.1" {
            let buttons = subviews.filter {
                String(describing: type(of: $0)).hasSuffix("Button")
            }
            if buttonFrames.count == buttons.count, size == bounds.size {
                zip(buttons, buttonFrames).forEach { $0.0.frame = $0.1 }
            } else {
                buttonFrames = buttons.map { $0.frame }
                size = bounds.size
            }
        }
    }
}

Immagino sia un bug di Apple, ma puoi provarlo come hotfix: basta creare una classe per la tua tabbar con il seguente codice:

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]})
        }
    }
}

Nel mio caso (iOS 12.1.4), ho scoperto che questo strano comportamento glitch è stato innescato da modali presentati con .modalPresentationStyle = .fullScreen

Dopo aver aggiornato la loro presentazione da .overFullScreen a .overFullScreen , il problema .overFullScreen è scomparso.


Puoi sostituire - (UIEdgeInsets)safeAreaInsets metodo - (UIEdgeInsets)safeAreaInsets per alcune sovversioni di iOS 12 con questo:

- (UIEdgeInsets)safeAreaInsets {
    UIEdgeInsets insets = [super safeAreaInsets];
    CGFloat h = CGRectGetHeight(self.frame);
    if (insets.bottom >= h) {
        insets.bottom = [self.window safeAreaInsets].bottom;
    }
    return insets;
}

Se desideri comunque mantenere la tua barra delle schede trasparente, devi sottoclassare da UITabBar e sovrascrivere la proprietà 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
    }
} 

}

L'idea è di non consentire al sistema di impostare zero inserimenti, quindi la barra delle schede non salterà.


ci sono due modi per risolvere questo problema, in primo luogo, in UITabBarController, impostare isTranslucent = false come:

[[UITabBar appearance] setTranslucent:NO];

sinceramente, se la prima soluzione non risolve il problema, provare in questo modo:

ecco il codice Objective-C

// .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

Ulteriori informazioni: https://github.com/ChenYilong/CYLTabBarController/commit/2c741c8bffd47763ad2fca198202946a2a63c4fc


ecco il codice rapido

extension UIApplication {
open override var next: UIResponder? {
    // Called before applicationDidFinishLaunching
    SwizzlingHelper.enableInjection()
    return super.next
}

}

class SwizzlingHelper {

static func enableInjection() {
    DispatchQueue.once(token: "com.SwizzlingInjection") {
        //what to need inject
        UITabbarButtonInjection.inject()
    }

} maggiori informazioni https://github.com/tonySwiftDev/UITabbar-fixIOS12.1Bug







uitabbar