Подтвердить что ты не робот

UISearchBar увеличивает высоту навигационной панели в iOS 11

У меня есть UISearchBar, который является частью панели навигации, например:

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

После обновления до iOS 11 что-то странное произошло с панелью поиска в моем приложении. На iOS 10 и раньше у меня была панель навигации, похожая на:

введите описание изображения здесь

Теперь с iOS 11 у меня есть:

введите описание изображения здесь

Как вы видите, есть разница в округлении двух строк поиска, которые меня не беспокоят. Проблема в том, что панель поиска увеличивает высоту панели навигации. Поэтому, когда я иду на другой контроллер, это выглядит странно:

введите описание изображения здесь

Фактически эта странная высота черной линии плюс текущая высота навигационной панели равна высоте навигационной панели, показанной на втором рисунке...

Любые идеи о том, как избавиться от черной линии и иметь постоянную высоту навигационной панели во всех контроллерах представлений?

4b9b3361

Ответ 1

Вы можете добавить ограничение высоты 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;
    }

Ответ 2

Я получил черную линию под NavigationBar с SearchBar в iOS 11 в двух случаях:

  • когда я выдвинул еще один ViewController из ViewController с помощью UISearchBar enter image description here

  • когда я отклонил ViewController с помощью UISearchBar с помощью "перетаскивать вправо, чтобы уволить" enter image description here

Мое решение было: добавить этот код в мой ViewController с помощью UISearchBar:

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

Ответ 3

Я верю в iOS 11 UISearchBar теперь имеет высоту, равную 56, а UINavigationBar использует автозапуск, чтобы соответствовать его подзонам, следовательно, он увеличивает высоту. Если вы все еще хотите иметь UISearchBar как titleView, как в pre-iOS 11, я выяснил, что лучший способ сделать это - встраивать 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  
    }  
} 

Ответ 4

попробуйте этот код в контроллере просмотра "ACKNOWGEDGMENTS" in viewDidLoad

self.extendedLayoutIncludesOpaqueBars = true

Ответ 5

В Objective-C

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

Ответ 6

Я не мог использовать решение для сохранения 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;
}

Ответ 7

Спасибо вам всем! Я наконец нашел решение.

Добавление следующего кода в ViewController с помощью UISearchBar.

  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
    }

Ответ 8

EDIT: ответ @zgjie является лучшим решением для этой проблемы: fooobar.com/questions/152636/...

Кажется, это происходит, потому что в iOS 11 значение высоты по умолчанию для SearchBar было изменено на 56, вместо этого 44 на предыдущих версиях iOS.

В настоящее время я применил это обходное решение, установив для параметра searchBar height значение 44:

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

Другим решением может быть использование нового свойства searchController в функции navigationItem в iOS 11:

navigationItem.searchController = searchController

Но таким образом da searchBar появляется под заголовком навигации.

Ответ 9

В моем случае большая высота 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;
            }
        }
    }
}

В принципе, мы ищем представления стека, содержащие элементы панели бара, а затем изменяя их верхние и нижние значения ограничений. Да, это взломать грязь, и не рекомендуется использовать его, если вы можете исправить свою проблему каким-либо другим способом.

Ответ 10

Я попробовал разные вещи, чтобы вернуть размер оригиналу 44, но тогда панель поиска всегда выглядит и ведет себя странно - например, к далекой протяженности, y-offset и тому подобному.

Я нашел приятное решение здесь (через какой-то другой пост stackoverflow): https://github.com/DreamTravelingLight/searchBarDemo

Просто выведите свой диспетчер представлений из SearchViewController и включите в свой проект классы SearchViewController и WMSearchbar. Работала из коробки для меня без уродства, если (iOS11) еще... уродство.

Ответ 11

Все решение не работало для меня, поэтому, прежде чем я нажал контроллер просмотра, я сделал:

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

    self.navigationItem.titleView = UIView()
}

И чтобы сделать панель поиска текущей при возвращении:

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

    self.navigationItem.titleView = UISearchBar()
}

Ответ 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

Привет людям, которые используют UISearchController а затем присоединяют его UISearchBar к navigationItem.titleView. Я потратил 4-5 часов своего дня, чтобы решить эту проблему. Следуя рекомендованному iOS 11+ подходу, который помещает searchController в navigation.searchController, не подходит для моего случая. Экран с этим searchController/searchBar имеет пользовательскую кнопку BackButton.

Я проверил это в iOS 10, iOS 11 и 12. На разных устройствах. Я просто должен был. Я не могу пойти домой без разгадки этого демона. Это самое лучшее, что я мог сделать на сегодня, учитывая мой сжатый срок.

Так что я просто хочу поделиться этой тяжелой работой, которую я проделал, от вас зависит, чтобы поместить все в то место, где вы хотите (например, переменные в вашей viewModel). Здесь это идет:

На моем первом экране (скажем, на viewDidLoad() экране, на котором нет этого контроллера поиска), я имею это в моем viewDidLoad().

self.extendedLayoutIncludesOpaqueBars = true

На моем втором экране, тот, который имеет searchController, у меня есть это в моем viewDidAppear.

переопределить функцию viewDidAppear (_ animated: Bool) {super.viewDidAppear(animated)

    let systemMajorVersion = ProcessInfo.processInfo.operatingSystemVersion.majorVersion
    if systemMajorVersion < 12 {
        // Place the search bar in the navigation item 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
}()

Поэтому я надеюсь, что это поможет кому-нибудь когда-нибудь.

Ответ 14

Не удалось прокомментировать, но хотел поделиться некоторыми дополнительными проблемами, с которыми я столкнулся, потратив много часов, пытаясь разобраться в этой проблеме даже после использования одного из других решений.

Похоже, лучшим решением для меня был ответ Андрея:

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 для isTranslucent установлено значение false, то контроллер вида с панелью поиска, по-видимому, не получает обратно настроенный макет представления при интерактивном отклонении (нормальное отклонение с помощью кнопки "назад" работает).
  • имеет фоновое изображение, установленное с помощью setBackgroundImage(UIImage(), for:.default), анимация перехода не работает должным образом и после завершения вернется к своей позиции.

Эти конкретные свойства были настроены таким образом, чтобы панель навигации отображалась определенным образом, поэтому мне нужно внести некоторые коррективы, чтобы вернуть ее назад или смириться со странным поведением. Постараюсь не забыть обновить вышеупомянутое, если я столкнусь с чем-либо еще или найду другие решения или различия в других версиях ОС.

Ответ 15

В моем случае мне нужно уменьшить высоту 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.

Ответ 16

Я исправил это, добавив ограничение на 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
    }
}

Ответ 17

Все, что вам нужно сделать, это создать подкласс UISearchBar и переопределить "intrinsicContentSize":

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

enter image description here

Ответ 18

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