UISearchBar 在 iOS 11 中增加导航栏高度

Posted

技术标签:

【中文标题】UISearchBar 在 iOS 11 中增加导航栏高度【英文标题】:UISearchBar increases navigation bar height in iOS 11 【发布时间】:2018-03-01 06:38:40 【问题描述】:

我的UISearchBar 是导航栏的一部分,例如:

 let searchBar = UISearchBar()
 //some more configuration to the search bar
 .....
 navigationItem.titleView = searchBar

更新到ios 11 后,我的应用中的搜索栏发生了一些奇怪的事情。在iOS 10 之前,我的导航栏看起来像:

现在iOS 11 我有:

如您所见,两个搜索栏的四舍五入有所不同,这并不困扰我。问题是搜索栏增加了导航栏的高度。所以当我转到另一个控制器时,它看起来也很奇怪:

事实上,奇怪的黑线高度加上当前导航栏的高度,就等于第二张图中导航栏的高度……

任何想法如何摆脱黑线并在所有视图控制器中保持一致的导航栏高度?

【问题讨论】:

iOS 11 SearchBar in NavigationBar的可能重复 【参考方案1】:

您可以在 iOS 11 的搜索栏中添加高度 44 的约束。

// 斯威夫特

if #available(iOS 11.0, *) 
    searchBar.heightAnchor.constraint(equalToConstant: 44).isActive = true

// 目标-C

if (@available(iOS 11.0, *)) 
    [searchBar.heightAnchor constraintEqualToConstant:44].active = YES;

【讨论】:

虽然这可以使高度恢复正常,但如果我点击我的搜索栏,由于某种原因它会切断搜索栏的底部。任何想法为什么会发生这种情况或其他人有这个问题? 如果你使用这段代码看起来很奇怪,因为它只是缩小了导航栏,但搜索栏的高度仍然是 56。 其他人找到解决方案了吗?这样做会导致搜索栏出现很多奇怪的行为问题。 这还不够。当你点击字段时,大小仍然乱七八糟。 这在 iOS 13 中不起作用。有什么解决方法吗?【参考方案2】:

这也发生在我身上,在 iOS 12.4 中运行良好,在 13 以上版本中变得很奇怪。 问题在于 iOS 13 导航栏高度从实现 searchBar 的 UIViewController 跳转后从 88 增加到 100。

在实现 searchBar 的 UIViewController 中试试这个。

override func viewWillDisappear(_ animated: Bool) 
    super.viewWillDisappear(animated)
    navigationController?.view.setNeedsLayout()
    navigationController?.view.layoutIfNeeded()

修复后预览:

修复前预览:

【讨论】:

【参考方案3】:

iOS 11 中带有 SearchBar 的 NavigationBar 下出现黑线,分两种情况:

当我使用 UISearchBar 从 ViewController 推送另一个 ViewControllers

当我用 UISearchBar 解散 ViewController 并使用“向右拖动解散”

我的解决方案是:使用 UISearchBar 将此代码添加到我的 ViewController:

-(void)viewWillDisappear:(BOOL)animated
    [super viewWillDisappear:animated];
    [self.navigationController.view setNeedsLayout]; // force update layout
    [self.navigationController.view layoutIfNeeded]; // to fix height of the navigation bar

Swift 4 更新

override func viewWillDisappear(_ animated: Bool) 
    super.viewWillDisappear(animated)
    navigationController?.view.setNeedsLayout() // force update layout
    navigationController?.view.layoutIfNeeded() // to fix height of the navigation bar

【讨论】:

谨慎使用它,因为对我来说,当试图滑回可渗透的视图控制器时,它会使整个应用程序冻结 这个解决方案真的让我松了一口气,谢谢@andrew【参考方案4】:

无法发表评论,但想分享一些我遇到的其他问题,即使在使用其他解决方案之一之后,我也花了很多时间试图弄清这个问题的根源。

看来对我来说最好的解决办法是Andrew's answer:

override func viewWillDisappear(_ animated: Bool) 
    super.viewWillDisappear(animated)
    navigationController?.view.setNeedsLayout() // force update layout
    navigationController?.view.layoutIfNeeded() // to fix height of the navigation bar

