Styling the cancel button in a UISearchBar

前端 未结 21 1367
-上瘾入骨i
-上瘾入骨i 2020-12-02 09:13

I have a UISearchBar that has a cancel button (it\'s displayed using -(void)setShowsCancelButton:animated). I\'ve changed the tintColor of the sear

相关标签:
21条回答
  • 2020-12-02 09:51
    - (void) searchBarTextDidBeginEditing:(UISearchBar *)theSearchBar 
    {        
        NSArray *arr = [theSearchBar subviews];
        UIButton *cancelButton = [arr objectAtIndex:3];
        [cancelButton setTitle:@"yourtitle" forState:UIControlStateNormal];    
    }
    

    Just take a log of arr amd see at which index control lies. In the same way u can set UITextField properties:

        NSArray *arr = [searchbar subviews];
        UITextField *searchfield = [arr objectAtIndex:2];
        [searchfield setTextAlignment:UITextAlignmentRight];
    
    0 讨论(0)
  • 2020-12-02 09:53

    The best way to style the cancelButton is without using UIAppearance is like this it is for Swift5 iOS13 and it works best with UISearchResultController.searchBar too

    extension UISearchBar {
        func changeSearchBarAppearance(appearance: MyAppearance) {
            self.barTintColor = appearance.searchbar.barTintColor
            self.tintColor  =  appearance.searchbar.tintColor
            if let textField = self.subviews.first?.subviews.last?.subviews.first {
                textField.tintColor = .black
            }
        }
    }
    

    setting serachBar tintColor will set the tintColor of all items including the cancelButton but with this the blinker in the searchField will also be set with the same tintColor so find the textfield and set its tintColor will solve the blinker issue

    0 讨论(0)
  • 2020-12-02 09:54

    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.

    0 讨论(0)
  • 2020-12-02 09:54

    I'll give a detailed answered regarding the UIAppearance technique. First, you need to understand that the cancel button is a private UINavigationButton:UIButton. 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:...];
    // only for a backButton in an UINavigationBar, not for a cancelButton in an UISearchBar
    //[barButtonItemAppearanceInBar setBackButtonBackgroundImage:... forState:... barMetrics:...];
    //[barButtonItemAppearanceInBar setBackButtonTitlePositionAdjustment:... forBarMetrics:...];
    //[barButtonItemAppearanceInBar setBackButtonBackgroundVerticalPositionAdjustment:... forBarMetrics:...];
    
    UIButton<UIAppearance> *buttonAppearanceInBar = [UIButton appearanceWhenContainedIn:barClass, nil];
    // warning: doesn't work for iOS7+
    [buttonAppearanceInBar setTitle:... forState:...];
    

    This will let you customize your Cancel button as much as you want.

    0 讨论(0)
  • 2020-12-02 09:56

    In iOS 5.0+, you can use the appearnce proxy.

    Before the search bar is showed.:

    UIBarButtonItem *searchBarButton = [UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil];
    [searchBarButton setBackgroundImage:myCancelButtonImageNormal forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
    [searchBarButton setBackgroundImage:myCancelButtonImageHighlighted forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
    [searchBarButton setTitleTextAttributes:barButtonTitleTextAttributesNormal forState:UIControlStateNormal];
    [searchBarButton setTitleTextAttributes:barButtonTitleTextAttributesHighlighted forState:UIControlStateHighlighted];
    

    If you use [UIButton appearanceWhenContainedIn:[UISearchBar class], nil], it will affect other buttons (e.g. clear button). So, you'd better not use UIButton's appearnce. Try UIBarButtonItem.

    0 讨论(0)
  • 2020-12-02 09:57

    I have many UISearchBar items throughout my app, so I wrote this category to add a property so you can access mySearchBar.cancelButton. (If you're new to categories, read more about extending objects with Categories here.)

    Keep in mind you should only access this when the Cancel button is visible because UISearchBar seems to create a new button object every time it shows. Don't save the pointer to the cancelButton, just get it when needed:

    @interface UISearchBar (cancelButton)
    
    @property (readonly) UIButton* cancelButton;
    
    - (UIButton *) cancelButton;
    
    @end
    
    @implementation UISearchBar (cancelButton)
    
    - (UIButton *) cancelButton {
        for (UIView *subView in self.subviews) {
            //Find the button
            if([subView isKindOfClass:[UIButton class]])
            {
                return (UIButton *)subView;
            }
        }
    
        NSLog(@"Error: no cancel button found on %@", self);
    
        return nil;
    }
    
    @end
    
    0 讨论(0)
提交回复
热议问题