Skip to content

Instantly share code, notes, and snippets.

@colinhumber
Created September 20, 2017 14:16
Show Gist options
  • Save colinhumber/b1ed339f28a5854cf0005841090176c8 to your computer and use it in GitHub Desktop.
Save colinhumber/b1ed339f28a5854cf0005841090176c8 to your computer and use it in GitHub Desktop.
Hacky way to get a custom nav bar height
#import "SBBaseNavigationBar.h"
@interface SBBaseNavigationBar (iOS11HacksPrivate)
@property (nullable, nonatomic, readonly) UIView *navigationBarContentView;
@end
@implementation SBBaseNavigationBar
@dynamic promptViewHeight;
#pragma mark - Initialization
- (instancetype)initWithNavigationBarHeight:(CGFloat)height {
self = [super initWithFrame:CGRectZero];
if (self) {
[self sb_navigationBarCommonInitWithNavigationBarHeight:height];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
return [self initWithNavigationBarHeight:60.0];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self sb_navigationBarCommonInitWithNavigationBarHeight:60.0];
}
return self;
}
- (void)sb_navigationBarCommonInitWithNavigationBarHeight:(CGFloat)height {
// enforce minimum height
if (height < SBBaseNavigationBar.minimumBarHeight) {
height = SBBaseNavigationBar.minimumBarHeight;
}
// defaults
_navigationBarHeight = height;
}
#pragma mark - Setters
- (void)setNavigationBarHeight:(CGFloat)navigationBarHeight {
if (navigationBarHeight < SBBaseNavigationBar.minimumBarHeight) {
navigationBarHeight = SBBaseNavigationBar.minimumBarHeight;
}
_navigationBarHeight = navigationBarHeight;
[self setNeedsLayout];
}
#pragma mark - Getters
+ (CGFloat)minimumBarHeight {
return 44.0;
}
#pragma mark - Layout
- (void)setBounds:(CGRect)bounds {
bounds.size.height = self.navigationBarHeight;
[super setBounds:bounds];
}
- (void)setFrame:(CGRect)frame {
//when nav is pushed modally, the navigationbar height isn't correct for the onscreen
//animation, and only takes the correct height once the animation is correct
//this forces the frame to adhere to the set navigationBarHeight for all instances
frame.size.height = self.navigationBarHeight;
// if we're adjusting the nav bar's layout to take the status bar into account, ensure the nav bar's origin.y is always at the bottom of the status bar.
// Works around an issue when presenting a full screen modal on compact where the nav bar's height is set correctly, but the
// origin is at 0
if (@available(iOS 11.0, *)) {
if (self.adjustsLayoutForStatusBar) {
frame.origin.y = CGRectGetHeight(UIApplication.extensionSafeSharedApplication.statusBarFrame);
}
}
[super setFrame:frame];
}
- (void)setCenter:(CGPoint)center {
if (@available(iOS 11.0, *)) {
center.y = (self.navigationBarHeight / 2) + self.centerOffset;
}
[super setCenter:center];
}
- (void)layoutSubviews {
[super layoutSubviews];
UIView *backgroundView = self.subviews.firstObject;
CGRect backgroundFrame = backgroundView.frame;
if (@available(iOS 11.0, *)) {
backgroundFrame.size.height = self.navigationBarHeight - CGRectGetMinY(backgroundFrame);
backgroundView.frame = backgroundFrame;
[self overrideNavigationContentViewLayout];
}
}
- (CGSize)sizeThatFits:(CGSize)size {
CGSize aSize = [super sizeThatFits:size];
aSize.height = self.navigationBarHeight;
return aSize;
}
@end
@implementation SBBaseNavigationBar (iOS11Hacks)
- (void)overrideNavigationContentViewLayout {
[self.navigationBarContentView.layer removeAnimationForKey:@"position"];
CGFloat statusBarInset = CGRectGetHeight(UIApplication.sharedApplication.statusBarFrame);
CGFloat contentViewOriginY = ((self.navigationBarHeight - SBBaseNavigationBar.minimumBarHeight - statusBarInset) / 2);
CGRect contentViewFrame = self.navigationBarContentView.frame;
contentViewFrame.origin.y = contentViewOriginY;
self.navigationBarContentView.frame = contentViewFrame;
}
@end
@implementation SBBaseNavigationBar (iOS11HacksPrivate)
- (UIView *)navigationBarContentView {
UIView *contentView = [self.subviews filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(UIView *evaluatedObject, NSDictionary<NSString *,id> *bindings) {
return [evaluatedObject isKindOfClass:NSClassFromString(@"_UINavigationBarContentView")];
}]].firstObject;
return contentView;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment