簡體   English   中英

UISearchBar 增加了 iOS 11 中的導航欄高度

[英]UISearchBar increases navigation bar height in iOS 11

我的UISearchBar是導航欄的一部分,例如:

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

更新到iOS 11后,我的應用程序中的搜索欄發生了一些奇怪的事情。 iOS 10及更早版本上,我的導航欄看起來像:

在此處輸入圖片說明

現在使用iOS 11我有:

在此處輸入圖片說明

正如您所看到的,兩個搜索欄的舍入有所不同,這並不困擾我。 問題是搜索欄增加了導航欄的高度。 所以當我去另一個控制器時,它看起來也很奇怪:

在此處輸入圖片說明

事實上,奇怪的黑線高度加上當前導航欄的高度等於第二張圖片中顯示的導航欄高度......

任何想法如何擺脫黑線並在所有視圖控制器中具有一致的導航欄高度?

在兩種情況下,我在 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
}

斯威夫特 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
}

您可以向 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;
}

我相信在 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  
    }  
} 

在 viewDidLoad 中的“ACKNOWLEDGEMENTS”視圖控制器上試試這個代碼

self.extendedLayoutIncludesOpaqueBars = true

謝謝大家! 我終於找到了解決方案。

使用 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
    }

在 Objective-C 中

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

這也發生在我身上,在 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()
}

修復后預覽:

在此處輸入圖片說明 在此處輸入圖片說明

修復前預覽:

在此處輸入圖片說明 在此處輸入圖片說明

編輯:@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出現在導航標題下方。

所有解決方案對我都不起作用,所以在我推送視圖控制器之前,我做了:

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

    self.navigationItem.titleView = UIView()
}

並在返回時顯示搜索欄:

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

    self.navigationItem.titleView = UISearchBar()
}

我無法使用將導航欄保持在 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;
}

你所要做的就是繼承 UISearchBar 並覆蓋“intrinsicContentSize”:

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

在此處輸入圖片說明

無法發表評論,但想分享一些我遇到的其他問題,即使在使用其他解決方案之一后,我也花了很多時間試圖深入了解這個問題。

對我來說最好的解決辦法是安德魯的回答

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)設置背景圖像,過渡動畫無法正常工作,完成后會跳回其位置。

這些特定的屬性被設置為讓導航欄以某種方式出現,所以我需要做一些調整才能讓它恢復,或者忍受奇怪的行為。 如果我遇到任何其他問題或在其他操作系統版本中找到其他解決方案或差異,將嘗試記住更新上述內容。

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

基本上,我們搜索包含條形按鈕項的堆棧視圖,然后更改它們的頂部和底部約束值。 是的,這是一個骯臟的黑客,如果您可以通過任何其他方式解決問題,則不建議使用它。

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

我發現 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

現在還有一個案例,我還沒有弄清楚。 要重現執行以下操作:
- 從縱向開始
- 激活搜索字段
- 旋轉到橫向
- 錯誤:欄不調整大小

我通過在嵌入搜索欄的地圖視圖控制器上向 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
    }
}

嗨,使用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(animated)

    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
}()

所以我希望有一天這對某人有所幫助。

我嘗試了各種方法來將大小恢復到原來的 44,但隨后搜索欄的外觀和行為總是很奇怪——比如被拉得太遠、y 偏移等等。

我在這里找到了一個不錯的解決方案(通過其他一些 stackoverflow 帖子): https : //github.com/DreamTravelingLight/searchBarDemo

只需從 SearchViewController 派生您的視圖控制器,並在您的項目中包含 SearchViewController 和 WMSearchbar 類。 為我開箱即用,沒有任何丑陋的 if (iOS11) else...丑陋。

就我而言,我必須將 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 的框架,您可以更改插圖。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM