I'd like to change the text from "Cancel" to "Done" of the Cancel button inside the UISearchBar
in iOS 8. I am using UISearchController
. I've tried different approaches for iOS 6 and iOS 7 and they do not work. Has anybody done this?
-
possible duplicate of [UISearchController change 'Cancel' button title](http://stackoverflow.com/questions/28642807/uisearchcontroller-change-cancel-button-title) – Praveen Gowda I V Mar 22 '15 at 17:12
-
I have written an answer to this topic here: http://stackoverflow.com/questions/19206757/how-to-change-textcolor-of-cancel-button-of-uisearchbar-in-ios7. Just use the SHSearchBar Cocoapod which is not such a pain in the ass like the UISearchBar. – blackjacx Nov 25 '16 at 12:40
-
Don't hesitate to accept polo987's answer as the earliest of the claenest to date. – Anton Tropashko Jun 01 '20 at 12:23
14 Answers
Objective-C:
[searchBar setValue:@"customString" forKey:@"_cancelButtonText"];
Swift:
searchBar.setValue("customString", forKey:"_cancelButtonText")

- 5,318
- 3
- 25
- 51
-
-
-
this does not work. Firstly the @ symbols cause an error, and when i delete them i get the following: "[
setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key _cancelButtonText." – rohaldb Jan 25 '16 at 06:58 -
Updated! Well you have to call this method on a UISearchBar and not on a UISearchViewController – Burhanuddin Sunelwala Jan 25 '16 at 07:04
-
2This I believe is not proof safe. If Apple decides to change the `_cancelButtonText` key for the button with an iOS update, this will result in a runtime crash. – George Marmaridis Aug 24 '16 at 07:01
-
You can access the runtime variables over here: https://github.com/JaviSoto/iOS10-Runtime-Headers. You may be right but is there any other alternative? – Burhanuddin Sunelwala Aug 24 '16 at 08:46
-
8This crashes in iOS 13. `Access to UISearchBar's set_cancelButtonText: ivar is prohibited.` – Casper Zandbergen Jun 25 '19 at 07:44
-
This throws an exception when built on the iOS 13 SDK and the app is run on iOS 13. Is there an updated solution? – user102008 Jul 24 '19 at 17:30
This worked for me in ios8 through ios13, did not try in ios7, but should do the trick, beware of placing this line in the early times of the app cycle (eg : appDelegate)
[[UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil] setTitle:@"Annuler"];
and as of ios9 you could also use
[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UISearchBar class]]] setTitle:@"Annuler"];
Swift version:
UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).title = "Annuler"
Hope that helps ;)

- 5,486
- 5
- 41
- 66

- 780
- 5
- 14
-
1The earliest answer without hacks that survives all the way to ios13. – Anton Tropashko Jun 01 '20 at 12:22
Found it!. in iOS 8, the cancel button is under the key "cancelButton". I used UISearchBar delegate searchBarTextDidBeginEditing to make the change.
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
let uiButton = searchBar.valueForKey("cancelButton") as UIButton
uiButton.setTitle("Done", forState: UIControlState.Normal)
}

- 753
- 1
- 5
- 9
-
5I would consider not setting the button title inside this callback. It seems a bit odd and unnecessary. Consider setting this value when you setup your view. – Steffen D. Sommer Apr 18 '15 at 10:46
-
1
-
@SteffenD.Sommer It made sense for me to set it inside the callback because I'm not showing the Cancel button until the UISearchBar is in focus. – toddg May 11 '16 at 02:00
-
4@Leo: `cancelButton` is also an undocumented private property. The absence of an underscore doesn't change that. – jamesk Jul 23 '16 at 11:36
-
in iOS 11.0 when the clicked for first time the *Cancel* button can not set. After the first time the text is changing as above. – eemrah Nov 13 '18 at 11:14
-
@elia If it still makes any sense, But it can be done if you make the searchBar as firstResponder in viewWillAppear – anamika41 Feb 19 '19 at 12:24
Swift 3.0:
In your AppDelegate add this:
UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).title = "my text"

- 7,246
- 13
- 68
- 106
for view in (UISearchBar.subviews[0]).subviews{
if let button = view as? UIButton{
button.setTitleColor(UIColor.whiteColor(), forState: .Normal)
button.setTitle("Cancel", forState:.Normal)
}
}
self.subviews[0]).subviews contain UISearchBarBackground,UISearchBarTextField,UINavigationButton in which UINavigationButton is subclass of UIButton.Check for View as UIButton and if yes then change property of UIButton.

- 6,082
- 2
- 19
- 30
-
Please use the [edit] link to add this information to your post. Comments are “second-class citizens” and could be deleted at any time. – Jed Fox Nov 03 '16 at 10:26
-
This answer almost worked for me - I put the code in `searchBarTextDidBeginEditing` and it changed the button's title properly. It didn't work the first time when the "Cancel" button is shown though - I suspect it's being created a few moments later than the call for `searchBarTextDidBeginEditing`. I ended up using [this solution](http://stackoverflow.com/a/40257292/1540248) in my `viewDidLoad` method - works like a charm! – MayaLekova Feb 02 '17 at 11:16
I was having trouble getting this to work for a UISearchBar within a UISearchController. The text didn't change the first time the cancel button was shown but it did the second time.
That is until I saw @polo987's answer. Here's what I did that worked:
UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).title = "Done"
For iOS 13 Support:
Swift:
UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).title = "Whatever"
Objective C:
[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:[NSArray arrayWithObject:[UISearchBar class]]] setTitle:@"Whatever"]