但是,至少 在 iOS 12.1 中,如果您的 UINavigationBar:

已将isTranslucent 设置为false,带有搜索栏的视图控制器在交互关闭时似乎无法调整其视图布局(通过后退按钮正常关闭似乎有效)。 是否使用setBackgroundImage(UIImage(), for: .default)设置了背景图,过渡动画无法正常工作,完成后会跳回原位。

这些特定的属性被设置为让导航栏以某种方式出现,所以我需要做一些调整来恢复它,或者忍受奇怪的行为。如果我遇到其他问题或找到其他解决方案或其他操作系统版本中的差异,将尝试记住更新上述内容。

【讨论】:

【参考方案5】:

向使用UISearchController 并将其UISearchBar 附加到navigationItem.titleView 的用户致意。我花了疯狂的 4-5 个小时来解决这个问题。遵循 iOS 11+ 推荐的方法,将 searchController 放入 navigation.searchController 并不适合我的情况。有这个 searchController/searchBar 的屏幕有一个 backButton,一个自定义的。

我在 iOS 10、iOS 11 和 12 中对此进行了测试。在不同的设备上。我不得不这样做。不解决这个恶魔我就不能回家。鉴于时间紧迫,这是我今天能做的最完美的事情。

所以我只想分享我所做的这项辛勤工作,您可以将所有内容放在您想要的任何位置(例如 viewModel 中的变量)。就是这样:

在我的第一个屏幕(比如主屏幕,没有此搜索控制器)中,我的viewDidLoad() 中有这个。

self.extendedLayoutIncludesOpaqueBars = true

在我的第二个屏幕中,有 searchController 的那个,我的 viewDidAppear 中有这个。

覆盖 func viewDidAppear(_ animated: Bool) super.viewDidAppear(动画)

    let systemMajorVersion = ProcessInfo.processInfo.operatingSystemVersion.majorVersion
    if systemMajorVersion < 12 
        // Place the search bar in the navigation item's title view.
        self.navigationItem.titleView = self.searchController.searchBar
    

    if systemMajorVersion >= 11 

        self.extendedLayoutIncludesOpaqueBars = true

        UIView.animate(withDuration: 0.3) 
            self.navigationController?.navigationBar.setNeedsLayout()
            self.navigationController?.navigationBar.layoutIfNeeded()
        

        self.tableView.contentInset = UIEdgeInsets(top: -40, left: 0, bottom: 0, right: 0)

        if self.viewHadAppeared 
            self.tableView.contentInset = .zero
        
    

    self.viewHadAppeared = true // this is set to false by default.

这是我的 searchController 的声明:

lazy var searchController: UISearchController = 
    let searchController = UISearchController(searchResultsController: nil)
    searchController.hidesNavigationBarDuringPresentation = false
    searchController.dimsBackgroundDuringPresentation = false
    searchController.searchBar.textField?.backgroundColor = .lalaDarkWhiteColor
    searchController.searchBar.textField?.tintColor = .lalaDarkGray
    searchController.searchBar.backgroundColor = .white
    return searchController
()

所以我希望有一天这对某人有所帮助。

【讨论】:

【参考方案6】:

谢谢大家!我终于找到了解决办法。

使用 UISearchBar 将以下代码添加到 ViewController。

    第一步:viewDidLoad
-(void)viewDidLoad

    [super viewDidLoad];
    self.extendedLayoutIncludesOpaqueBars = YES;
    ...

override func viewDidLoad() 
    super.viewDidLoad()
    self.extendedLayoutIncludesOpaqueBars = true

    第二步:viewWillDisappear
-(void)viewWillDisappear:(BOOL)animated
    [super viewWillDisappear:animated];
     // force update layout
    [self.navigationController.view setNeedsLayout]; 
    // to fix height of the navigation bar
    [self.navigationController.view layoutIfNeeded];  

    override func viewWillDisappear(_ animated: Bool) 
        super.viewWillDisappear(animated)
        navigationController?.view.setNeedsLayout() // force update layout
        navigationController?.view.layoutIfNeeded() // to fix height of the navigation bar
    

【讨论】:

【参考方案7】:

您所要做的就是继承 UISearchBar 并覆盖“intrinsicContentSize”:

