iOS 11导航栏高度自定义

现在在iOS 11中, sizeThatFits方法不是从UINavigationBar子类中调用的。 更改UINavigationBar的框架会导致毛刺和错误的插入。 那么,现在有什么想法如何自定义导航栏高度?

补充:在iOS 11 beta 6中解决了这个问题,所以下面的代码是没有用的^ _ ^


原始答案:

用下面的代码解决:

(我总是希望navigationBar.height + statusBar.height == 64 statusBar的隐藏是否为true)

  @implementation P1AlwaysBigNavigationBar - (CGSize)sizeThatFits:(CGSize)size { CGSize sizeThatFit = [super sizeThatFits:size]; if ([UIApplication sharedApplication].isStatusBarHidden) { if (sizeThatFit.height < 64.f) { sizeThatFit.height = 64.f; } } return sizeThatFit; } - (void)setFrame:(CGRect)frame { if ([UIApplication sharedApplication].isStatusBarHidden) { frame.size.height = 64; } [super setFrame:frame]; } - (void)layoutSubviews { [super layoutSubviews]; if (![UIApplication sharedApplication].isStatusBarHidden) { return; } for (UIView *subview in self.subviews) { NSString* subViewClassName = NSStringFromClass([subview class]); if ([subViewClassName containsString:@"UIBarBackground"]) { subview.frame = self.bounds; }else if ([subViewClassName containsString:@"UINavigationBarContentView"]) { if (subview.height < 64) { subview.y = 64 - subview.height; }else { subview.y = 0; } } } } @end 

虽然它已经在beta 4中修复了,但是看起来导航栏的背景图片并没有与实际的视图进行比例缩放(您可以通过在视图层次结构查看器中查看来进行validation)。 目前的解决方法是重写您的自定义UINavigationBar layoutSubviews ,然后使用以下代码:

 - (void)layoutSubviews { [super layoutSubviews]; for (UIView *subview in self.subviews) { if ([NSStringFromClass([subview class]) containsString:@"BarBackground"]) { CGRect subViewFrame = subview.frame; subViewFrame.origin.y = -20; subViewFrame.size.height = CUSTOM_FIXED_HEIGHT+20; [subview setFrame: subViewFrame]; } } } 

如果您注意到,实际上吧背景的偏移量为-20 ,使其出现在状态栏的后面,所以上面的计算将其添加到中。

这对我有用:

 - (CGSize)sizeThatFits:(CGSize)size { CGSize sizeThatFit = [super sizeThatFits:size]; if ([UIApplication sharedApplication].isStatusBarHidden) { if (sizeThatFit.height < 64.f) { sizeThatFit.height = 64.f; } } return sizeThatFit; } - (void)setFrame:(CGRect)frame { if ([UIApplication sharedApplication].isStatusBarHidden) { frame.size.height = 64; } [super setFrame:frame]; } - (void)layoutSubviews { [super layoutSubviews]; for (UIView *subview in self.subviews) { if ([NSStringFromClass([subview class]) containsString:@"BarBackground"]) { CGRect subViewFrame = subview.frame; subViewFrame.origin.y = 0; subViewFrame.size.height = 64; [subview setFrame: subViewFrame]; } if ([NSStringFromClass([subview class]) containsString:@"BarContentView"]) { CGRect subViewFrame = subview.frame; subViewFrame.origin.y = 20; subViewFrame.size.height = 44; [subview setFrame: subViewFrame]; } } } 

除了覆盖-layoutSubviews-setFrame:如果你不想resize的导航栏隐藏你的内容,你应该检查出新添加的UIViewController的additionalSafereaInsets属性( Apple文档 )。

在Xcode 9 Beta 6我仍然有这个问题。 酒吧总是看起来44像素的高度,并推到状态栏下。

为了解决我使用@strangetimes代码(在Swift中)创build了一个子类,

 class NavigationBar: UINavigationBar { override func layoutSubviews() { super.layoutSubviews() for subview in self.subviews { var stringFromClass = NSStringFromClass(subview.classForCoder) print("--------- \(stringFromClass)") if stringFromClass.contains("BarBackground") { subview.frame.origin.y = -20 subview.frame.size.height = 64 } } } } 

和我把酒吧低于状态栏

 let newNavigationBar = NavigationBar(frame: CGRect(origin: CGPoint(x: 0, y: 20), size: CGSize(width: view.frame.width, height: 64) ) ) 

