it-swarm.com.de

UISearchBar erhöht die Höhe der Navigationsleiste in iOS 11

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:

 enter image description here

Jetzt mit iOS 11 habe ich: 

 enter image description here

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:

 enter image description here

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? 

69
radioaktiv

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;
    }
63
zgjie

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 enter image description here

  • wenn ich ViewController mit UISearchBar mit "Ziehen nach rechts zum Entlassen" enter image description here

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
}
40
Andrew

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  
    }  
} 
32
Mai Mai

versuchen Sie diesen Code auf dem View-Controller "ACKNOWLEDGMENTS "in viewDidLoad

self.extendedLayoutIncludesOpaqueBars = true
11
Silverwind

In Ziel-C

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

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;
}
1
Zeev Vax

Danke euch allen! Ich habe endlich eine Lösung gefunden.

Hinzufügen des folgenden Codes zu ViewController mit UISearchBar.

  1. Erster Schritt: viewDidLoad
-(void)viewDidLoad
{
    [super viewDidLoad];
    self.extendedLayoutIncludesOpaqueBars = YES;
    ...
}
override func viewDidLoad() {
    super.viewDidLoad()
    self.extendedLayoutIncludesOpaqueBars = true
}
  1. Zweiter Schritt: 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
    }
1
BlessNeo

Alles, was Sie tun müssen, ist UISearchBar zu subclassieren und "intrinsicContentSize" zu überschreiben:

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

 enter image description here

0
Jagie

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 

0
de.

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.

0
Roman Serga

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
    }
}
0
Akshay

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:

  • wurde isTranslucent auf false gesetzt, scheint der Ansichts-Controller mit der Suchleiste das Layout der Ansicht nicht wiederherzustellen, wenn interaktiv gekündigt wird.
  • wenn das Hintergrundbild mit 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.

0
Ohifriend
//
//  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
            }
        }
    }
}
0
Sang_longan

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.

0
Glenn

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.

0