Bit of an update on this, as of iOS 15, it looks like changes in iOS 15 alter the order of which the searchBarCancelButtonClicked
and searchBarTextDidEndEditing
delegate methods are called.
Prior to iOS 15, a stateful solution such as the following would work for most use cases:
var searchTerms = ""
var searchWasCancelled = false
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
searchWasCancelled = false
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
searchWasCancelled = true
}
func searchBarTextDidEndEditing(searchBar: UISearchBar) {
if searchWasCancelled {
searchBar.text = self.searchTerms
} else {
searchTerms = searchBar.text
}
}
Sadly, this breaks in iOS 15 and after some quick investigation, I can see that the searchBarTextDidEndEditing
delegate method is always called prior to the searchBarCancelButtonClicked
method. It’s unclear if this was an intended change by Apple or if it is indeed a bug.
All things aside, I've found a simple workaround to the original question and found that setting the searchBar.text
from an async dispatch block, will successfully modify the text. Like so:
var previousSearchTerm = ""
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
previousSearchTerm = searchBar.text ?? ""
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
DispatchQueue.main.async {
searchBar.text = self.previousSearchTerm
}
}
Now this works for iOS 15 and prior, in my particular use case. However, depending what you're trying to achieve this may not be enough for backwards compatibility. It's also worth noting that this, like any other workaround modifying the stubborn default behaviour of UIKit, this comes with inherent risk of unknown side effects now or later on.
UISearchBar
withoutcancel
button – Hewittcancel
button. – Hewittcancel
button? It does not seem obvious how to get a reference to this button. – Almaraz