I know how to change the font colour of a UIActionSheet using a tint
- (void)willPresentActionSheet:(UIActionSheet *)actionSheet
{
[[UIView appearanceWhenContainedIn:[UIAlertController class], nil] setTintColor:[[ColorUtil alloc] colorWithHexString:#"25A559"]];
}
How do I apply it to an item. EG. The first item red, the second item green.
Check my answer
#pragma mark - changeImage
- (IBAction)myActionSheet:(id)sender
{
UIActionSheet *actionSheet=[[UIActionSheet alloc] initWithTitle : #"Choose Image" delegate : self cancelButtonTitle :#"Cancel" destructiveButtonTitle:nil otherButtonTitles:#"Gallery",#"Camera",nil];
actionSheet.tag = 1000;
[actionSheet showInView :self.view];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
{
UIAlertController *alertController = [actionSheet valueForKey:#"_alertController"];
if ([alertController isKindOfClass:[UIAlertController class]])
{
alertController.view.tintColor = [UIColor redColor];// set your color here
}
}
}
and for iOS version less than 8.0, use the same old code of UIActionSheet delegate method:
#pragma mark - actionsheet delgates
- (void)willPresentActionSheet:(UIActionSheet *)actionSheet {
for (UIView *subview in actionSheet.subviews) {
if ([subview isKindOfClass:[UIButton class]]) {
UIButton *button = (UIButton *)subview;
[button setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; // set your color
}
}
}
i wanted to change buttons color in action sheet so i wrote this code
- (void)willPresentActionSheet:(UIActionSheet *)actionSheet {
for (UIView *_currentView in actionSheet.subviews) {
if ([_currentView isKindOfClass:[UIButton class]]) {
UIButton *button = (UIButton *)_currentView;
button.titleLabel.textColor = [UIColor darkGrayColor];
NSLog(#"%#",#"ahmed");
}
}
}
but nothing happen although i made delegate to self and called UIActionSheetDelegate .
although i have 6 buttons ,i see as this function wasn't be called at all
what is the problem?????
I know it's not officially possible. I don't want to release it to the store, it's just a prototype.
I tried finding the button:
MFMessageComposeViewController *messageController = [[MFMessageComposeViewController alloc] init];
messageController.messageComposeDelegate = self;
NSArray * allViewControllers = [messageController viewControllers];
for (UIViewController *viewController in allViewControllers)
{
NSArray *allSubviews = [viewController.view subviews];
NSLog(#"class name: %#", viewController.class);
for(UIView *view in allSubviews)
{
if([view isMemberOfClass:[UIButton class]])
{
UIButton *button = (UIButton *)view;
NSLog(#"title: %#", button.titleLabel.text);
}
}
}
But nothing worked, so is it possible to change that send button? A sort of a hack? or importing a private header?
Thanks you.
Update:
Tried the following:
NSArray *allSubviews = [[messageController toolbar] subviews];
for(UIView *view in allSubviews)
{
if ([view isKindOfClass:[UIToolbar class]])
{
UIToolbar *navigationBar = (UIToolbar *)view;
for(UIView *subview in navigationBar.subviews)
{
NSLog(#"%#", [subview subviews]);
if([subview isMemberOfClass:[UIBarButtonItem class]])
{
UIBarButtonItem *button = (UIBarButtonItem *)view;
NSLog(#"title: %#", button.title);
}
}
}
Behind the hood, yes you can trick the label of Send Button.
Officially it is not allowed, but for private use you can use following framework which is available on github.
https://github.com/nst/iOS-Runtime-Headers/tree/master/Frameworks/MessageUI.framework.
Instead of default framework include this in your project.
And change whatever you want.
If any query let me know i will show the code.
I've never done this but here are some ideas to point you in the right direction.
MFMailComposeViewController is a UINavigationController. The "Send" and "Cancel" buttons are actually in its navigation bar. The navigation bar is a subview of the UINavigationController's view, rather than belonging to any of its children. You are logging the subviews of each child view, so you would never actually see the navigation bar since it is managed by the parent.
Try logging the subviews of the messageController.view, that should give you the navigation bar. The other thing is that you are checking for UIButton whereas you may want to be checking for a UIBarButtonItem.
I've been searching the internet and trying different thing but cannot seem to change the button title colour on my action sheet.
Here is the action sheet code.
- (void)showActionSheet
{
UIActionSheet *actionSheet = [[UIActionSheet alloc]
initWithTitle:#"Select Country"
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:nil];
for (NSString *listCountry in resultSet) [actionSheet addButtonWithTitle:listCountry];
[actionSheet showInView:[self.view window]];
}
I've tried this delegate but it does not work.
- (void)willPresentActionSheet:(UIActionSheet *)actionSheet
{
for (UIView *subview in actionSheet.subviews) {
if ([subview isKindOfClass:[UIButton class]]) {
UIButton *button = (UIButton *)subview;
button.titleLabel.textColor = [UIColor greenColor];
}
}
}
Make sure you set, UIActionSheet delegate and set breakpoint on this method,
willPresentActionSheet,
check whether this method getting called .
If you have set delegate means, try this one
- (void) changeTextColorForUIActionSheet:(UIActionSheet*)actionSheet {
UIColor *tintColor = [UIColor redColor];
NSArray *actionSheetButtons = actionSheet.subviews;
for (int i = 0; [actionSheetButtons count] > i; i++) {
UIView *view = (UIView*)[actionSheetButtons objectAtIndex:i];
if([view isKindOfClass:[UIButton class]]){
UIButton *btn = (UIButton*)view;
[btn setTitleColor:tintColor forState:UIControlStateNormal];
}
}
}
Update:
https://github.com/seivan/SHActionSheetBlocks
https://github.com/ianb821/IBActionSheet
You must implement UIActionSheetDelegate and then you can use willPresentActionSheet delegate method
- (void)willPresentActionSheet:(UIActionSheet *)actionSheet
{
for (UIView *subview in actionSheet.subviews) {
if ([subview isKindOfClass:[UIButton class]]) {
UIButton *button = (UIButton *)subview;
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
}
}
}
I am developing an Application where I wanted to change the text of Search String in the SearchBar. I wanted to change the text of Cancel Button Also which appears next to the SearchBar. Before entering any string in the search bar we wil get the Search String as the default string. I wanted to change the text of that string and when we click on that searchbar we get a cancel button next to searchbar and I wanted to change the text of that cancel button.
Use the appearance proxy:
id barButtonAppearanceInSearchBar = [UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil];
[barButtonAppearanceInSearchBar setBackgroundImage:grayBackgroundImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[barButtonAppearanceInSearchBar setTitleTextAttributes:#{
NSFontAttributeName : [UIFont fontWithName:#"HelveticaNeue-CondensedBold" size:20],
NSForegroundColorAttributeName : [UIColor blackColor]
} forState:UIControlStateNormal];
[barButtonAppearanceInSearchBar setTitle:#"X"];
You also need to have the "searchBar setShowsCancelButton" before the procedure.
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
[theSearchBar setShowsCancelButton:YES animated:NO];
for (UIView *subView in theSearchBar.subviews){
if([subView isKindOfClass:[UIButton class]]){
[(UIButton*)subView setTitle:#"Done" forState:UIControlStateNormal];
}
}
}
Note also: use UIButton to avoid problems with Apple!
Solution for iOS 7. All credits for this go to Mr. Jesper Nielsen - he wrote the code.
-(void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller {
UIButton *cancelButton;
UIView *topView = theSearchBar.subviews[0];
for (UIView *subView in topView.subviews) {
if ([subView isKindOfClass:NSClassFromString(#"UINavigationButton")]) {
cancelButton = (UIButton*)subView;
}
}
if (cancelButton) {
[cancelButton setTitle:#"YourTitle" forState:UIControlStateNormal];
}
}
If by "Search String", you mean the placeholder, then this should do it:
[searchBar setPlaceholder:#"Whatever you want"];
As for changing the text of the cancel button, that may be a bit more difficult. Apple does not use a standard UIBarButtonItem for this, or even a non-standard UIButton. Instead they use a UINavigationButton for the cancel button in the search bar. Since this is not a documented public class, attempting to modify it could very well get your app rejected from the App Store. If you do want to risk rejection, then you could search through the subviews of searchBar:
for(UIView *view in [searchBar subviews]) {
if([view isKindOfClass:[NSClassFromString(#"UINavigationButton") class]]) {
[(UIBarItem *)view setTitle:#"Whatever you want"];
}
}
Note that the cancel button is loaded lazily, so you will have to do this modification when the search bar is activated by the user.
In iOS 7 if you are using UISearchBar just write this code in searchBarTextDidBeginEditing: method
searchBar.showsCancelButton = YES;UIView* view=searchBar.subviews[0];
for (UIView *subView in view.subviews) {
if ([subView isKindOfClass:[UIButton class]]) {
UIButton *cancelButton = (UIButton*)subView;
[cancelButton setTitle:#"إلغاء" forState:UIControlStateNormal];
}
}
I would like to fix the UIAppearance technique, as yar1vn code won't work with Xcode 5. With the following you will have code that works perfectly for both iOS 6 and iOS 7.
First, you need to understand that the cancel button is a private UINavigationButton:UIButton. Hence, it is not an UIBarButtonItem. After some inspection, it appears that UINavigationButton will respond to those UIAppearance selectors:
// inherited from UINavigationButton
#selector(setTintColor:)
#selector(setBackgroundImage:forState:style:barMetrics:)
#selector(setBackgroundImage:forState:barMetrics:)
#selector(setTitleTextAttributes:forState:)
#selector(setBackgroundVerticalPositionAdjustment:forBarMetrics:)
#selector(setTitlePositionAdjustment:forBarMetrics:)
#selector(setBackButtonBackgroundImage:forState:barMetrics:)
#selector(setBackButtonTitlePositionAdjustment:forBarMetrics:)
#selector(setBackButtonBackgroundVerticalPositionAdjustment:forBarMetrics:)
// inherited from UIButton
#selector(setTitle:forState:)
Coincidentally, those selectors match those of a UIBarButtonItem. Meaning the trick is to use two separate UIAppearance to handle the private class UINavigationButton.
/* dual appearance technique by Cœur to customize a UINavigationButton */
Class barClass = [UISearchBar self];
UIBarButtonItem<UIAppearance> *barButtonItemAppearanceInBar = [UIBarButtonItem appearanceWhenContainedIn:barClass, nil];
[barButtonItemAppearanceInBar setTintColor:...];
[barButtonItemAppearanceInBar setBackgroundImage:... forState:... style:... barMetrics:...];
[barButtonItemAppearanceInBar setBackgroundImage:... forState:... barMetrics:...];
[barButtonItemAppearanceInBar setTitleTextAttributes:... forState:...];
[barButtonItemAppearanceInBar setBackgroundVerticalPositionAdjustment:... forBarMetrics:...];
[barButtonItemAppearanceInBar setTitlePositionAdjustment:... forBarMetrics:...];
[barButtonItemAppearanceInBar setBackButtonBackgroundImage:... forState:... barMetrics:...];
[barButtonItemAppearanceInBar setBackButtonTitlePositionAdjustment:... forBarMetrics:...];
[barButtonItemAppearanceInBar setBackButtonBackgroundVerticalPositionAdjustment:... forBarMetrics:...];
UIButton<UIAppearance> *buttonAppearanceInBar = [UIButton appearanceWhenContainedIn:barClass, nil];
[buttonAppearanceInBar setTitle:... forState:...];
Now, this technique works for the Cancel button, but it also works for the Back button if you change the barClass to [UINavigationBar self].
This solution work for me - iOs7 and iOs8:
#interface ... : ...
#property (strong, nonatomic) IBOutlet UISearchBar *search;
#end
and
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
[searchBar setShowsCancelButton:YES animated:YES];
NSArray *searchBarSubViews = [[self.search.subviews objectAtIndex:0] subviews];
UIButton *cancelButton;
for (UIView *subView in searchBarSubViews) {
if ([subView isKindOfClass:NSClassFromString(#"UINavigationButton")]) {
cancelButton = (UIButton*)subView;
break;
}
}
if (cancelButton) {
[cancelButton setTitle:#"New cancel" forState:UIControlStateNormal];
}
//insert this two lines below if you have a button appearance like this "Ne...cel"
[searchBar setShowsCancelButton:NO animated:YES];
[searchBar setShowsCancelButton:YES animated:YES];
}
On iOS 7, if you've set displaysSearchBarInNavigationBar = YES on UISearchDisplayController, replacing the cancel button title via subview recursion or the appearance proxy will not work.
Instead, use your own bar button in viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
self.searchDisplayController.displaysSearchBarInNavigationBar = YES;
UIBarButtonItem *barItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(#"A Custom Title", nil)
style:UIBarButtonItemStyleBordered
target:self
action:#selector(cancelButtonTapped:)];
// NB: Order is important here.
// Only do this *after* setting displaysSearchBarInNavigationBar to YES
// as that's when UISearchDisplayController creates it's navigationItem
self.searchDisplayController.navigationItem.rightBarButtonItem = barItem;
}
Jeremytripp 's working Code in Swift
I couldn't find the same code in Swift so I "translated" it myself:
func searchDisplayControllerWillBeginSearch(controller: UISearchDisplayController) {
self.searchDisplayController?.searchBar.showsCancelButton = true
var cancelButton: UIButton
var topView: UIView = self.searchDisplayController?.searchBar.subviews[0] as UIView
for subView in topView.subviews {
if subView.isKindOfClass(NSClassFromString("UINavigationButton")) {
cancelButton = subView as UIButton
cancelButton.setTitle("My Custom Title", forState: UIControlState.Normal)
}
}
}
If you just want to localized the default "Cancel" title for cancel button, I prefer to change the value of CFBundleDevelopmentRegion key from en to your localized region in Info.plist file in project.
Here is my change,
<key>CFBundleDevelopmentRegion</key>
<string>zh_CN</string>
after that, the default "Cancel" title will show as Chinese "取消". This change will also affect all the default region values, for example, the pasteboard operations' action titles on UITextField/UITextView will be localized, "Select" -> "选择", "Paste" -> "粘贴"...
By the way, the Info.plist file could be localized perfectly.
Enjoy!
Instead of referencing the non-public UINavigationButton class, I did the following. I'm hoping that it will make it through App Store review!
for (id subview in searchBar.subviews) {
if ([subview respondsToSelector:#selector(setTitle:)]) {
[subview setTitle:#"Map"];
}
}
If you're still having trouble with changing the Cancel button in iOS7, this is currently working for me:
-(void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller{
self.searchDisplayController.searchBar.showsCancelButton = YES;
UIButton *cancelButton;
UIView *topView = self.searchDisplayController.searchBar.subviews[0];
for (UIView *subView in topView.subviews) {
if ([subView isKindOfClass:NSClassFromString(#"UINavigationButton")]) {
cancelButton = (UIButton*)subView;
}
}
if (cancelButton) {
//Set the new title of the cancel button
[cancelButton setTitle:#"Hi" forState:UIControlStateNormal];
}
}
if the SearchBar is in the navigationBar, the code will be different than the usual answer; You need to search for NavigationBar's subviews instead.
-(void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller{
UINavigationBar * navigationBar = self.navigationController.navigationBar;
for (UIView *subView in navigationBar.subviews){
if([subView isKindOfClass:NSClassFromString(#"UINavigationButton")]){
[(UIButton*)subView setTitle:#"İptal" forState:UIControlStateNormal];
}
}}
and This work in iOS7+ , if you still can't set the title you should learn view debugging - This is how I solved this problem of mine.
This brief tutorial outlines the key points of View-Debugging very well:
http://www.raywenderlich.com/98356/view-debugging-in-xcode-6
if #available(iOS 13.0, *) {
controller.searchBar.setValue("Done", forKey:"cancelButtonText")
} else {
controller.searchBar.setValue("Done", forKey:"_cancelButtonText")
}
🤦♂️
Actually controller.searchBar.setValue("Done", forKey:"cancelButtonText") works for all iOS versions
Working short code in Swift 2.1 (iOS7-9 tested)
#IBOutlet weak var searchBar: UISearchBar!
func enableSearchBarCancelButton(enable: Bool, title: String? = nil) {
searchBar?.showsCancelButton = enable
if enable {
if let _cancelButton = searchBar?.valueForKey("_cancelButton"),
let cancelButton = _cancelButton as? UIButton {
cancelButton.enabled = enable //comment out if you want this button disabled when keyboard is not visible
if title != nil {
cancelButton.setTitle(title, forState: UIControlState.Normal)
}
}
}
}