I want to add a "filter" or "sort" button next to the searchBar
inside a UISearchController
. I have already tried to add the UIButton
and the searchbar
into a UIView
and set this as my UITableView tableView.tableHeaderView
This does not work because the tableView.tableHeaderView
get's set back to the UISearchBar
only when dismissing the controller. Here is an example of how I want it to look:
I could create a search filter with a few lines of code. I hope it helps.
*I chose the bookmark button to apply filter icon; because I have already used cancel button for custom action.
First, you have to delegate searchBar to handle bookmark button click action. Then you should set properties as below. If you want to give custom position (default position is right side), you need to setPositionAdjustment.
searchController.searchBar.delegate = self
searchController.searchBar.showsBookmarkButton = true
searchController.searchBar.setImage(UIImage(named: "Sort"), for: .bookmark, state: .normal)
// MARK: You may change position of bookmark button.
//searchController.searchBar.setPositionAdjustment(UIOffset(horizontal: 0, vertical: 0), for: .bookmark)
Then, override searchBarBookmarkButtonClicked function inside your VC. You can handle click event inside this method.
func searchBarBookmarkButtonClicked(_ searchBar: UISearchBar) {
//showAlert, presentVC or whatever you want here
}
Also if you want to hide bookmark button while search controller is active, you need to apply control inside updateSearchResults method.
if searchController.isActive {
searchController.searchBar.showsBookmarkButton = false
} else {
searchController.searchBar.showsBookmarkButton = true
}
When searchController is not active:
When searchController is active:
Instead of customising the UISearchBar and complicating things, you can keep it simple by adding your own button beside SearchBar.
Set No the Shows cancel button option for SearchBar using
self.searchBar.showsCancelButton = true
OR
uncheck Shows cancel button option for SearchBar in StoryBoard
Add your own button beside SearchBar as shown below
And then accordingly add IBaction to the button and perform your actions in it.
Also in ideal case, it is not recommendable to put this view in Header of tableview. It will be better if it is above the tableview as also followed by Apple.
searchBarCancelButtonClicked
, the searchBar gets focused again :/ –
Necklace I tried lot of way's to customize searchbar but nothing helped me at last found a way to full-fill your requirement
let mysearchController = UISearchController(searchResultsController: nil)
mysearchController.searchBar.showsCancelButton = true
mysearchController.searchBar.barStyle = UIBarStyle.Black
for var subView1 in mysearchController.searchBar.subviews{
for var subView2 in subView1.subviews{
if subView2 .isKindOfClass(UIButton){
let customizedCancelButton:UIButton = subView2 as! UIButton
customizedCancelButton.enabled = true
customizedCancelButton.setTitle("", forState: UIControlState.Normal)
let image1 = UIImage(named: "Sort")
customizedCancelButton.setBackgroundImage(image1, forState: UIControlState.Normal)
customizedCancelButton.addTarget(self, action: "sort", forControlEvents: UIControlEvents.TouchUpInside)
}
}
}
above code is much simple to understand.
func sort(){
print("sort button fired!!!")
}
Updated for iOS 13, supports early versions of iOS, based on Gokul G answer.
Add extension file to project — UISearchControllerCancelButton.swift (View on gist.github.com):
extension UISearchController {
func cancelButton() -> UIButton? {
if #available(iOS 13.0, *) {
return findCancelButton13()
}
return findCancelButtonOld()
}
func findCancelButtonOld() -> UIButton? {
for subView in searchBar.subviews {
for v in subView.subviews {
if let button = v as? UIButton {
return button
}
}
}
return nil
}
@available(iOS 13.0, *)
func findCancelButton13() -> UIButton? {
for subView in searchBar.subviews {
for v in subView.subviews {
for b in v.subviews {
if let button = b as? UIButton {
return button
}
}
}
}
return nil
}
}
Use:
let controller = UISearchController(searchResultsController: nil)
controller.searchBar.showsCancelButton = true
if let button = controller.cancelButton() {
// Customize button
let iconImage = UIImage(named: "yourIconName")
button.isEnabled = true
button.setTitle("", for: .normal)
button.setBackgroundImage(iconImage, for: .normal)
button.addTarget(self, action: #selector(yourMethodName), for: .touchUpInside)
}
Method declaration for button touch event:
@objc func yourMethodName() {
print("Cancel button tap")
}
I'd lose the search controller, it doesn't offer any features you want really and the additional view / VC that you need to supply adds complexity / code duplication.
Your existing table view already have everything you need, because it has a data source. That's either an array or an FRC. If it's an array then you can replace the contents of the array with your filtered contents, and if it's an FRC you can change the predicate.
Note: in the array case, have 2 array references, 1 that points to an array holding all your unfiltered items and another that is initially a reference to the same array - the second reference is used to populate the table view. Then, when you filter, you create a new array with only the filtered items and point the second reference at that.
Instead of implementing the search controller delegate method you implement the search bar delegate methods and use that to trigger filtering and reset.
Now you've gotten rid of the search controller you also aren't switching table views, so you only have 1 TV and 1 header view. Now there is no reason to change the header view and everything just works.
© 2022 - 2024 — McMap. All rights reserved.