@implementation CJSearchBar
-(CGSize)intrinsicContentSize
    CGSize s = [super intrinsicContentSize];
    s.height = 44;
    return s;

@end

【讨论】:

【参考方案8】:

我通过将约束添加到嵌入搜索栏的地图视图控制器上的 viewDidAppear 来解决此问题

public override func viewDidAppear(_ animated: Bool) 
    if #available(iOS 11.0, *) 

        resultSearchController?.searchBar.heightAnchor.constraint(equalToConstant: 44).isActive = true
        // searchBar.heightAnchor.constraint(equalToConstant: 44).isActive = true
    

【讨论】:

你错过了 super.viewDidAppear(animated) 的调用【参考方案9】:

就我而言,我必须将 textField 的高度降低 36pt -> 28pt。

所以我尝试改变框架的高度,图层的高度。但这些方法没有奏效。

最后,我找到了一个解决方案,那就是面具。 我认为,这不是一个好方法,但它确实有效。

    let textField              = searchBar.value(forKey: "searchField") as? UITextField
    textField?.font            = UIFont.systemFont(ofSize: 14.0, weight: .regular)
    textField?.textColor       = #colorLiteral(red: 0.1960784314, green: 0.1960784314, blue: 0.1960784314, alpha: 1)
    textField?.textAlignment   = .left

    if #available(iOS 11, *) 
        let radius: CGFloat           = 5.0
        let magnifyIconWidth: CGFloat = 16.0
        let inset                     = UIEdgeInsets(top: 4.0, left: 0, bottom: 4.0, right: 0)

        let path = CGMutablePath()
        path.addArc(center: CGPoint(x: searchBar.bounds.size.width - radius - inset.right - magnifyIconWidth, y: inset.top + radius), radius: radius, startAngle: .pi * 3.0/2.0, endAngle: .pi*2.0, clockwise: false)                        // Right top
        path.addArc(center: CGPoint(x: searchBar.bounds.size.width - radius - inset.right - magnifyIconWidth, y: searchBar.bounds.size.height - radius - inset.bottom), radius: radius, startAngle: 0, endAngle: .pi/2.0, clockwise: false)  // Right Bottom
        path.addArc(center: CGPoint(x: inset.left + radius, y: searchBar.bounds.size.height - radius - inset.bottom), radius: radius, startAngle: .pi/2.0, endAngle: .pi, clockwise: false)                                                  // Left Bottom
        path.addArc(center: CGPoint(x: inset.left + radius, y: inset.top + radius),  radius: radius, startAngle: .pi, endAngle: .pi * 3.0/2.0, clockwise: false)                                                                             // Left top

        let maskLayer      = CAShapeLayer()
        maskLayer.path     = path
        maskLayer.fillRule = kCAFillRuleEvenOdd

        textField?.layer.mask = maskLayer
    

如果你想改变 textField 的框架,你可以改变 insets。

【讨论】:

【参考方案10】:

我无法使用将导航栏保持在 44 的解决方案。 所以我花了一天的时间,但最后,我找到了一个不改变栏高度并将按钮放在栏中间的解决方案。问题是按钮放置在配置为水平堆栈视图的堆栈视图中,因此不会适应高度变化。

这是在初始化时完成的:

UIBarButtonItem *cancelButton;
if (@available(iOS 11.0, *)) 
    // For iOS11 creating custom button to accomadate the change of navbar + search bar being 56 points
    self.navBarCustomButton = [UIButton buttonWithType:UIButtonTypeCustom];
    [self.navBarCustomButton setTitle:@"Cancel"];
    [self.navBarCustomButton addTarget:self action:@selector(cancelButtonTapped) forControlEvents:UIControlEventTouchUpInside];
    cancelButton = [[UIBarButtonItem alloc] initWithCustomView:self.navBarCustomButton];
 else 
    cancelButton = [[UIBarButtonItem alloc] initWithTitle:MagicLocalizedString(@"button.cancel", @"Cancel")
                                                                                         style:UIBarButtonItemStylePlain
                                                                                        target:self
                                                                                        action:@selector(cancelButtonTapped)];

