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


89

UISearchBar像下面那样成为导航栏的一部分:

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

更新后iOS 11,我的应用程序中的搜索栏发生了奇怪的变化。在此iOS 10之前,我的导航栏如下所示:

在此处输入图片说明

现在iOS 11有了:

在此处输入图片说明

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

在此处输入图片说明

实际上,奇怪的黑线的高度加上当前导航栏的高度等于第二张图片中显示的导航栏的高度...

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


Answers:


66

在两种情况下,iOS 11中的NavigationBar和SearchBar下出现黑线:

  • 当我使用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
}

2
请谨慎使用,因为对我来说,尝试滑动回以前的视图控制器时,它冻结了整个应用程序
Abd Al-rhman Taher Badary

1
@andrew
Sagar Daundkar,

67

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

//迅捷

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

// Objective-C

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

12
尽管这样做可以使高度恢复正常,但是如果我点击搜索栏,由于某种原因,它会切断搜索栏的底部。有任何想法为什么会发生这种情况,还是有其他人遇到这个问题?
Christopher Smit

9
共怪异,如果您使用此代码,因为它只是缩小了导航栏,但在搜索栏的高度仍然是56.寻找
OtakuFitness

4
还有其他人找到解决方案吗?这样做会在搜索栏上产生很多奇怪的行为问题。
Gustavo_fringe

15
这是不够的答案。当您单击该字段时,大小仍然混乱。
罗斯,

5
这在iOS 13中不起作用。对此有任何解决方法?
VV

42

我相信在iOS 11中,UISearchBar的高度现在等于56,而UINavigationBar使用自动布局来适应其子视图,因此会增加高度。如果您仍希望像iOS 11之前一样将UISearchBar作为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  
    }  
} 

这项工作正常,但是当您将其从纵向更改为横向时遇到问题。
马拉夫·索尼

如果您不使用自动版式,这是一个很好的解决方案。到目前为止,这在模拟器中似乎也适用于横向模式。
瑞安H.17年

我已经发布了解决方向更改问题的解决方案。

这个答案解决了所有问题。zgjie的答案很好,但是在点击文本框后,搜索栏框架仍然发生了变化。
查理·洪

19

在viewDidLoad中的“ ACKNOWLEDGEMENTS”视图控制器上尝试此代码

self.extendedLayoutIncludesOpaqueBars = true

谢谢!这是我最终要解决的问题,因为我没有使用半透明条。
mmh02 '18

确实,这应该是答案。但是我想在这段代码中添加这条代码到搜索栏控制器上方和下方的控制器中。
Amrit Sidhu '18

这对我有用,但是必须在viewWillDisappear中添加navigationController?.view.layoutSubviews()。
douglasd3 '18

这非常有效,如果您仅告诉我们extendedLayoutIncludesOpaqueBars 财产的用途,我将不胜感激。
Anirudha Mahale

extendedLayoutIncludesOpaqueBars在导航和状态栏下扩展您的视图(self.view)。如果将子视图添加到视图(例如UIImageView)并将其约束设置到视图的左上角并运行应用程序,则在将此属性设置为true和false时会看到不同。
Silverwind '18

6

谢谢你们!我终于找到了解决方案。

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

  1. 第一步: viewDidLoad
-(void)viewDidLoad
{
    [super viewDidLoad];
    self.extendedLayoutIncludesOpaqueBars = YES;
    ...
}
override func viewDidLoad() {
    super.viewDidLoad()
    self.extendedLayoutIncludesOpaqueBars = true
}
  1. 第二步: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
    }

4

在Objective-C中

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

2

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

在实现searchBar的UIViewController中尝试此操作。

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

修复后预览:

在此处输入图片说明 在此处输入图片说明

修复前预览:

在此处输入图片说明 在此处输入图片说明


1

编辑:@zgjie答案是针对此问题的更好解决方案:https ://stackoverflow.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)    

另一个解决方案是在iOS 11中使用navigationItem上新searchController属性

