现在在iOS 11中,sizeThatFits
不从UINavigationBar
子类调用该方法.更改UINavigationBar
导致故障和错误插入的框架.那么,现在如何定制导航栏高度的任何想法?
根据Apple开发人员(在此处,此处和此处),不支持更改iOS 11中的导航栏高度.在这里,他们建议做一些变通办法,例如在导航栏下面(但在其外面),然后删除导航栏边框.因此,您将在故事板中拥有此内容:
在设备上看起来像这样:
现在你可以做到这一点在其他答案提出了一个解决办法:创建一个自定义子类UINavigationBar
,定制大子视图添加到它,覆盖sizeThatFits
和layoutSubviews
,然后设置additionalSafeAreaInsets.top
为导航的顶级控制器的差异customHeight - 44px
,但酒吧视图仍将是默认44px,即使在视觉上一切都会看起来很完美.我没有尝试覆盖setFrame
,也许它有效,但是,正如Apple开发人员在上面的一个链接中所写:"......并且[支持]都没有改变UINavigationController拥有的导航栏的框架(导航)只要它认为合适,控制器就会愉快地踩踏你的框架变化.)
在我的情况下,上面的解决方法使视图看起来像这样(调试视图显示边框):
正如您所看到的,视觉外观相当不错,additionalSafeAreaInsets
正确推送内容,大导航栏可见,但是我在此栏中有一个自定义按钮,只有标准44像素导航栏下面的区域是可点击的(图中的绿色区域).标准导航栏高度以下的触摸不会到达我的自定义子视图,因此我需要调整导航栏本身的大小,Apple开发人员说不支持.
2018年1月7日更新
此代码支持XCode 9.2,iOS 11.2
我有同样的问题.以下是我的解决方案.我假设身高是66.
如果有帮助,请选择我的答案.
创建CINavgationBar.swift
import UIKit @IBDesignable class CINavigationBar: UINavigationBar { //set NavigationBar's height @IBInspectable var customHeight : CGFloat = 66 override func sizeThatFits(_ size: CGSize) -> CGSize { return CGSize(width: UIScreen.main.bounds.width, height: customHeight) } override func layoutSubviews() { super.layoutSubviews() print("It called") self.tintColor = .black self.backgroundColor = .red for subview in self.subviews { var stringFromClass = NSStringFromClass(subview.classForCoder) if stringFromClass.contains("UIBarBackground") { subview.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: customHeight) subview.backgroundColor = .green subview.sizeToFit() } stringFromClass = NSStringFromClass(subview.classForCoder) //Can't set height of the UINavigationBarContentView if stringFromClass.contains("UINavigationBarContentView") { //Set Center Y let centerY = (customHeight - subview.frame.height) / 2.0 subview.frame = CGRect(x: 0, y: centerY, width: self.frame.width, height: subview.frame.height) subview.backgroundColor = .yellow subview.sizeToFit() } } } }
设置故事板
设置自定义NavigationBar类
添加TestView + Set 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.
而已.
补充:问题在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
这对我有用:
- (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]; } } }
使用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 } } } } }