在 viewWillApear 上(或视图添加到导航堆栈后的任何时间)

   if (@available(iOS 11.0, *)) 
        UIView *buttonsStackView = [navigationController.navigationBar subviewOfClass:[UIStackView class]];
        if (buttonsStackView ) 
            [buttonsStackView.centerYAnchor constraintEqualToAnchor:navigationController.navigationBar.centerYAnchor].active = YES;
            [self.navBarCustomButton.heightAnchor constraintEqualToAnchor:buttonsStackView.heightAnchor];
        
    

而 subviewOfClass 是 UIView 上的一个类别:

- (__kindof UIView *)subviewOfClass:(Class)targetClass 
     // base case
     if ([self isKindOfClass:targetClass]) 
        return self;
     

     // recursive
    for (UIView *subview in self.subviews) 
        UIView *dfsResult = [subview subviewOfClass:targetClass];

        if (dfsResult) 
           return dfsResult;
       
   
   return nil;

【讨论】:

【参考方案11】:

在“ACKNOWLEDGEMENTS”视图控制器上试试这个代码 在 viewDidLoad 中

self.extendedLayoutIncludesOpaqueBars = true

【讨论】:

谢谢!这是我最终采用的解决方案,因为我没有使用半透明条。 确实应该是这个答案。但我想将这段代码添加到搜索栏控制器上方和下方的控制器中。 这对我有用,但必须在 viewWillDisappear 中添加 navigationController?.view.layoutSubviews()。 这很好用,如果您能告诉我们extendedLayoutIncludesOpaqueBars 属性的作用,我将不胜感激。 extendedLayoutIncludesOpaqueBars 在导航和状态栏下展开您的视图(self.view)。如果您将子视图添加到视图(例如,UIImageView)并将其设置为视图左上角的约束并运行应用程序,那么当您将此属性设置为 true 和 false 时,您会看到不同。【参考方案12】:

我发现 Mai Mai 的解决方案是唯一真正可用的解决方案。 然而它仍然不完美: 旋转设备时,搜索栏未正确调整大小并保持较小的尺寸。

我已经找到了解决办法。这是我在 Objective C 中的代码,相关部分已注释:

// improvements in the search bar wrapper
@interface SearchBarWrapper : UIView
@property (nonatomic, strong) UISearchBar *searchBar;
- (instancetype)initWithSearchBar:(UISearchBar *)searchBar;
@end
@implementation SearchBarWrapper
- (instancetype)initWithSearchBar:(UISearchBar *)searchBar 
    // setting width to a large value fixes stretch-on-rotation
    self = [super initWithFrame:CGRectMake(0, 0, 4000, 44)];
    if (self) 
        self.searchBar = searchBar;
        [self addSubview:searchBar];
    
    return self;

- (void)layoutSubviews 
    [super layoutSubviews];
    self.searchBar.frame = self.bounds;

// fixes width some cases of resizing while search is active
- (CGSize)sizeThatFits:(CGSize)size 
    return size;

@end

// then use it in your VC
@implementation MyViewController
- (void)viewDidLoad 
    [super viewDidLoad];
    self.navigationItem.titleView = [[SearchBarWrapper alloc] initWithSearchBar:self.searchController.searchBar];

@end

现在还有一个案例我还没有弄清楚。要重现,请执行以下操作: - 从纵向开始 - 激活搜索字段 - 旋转到横向 - 错误:栏没有调整大小

【讨论】:

【参考方案13】:

所有解决方案都不适合我,所以在我推送视图控制器之前我做了:

override func viewWillDisappear(_ animated: Bool) 
    super.viewWillDisappear(animated)

    self.navigationItem.titleView = UIView()

并在返回时显示搜索栏:

override func viewWillAppear(_ animated: Bool) 
    super.viewWillAppear(animated)

    self.navigationItem.titleView = UISearchBar()

【讨论】:

这删除了新视图控制器中的黑色视图,但是当返回到这个视图控制器(包含 searchBar 的那个)时,rootView 实际上向上移动了一点。【参考方案14】:
//
//  Created by Sang Nguyen on 10/23/17.
//  Copyright © 2017 Sang. All rights reserved.
//

import Foundation
import UIKit

