I would like to add a button to the left of the search bar. However, I don't want the the scope titles to shift over as well, leaving me with a gap somewhere. I believe my options to be:
remove scope bar, use UISegmentedControl below, and add a button in
modify the UISearchBarClass with a button
?? container view as header, includes button and search controller ??
Here is my code, refactored to use a UIViewController for easy modification. Note that I am using the SnapKit libary for constraints (table cell logic removed). How can I accomplish this?
class TeamSearchController: UIViewController {
let tableView = UITableView()
var searchBar = UISearchBar()
override func viewDidLoad() {
super.viewDidLoad()
initUI()
}
func initUI() {
self.view.addSubview(tableView)
initSearchController()
initTableView()
}
func initSearchController() {
// searchController.dimsBackgroundDuringPresentation = false
searchBar.placeholder = "Search for a team here..."
searchBar.delegate = nil
searchBar.sizeToFit()
searchBar.showsScopeBar = true
searchBar.scopeButtonTitles = ["Cross Country", "Track"]
// searchController.definesPresentationContext = true
}
func initTableView() {
tableView.dataSource = nil
tableView.delegate = nil
tableView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
tableView.tableHeaderView = searchBar
tableView.estimatedRowHeight = 40
tableView.rowHeight = UITableView.automaticDimension
}
}
Current Status:
Related
i want to disable right cancel button when a tap at search Bar.
Because of using Google Place search i put i should use searchController?.searchBar
I try to disable cancel button at
func presentSearchController(_ searchController: UISearchController) {
searchController.searchBar.showsCancelButton = false
}
but when i tap a searchBar i see how cancel button appear and disappear, thats looks ugly
Please give me advice!
override func viewDidLoad() {
super.viewDidLoad()
resultsViewController = GMSAutocompleteResultsViewController()
resultsViewController?.delegate = self
searchController = UISearchController(searchResultsController: resultsViewController)
searchController?.searchResultsUpdater = resultsViewController
searchController?.searchBar.sizeToFit()
navigationItem.titleView = searchController?.searchBar
searchController?.searchBar.placeholder = searchBarPlaceholderText
searchController?.searchBar.tintColor = #colorLiteral(red: 0.1019607843, green: 0.5490196078, blue: 1, alpha: 1)
searchController?.searchBar.delegate = self
searchController?.delegate = self
searchController?.searchBar.searchBarStyle = .prominent
definesPresentationContext = true
mapView.delegate = self
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
checklocationAuthorizationStatus()
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
locationManager.startUpdatingLocation()
}
func presentSearchController(_ searchController: UISearchController) {
searchController.searchBar.showsCancelButton = false
}
You can create a custom class and subclass UISearchBar and UISearchViewController.
For example:-
class CustomizedSearchBar: UISearchBar {
override func layoutSubviews() {
super.layoutSubviews()
setShowsCancelButton(false, animated: false)
}
}
Now Create Object of the CustomizedSearchBar and use it within other viewController.
Or you can create a customized searchViewController as follows:
class CustomizedSearchController: UISearchController, UISearchBarDelegate {
lazy var _searchBar: CustomSearchBar = {
[unowned self] in
let result = CustomSearchBar(frame: CGRectZero)
result.delegate = self
return result
}()
override var searchBar: UISearchBar {
get {
return _searchBar
}
}
}
Please follow this link for more detail information.
I have implemented searchBar using UISearchController using following code -
var searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search here..."
definesPresentationContext = true
searchController.searchBar.delegate = self
searchController.searchBar.sizeToFit()
if #available(iOS 11.0, *) {
self.navigationItem.searchController = searchController
} else {
// Fallback on earlier versions
navigationItem.titleView = searchController.searchBar
navigationItem.titleView?.layoutSubviews()
}
Now I have two issues-
SearchBar comes below the navigationBar(See the image attached), how do I get the searchBar on top of NavigationBar that used to come when we implement searchBar with UISearch bar.
The cancel button is not coming on the right side of search bar.
I don't think you can do this natively. But you can activate the search bar when you open the menu (dont forget to set searchController.hidesNavigationBarDuringPresentation to true):
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
searchController.isActive = true
}
But it will hide the UINavigationBar so this is not what you really want. So, maybe better, you can create a custom navigation bar and hide the native one. Here is a quick example:
1 - Create a swift a xib file NavigationBarView with an horizontal UIStackView, a back UIButton with a fixed width and a UISearchBar:
class NavigationBarView: UIView {
var backAction: (()->Void)?
#IBOutlet weak var searchBarView: UISearchBar!
override func awakeFromNib() {
super.awakeFromNib()
// Customize your search bar
self.searchBarView.showsCancelButton = true
}
#IBAction func backButtonPressed(_ sender: Any) {
self.backAction?()
}
}
2 - Instead of using a UITableViewController, create a UIViewController with a vertical UIStackView which contains a view with a fixed height of 64 and a UITableView:
class TableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var containerView: UIView!
let navigationBarView: NavigationBarView = NavigationBarView.viewFromNib() // Custom helper to instantiate a view, see below
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.isHidden = true // hide the native UINavigationBar
self.navigationBarView.backAction = {
self.navigationController?.popViewController(animated: true)
}
self.navigationBarView.searchBarView.delegate = self
self.navigationBarView.add(in: self.containerView) // Custom helper to put a view in a container view, see below
// Other stuff
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
Here is my helpers:
extension UIView {
static public func viewFromNib <GenericView: UIView> () -> GenericView {
let className = String(describing: self)
guard let instance = UINib(nibName: className, bundle: nil)
.instantiate(withOwner: nil, options: nil).first as? GenericView else {
// If this happens, it means the xcodeproj is broken
fatalError("Ho no its broken!")
}
return instance
}
func add(in superView: UIView) {
self.translatesAutoresizingMaskIntoConstraints = false
superView.addSubview(self)
self.topAnchor.constraint(equalTo: superView.topAnchor).isActive = true
self.bottomAnchor.constraint(equalTo: superView.bottomAnchor).isActive = true
self.leftAnchor.constraint(equalTo: superView.leftAnchor).isActive = true
self.rightAnchor.constraint(equalTo: superView.rightAnchor).isActive = true
}
}
Yo can try below code and please let me know if you are facing any issue.
if self.searchController != nil {
self.searchController.isActive = false
}
isSearching = true
self.searchController = UISearchController(searchResultsController: nil)
self.searchController.searchResultsUpdater = self
self.searchController.delegate = self
self.searchController.searchBar.delegate = self
self.searchController.hidesNavigationBarDuringPresentation = false
self.searchController.dimsBackgroundDuringPresentation = false
self.navigationItem.titleView = searchController.searchBar
self.definesPresentationContext = false
self.searchController.searchBar.returnKeyType = .done
There is a property for this
searchController.hidesNavigationBarDuringPresentation = true
There is a gap, so it might be a white text Canel button. ou can know it for sure in Debugger Navigator (Cmd+7) -> View UI Hierarcy. White button text might be caused by custom navigation bar style
I had this code in a UITableViewController and it worked perfectly.
func setupSearchBar() {
let searchBar: UISearchBar = searchController.searchBar
tableView.tableHeaderView = searchBar
let point = CGPoint(x: 0, y: searchBar.frame.size.height)
tableView.setContentOffset(point, animated: true
}
Now I'm refactoring my code to fit more of an MVC style architecture. What I did is create a UITableView in the View class:
class View: UIView {
lazy var tableView: UITableView = {
let table = UITableView()
table.translatesAutoresizingMaskIntoConstraints = false
return table
}()
func configureView() {
// tableView
addSubview(tableView)
tableView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
tableView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
tableView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true
tableView.heightAnchor.constraint(equalTo: heightAnchor).isActive = true
}
}
and then use the View class in my ViewController:
class ViewController: UIViewController {
var newView: View! { return self.view as! View }
override func loadView() {
view = View(frame: UIScreen.main.bounds)
newView.configureView()
}
override func viewDidLoad() {
super.viewDidLoad()
setupSearchBar()
}
func setupSearchBar() {
let searchBar: UISearchBar = searchController.searchBar
newView.tableView.tableHeaderView = searchBar
let point = CGPoint(x: 0, y: searchBar.frame.size.height)
newView.tableView.setContentOffset(point, animated: true)
}
The tableView shows up no problem and everything else is fine. The only thing that's not working is the setContentOffset is being called, but it's not offsetting the content. I want the searchbar to be hidden by default when the user first opens this viewController (similar to iMessage), but after I moved the code from a UITableViewController to separate files (UIView + UIViewController) like in this example, the searchbar always shows by default.
I'm not sure why it's not working. Any help would be greatly appreciated.
It's probably a timing problem relative to layout. Instead of calling setUpSearchBar in viewDidLoad, do it later, in viewDidLayoutSubviews, when initial layout has actually taken place. This method can be called many times, so use a flag to prevent it from being called more than once:
var didSetUp = false
override func viewDidLayoutSubviews() {
if !didSetUp {
didSetUp = true
setUpSearchBar()
}
}
Also: Your animated value is wrong:
newView.tableView.setContentOffset(point, animated: true)
You mean false. You don't want this movement to be visible. The table view should just appear with the search bar out of sight.
I'm trying to achieve something similar to the Calendar App, so far i have below code which when i click on the leftBarButton nothing than a small empty white space appears? why is there no searchBar in it?
What i want is when u click a new navigationBar appear with a searchBar
illustration when i click searchButton:
code:
override func viewDidLoad() {
super.viewDidLoad()
//searchBar
theSearchBar = UISearchBar(frame: CGRectZero)
theSearchBar?.delegate = self
theSearchBar?.showsCancelButton = true
theSearchBar?.placeholder = "Søg efter produkter"
searchController = UISearchDisplayController(searchBar: theSearchBar!, contentsController: self)
searchController?.delegate = self
searchController?.searchResultsDelegate = self
searchController?.searchResultsDataSource = self
}
#IBAction func showSearchBar(sender: UIBarButtonItem) {
theSearchBar?.becomeFirstResponder()
searchController?.setActive(true, animated: true)
}
So I've tried everything trying to get a search bar into the navigation bar in Swift. But sadly I haven't gotten it working, just yet...
For those of you who don't know what I'm talking about, I'm trying to do something like this
Note the search bar in the navigation bar. So here's what I'm currently using
self.searchDisplayController?.displaysSearchBarInNavigationBar = true
I popped that in my viewDidLoad, and then when I load up the app I'm presented with, just an empty navigation bar.... :( Any ideas?
Try this
let leftNavBarButton = UIBarButtonItem(customView:Yoursearchbar)
self.navigationItem.leftBarButtonItem = leftNavBarButton
Update
You keep a lazy UISearchBar property
lazy var searchBar:UISearchBar = UISearchBar(frame: CGRectMake(0, 0, 200, 20))
In viewDidLoad
searchBar.placeholder = "Your placeholder"
var leftNavBarButton = UIBarButtonItem(customView:searchBar)
self.navigationItem.leftBarButtonItem = leftNavBarButton
If you want to use storyboard
just drag your searchbar as a outlet,then replace the lazy property with your outlet searchbar
// create the search bar programatically since you won't be
// able to drag one onto the navigation bar
searchBar = UISearchBar()
searchBar.sizeToFit()
// the UIViewController comes with a navigationItem property
// this will automatically be initialized for you if when the
// view controller is added to a navigation controller's stack
// you just need to set the titleView to be the search bar
navigationItem.titleView = searchBar
Swift 5, XCode 11, Storyboard way so you can easily add all the search bar attributes through the storyboard and you have less code in your view controller class.
1.) Add your search bar view as external view in viewcontroller.
2.) Connect searchBarView to you viewcontroller.
3.) Add your searchBarView to your navigationBar title item.
navigationItem.titleView = searchBarView
Result:
In your view controller:
lazy var searchBar = UISearchBar(frame: CGRectZero)
override func viewDidLoad() {
super.viewDidLoad()
searchBar.placeholder = "Search"
navigationItem.titleView = searchBar
}
Doing it this way, by setting the navigationItem.titleView, the search bar is automatically centered across the iPhone and iPad devices. Note: only tested with v8.4 and v9.0
for SWIFT 3
lazy var searchBar = UISearchBar(frame: CGRect.zero)
In 2019, you should use UISearchController.
override func viewDidLoad() {
super.viewDidLoad()
let searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self.viewModel
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search artists"
self.navigationItem.searchController = searchController
self.definesPresentationContext = true
}
And some class should conform to UISearchResultsUpdating. I usually add this as extension to my ViewModel.
extension ArtistSearchViewModel: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
print("Searching with: " + (searchController.searchBar.text ?? ""))
let searchText = (searchController.searchBar.text ?? "")
self.currentSearchText = searchText
search()
}
}
This will spawn something like this:
For iOS 11 and above
navigationItem.searchController = searchController
For iOS 10 and below
navigationItem.titleView = searchController.searchBar;
or you can assign it as leftBarButtonItem as described in this answer
For Swift 5 or letter
also, you can use this code. Fully Programmatically
import UIKit
class SearchTableViewController: UITableViewController {
private lazy var searchController: UISearchController = {
let sc = UISearchController(searchResultsController: nil)
sc.searchResultsUpdater = self
sc.delegate = self
sc.obscuresBackgroundDuringPresentation = false
sc.searchBar.placeholder = "Enter A Compiny Name Or Symbole"
sc.searchBar.autocapitalizationType = .allCharacters
return sc
}()
override func viewDidLoad() {
super.viewDidLoad()
setupNavigationBar()
}
private func setupNavigationBar() {
navigationItem.searchController = searchController
}
}
// MARK: - UISearchResult Updating and UISearchControllerDelegate Extension
extension SearchTableViewController: UISearchResultsUpdating, UISearchControllerDelegate {
func updateSearchResults(for searchController: UISearchController) {
}
}
let searchBar = UISearchBar()
searchBar.sizeToFit()
searchBar.placeholder = ""
self.navigationController?.navigationBar.topItem?.titleView = searchBar
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
searchBar.endEditing(true)
searchBar.text = nil
print("## search btn clicked : \(searchBar.text ?? "")")
}
Setting SearchBar as titleView, changes height of navigationBar to 56. To fix this, you can embed searchBar in view and set that as titleView.
var offset: CGFloat = 20
// If VC is pushed, back button should be visible
if navigationController?.navigationBar.backItem != nil {
offset = 40
}
let customFrame = CGRect(x: 0, y: 0, width: view.frame.size.width - offset, height: 44.0)
let searchBarContainer = UIView(frame: customFrame)
searchBar = UISearchBar(frame: customFrame)
searchBarContainer.addSubview(searchBar)
navigationItem.titleView = searchBarContainer