用Swift简化了4。

 class CustomNavigationBar : UINavigationBar { private let hiddenStatusBar: Bool // MARK: Init init(hiddenStatusBar: Bool = false) { self.hiddenStatusBar = hiddenStatusBar super.init(frame: .zero) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: Overrides override func layoutSubviews() { super.layoutSubviews() if #available(iOS 11.0, *) { for subview in self.subviews { let stringFromClass = NSStringFromClass(subview.classForCoder) if stringFromClass.contains("BarBackground") { subview.frame = self.bounds } else if stringFromClass.contains("BarContentView") { let statusBarHeight = self.hiddenStatusBar ? 0 : UIApplication.shared.statusBarFrame.height subview.frame.origin.y = statusBarHeight subview.frame.size.height = self.bounds.height - statusBarHeight } } } } } 

这是我用的。 它适用于普通内容(44.0像素),如果您使用UISearchBar作为标题或其他视图来修改酒吧内容的大小,您必须相应地更新值。 由于可能在某个时刻刹车,因此请自行承担风险。

这是硬编码高度为90.0px的导航栏,适用于iOS 11和更早版本。 您可能需要添加一些插件到iOS 11的UIBarButtonItem看起来相同。

 class NavBar: UINavigationBar { override init(frame: CGRect) { super.init(frame: frame) if #available(iOS 11, *) { translatesAutoresizingMaskIntoConstraints = false } } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func sizeThatFits(_ size: CGSize) -> CGSize { return CGSize(width: UIScreen.main.bounds.width, height: 70.0) } override func layoutSubviews() { super.layoutSubviews() guard #available(iOS 11, *) else { return } frame = CGRect(x: frame.origin.x, y: 0, width: frame.size.width, height: 90) if let parent = superview { parent.layoutIfNeeded() for view in parent.subviews { let stringFromClass = NSStringFromClass(view.classForCoder) if stringFromClass.contains("NavigationTransition") { view.frame = CGRect(x: view.frame.origin.x, y: frame.size.height - 64, width: view.frame.size.width, height: parent.bounds.size.height - frame.size.height + 4) } } } for subview in self.subviews { var stringFromClass = NSStringFromClass(subview.classForCoder) if stringFromClass.contains("BarBackground") { subview.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: 90) subview.backgroundColor = .yellow } stringFromClass = NSStringFromClass(subview.classForCoder) if stringFromClass.contains("BarContent") { subview.frame = CGRect(x: subview.frame.origin.x, y: 40, width: subview.frame.width, height: subview.frame.height) } } } } 

你可以像下面这样将它添加到UINavigationController子类中:

 class CustomBarNavigationViewController: UINavigationController { init() { super.init(navigationBarClass: NavBar.self, toolbarClass: nil) } override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) } override init(rootViewController: UIViewController) { super.init(navigationBarClass: NavBar.self, toolbarClass: nil) self.viewControllers = [rootViewController] } required public init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } 

我有同样的问题。 以下是我的解决scheme。 我假设身高尺寸是66。

我的解决scheme工作正常iOS 10,11。

请select我的答案,如果它可以帮助你。

创buildNavgationBar.swift

 import UIKit class NavigationBar: UINavigationBar { //set NavigationBar's height var customHeight : CGFloat = 66 override func sizeThatFits(_ size: CGSize) -> CGSize { return CGSize(width: UIScreen.main.bounds.width, height: customHeight) } override func layoutSubviews() { super.layoutSubviews() frame = CGRect(x: frame.origin.x, y: 0, width: frame.size.width, height: customHeight) // title position (statusbar height / 2) setTitleVerticalPositionAdjustment(-10, for: UIBarMetrics.default) for subview in self.subviews { var stringFromClass = NSStringFromClass(subview.classForCoder) if stringFromClass.contains("BarBackground") { subview.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: customHeight) subview.backgroundColor = .yellow } stringFromClass = NSStringFromClass(subview.classForCoder) if stringFromClass.contains("BarContent") { subview.frame = CGRect(x: subview.frame.origin.x, y: 20, width: subview.frame.width, height: customHeight - 20) subview.backgroundColor = UIColor(red: 20/255, green: 20/255, blue: 20/255, alpha: 0.4) } } } } 

设置故事板

在这里输入图像说明

设置NavigationBar类

设置自定义NavigationBar类

添加TestView

在这里输入图像说明

添加TestView +设置SafeArea

ViewController.swift

 import UIKit class ViewController: UIViewController { var navbar : UINavigationBar! @IBOutlet weak var testView: UIView! override func viewDidLoad() { super.viewDidLoad() //update NavigationBar's frame self.navigationController?.navigationBar.sizeToFit() print("NavigationBar Frame : \(String(describing: self.navigationController!.navigationBar.frame))") } //Hide Statusbar override var prefersStatusBarHidden: Bool { return true } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(false) //Important! if #available(iOS 11.0, *) { //Default NavigationBar Height is 44. Custom NavigationBar Height is 66. So We should set additionalSafeAreaInsets to 66-44 = 22 self.additionalSafeAreaInsets.top = 22 } } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } 

SecondViewController.swift

 import UIKit class SecondViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. // Create BackButton var backButton: UIBarButtonItem! let backImage = imageFromText("Back", font: UIFont.systemFont(ofSize: 16), maxWidth: 1000, color:UIColor.white) backButton = UIBarButtonItem(image: backImage, style: UIBarButtonItemStyle.plain, target: self, action: #selector(SecondViewController.back(_:))) self.navigationItem.leftBarButtonItem = backButton self.navigationItem.leftBarButtonItem?.setBackgroundVerticalPositionAdjustment(-10, for: UIBarMetrics.default) } override var prefersStatusBarHidden: Bool { return true } @objc func back(_ sender: UITabBarItem){ self.navigationController?.popViewController(animated: true) } //Helper Function : Get String CGSize func sizeOfAttributeString(_ str: NSAttributedString, maxWidth: CGFloat) -> CGSize { let size = str.boundingRect(with: CGSize(width: maxWidth, height: 1000), options:(NSStringDrawingOptions.usesLineFragmentOrigin), context:nil).size return size } //Helper Function : Convert String to UIImage func imageFromText(_ text:NSString, font:UIFont, maxWidth:CGFloat, color:UIColor) -> UIImage { let paragraph = NSMutableParagraphStyle() paragraph.lineBreakMode = NSLineBreakMode.byWordWrapping paragraph.alignment = .center // potentially this can be an input param too, but i guess in most use cases we want center align let attributedString = NSAttributedString(string: text as String, attributes: [NSAttributedStringKey.font: font, NSAttributedStringKey.foregroundColor: color, NSAttributedStringKey.paragraphStyle:paragraph]) let size = sizeOfAttributeString(attributedString, maxWidth: maxWidth) UIGraphicsBeginImageContextWithOptions(size, false , 0.0) attributedString.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height)) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image! } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } 

在这里输入图像说明 在这里输入图像说明

黄色是barbackgroundView。 黑色不透明是BarContentView。

我删除了BarContentView的backgroundColor。

在这里输入图像说明

而已。

我将导航栏的高度加倍,这样我就可以在默认导航控件的上方添加一行状态图标,通过inheritanceUINavigationBar并使用sizeThatFits来覆盖高度。 幸运的是,这具有相同的效果,并且更简单,副作用更less。 我用iOS 8到11testing了一下。把它放在你的视图控制器中:

 - (void)viewDidLoad { [super viewDidLoad]; if (self.navigationController) { self.navigationItem.prompt = @" "; // this adds empty space on top } } 

上面的大部分答案都可以解决个人问题或者为很多人工作,但是我发现有用的东西,并且用几行代码解决了我的问题。

在我的情况下, 我有 隐藏 的导航栏 和状态栏 的自定义类 。 而从iOS 11来的时候,我的导航栏开始向上移动时,一个模态呈现的控制器被解雇。

然后,我发现sizeThatFits只触发一次,当栏被初始化在iOS 11,这是防止改变自定义大小。

我所要做的就是在我的自定义类的Y偏移量栏中添加一个var,并根据系统版本进行设置:

如果在Objective C中为系统版本定义macros,否则使用if #available

 if SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"11.0") { //custom y offset customYoffset = 20.0; } else { customYoffset = 0; } //set the custom y Offset in the setFrame function -(void)setFrame:(CGRect)frame { if ([UIApplication sharedApplication].isStatusBarHidden) { frame.size.height = customHeight; frame.origin.y = customYoffset; } [super setFrame:frame]; } 

这就是一切都完美的作品, 这是明确你的导航栏是明确的颜色