Why page Push animation Tabbar moving up in the iPhone X

Viewed 7046

I build a app Demo, use hidesBottomBarWhenPushed hide Tabbar in Push Animation.

Page Relationship

But, When I click Jump Button Tabbar move up!? like this: Tabbar Move up

7 Answers

Answer provided by VoidLess fixes TabBar problems only partially. It fixes layout problems within tabbar, but if you use viewcontroller that hides tabbar, the tabbar is rendered incorrectly during animations (to reproduce it is best 2 have 2 segues - one modal and one push. If you alternate the segues, you can see tabbar being rendered out of place). See the code bellow that fixes both of the problems. Good job apple.

class SafeAreaFixTabBar: UITabBar {

var oldSafeAreaInsets = UIEdgeInsets.zero

@available(iOS 11.0, *)
override func safeAreaInsetsDidChange() {
    super.safeAreaInsetsDidChange()

    if oldSafeAreaInsets != safeAreaInsets {
        oldSafeAreaInsets = safeAreaInsets

        invalidateIntrinsicContentSize()
        superview?.setNeedsLayout()
        superview?.layoutSubviews()
    }
}

override func sizeThatFits(_ size: CGSize) -> CGSize {
    var size = super.sizeThatFits(size)
    if #available(iOS 11.0, *) {
        let bottomInset = safeAreaInsets.bottom
        if bottomInset > 0 && size.height < 50 && (size.height + bottomInset < 90) {
            size.height += bottomInset
        }
    }
    return size
}

override var frame: CGRect {
    get {
        return super.frame
    }
    set {
        var tmp = newValue
        if let superview = superview, tmp.maxY != 
        superview.frame.height {
            tmp.origin.y = superview.frame.height - tmp.height
        }

        super.frame = tmp
        }
    }
}
}

Objective-C code:

@implementation VSTabBarFix {
    UIEdgeInsets oldSafeAreaInsets;
}


- (void)awakeFromNib {
    [super awakeFromNib];

    oldSafeAreaInsets = UIEdgeInsetsZero;
}


- (void)safeAreaInsetsDidChange {
    [super safeAreaInsetsDidChange];

    if (!UIEdgeInsetsEqualToEdgeInsets(oldSafeAreaInsets, self.safeAreaInsets)) {
        [self invalidateIntrinsicContentSize];

        if (self.superview) {
            [self.superview setNeedsLayout];
            [self.superview layoutSubviews];
        }
    }
}

- (CGSize)sizeThatFits:(CGSize)size {
    size = [super sizeThatFits:size];

    if (@available(iOS 11.0, *)) {
        float bottomInset = self.safeAreaInsets.bottom;
        if (bottomInset > 0 && size.height < 50 && (size.height + bottomInset < 90)) {
            size.height += bottomInset;
        }
    }

    return size;
}


- (void)setFrame:(CGRect)frame {
    if (self.superview) {
        if (frame.origin.y + frame.size.height != self.superview.frame.size.height) {
            frame.origin.y = self.superview.frame.size.height - frame.size.height;
        }
    }
    [super setFrame:frame];
}


@end

This is my way。 Declare a subclass of UITabBar, such as ActionTabBar

swift 3,4

class ActionTabBar: UITabBar {

    override var frame: CGRect {
        get {
            return super.frame
        }
        set {
            var tmp = newValue
            if let superview = self.superview, tmp.maxY != superview.frame.height {
                tmp.origin.y = superview.frame.height - tmp.height
            }
            super.frame = tmp
        }
    }
}

Objective-C

@implementation ActionTabbar

- (void)setFrame:(CGRect)frame
{
    if (self.superview && CGRectGetMaxY(self.superview.bounds) != CGRectGetMaxY(frame)) {
        frame.origin.y = CGRectGetHeight(self.superview.bounds) - CGRectGetHeight(frame);
    }
    [super setFrame:frame];
}

@end

Declare a subclass of NavigationController

 @implementation XXNavigationController
    - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
        [super pushViewController:viewController animated:animated];
        CGRect frame = self.tabBarController.tabBar.frame;
        frame.origin.y = [UIScreen mainScreen].bounds.size.height - frame.size.height;
        self.tabBarController.tabBar.frame = frame;
    }

Edit: After the release of 12.1.1, this issue has been fixed. You can keep the original structure.


If you change your structure from

UITabBarController -> UINavigationController -> UIViewController

to

UINavigationController -> UITabBarController -> UIViewController

you will find this issue has been resolved. I really don't know why Apple doesn't fix this issue.

In iOS 12.1, this problem becomes more serious. You can see the TabBar text jump above the TabBar every time, if you use gesture to pop back.

Note: This way can definitely solve this problem, but I am not sure whether it's a good idea. Also, if your structure is quite complicated, you need to change lots of stuff.

i'll provide another solution for this bug(seems apple made).

and the solution is not to forbid the tabbar move up , but to make the black area will not show when tabbar move up

the core thing is add a subview to your viewcontroller as it deepest subview and this subview's frame is the window size.so when the tabbar moves up , this subview will shown insteadof black area

if (@available(iOS 11.0, *)) {
    UIView* bgView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    bgView.autoresizingMask = UIViewAutoresizingNone;
    bgView.backgroundColor = UIColorFromRGB(0xefeff4);
    [self.view addSubview:bgView];

}

the convenience of this method is you will not have to subclass tabbar or overwrite navigationcontroller's push method

Just need to add a launch image specifically for iPhone X to asset catalog (because it uses @3x) of size 1125 x 2436.

I hope this will solve your problem.

Related