UISearchBar увеличивает высоту панели навигации в iOS 11
у меня UISearchBar являющийся частью панели навигации, как:
let searchBar = UISearchBar()
//some more configuration to the search bar
.....
navigationItem.titleView = searchBar
после обновления до iOS 11 что-то странное произошло в строке поиска в моем приложении. На iOS 10 и до этого у меня была панель навигации, похожая на:
теперь iOS 11 Я:
как вы можете видеть, есть разница в округлении двух строк поиска, которая меня не беспокоит. Этот проблема в том, что панель поиска увеличивает высоту панели навигации. Поэтому, когда я иду к другому контроллеру, это тоже выглядит странно:
фактически, высота странной черной линии плюс высота текущей навигационной панели равна высоте навигационной панели, показанной на втором рисунке ...
любые идеи, как избавиться от черной линии и иметь постоянную высоту панели навигации по всем контроллерам вида ?
16 ответов:
вы можете добавить ограничение высоты 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; }
Я считаю, что в iOS 11 UISearchBar теперь имеет высоту, равную 56, а UINavigationBar использует autolayout для подгонки своих подвидов, поэтому он увеличивает высоту. Если вы все еще хотите иметь 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 } }
Я получил черную линию под навигационной панелью с панелью поиска в iOS 11 в двух случаях:
когда я нажал ViewControllers от ViewController с UISearchBar
когда я уволил ViewController с UISearchBar с "перетащите право уволить"
мое решение было: добавление этого кода в мой 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 Update
override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) navigationController?.view.setNeedsLayout() // force update layout navigationController?.view.layoutIfNeeded() // to fix height of the navigation bar }
попробуйте этот код на контроллере представления " подтверждения в метод viewDidLoad
self.extendedLayoutIncludesOpaqueBars = true
В Объективе-C
if (@available(iOS 11.0, *)) { [self.searchBar.heightAnchor constraintLessThanOrEqualToConstant: 44].active = YES; }
Я не мог использовать решение держать 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; }
в моем случае, большая высота 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; } } } }в основном, мы ищем представления стека, которые содержат элементы кнопок панели, а затем меняем их верхние и нижние значения ограничений. Да, это грязный хак, и не рекомендую использовать его, если вы можете исправить свою проблему каким-либо другим способом.
EDIT: ответ @zgjie является лучшим решением этой проблемы:https://stackoverflow.com/a/46356265/1713123
похоже, это происходит потому, что в iOS 11 значение высоты по умолчанию SearchBar было изменено на 56, а не 44 в предыдущих версиях iOS.
на данный момент я применил этот обходной путь, установив высоту панели поиска обратно в 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 появляется под заголовком навигации.
Я пробовал разные вещи, чтобы вернуть размер к исходному 44, но тогда панель поиска всегда выглядит и ведет себя странно - как будто она сильно растянута, y-смещена и так далее.
Я нашел хорошее решение здесь (через некоторые другие сообщения stackoverflow): https://github.com/DreamTravelingLight/searchBarDemo
просто выведите свой viewcontroller из SearchViewController и включите в свой проект классы SearchViewController и WMSearchbar. Работал из коробки для меня без каких-либо уродливых if (iOS11) еще... уродство.
все решение не работает для меня, поэтому, прежде чем я нажал view controller я сделал:
override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) self.navigationItem.titleView = UIView() }и сделать панель поиска присутствует при возвращении:
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.navigationItem.titleView = UISearchBar() }
Я нашел решение 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теперь есть еще один случай, который я еще не выяснил. Для воспроизведения выполните следующие действия:
- начинай! портрет
- активировать поле поиска
- поворот к пейзажу
- ошибка: бар не изменяет размер
спасибо всем! Наконец-то я нашел решение.
добавление следующего кода в ViewController с помощью UISearchBar.
- первый этап:
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 }
в моем случае, я должен уменьшить высоту текстового поля 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 }вы можете изменить вставки, если вы хотите изменить рамку текстового поля.
я исправил это, добавив ограничение на 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 } }
// // 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 } } } }







Comments