views:

2585

answers:

4

I have a UISearchBar that has a cancel button (it's displayed using -(void)setShowsCancelButton:animated). I've changed the tintColor of the search bar like this in an attempt to get a grayish searchbar:

UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 40)];
searchBar.tintColor = [UIColor colorWithWhite:0.8 alpha:1.0];

This is what it looks like now - notice how the cancel button is also gray: http://twitpic.com/c0hte

Is there a way to set the color of the cancel button separately so it looks more like this: http://twitpic.com/c0i6q

+3  A: 

What you want to do is pretty tough. There is no built-in hook to get at the cancel button.

However, there are a couple of options if you are willing to jimmy open the hood.

First off, UISearchBar is a UIView, and the Cancel button is also a view, which is added into the search bar as a subview, just as you would expect.

I have experimented a little, and can tell you that when the button is onscreen it has a size of 48,30.

So in viewWillAppear, you can do something like this:

  1. Find the cancel button view in [searchBar subviews] by looking for one with size 48,30. (There only seems to be one -- this could change...) You could be doubly careful and look for one that is in approximately the correct position (differs in landscape and portrait).

  2. Add a subview to the cancel button.

  3. The subview should be a UIControl (so that you can set enabled = NO, in order to make sure touch events get to the actual cancel button)

  4. It needs to have the right color and rounded corners; you will need to fudge the size for reasons I don't yet understand (55,30 seems to work)

  5. This will work if searchBar.showsCancelButton is always YES; if you want it to disappear when not editing the search string, you will need to find a hook to add the overlay each time the cancel button appears.

  6. As you can see, this is some ugly tinkering. Do it with eyes wide open.

Amagrammer
I was hoping it wouldn't come to something like this :) It does work the way you described (except the size of the cancel button is somehow 55x30 for me). Still need to play around with getting the look right but it probably works, other than the ugliness of this approach. Thanks a lot for your help!
Chu Yeow
Hmm! All I'd like to do is change the tintColor of the Scope Button bar. Perhaps something similar would get me that view too ... but it's so fragile. Yecch. Decisions decisions.
Joe D'Andrea
jandrea -- put up your own question and point me to it -- I'll try to explain how to do something similar.
Amagrammer
A: 

You can find the cancel button by looping through the subviews of the search bar and checking for the class type (instead of the size):

UIButton *cancelButton = nil;
for(UIView *subView in yourSearchBar.subviews){
    if([subView isKindOfClass:UIButton.class]){
    cancelButton = (UIButton*)subView;
    }
}

And then change the tint color:

[cancelButton setTintColor:[UIColor colorWithRed:145.0/255.0 green:159.0/255.0 blue:179.0/255.0 alpha:1.0]];
caelavel
This gives a warning that cancelButton may not respond to setTintColor since this is an undocumented API. This also likely gets your app refused by Apple for using an undocumented API.
Ben S
A: 

Well, here is function, which can change Cancel's button label. Modify it, if you want. Usage is:

nStaticReplaceStringInView(mySearchBar, @"Cancel", @"NewCancelButtonLabel");

void nStaticReplaceStringInView(UIView * view, NSString * haystack, NSString * needle)
{
 for(int i=0; i<[view.subviews count]; i++)
 {
  nStaticReplaceStringInView([view.subviews objectAtIndex:i], haystack,needle);
 }
 if([view respondsToSelector:@selector(titleForState:)])
 {
  //NSLog(@"%@ || %@",[view titleForState:UIControlStateNormal], haystack);
  if(NSStrEq([view titleForState:UIControlStateNormal] , haystack))
  {
   [view setTitle: needle forState: UIControlStateNormal];
  }
 }
}
Arenim
+1  A: 

Though this might not be exactly relevant to the original question, the solution is still applicable in the larger sense of trying to customize the Cancel button in the UISearchBar. Thought this will help others who are stuck in such a scenario.

My situation was to change the cancel button's title, but with a twist, wherein I did not want to show the cancel button by default but only wanted it to show up, when the user enters the search mode (by clicking inside the search text field). At this instant, I wanted the cancel button to carry the caption "Done" ("Cancel" was giving a different meaning to my screen, hence the customization).

Nevertheless, here's what I did (a combination of caelavel's and Arenim's solutions):

Subclassed UISearchBar as MyUISearchBar with these two methods:

-(void) setCloseButtonTitle: (NSString *) title forState: (UIControlState)state
{
    [self setTitle: title forState: state forView:self];
}

-(void) setTitle: (NSString *) title forState: (UIControlState)state forView: (UIView *)view
{
    UIButton *cancelButton = nil;
    for(UIView *subView in view.subviews){
        if([subView isKindOfClass:UIButton.class])
        {
            cancelButton = (UIButton*)subView;
        }
        else
        {
            [self setTitle:title forState:state forView:subView];
        }
    }

    if (cancelButton)
        [cancelButton setTitle:title forState:state];

}

And in the viewcontroller which uses this Searchbar, the following piece of code takes care of showing the cancel button and customizing its title:

- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
    MyUISearchBar *sBar = (MyUISearchBar *)searchBar;
    [sBar setShowsCancelButton:YES];
    [sBar setCloseButtonTitle:@"Done" forState:UIControlStateNormal];   
}

Strangely enough, I did not have to do anything to hide the cancel button, as it is hidden by default, when the search mode is exited.

Sara