31

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

AskNilesh
  • 67,701
  • 16
  • 123
  • 163
Tommy
  • 319
  • 3
  • 4

7 Answers7

14

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
Raimundas Sakalauskas
  • 2,016
  • 21
  • 24
  • Thanks for your answer, it works for me. Could you explain how does the hard coded value work in your solution. if (bottomInset > 0 && size.height < 50 && (size.height + bottomInset < 90)) – TS.xy Jan 03 '18 at 02:33
  • 1
    There's some magic that's out of our control happening with insets and height during animation. The first part of the if fixes the tabbar from sticking to bottom of the view (if I remember correctly). However there's side effect if you have 2 different views that are shown using modal & push segues. Basically if you use swipe back gesture, the tabbar's safe area grows indefinitely unless the 2nd part of if is present (you start swipe back and release it before it triggers & repeat). If I recall correctly only happens if navbar is hidden and if "modal" view was visible before "push" view. – Raimundas Sakalauskas Jan 03 '18 at 13:02
  • Thanks a lot, Raimundas! You are my time saviour today! – Serzhas Jan 30 '18 at 09:44
  • This isn't working for me for iPhone XR, XS and XS Max – Pranav Kasetti Nov 13 '18 at 14:56
  • Did you try it with X, cause I really doubt it's something so hardware specific? Maybe it's something in your implementation that's not working. Also given people stopped up-voting my solution, i think this problem should be already fixed by apple itself? – Raimundas Sakalauskas Nov 13 '18 at 18:48
  • 1
    @RaimundasSakalauskas Its actually an ios12 issue, so don't worry your solution still works for older devices. – Pranav Kasetti Nov 22 '18 at 20:27
9

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
xu tong
  • 453
  • 4
  • 8
2

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;
    }
sukeyang
  • 21
  • 1
2

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.

Jeff Zhang
  • 140
  • 1
  • 7
0

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

ximmyxiao
  • 2,622
  • 3
  • 20
  • 35
-4

this problem seems fixed in iOS 11.2

ximmyxiao
  • 2,622
  • 3
  • 20
  • 35
-5

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.