iOS 12.1의 뒤로 탐색에서 점프하는 UITabBar 항목
UITabBarController
마스터 화면에 있는 iOS 앱이 있으며 UITabBarController
with 설정을 숨기는 세부 화면 으로 이동합니다 hidesBottomBarWhenPushed = true
.
마스터 화면으로 돌아 가면 UITabBarController
이 GIF에 표시된대로 이상한 "점프"가 발생합니다.
이것은 12.0 또는 11.x가 아닌 iOS 12.1에서만 발생 합니다 .
이 동작이있는 FB Messenger와 같은 다른 앱을 발견했기 때문에 iOS 12.1 버그처럼 보이지만 이에 대한 해결 방법이 있습니까?
당신에 UITabBarController
, 세트isTranslucent = false
Apple은 이제 iOS 12.1.1 에서이를 수정했습니다.
나는 그것이 애플의 버그라고 생각한다. 그러나 당신은 이것을 핫 픽스로 시도 할 수있다 : 단지 다음 코드로 당신의 tabBar에 대한 클래스를 생성하라 :
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]})
}
}
}
다음은 추가 또는 제거되는 회전 및 탭 표시 줄 항목을 처리 할 수있는 솔루션입니다.
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
}
}
}
}
제 경우 (iOS 12.1.4)에서는이 이상한 글리치 동작이 모달이 .modalPresentationStyle = .fullScreen
presentationStyle을으로 업데이트 한 후 .overFullScreen
결함이 사라졌습니다.
import UIKit
extension UITabBar{
open override func layoutSubviews() {
super.layoutSubviews()
if let UITabBarButtonClass = NSClassFromString("UITabBarButton") as? NSObject.Type{
let subItems = self.subviews.filter({return $0.isKind(of: UITabBarButtonClass)})
if subItems.count > 0{
let tmpWidth = UIScreen.main.bounds.width / CGFloat(subItems.count)
for (index,item) in subItems.enumerated(){
item.frame = CGRect(x: CGFloat(index) * tmpWidth, y: 0, width: tmpWidth, height: item.bounds.height)
}
}
}
}
open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if let view:UITabBar = super.hitTest(point, with: event) as? UITabBar{
for item in view.subviews{
if point.x >= item.frame.origin.x && point.x <= item.frame.origin.x + item.frame.size.width{
return item
}
}
}
return super.hitTest(point, with: event)
}
}
이 문제를 해결하는 두 가지 방법이 있습니다. 첫째, UITabBarController에서 다음과 같이 isTranslucent = false를 설정합니다.
[[UITabBar appearance] setTranslucent:NO];
현명하게도 첫 번째 솔루션으로 issur가 해결되지 않으면 다음과 같이 시도하십시오.
다음은 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
추가 정보 : https://github.com/ChenYilong/CYLTabBarController/commit/2c741c8bffd47763ad2fca198202946a2a63c4fc
다음 - (UIEdgeInsets)safeAreaInsets
과 같이 몇 가지 iOS 12 하위 버전에 대한 메서드를 재정의 할 수 있습니다 .
- (UIEdgeInsets)safeAreaInsets {
UIEdgeInsets insets = [super safeAreaInsets];
CGFloat h = CGRectGetHeight(self.frame);
if (insets.bottom >= h) {
insets.bottom = [self.window safeAreaInsets].bottom;
}
return insets;
}
@ElonChan 아이디어에 감사 드리며 , overrideImplementation
너무 많이 사용하지 않을 것이기 때문에 c 인라인 함수를 OC 정적 메서드로 변경했습니다 . 또한이 스 니펫은 이제 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
여기에 신속한 코드가 있습니다
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()
}
} 자세한 정보 https://github.com/tonySwiftDev/UITabbar-fixIOS12.1Bug
앱이 탭당 하나의 탐색 컨트롤러로 아키텍처 화 된 똑같은 문제에 직면했습니다. 나는이 문제를 해결하기 위해 발견 가장 쉬운 비 해키 방법은 배치했다 UITabBarController
내부를 UINavigationController
, 개별 제거 UINavigationController
들.
전에:
-> UINavigationController -> UIViewController
-> UINavigationController -> UIViewController
UITabBarController -> UINavigationController -> UIViewController
-> UINavigationController -> UIViewController
-> UINavigationController -> UIViewController
후:
-> UIViewController
-> UIViewController
UINavigationController -> UITabBarController -> UIViewController
-> UIViewController
-> UIViewController
outer를 사용하면 뷰 컨트롤러를 탐색 스택에 푸시 할 때 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
탐색 아키텍처가 수정 된 경우를 포착하기 위해 a 를 추가했습니다.
그래도 탭 막대를 반투명하게 유지하려면 UITabBar
속성 에서 하위 클래스를 만들고 재정의해야합니다.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
인셋 을 설정하지 못하도록하는 것이므로 탭 바가 점프하지 않습니다.
참고 URL : https://stackoverflow.com/questions/53084806/uitabbar-items-jumping-on-back-navigation-on-ios-12-1
'Nice programing' 카테고리의 다른 글
Entity Framework를 사용하여 읽기시 테이블을 어떻게 잠글 수 있습니까? (0) | 2020.12.12 |
---|---|
Angular 2 : URL 변경없이 라우팅 (0) | 2020.12.12 |
서버 측에서 많은 TIME_WAIT의 비용은 얼마입니까? (0) | 2020.12.12 |
MD5 충돌을 일으키는 가장 짧은 문자열 쌍은 무엇입니까? (0) | 2020.12.12 |
onTouchevent () 대 onTouch () (0) | 2020.12.12 |