navigationItem.searchController = searchController

但是这种方式da searchBar出现在导航标题下方。


1
变更是否记录在某处?我在这里找不到。
尤利安·奥诺夫雷

1
仅供参考,您可以修改框架而无需为其创建变量;CGRect().insetBy(dx:dy:)并且CGRect().offsetBy(dx:dy:),在这种情况下-searchController.searchBar.frame = searchController.searchBar.frame.insetBy(dx: 0, dy: -12)
奥斯卡Apeland

此解决方案有效,但是当您单击搜索栏时,高度会重置。
Gustavo_fringe

@Gustavo_fringe您是否找到任何解决方案。
Shivam Pokhriyal

1

所有解决方案都对我不起作用,因此在推送视图控制器之前,我做了以下工作:

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

    self.navigationItem.titleView = UIView()
}

并在返回时使搜索栏出现:

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

    self.navigationItem.titleView = UISearchBar()
}

这删除了新的viewcontroller中的黑色视图,但是当返回到该viewcontroller(包含searchBar的那个)时,rootView实际上向上移动了一点。
code4latte

1

我无法使用将navBar保持在44的解决方案。所以花了我一天的时间,但最后,我找到了一种解决方案,该解决方案不会更改条形图的高度并将按钮置于条形图的中间。问题在于按钮被放置在配置为“水平”堆栈视图的堆栈视图中,因此无法适应高度变化。

这是在init上完成的:

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;
}

1

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

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

在此处输入图片说明


1

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

看来对我来说最好的解决方法是安德鲁的答案

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),过渡动画无法正常工作,完成后将跳回到其位置。

设置这些特定的属性是为了使导航栏以某种方式显示,因此,我需要进行一些调整以使其恢复原状,或者忍受怪异的行为。如果遇到其他问题或发现其他解决方案或其他OS版本的差异,将尝试记住更新以上内容。


0

就我而言,更大的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;
            }
        }
    }
}

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


我想做与您相同的事情,但是我将等到提出更优雅的解决方案。感谢您的分享。
Michael Pirotte

0
//
//  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
            }
        }
    }
}

0

我发现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

现在还有一个案件我还没有弄清楚。要重现,请执行以下操作:
-从纵向开始
-激活搜索范围
-旋转至横向
-错误:条形图没有调整大小


0

我通过将约束添加到嵌入搜索栏的地图视图控制器上的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
    }
}

1
您错过了对super.viewDidAppear(动画)的调用
Reconquistador

0

嗨,使用UISearchController它的人,然后将其附加UISearchBarnavigationItem.titleView。我每天要花4-5个小时疯狂地解决这个问题。继iOS的11+推荐的方法,这是放searchControllernavigation.searchController不只是适合我的情况。具有此searchController / searchBar的屏幕具有一个backButton,这是一个自定义按钮。

我已经在iOS 10,iOS 11和12中对此进行了测试。我只是必须。我不能不解决这个恶魔就回家。考虑到我的截止日期,这是我今天能做的最完美的事情。

因此,我只想分享我所做的辛勤工作,这取决于您将所有内容放到您想要的任何位置(例如,viewModel中的变量)。它去了:

在我的第一个屏幕(例如,没有此搜索控制器的主屏幕)中,我已经有了它viewDidLoad()

self.extendedLayoutIncludesOpaqueBars = true

在我的第二个屏幕(具有searchController的屏幕)中,我已经有了这个viewDidAppear

覆盖func viewDidAppear(_动画:布尔){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
}()

因此,我希望有一天能对某人有所帮助。


-1

我尝试了各种方法使尺寸恢复到原始的44,但是搜索栏总是看起来和行为很奇怪-就像被拉得太远,y偏移等等。

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

只需从SearchViewController派生您的viewcontroller并在您的项目中包含SearchViewController和WMSearchbar类。如果不使用(iOS11)否则很难为我工作。


-1

就我而言,我必须降低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的框架,则可以更改插图。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.