Jestem UISearchBar
częścią paska nawigacyjnego, takiego jak:
let searchBar = UISearchBar()
//some more configuration to the search bar
.....
navigationItem.titleView = searchBar
Po aktualizacji do iOS 11
czegoś dziwnego stało się z paskiem wyszukiwania w mojej aplikacji. On iOS 10
i przed miałem pasek nawigacji wyglądające jak:
Teraz iOS 11
mam:
Jak widać, istnieje różnica w zaokrągleniu dwóch pasków wyszukiwania, co mi nie przeszkadza. Problem w tym, że pasek wyszukiwania zwiększa wysokość paska nawigacji. Więc kiedy idę do innego kontrolera, też wygląda to dziwnie:
W rzeczywistości ta dziwna wysokość czarnej linii plus aktualna wysokość paska nawigacji jest równa wysokości paska nawigacji pokazanego na drugim obrazku ...
Jakieś pomysły, jak pozbyć się czarnej linii i mieć spójną wysokość paska nawigacji we wszystkich kontrolerach widoku?
ios
swift
uinavigationbar
ios11
radioaktiv
źródło
źródło
Odpowiedzi:
Mam czarną linię pod NavigationBar z SearchBar w iOS 11 w dwóch przypadkach:
kiedy pchnąłem kolejne ViewControllers z ViewController z UISearchBar
kiedy zamknąłem ViewController z UISearchBar z „przeciągnij w prawo, aby odrzucić”
Moje rozwiązanie brzmiało: dodanie tego kodu do mojego ViewController za pomocą 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 }
Aktualizacja 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 }
źródło
Możesz dodać ograniczenie wysokości 44 do paska wyszukiwania dla iOS 11.
// Swift
if #available(iOS 11.0, *) { searchBar.heightAnchor.constraint(equalToConstant: 44).isActive = true }
// Cel C
if (@available(iOS 11.0, *)) { [searchBar.heightAnchor constraintEqualToConstant:44].active = YES; }
źródło
Wierzę, że UISearchBar w iOS 11 ma teraz wysokość równą 56, a UINavigationBar używa autolayout, aby dopasować jego podwidoki, dlatego zwiększa wysokość. Jeśli nadal chcesz mieć UISearchBar jako titleView, tak jak w wersji przed iOS 11, odkryłem, że najlepszym sposobem na to jest osadzenie UISearchBar w niestandardowym widoku i ustawienie wysokości tego widoku na 44 i przypisanie go do 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 } }
źródło
wypróbuj ten kod na kontrolerze widoku „ACKNOWLEDGMENTS” w viewDidLoad
self.extendedLayoutIncludesOpaqueBars = true
źródło
extendedLayoutIncludesOpaqueBars
zajmuje się nieruchomość.Dziękuję wam wszystkim! W końcu znalazłem rozwiązanie.
Dodanie następującego kodu do ViewController za pomocą 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 }
źródło
W celu-C
if (@available(iOS 11.0, *)) { [self.searchBar.heightAnchor constraintLessThanOrEqualToConstant: 44].active = YES; }
źródło
Mnie też się to zdarza, wszystko działa dobrze w iOS 12.4 i robi się dziwnie w 13 powyżej. Problem polega na wzroście wysokości paska nawigacji iOS 13 z 88 do 100 po skoku z UIViewController, który implementuje searchBar.
Wypróbuj to w swoim UIViewController, który implementuje searchBar.
override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) navigationController?.view.setNeedsLayout() navigationController?.view.layoutIfNeeded() }
Podgląd po naprawie:
Podgląd przed naprawą:
źródło
EDYTUJ: Odpowiedź @zgjie jest lepszym rozwiązaniem tego problemu: https://stackoverflow.com/a/46356265/1713123
Wygląda na to, że dzieje się tak, ponieważ w iOS 11 domyślna wartość wysokości SearchBar została zmieniona na 56, zamiast 44 w poprzednich wersjach iOS.
Na razie zastosowałem to obejście, ustawiając wysokość searchBar z powrotem na 44:
let barFrame = searchController.searchBar.frame searchController.searchBar.frame = CGRect(x: 0, y: 0, width: barFrame.width, height: 44)
Innym rozwiązaniem mogłoby być użycie nowej właściwości searchController w navigationItem w iOS 11 :
Ale w ten sposób da searchBar pojawia się pod tytułem nawigacji.
źródło
CGRect().insetBy(dx:dy:)
awCGRect().offsetBy(dx:dy:)
tym przypadku -searchController.searchBar.frame = searchController.searchBar.frame.insetBy(dx: 0, dy: -12)
Całe rozwiązanie nie działało dla mnie, więc zanim pchnąłem kontroler widoku, zrobiłem:
override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) self.navigationItem.titleView = UIView() }
Aby wyświetlić pasek wyszukiwania podczas cofania:
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.navigationItem.titleView = UISearchBar() }
źródło
Nie mogłem skorzystać z rozwiązania polegającego na utrzymaniu navBara na 44. Więc zajęło mi to dzień, ale w końcu znalazłem rozwiązanie, które nie zmienia wysokości paska i umieszcza przycisk na środku paska. Problem polega na tym, że przyciski są umieszczane w widoku stosu, który jest skonfigurowany jako poziomy widok stosu i dlatego nie dostosowuje się do zmiany wysokości.
Odbywa się to na 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 (lub w dowolnym momencie po dodaniu widoku do stosu nawigacji)
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]; } }
A subviewOfClass to kategoria w 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; }
źródło
Wszystko, co musisz zrobić, to utworzyć podklasę UISearchBar i zastąpić wartość „intrinsicContentSize”:
@implementation CJSearchBar -(CGSize)intrinsicContentSize{ CGSize s = [super intrinsicContentSize]; s.height = 44; return s; } @end
źródło
Nie można komentować, ale chciałem podzielić się kilkoma dodatkowymi problemami, na które napotkałem, spędzając wiele godzin, próbując dotrzeć do sedna tego problemu, nawet po zastosowaniu jednego z innych rozwiązań.
Wydaje się, że najlepszym rozwiązaniem dla mnie była odpowiedź Andrew :
override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) navigationController?.view.setNeedsLayout() // force update layout navigationController?.view.layoutIfNeeded() // to fix height of the navigation bar }
Jednak przynajmniej w iOS 12.1 , jeśli
UINavigationBar
:isTranslucent
ustawiony nafalse
, widok z kontrolera pojawi się pasek wyszukiwania, aby nie dać się dostosować układ kopię widoku, gdy interaktywnie odwoływanie (normalny odwoływanie poprzez pojawi się przycisk z powrotem do pracy).setBackgroundImage(UIImage(), for: .default)
, animacja przejścia nie działa poprawnie i po zakończeniu wróci do swojej pozycji.Te szczególne właściwości zostały ustawione tak, aby pasek nawigacji pojawiał się w określony sposób, więc muszę trochę dostosować, aby go odzyskać lub znosić dziwne zachowanie. Postaram się pamiętać, aby zaktualizować powyższe, jeśli napotkam coś innego lub znajdę inne rozwiązania lub różnice w innych wersjach systemu operacyjnego.
źródło
W moim przypadku większa wysokość UINavigationBar nie była dla mnie problemem. Musiałem tylko ponownie wyrównać lewy i prawy przycisk paska. Oto rozwiązanie, które wymyśliłem:
- (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; } } } }
Zasadniczo wyszukujemy widoki stosu, które zawierają elementy przycisków paska, a następnie zmieniamy ich górne i dolne wartości ograniczeń. Tak, to brudny hack i nie polecam go używać, jeśli możesz rozwiązać problem w inny sposób.
źródło
// // 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 } } } }
źródło
Uważam, że rozwiązanie Mai Mai jest jedynym, które jest naprawdę użyteczne.
Jednak nadal nie jest idealny:
podczas obracania urządzenia pasek wyszukiwania nie jest odpowiednio zmieniany i pozostaje w mniejszym wymiarze.
Znalazłem na to rozwiązanie. Oto mój kod w Objective C z adnotacjami dotyczącymi odpowiednich części:
// 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
Pozostał jeszcze jeden przypadek, którego jeszcze nie rozgryzłem. Aby odtworzyć, wykonaj następujące czynności:
- zacznij w pionie
- aktywuj pole wyszukiwania
- obróć do pozycji poziomej
- błąd: pasek nie zmienia rozmiaru
źródło
Naprawiłem to, dodając ograniczenie do viewDidAppear na kontrolerze widoku mapy, w którym osadzony jest pasek wyszukiwania
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 } }
źródło
Witam ludzi, którzy używają,
UISearchController
a następnie dołączają goUISearchBar
donavigationItem.titleView
. Spędziłem szalone 4-5 godzin dnia, aby rozwiązać ten problem. Po iOS 11+ zalecane podejście, które jest oddaniesearchController
donavigation.searchController
nie znajduje się tuż za moim przypadku. Ekran, na którym jest to searchController / searchBar, ma niestandardowy backButton.Przetestowałem to w iOS 10, iOS 11 i 12. Na różnych urządzeniach. Po prostu musiałem. Nie mogę wrócić do domu bez rozwiązania tego demona. To najdoskonalszy, jaki mogłem zrobić dzisiaj, biorąc pod uwagę mój napięty termin.
Więc chcę tylko podzielić się tą ciężką pracą, którą wykonałem, to od Ciebie zależy, czy umieścisz wszystko tam, gdzie chcesz (np. Zmienne w Twoim ViewModel). Oto jest:
Na moim pierwszym ekranie (powiedzmy ekranie głównym, który nie ma tego kontrolera wyszukiwania), mam to w moim
viewDidLoad()
.self.extendedLayoutIncludesOpaqueBars = true
Na moim drugim ekranie, tym, który ma searchController, mam to w moim
viewDidAppear
.override func viewDidAppear (_ animated: Bool) {super.viewDidAppear (animated)
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. }
a oto moja deklaracja searchControllera:
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 }()
Mam więc nadzieję, że to kiedyś komuś pomoże.
źródło
Próbowałem różnych rzeczy, aby przywrócić rozmiar do pierwotnego 44, ale pasek wyszukiwania zawsze wygląda i zachowuje się dziwnie - jak zbyt daleko rozciągnięty, przesunięty w osi Y i podobnie.
Znalazłem tutaj fajne rozwiązanie (przez inny post stackoverflow): https://github.com/DreamTravelingLight/searchBarDemo
Po prostu wyprowadź swój viewcontroller z SearchViewController i dołącz do projektu klasy SearchViewController i WMSearchbar. Zadziałało dla mnie po wyjęciu z pudełka bez brzydkiego, gdyby (iOS11) innego ... brzydoty.
źródło
W moim przypadku muszę zmniejszyć wysokość textField o 36pt -> 28pt.
Próbowałem więc zmienić wysokość ramki, wysokość warstwy. Ale sposoby nie działały.
Wreszcie znalazłem rozwiązanie, którym jest maska. Myślę, że to nie jest dobry sposób, ale działa.
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 }
Możesz zmienić wstawki, jeśli chcesz zmienić ramkę textField.
źródło