2
//Set all cancel buttons in search bars to "Done"
id searchBarButton = [UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil];
if([[[UIDevice currentDevice] systemVersion] floatValue] < 7) {
    [searchBarButton setTitle:@"Done"];
} else {
    //Can't do anything here or i get EXC_BAD_ACCESS
}

This is giving a EXC_BAD_ACCESS when called in viewDidLoad only on iOS 7 Gold Master and newer. iOS 7 beta 6 and older is fine.

Is there a different way to do this in iOS 7?

NSLog("%@", searchBarButton) results in this on iOS7:

2013-10-01 16:14:25.972 MP Staging[12293:a0b] <_UIBarItemAppearance:0x1aaf72d0> <Customizable class: UIBarButtonItem> when contained in ( UISearchBar ) with invocations (null)>

and this on iOS 6

<_UIBarItemAppearance: 0x1c671aa0>

mverderese
  • 5,314
  • 6
  • 27
  • 36

3 Answers3

5

setTitle will fail in iOS7.

Try below code from this blog:

-(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:@"Annuller" forState:UIControlStateNormal];
    }
}
Tarek Hallak
  • 18,422
  • 7
  • 59
  • 68
  • If I put a breakpoint at `cancelButton = (UIButton*)subview` it never reaches it – mverderese Oct 01 '13 at 20:41
  • I changed the `self.searchDisplayController.searchBar` to `self.venueSearchBar` and added a breakpoint. The two subview classes are `UISearchBarBackground` and `UISearchBarTextField` – mverderese Oct 01 '13 at 20:46
  • Ok try the update now, you need to put this code in `searchDisplayControllerWillBeginSearch` delegate. – Tarek Hallak Oct 01 '13 at 20:46
  • If i put this code in `searchDisplayControllerWillBeginSearch` it doesn't work, but if I put it in `searchDisplayControllerDidBeginSearch` then it shows "cancel" for a second then fades to "Done" – mverderese Oct 01 '13 at 20:52
  • I put the code into `searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString` and it actually gives me exactly the effect I'm looking for. It says "cancel" until you start typing, and then it says done when you start – mverderese Oct 01 '13 at 20:54
2

I'm using this without any problems in 7.1, however, it does seem to crash on 7.0.x (device and sim) - hopefully this means they've brought the property back in 7.1, but it also means that we have to use one of the above subview traversing hacks for the interim versions.

    id barButtonAppearanceInSearchBar = [UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil];
    [barButtonAppearanceInSearchBar setTitleTextAttributes:@{NSFontAttributeName:[UIFont fontWithName:@"HelveticaNeue" size:15],
                                                             NSForegroundColorAttributeName : [UIColor blackColor]
                                                             } forState:UIControlStateNormal];
    [barButtonAppearanceInSearchBar setTitle:@"Done"];
siburb
  • 4,880
  • 1
  • 25
  • 34
1

UIBarButtonItem's title property is not available through the UIAppearance proxy.

I don't know why it was working in iOS 6, but it's definitely not supposed to.

The only alternative you seem to have is to "hack" the UISearchBar by crawling its subviews looking for the button and setting the title, but:

  • it's very fragile, as any implementation change to the subviews structure will break your code
  • it's not global you will have to do this on any UISearchBar instance

According to this answer you can perform this "hack" in the searchDisplayControllerWillBeginSearch: method of UISearchDisplayDelegate like follows:

- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
     [theSearchBar setShowsCancelButton:YES animated:NO];

    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];
    }
}
Community
  • 1
  • 1
Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
  • What is the best practice for setting the cancel button text on ios6 and ios7 then? – mverderese Oct 01 '13 at 20:42
  • If i put this code in `searchDisplayControllerWillBeginSearch` it doesn't work, but if I put it in `searchDisplayControllerDidBeginSearch` then it shows "cancel" for a second then fades to "Done" – mverderese Oct 01 '13 at 20:52