class CustomSearchBarView: UISearchBar 
    final let SearchBarHeight: CGFloat = 44
    final let SearchBarPaddingTop: CGFloat = 8
    override open func awakeFromNib() 
        super.awakeFromNib()
        self.setupUI()
    

    override init(frame: CGRect) 
        super.init(frame: frame)
        self.setupUI()
    

    required init?(coder aDecoder: NSCoder) 
        super.init(coder: aDecoder)
       // fatalError("init(coder:) has not been implemented")
    
    func findTextfield()-> UITextField?
        for view in self.subviews 
            if view is UITextField 
                return view as? UITextField
             else 
                for textfield in view.subviews 
                    if textfield is UITextField 
                        return textfield as? UITextField
                    
                
            
        
        return nil;
    
    func setupUI()
        if #available(iOS 11.0, *) 
            self.translatesAutoresizingMaskIntoConstraints = false
            self.heightAnchor.constraint(equalToConstant: SearchBarHeight).isActive = true
        
    

    override func layoutSubviews() 
        super.layoutSubviews()
        if #available(iOS 11.0, *) 
            if let textfield = self.findTextfield() 
                textfield.frame = CGRect(x: textfield.frame.origin.x, y: SearchBarPaddingTop, width: textfield.frame.width, height: SearchBarHeight - SearchBarPaddingTop * 2)`enter code here`
                return
            
        
    

【讨论】:

【参考方案15】:

在 Objective-C 中

if (@available(iOS 11.0, *)) 
        [self.searchBar.heightAnchor constraintLessThanOrEqualToConstant: 44].active = YES;
              

【讨论】:

【参考方案16】:

我尝试了各种方法来将大小恢复到原来的 44,但是搜索栏的外观和行为总是很奇怪——比如被拉远、y 偏移等等。

我在这里找到了一个不错的解决方案(通过其他一些 *** 帖子): https://github.com/DreamTravelingLight/searchBarDemo

只需从 SearchViewController 派生您的视图控制器,并在您的项目中包含 SearchViewController 和 WMSearchbar 类。为我开箱即用,没有任何丑陋的 if (iOS11) else... 丑陋。

【讨论】:

【参考方案17】:

编辑:@zgjie 答案是解决此问题的更好方法:https://***.com/a/46356265/1713123

这似乎是因为在 iOS 11 中 SearchBar 的默认高度值已更改为 56,而不是之前的 iOS 版本中的 44。

目前,我已应用此解决方法,将 searchBar 高度设置回 44:

let barFrame = searchController.searchBar.frame
searchController.searchBar.frame = CGRect(x: 0, y: 0, width: barFrame.width, height: 44)    

另一种解决方案是使用new searchController property on navigationItem in iOS 11:

navigationItem.searchController = searchController

但是这样 da searchBar 会出现在导航标题下方。

【讨论】:

更改是否记录在某处?我找不到它here。 仅供参考,您可以修改框架而不为其创建变量; CGRect().insetBy(dx:dy:)CGRect().offsetBy(dx:dy:),在这种情况下 - searchController.searchBar.frame = searchController.searchBar.frame.insetBy(dx: 0, dy: -12) 此解决方案有效,但当您单击搜索栏时,高度会重置。 @Gustavo_fringe 你有没有找到任何解决方案。【参考方案18】:

我相信 iOS 11 UISearchBar 现在的高度等于 56,并且 UINavigationBar 使用自动布局来适应它的子视图,因此它增加了高度。如果您仍然希望 UISearchBar 作为 iOS 11 之前的 titleView,我发现最好的方法是将 UISearchBar 嵌入自定义视图中,并将该视图的高度设置为 44,并将其分配给 navigationItem.titleView

class SearchBarContainerView: UIView   

    let searchBar: UISearchBar  

    init(customSearchBar: UISearchBar)   
        searchBar = customSearchBar  
        super.init(frame: CGRect.zero)  

        addSubview(searchBar)  
    

    override convenience init(frame: CGRect)   
        self.init(customSearchBar: UISearchBar())  
        self.frame = frame  
      

    required init?(coder aDecoder: NSCoder)   
        fatalError("init(coder:) has not been implemented")  
      

    override func layoutSubviews()   
        super.layoutSubviews()  
        searchBar.frame = bounds  
      
  

