Ich habe meine UISearchBar
als Teil der Navigationsleiste wie:
let searchBar = UISearchBar()
//some more configuration to the search bar
.....
navigationItem.titleView = searchBar
Nach der Aktualisierung auf iOS 11
ist der Suchleiste in meiner App etwas Seltsames passiert. Auf iOS 10
und früher hatte ich meine Navigationsleiste wie folgt:
Jetzt mit iOS 11
habe ich:
Wie Sie sehen, gibt es Unterschiede in der Rundung der beiden Suchbalken, was mich nicht stört. Das Problem ist, dass die Suchleiste die Höhe der Navigationsleiste erhöht. Wenn ich zu einem anderen Controller gehe, sieht es auch komisch aus:
Tatsächlich entspricht die Höhe der seltsamen schwarzen Linie plus der Höhe der aktuellen Navigationsleiste der Höhe der Navigationsleiste im zweiten Bild ...
Haben Sie Ideen, wie Sie die schwarze Linie loswerden und eine einheitliche Höhe der Navigationsleiste für alle Ansichts-Controller haben?
Sie können der Suchleiste für iOS 11 eine Einschränkung der Höhe 44 hinzufügen.
if #available(iOS 11.0, *) {
searchBar.heightAnchor.constraint(equalToConstant: 44).isActive = true
}
// Ziel c
if (@available(iOS 11.0, *)) {
[searchBar.heightAnchor constraintEqualToConstant:44].active = YES;
}
Ich habe in iOS 11 in zwei Fällen eine schwarze Linie unter NavigationBar mit SearchBar erhalten:
wenn ich einen anderen ViewControllers von ViewController mit UISearchBar
wenn ich ViewController mit UISearchBar mit "Ziehen nach rechts zum Entlassen"
Meine Lösung war: diesen Code mit UISearchBar meinem ViewController hinzufügen:
-(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
}
Ich glaube in iOS 11, dass UISearchBar jetzt eine Höhe von 56 hat, und UINavigationBar verwendet Autolayout, um seine Unteransichten anzupassen, wodurch die Höhe erhöht wird. Wenn Sie UISearchBar weiterhin als titleView wie unter iOS 11 verwenden möchten, habe ich herausgefunden, dass Sie UISearchBar am besten in eine benutzerdefinierte Ansicht einbetten, die Höhe dieser Ansicht auf 44 setzen und navigationItem.titleView zuweisen
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
}
}
versuchen Sie diesen Code auf dem View-Controller "ACKNOWLEDGMENTS "in viewDidLoad
self.extendedLayoutIncludesOpaqueBars = true
In Ziel-C
if (@available(iOS 11.0, *)) {
[self.searchBar.heightAnchor constraintLessThanOrEqualToConstant: 44].active = YES;
}
Ich konnte die Lösung nicht verwenden, die navBar auf 44 zu belassen ... Also brauchte ich einen Tag, aber schließlich fand ich eine Lösung, bei der die Höhe der Bar nicht geändert wurde und der Knopf in der Mitte der Bar positioniert wurde. Das Problem ist, dass sich die Schaltflächen in einer Stapelansicht befinden, die als horizontale Stapelansicht konfiguriert ist und sich daher nicht an die Höhenänderung anpasst.
Dies geschieht am 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)];
}
on viewWillApear (oder jederzeit nachdem die Ansicht zum Navigationsstapel hinzugefügt wurde)
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];
}
}
Und subviewOfClass ist eine Kategorie in 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;
}
Danke euch allen! Ich habe endlich eine Lösung gefunden.
Hinzufügen des folgenden Codes zu ViewController mit 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
}
Ich fand, dass Mai Mai die einzige Lösung ist, die wirklich brauchbar ist.
Es ist jedoch immer noch nicht perfekt:
Beim Drehen des Geräts wird die Größe der Suchleiste nicht richtig angepasst und bleibt in der kleineren Dimension.
Ich habe einen Fix dafür gefunden. Hier ist mein Code in Ziel C mit den entsprechenden Teilen, die mit Anmerkungen versehen sind:
// 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
Jetzt gibt es noch einen Fall, den ich noch nicht herausgefunden habe. Um zu reproduzieren, gehen Sie folgendermaßen vor:
- im Portrait beginnen
- Suchfeld aktivieren
- zur Landschaft drehen
- Fehler: Die Größe der Leiste wird nicht geändert
In meinem Fall war eine größere Höhe der UINavigationBar kein Problem für mich. Ich musste nur die Elemente der linken und rechten Leiste neu ausrichten. Das ist die Lösung, die ich mir ausgedacht habe:
- (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;
}
}
}
}
Grundsätzlich suchen wir nach Stack-Ansichten, die Elemente der Balkenschaltfläche enthalten, und ändern dann die oberen und unteren Randbedingungswerte. Ja, es ist ein Drecksack und wird nicht empfohlen, ihn zu verwenden, wenn Sie Ihr Problem auf andere Weise beheben können.
Die gesamte Lösung funktionierte nicht für mich. Bevor ich den View Controller schob, tat ich Folgendes:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationItem.titleView = UIView()
}
Und um die Suchleiste beim Zurückgehen zur Verfügung zu stellen:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationItem.titleView = UISearchBar()
}
Dies wurde behoben, indem die Einschränkung für viewDidAppear auf dem Kartenansicht-Controller hinzugefügt wurde, in den die Suchleiste eingebettet ist
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
}
}
Kann nicht kommentieren, wollte aber einige zusätzliche Probleme mitteilen, auf die ich gestoßen bin, während ich viele Stunden damit verbracht habe, dieses Problem zu lösen, selbst nachdem ich eine der anderen Lösungen verwendet hatte.
Es scheint, dass die beste Lösung für mich war Andrews Antwort :
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.view.setNeedsLayout() // force update layout
navigationController?.view.layoutIfNeeded() // to fix height of the navigation bar
}
Zumindest in iOS 12.1, wenn Ihre UINavigationBar
:
isTranslucent
auf false
gesetzt, scheint der Ansichts-Controller mit der Suchleiste das Layout der Ansicht nicht wiederherzustellen, wenn interaktiv gekündigt wird.setBackgroundImage(UIImage(), for: .default)
eingestellt ist, funktioniert die Übergangsanimation nicht ordnungsgemäß und springt nach dem Beenden an ihre Position zurück.Diese speziellen Eigenschaften wurden so eingestellt, dass die Navigationsleiste auf eine bestimmte Art und Weise angezeigt wird. Daher muss ich einige Anpassungen vornehmen, um sie wiederherzustellen oder das seltsame Verhalten in Kauf zu nehmen. Ich werde versuchen, mich daran zu erinnern, obige Informationen zu aktualisieren, wenn ich etwas anderes finde oder andere Lösungen oder Unterschiede in anderen Betriebssystemversionen finde.
//
// 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
}
}
}
}
Hallo an die Leute, die UISearchController
verwenden und dann ihre UISearchBar
an den navigationItem.titleView
anhängen. Ich habe verrückte 4-5 Stunden meines Tages damit verbracht, dieses Problem zu lösen. Nach dem von iOS 11+ empfohlenen Ansatz ist die Änderung der searchController
auf den navigation.searchController
für meinen Fall nicht gerade richtig. Der Bildschirm, der über diesen searchController/searchBar verfügt, hat einen Rückwand-Button, einen benutzerdefinierten.
Ich habe dies in iOS 10, iOS 11 und 12 getestet. In verschiedenen Geräten. Ich musste nur Ich kann nicht nach Hause gehen, ohne diesen Dämon zu lösen. Dies ist das perfekteste, was ich heute für meine knappe Frist tun könnte.
Ich möchte also einfach diese harte Arbeit teilen, die ich gemacht habe. Es liegt an Ihnen, alles dort zu platzieren, wo Sie wollen (zB Variablen in Ihrem viewModel). Hier kommt's:
In meinem ersten Bildschirm (sprich Startbildschirm, der diesen Suchcontroller nicht hat) habe ich dies in meiner viewDidLoad()
.
self.extendedLayoutIncludesOpaqueBars = true
In meinem zweiten Bildschirm, demjenigen, der den searchController hat, habe ich dies in meiner viewDidAppear
.
func viewDidAppear überschreiben (_ animiert: Bool) { super.viewDidAppear (animiert)
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.
}
und hier ist meine searchController-Erklärung:
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
}()
Ich hoffe also, dass dies irgendwann jemandem hilft.
BEARBEITEN: Die Antwort von @zgjie ist eine bessere Lösung für dieses Problem: https://stackoverflow.com/a/46356265/1713123
Es scheint, dass dies der Fall ist, weil in iOS 11 der Standardwert für die Höheneinstellung von SearchBar auf 56 statt 44 in früheren iOS-Versionen geändert wurde.
Im Moment habe ich diese Problemumgehung angewendet und die Höhe der searchBar auf 44 zurückgesetzt:
let barFrame = searchController.searchBar.frame
searchController.searchBar.frame = CGRect(x: 0, y: 0, width: barFrame.width, height: 44)
Eine andere Lösung könnte die Verwendung der new searchController -Eigenschaft in navigationItem in iOS 11 sein :
navigationItem.searchController = searchController
Auf diese Weise erscheint da searchBar unter dem Navigationstitel.