Я UISearchBar
являюсь частью панели навигации, например:
let searchBar = UISearchBar()
//some more configuration to the search bar
.....
navigationItem.titleView = searchBar
После обновления iOS 11
произошло что-то странное с панелью поиска в моем приложении. На iOS 10
и до я моя панель навигации выглядит как:
Теперь у iOS 11
меня есть:
Как видите, разница в округлении двух полос поиска меня не беспокоит. Проблема в том, что панель поиска увеличивает высоту панели навигации. Поэтому, когда я перехожу к другому контроллеру, это тоже выглядит странно:
Фактически, высота этой странной черной линии плюс высота текущей панели навигации равна высоте панели навигации, показанной на втором рисунке ...
Есть идеи, как избавиться от черной линии и иметь одинаковую высоту панели навигации для всех контроллеров представления?
ios
swift
uinavigationbar
ios11
радиоактив
источник
источник
Ответы:
У меня черная линия под панелью навигации с панелью поиска в iOS 11 была в двух случаях:
когда я нажал другой ViewController из 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 Обновление
override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) navigationController?.view.setNeedsLayout() // force update layout navigationController?.view.layoutIfNeeded() // to fix height of the navigation bar }
источник
Вы можете добавить ограничение высоты 44 в строку поиска для iOS 11.
// Swift
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 использует автопрокладку, чтобы соответствовать своим подпредставлениям, поэтому он увеличивает высоту. Если вы по-прежнему хотите иметь UISearchBar в качестве titleView, как в предшествующей 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 } }
источник
попробуйте этот код на контроллере представления "ACKNOWLEDGMENTS" в viewDidLoad
self.extendedLayoutIncludesOpaqueBars = true
источник
extendedLayoutIncludesOpaqueBars
собственность.Спасибо вам всем! Наконец-то я нашел решение.
Добавление следующего кода в 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 }
источник
В Objective-C
if (@available(iOS 11.0, *)) { [self.searchBar.heightAnchor constraintLessThanOrEqualToConstant: 44].active = YES; }
источник
Это случилось и со мной, все работало хорошо в iOS 12.4 и становилось странным в 13 выше. Проблема заключается в увеличении высоты панели навигации iOS 13 с 88 до 100 после перехода из UIViewController, реализующего searchBar.
Попробуйте это в своем UIViewController, который реализует searchBar.
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, а не на 44 в предыдущих версиях iOS.
На данный момент я применил этот обходной путь, установив высоту searchBar обратно на 44:
let barFrame = searchController.searchBar.frame searchController.searchBar.frame = CGRect(x: 0, y: 0, width: barFrame.width, height: 44)
Другим решением может быть использование нового свойства searchController в navigationItem в iOS 11 :
Но так da searchBar появляется под заголовком навигации.
источник
CGRect().insetBy(dx:dy:)
иCGRect().offsetBy(dx:dy:)
, в этом случае -searchController.searchBar.frame = searchController.searchBar.frame.insetBy(dx: 0, dy: -12)
Все решения у меня не работали, поэтому, прежде чем я нажал на контроллер представления, я сделал:
override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) self.navigationItem.titleView = UIView() }
И чтобы при возвращении отображалась панель поиска:
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.navigationItem.titleView = UISearchBar() }
источник
Я не мог использовать решение с сохранением navBar на уровне 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; }
источник
Все, что вам нужно сделать, это создать подкласс 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
, в View Controller с появляется панель поиска , чтобы не получить макет регулируется подпереть его Высказаться, когда в интерактивном режиме увольнения (нормальный , отказавшие через спину появляется кнопка для работы).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. На разных устройствах. Мне просто пришлось. Я не могу пойти домой, не разгадав этого демона. Это лучшее, что я мог сделать на сегодня, учитывая мои сжатые сроки.
Итак, я просто хочу поделиться этой тяжелой работой, которую я проделал, вам решать, где вы хотите поместить все, где хотите (например, переменные в вашей модели просмотра). Вот оно:
На моем первом экране (скажем, на главном экране, на котором нет этого контроллера поиска) у меня есть это в моем
viewDidLoad()
.self.extendedLayoutIncludesOpaqueBars = true
На моем втором экране, на котором есть searchController, у меня это в моем
viewDidAppear
.переопределить функцию 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 }()
Так что я надеюсь, что это когда-нибудь поможет кому-нибудь.
источник
Я пробовал разные вещи, чтобы вернуть размер к исходному 44, но тогда панель поиска всегда выглядит и ведет себя странно - например, слишком растянута, смещение по оси Y и тому подобное.
Я нашел здесь хорошее решение (через какое-то другое сообщение stackoverflow): https://github.com/DreamTravelingLight/searchBarDemo
Просто извлеките свой viewcontroller из SearchViewController и включите в свой проект классы SearchViewController и WMSearchbar. Сработало для меня из коробки без какого-либо уродливого if (iOS11) else ... уродства.
источник
В моем случае мне нужно уменьшить высоту текстового поля с 36 пунктов до 28 пунктов.
Поэтому я попытался изменить высоту кадра, высоту слоя. Но способы не сработали.
Наконец, я нашел решение - маску. Думаю, это не лучший способ, но он работает.
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.
источник