class MyViewController: UIViewController   

    func setupNavigationBar()   
        let searchBar = UISearchBar()  
        let searchBarContainer = SearchBarContainerView(customSearchBar: searchBar)  
        searchBarContainer.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 44)  
        navigationItem.titleView = searchBarContainer  
      
 

【讨论】:

这可以正常工作,但是当您将其从纵向更改为横向时会出现问题。 如果您不使用自动布局,这是一个很好的解决方案。到目前为止,这似乎也适用于模拟器中的横向模式。 我已经发布了一个解决方向变化问题的解决方案。 这个答案解决了所有问题。 zgjie 的回答很好,但是点击文本框后搜索栏框架仍然改变。【参考方案19】:

就我而言,更大的 UINavigationBar 的高度对我来说不是问题。我只需要重新对齐左右栏按钮项目。这就是我想出的解决方案:

- (void)iOS11FixNavigationItemsVerticalAlignment

    [self.navigationController.navigationBar layoutIfNeeded];

    NSString * currSysVer = [[UIDevice currentDevice] systemVersion];
    if ([currSysVer compare:@"11" options:NSNumericSearch] != NSOrderedAscending)
    
        UIView * navigationBarContentView;
        for (UIView * subview in [self.navigationController.navigationBar subviews])
        
            if ([subview isKindOfClass:NSClassFromString(@"_UINavigationBarContentView")])
            
                navigationBarContentView = subview;
                break;
            
        

        if (navigationBarContentView)
        
            for (UIView * subview in [navigationBarContentView subviews])
            
                if (![subview isKindOfClass:NSClassFromString(@"_UIButtonBarStackView")]) continue;

                NSLayoutConstraint * topSpaceConstraint;
                NSLayoutConstraint * bottomSpaceConstraint;

                CGFloat topConstraintMultiplier = 1.0f;
                CGFloat bottomConstraintMultiplier = 1.0f;

                for (NSLayoutConstraint * constraint in navigationBarContentView.constraints)
                
                    if (constraint.firstItem == subview && constraint.firstAttribute == NSLayoutAttributeTop)
                    
                        topSpaceConstraint = constraint;
                        break;
                    

                    if (constraint.secondItem == subview && constraint.secondAttribute == NSLayoutAttributeTop)
                    
                        topConstraintMultiplier = -1.0f;
                        topSpaceConstraint = constraint;
                        break;
                    
                

                for (NSLayoutConstraint * constraint in navigationBarContentView.constraints)
                
                    if (constraint.firstItem == subview && constraint.firstAttribute == NSLayoutAttributeBottom)
                    
                        bottomSpaceConstraint = constraint;
                        break;
                    

                    if (constraint.secondItem == subview && constraint.secondAttribute == NSLayoutAttributeBottom)
                    
                        bottomConstraintMultiplier = -1.0f;
                        bottomSpaceConstraint = constraint;
                        break;
                    
                

                CGFloat contentViewHeight = navigationBarContentView.frame.size.height;
                CGFloat subviewHeight = subview.frame.size.height;
                topSpaceConstraint.constant = topConstraintMultiplier * (contentViewHeight - subviewHeight) / 2.0f;
                bottomSpaceConstraint.constant = bottomConstraintMultiplier * (contentViewHeight - subviewHeight) / 2.0f;
            
        
    

基本上,我们搜索包含条形按钮项的堆栈视图,然后更改它们的顶部和底部约束值。是的,这是一个肮脏的黑客,如果您可以通过任何其他方式解决您的问题,则不建议使用它。

【讨论】:

我想做和你一样的事情,但我会等到一个更优雅的解决方案出现。无论如何感谢您的分享。

以上是关于UISearchBar 在 iOS 11 中增加导航栏高度的主要内容,如果未能解决你的问题,请参考以下文章

UISearchBar在iOS 11中的错误位置

嵌入 UISplitViewController 时,UINavigationBar 中缺少 iOS11 UISearchBar

ios11:导航栏中的 UISearchBar

iOS 11:UISearchController 在 UITableView 和 UISearchBar 之间有一个奇怪的空间

在 iOS 9+ 中更改 UISearchBar 文本颜色

如何在 iOS7 中更改 UISearchBar 的 inputView?