I am trying to display a MySearchBar
(UISearchBar
with added UILabel
to display the title) as the navigationItem
's view:
// Make search bar autoresize and occupy maximum width
self.navigationItem.titleView = [[UIView alloc] initWithFrame:self.navigationController.navigationBar.bounds];
self.navigationItem.titleView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[self.navigationItem.titleView addSubview:self.mySearchBar.view];
self.mySearchBar.view.frame = self.navigationItem.titleView.frame;
The search bar sits in place until it is tapped. After tapping, it moves down and it no longer occupies full width (around 16px are missing).
Before editing (how it should look):
Editing began (BUG - after tapping the search bar grows in height to the default 56px and loses width):
Editing ended (remains misplaced)
Here is the initialization code of MySearchBar
. It consists of two main views, titleView
wraps the UILabel
with title and searchBarWrapperView
wraps the UISearchBar
:
-(id) init {
// Initialize the two wrapping views
UIView *titleView = [UIView new];
self.searchBarWrapperView = [UIView new];
// Resize UILabel to fit its content
self.titleLabel = [UILabel new];
self.titleLabel.numberOfLines = 0;
[self.titleLabel sizeToFit];
self.searchBar = self.searchController.searchBar;
self.searchBarTextField = [self.searchBar valueForKey:@"searchField"];// This could be the text field that gets displaced???
// Add two main subviews (wrappers)
[self.view addSubview:titleView];
[self.view addSubview:self.searchBarWrapperView];
// Add title label and search bar to the subviews
[titleView addSubview:self.titleLabel];
[self.searchBarWrapperView addSubview:self.searchBar];
// Disable auto constraints
[titleView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.searchBarWrapperView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.titleLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
// Set constraints for title view
[titleView.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES;
[titleView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor].active = YES;
[titleView.rightAnchor constraintEqualToAnchor:self.view.rightAnchor].active = YES;
[titleView.bottomAnchor constraintEqualToAnchor:self.titleLabel.bottomAnchor].active = YES;
// Set constraints for title label
[self.titleLabel.topAnchor constraintEqualToAnchor:titleView.topAnchor].active = YES;
[self.titleLabel.leftAnchor constraintEqualToAnchor:titleView.leftAnchor].active = YES;
[self.titleLabel.rightAnchor constraintEqualToAnchor:titleView.rightAnchor].active = YES;
// Set constraints for search bar wrapper
[self.searchBarWrapperView.heightAnchor constraintGreaterThanOrEqualToConstant:30].active = YES;// The search bar together with the title label should occupy the whole 44px height of the navigation bar
[self.searchBarWrapperView.topAnchor constraintEqualToAnchor:titleView.bottomAnchor].active = YES;
[self.searchBarWrapperView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = YES;
[self.searchBarWrapperView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor].active = YES;
[self.searchBarWrapperView.rightAnchor constraintEqualToAnchor:self.view.rightAnchor constant:16].active = YES;// Add space that is lost by setSearchFieldBackgroundPositionAdjustment
// Configure autoresizing mask for UISearchBar
[self.searchBar setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
[self.searchBar setFrame:self.searchBarWrapperView.frame];
//Remove left and right blank spaces around UISearchBar
[self.searchBar setSearchFieldBackgroundPositionAdjustment:UIOffsetMake(-8,0)];
// Colors for debugging
self.view.backgroundColor = [UIColor blueColor];
titleView.backgroundColor = [UIColor redColor];
searchBarWrapperView.backgroundColor = [UIColor greenColor];
self.searchBar.backgroundColor = [UIColor yellowColor];
self.searchBarTextField.backgroundColor = [UIColor brownColor];
return self;
}
The solution I used was to subclass the UISearchBar
and the UISearchController
. That way I can set the frame and control the search bar events.
SearchBar.swift
:
import Foundation
class SearchBar : UISearchBar {
var preferredFont:UIFont?
var preferredTextColor:UIColor?
init(frame: CGRect, font: UIFont, textColor: UIColor) {
super.init(frame: frame)
self.frame = frame
self.preferredFont = font
self.preferredTextColor = textColor
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
}
SearchController.swift
:
import Foundation
protocol SearchControllerDelegate {
func didStartSearching()
func didTapOnSearchButton()
func didTapOnCancelButton()
func didChangeSearchText(searchText: String)
}
class SearchController : UISearchController, UISearchBarDelegate, SearchControllerDelegate {
var customSearchBar: SearchBar!
var customDelegate: SearchControllerDelegate!
init(searchResultsController: UIViewController!, searchBarFrame: CGRect, searchBarFont: UIFont, searchBarTextColor: UIColor, searchBarTintColor: UIColor) {
super.init(searchResultsController: searchResultsController)
configureSearchBar(frame: searchBarFrame, font: searchBarFont, textColor: searchBarTextColor, bgColor: searchBarTintColor)
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
func configureSearchBar(frame: CGRect, font: UIFont, textColor: UIColor, bgColor: UIColor) {
self.customSearchBar = SearchBar(frame: frame, font: font , textColor: textColor)
self.customSearchBar.placeholder = "Search"
self.customSearchBar.barTintColor = bgColor
self.customSearchBar.tintColor = textColor
self.customSearchBar.showsBookmarkButton = false
self.customSearchBar.showsCancelButton = false
self.customSearchBar.delegate = self
self.customDelegate = self;
let searchBarTextField:UITextField = self.customSearchBar.value(forKey: "searchField") as! UITextField
searchBarTextField.font = font
searchBarTextField.layer.borderWidth = 1
searchBarTextField.layer.cornerRadius = 3
searchBarTextField.layer.borderColor = UIColor.lightGray.cgColor
}
// UISearchBarDelegate
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
customDelegate.didStartSearching()
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
customSearchBar.resignFirstResponder()
customDelegate.didTapOnSearchButton()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
customSearchBar.resignFirstResponder()
customDelegate.didTapOnCancelButton()
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
customDelegate.didChangeSearchText(searchText: searchText)
}
// SearchControllerDelegate
func didStartSearching() {
}
func didTapOnSearchButton() {
var searchText:String = ""
if (self.customSearchBar.text != nil) {
searchText = self.customSearchBar.text!
}
self.search(searchQuery: searchText)
}
func didTapOnCancelButton() {
}
func didChangeSearchText(searchText: String) {
self.search(searchQuery: searchText)
}
// Search
func search(searchQuery: String) {
// Start searching
}
}