In landscape orientation UISearchController getting UI glitch issue. Using Xcode 10.2. There is not built-in navigationBar used.
Landscape Preview
Portrait Preview
Instance declaration
let searchController = UISearchController(searchResultsController: nil)
#IBOutlet weak var vwSearch: UIView!
#IBOutlet weak var vwSearchHeightConstant: NSLayoutConstraint!
Initialization in viewDidload
func setupSerachResultController()
{
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = true
searchController.searchBar.placeholder = "Search Email"
searchController.searchBar.searchBarStyle = .minimal
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.scopeBarBackgroundImage = UIImage.imageWithColor(color: UIColor.clear)
searchController.searchBar.tintColor = UIColor.white
vwSearch.addSubview(searchController.searchBar)
searchController.searchBar.scopeButtonTitles = ["One","Two","Three","Four"]
searchController.searchBar.delegate = self
}
Initial Preview
UISearchBarDelegate methods
extension ListViewController: UISearchBarDelegate,UISearchResultsUpdating {
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
}
func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
UIView.animate(withDuration: 0.3) {
self.vwSearchHeightConstant.constant = 100
self.view.layoutIfNeeded()
}
return true
}
func updateSearchResults(for searchController: UISearchController) {
let searchBar = searchController.searchBar
let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
UIView.animate(withDuration: 0.3) {
self.vwSearchHeightConstant.constant = 60
self.view.layoutIfNeeded()
}
}
}
As shown in initial preview there is not scope bar visible so height
of vwSearch 60, which managed using vwSearchHeightConstant constraint. For scope bar height changed to 100, which toggle managed in searchBarShouldBeginEditing and searchBarCancelButtonClicked.
Any help much appreciated.
Thank you.
Related
I am trying to autofocus a searchbar when i click on a bar button, but it does not seem to work with becomeFirstResponder, follow the GIF:
I've already tried using becomeFirstResponder and it does not work, this is how i am handling the searchbar:
// MARK: SearchBar Delegate
#IBAction func showSearchBarAction(_ sender: Any) {
let searchController = UISearchController(searchResultsController: nil)
searchController.searchBar.delegate = self
searchController.searchResultsUpdater = self
searchController.searchBar.keyboardAppearance = .dark
searchController.dimsBackgroundDuringPresentation = false
navigationItem.searchController = searchController
navigationItem.searchController?.isActive = true
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
navigationItem.searchController?.isActive = false
self.navigationItem.searchController = nil
}
All of it works perfectly, but the autofocus is something i really want and i can't change my design as well
Solution
This code is the solution:
This is how my class declaration looks like:
class YourViewController: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate, UISearchControllerDelegate {
// MARK: SearchBar Delegate
#IBAction func showSearchBarAction(_ sender: Any) {
let searchController = UISearchController(searchResultsController: nil)
searchController.delegate = self
searchController.searchBar.delegate = self
searchController.searchResultsUpdater = self
searchController.searchBar.keyboardAppearance = .dark
searchController.dimsBackgroundDuringPresentation = false
self.navigationItem.searchController = searchController
self.navigationItem.searchController?.isActive = true
}
func didPresentSearchController(_ searchController: UISearchController) {
DispatchQueue.main.async {
searchController.searchBar.becomeFirstResponder()
}
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
navigationItem.searchController?.isActive = false
self.navigationItem.searchController = nil
}
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 have set up a search bar using the embed nav bar. The func searchBarSearchButtonClicked is not being detected. I'd like to make it so that when the user taps on the search bar, another function will be called. I've taken out some extraneous code not relevant to this question. What could be the issue?
class FirstViewController: UIViewController, UISearchBarDelegate {
var resultSearchController: UISearchController? = nil
override func viewDidLoad() {
super.viewDidLoad()
// set up the search results table
let locationSearchTable = storyboard!.instantiateViewController(withIdentifier: "LocationSearchTable") as! LocationSearchTable
resultSearchController = UISearchController(searchResultsController: locationSearchTable)
resultSearchController?.searchResultsUpdater = locationSearchTable
let searchBar = resultSearchController!.searchBar
searchBar.sizeToFit()
searchBar.placeholder = "Where would you like to go"
navigationItem.titleView = resultSearchController?.searchBar
searchBar.delegate = self
resultSearchController?.hidesNavigationBarDuringPresentation = false
resultSearchController?.dimsBackgroundDuringPresentation = true
definesPresentationContext = true
}
func searchBarSearchButtonClicked(_: UISearchBar) {
// closeMapType()
// self.mapType.transform = .identity
print("it worked")
}
}
Below function will call when the user taps on the search bar :
func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
return true
}
This will call when we click on the "Search" button in the keyboard
func searchBarSearchButtonClicked(_: UISearchBar)
I have a searchbar in my tableview header. For that i am using uisearchcontroler. But it update tablview data when i am texting in searchbar, i need to update tablview when search button in keyboard is clicked, because i get data for search in api and everytime when i am texting in searchbar it requesting and it takes long time. How i can resolve this?
var resultSearchController = UISearchController()
var indicator:UIActivityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.Gray)
var ref=UIRefreshControl()
override func viewDidLoad() {
super.viewDidLoad()
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
self.tableView.tableHeaderView = controller.searchBar
return controller
})()
indicator.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
indicator.center = view.center
view.addSubview(indicator)
indicator.bringSubviewToFront(view)
indicator.startAnimating()
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
getRequests.getType1(tableView,spinner: indicator)
ref.addTarget(self, action: #selector(Catalog1TableViewController.refresh(_:)), forControlEvents: UIControlEvents.ValueChanged)
ref.attributedTitle = NSAttributedString(string: "Загрузка")
tableView.addSubview(ref)
}
func updateSearchResultsForSearchController(searchController: UISearchController)
{
indicator.startAnimating()
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
tableView.reloadData()
getRequests.getProduct(tableView, spinner: indicator,name: searchController.searchBar.text!)
for item in getRequests.product {
if item.productTitle.containsString(searchController.searchBar.text!){
filteredTableData.append(item)
}
}
tableView.reloadData()
}
Try to implement UISearchBar Delegate method :
func searchBarSearchButtonClicked(searchBar: UISearchBar)
You should implement something like this in your view controller.
var resultSearchController = UISearchController()
resultSearchController.searchBar.delegate = self
extension ViewController: UISearchBarDelegate {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
print("search button click")
}
}
I'm trying to hide the Cancel button of the search bar in the UISearchController, but unfortunately setting the following in viewDidLoad() does not work:
override func viewDidLoad() {
super.viewDidLoad()
searchResultsTableController = UITableViewController()
searchResultsTableController.tableView.delegate = self
searchController = UISearchController(searchResultsController: searchResultsTableController)
searchController.searchResultsUpdater = self
searchController.searchBar.sizeToFit()
searchResultsView.tableHeaderView = searchController.searchBar
searchController.delegate = self
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.delegate = self
searchController.searchBar.searchBarStyle = .Minimal
searchController.searchBar.showsCancelButton = false
definesPresentationContext = true
}
I have also tried using the above code in this delegate method:
func didPresentSearchController(searchController: UISearchController) {
searchController.searchBar.showsCancelButton = false
}
This approach works but will show the Cancel button briefly before hiding it, which is not ideal. Any suggestions?
I ended up subclassing both UISearchBar and UISearchController as suggested:
CustomSearchBar.swift
import UIKit
class CustomSearchBar: UISearchBar {
override func layoutSubviews() {
super.layoutSubviews()
setShowsCancelButton(false, animated: false)
}
}
CustomSearchController.swift
import UIKit
class CustomSearchController: 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
}
}
}
Rejoice! As of iOS 13, there is access to automaticallyShowsCancelButton on UISearchController. Set it to false to hide the cancel button.
func didPresentSearchController(_ searchController: UISearchController) {
searchController.searchBar.becomeFirstResponder()
searchController.searchBar.showsCancelButton = true
}
func didDismissSearchController(_ searchController: UISearchController) {
searchController.searchBar.showsCancelButton = false
}
In my case all the above solutions dint work. You need to show in didPresentSearchController and hide it in didDismissSearchController. Earlier I was just hiding in didDismissSearchController which was still showing Cancel button on cancelling.
Hide the Cancel button in search bar delegate methods and set your delegate searchController.searchBar.delegate=self UISearchBarDelegate
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
}
func searchBarTextDidEndEditing(searchBar: UISearchBar) {
}
Try subclassing UISearchBar and implement:
override func layoutSubviews() {
super.layoutSubviews()
self.setShowsCancelButton(false, animated: false)
}
This SO thread may help you more in this direction.
The same answer as given by #Griffith and #Abhinav but using extension:
extension UISearchBar {
override open func layoutSubviews() {
super.layoutSubviews()
setShowsCancelButton(false, animated: false)
}
}
This code is from Swift 4.