- 119
- 1
- 6
In Swift:
searchBar.setValue("customString", forKey: "_cancelButtonText")
Based on Burhanuddin Sunelwala answer.

- 8,647
- 6
- 38
- 44
-
4This is a dangerous solution. If apple change the name of the key "_cancelButtonText" this will cause your app to crash. – Josh Apr 24 '18 at 10:10
In Swift4
Change Title:
(searchBar.value(forKey: "cancelButton") as! UIButton).setTitle("Done", for: .normal)
Change Color:
(searchBar.value(forKey: "cancelButton") as! UIButton).setTitleColor(UIColor.blue, for: .normal)

- 575
- 1
- 7
- 17
I have added titleLabel.font to the mix...
This goes straight into my ViewDidLoad() {
let searchFont = UIFont(name: "BrandonGrotesque-Medium", size: 12)
let textFieldSearchBar = self.searchBar.valueForKey("searchField") as! UITextField
textFieldSearchBar.font = searchFont
let cancelButton = searchBar.valueForKey("cancelButton") as! UIButton
cancelButton.setTitle("Done", forState: .Normal)
cancelButton.titleLabel!.font = searchFont

- 1,550
- 1
- 18
- 31
Swift 4.2, 4.0+
A custom class to customize the most common elements in search bar.
class SearchBar: UISearchBar {
private enum SubviewKey: String {
case searchField, clearButton, cancelButton, placeholderLabel
}
// Button/Icon images
public var clearButtonImage: UIImage?
public var resultsButtonImage: UIImage?
public var searchImage: UIImage?
// Button/Icon colors
public var searchIconColor: UIColor?
public var clearButtonColor: UIColor?
public var cancelButtonColor: UIColor?
public var capabilityButtonColor: UIColor?
// Text
public var textColor: UIColor?
public var placeholderColor: UIColor?
public var cancelTitle: String?
// Cancel button to change the appearance.
public var cancelButton: UIButton? {
guard showsCancelButton else { return nil }
return self.value(forKey: SubviewKey.cancelButton.rawValue) as? UIButton
}
override func layoutSubviews() {
super.layoutSubviews()
if let cancelColor = cancelButtonColor {
self.cancelButton?.setTitleColor(cancelColor, for: .normal)
}
if let cancelTitle = cancelTitle {
self.cancelButton?.setTitle(cancelTitle, for: .normal)
}
guard let textField = self.value(forKey: SubviewKey.searchField.rawValue) as? UITextField else { return }
if let clearButton = textField.value(forKey: SubviewKey.clearButton.rawValue) as? UIButton {
update(button: clearButton, image: clearButtonImage, color: clearButtonColor)
}
if let resultsButton = textField.rightView as? UIButton {
update(button: resultsButton, image: resultsButtonImage, color: capabilityButtonColor)
}
if let searchView = textField.leftView as? UIImageView {
searchView.image = (searchImage ?? searchView.image)?.withRenderingMode(.alwaysTemplate)
if let color = searchIconColor {
searchView.tintColor = color
}
}
if let placeholderLabel = textField.value(forKey: SubviewKey.placeholderLabel.rawValue) as? UILabel,
let color = placeholderColor {
placeholderLabel.textColor = color
}
if let textColor = textColor {
textField.textColor = textColor
}
}
private func update(button: UIButton, image: UIImage?, color: UIColor?) {
let image = (image ?? button.currentImage)?.withRenderingMode(.alwaysTemplate)
button.setImage(image, for: .normal)
button.setImage(image, for: .highlighted)
if let color = color {
button.tintColor = color
}
}
}
Usage:
class ViewController: UIViewController {
@IBOutlet private weak var searchBar: SearchBar!
override func viewDidLoad() {
super.viewDidLoad()
searchBar.clearButtonColor = .purple
searchBar.cancelButtonColor = .magenta
searchBar.searchIconColor = .red
searchBar.placeholderColor = .green
searchBar.textColor = .orange
searchBar.capabilityButtonColor = .green
}
}

- 14,987
- 4
- 33
- 51
I know this may seem to be a bit irrelevant but in my opinion this is safer than using private apis and more efficient than taking a dive into the unknown views. This solution makes sense only if you have your own localisation engine and you do want Apple to follow your mechanism all over the app. Basically my idea is to switch the language the iOS SDK uses to localise the button by altering the "AppleLanguages" key in the NSUserDefaults. You can go here for more information about how this works.
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"en", @"fr", nil] forKey:@"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];
Put this code to use the English localisation and a French fallback no matter what language is set on the phone. This only takes effect within the app.

- 785
- 8
- 8
Here is Swift solution:
for (view) in _searchController.searchBar.subviews {
for (subview) in view.subviews {
if (subview.isKindOfClass(UIButton.self)) {
let cancelButton = subview as! UIButton
cancelButton.setTitle("BlaBla", forState: .Normal)
break
}
}
}

- 425
- 6
- 6
let uiButton = searchBar.value(forKey: "cancelButton") as! UIButton uiButton.setTitle(NSLocalizedString("Cancel", comment: ""), for: .normal)

- 503
- 2
- 12