Why page Push animation Tabbar moving up in the iPhone X
Solution 1:
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
Solution 2:
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
Solution 3:
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;
}
Solution 4:
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.