28

我有一个 UISearchBar 有一个取消按钮(它使用 显示-(void)setShowsCancelButton:animated)。我已经tintColor像这样更改了搜索栏,试图获得一个灰色的搜索栏:

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

这就是现在的样子——注意取消按钮也是灰色的: http: //twitpic.com/c0hte

有没有办法单独设置取消按钮的颜色,所以看起来更像这样:http ://twitpic.com/c0i6q

4

21 回答 21

27

您可以UIAppearance在不迭代 的子视图的情况下使用取消按钮的样式UISearchBar,但标题当前没有任何用 注释的方法UIButtonUI_APPEARANCE_SELECTOR

编辑:向下钻取子视图,直到获得取消按钮

但这通常返回 nil 直到 searchBar.setShowsCancelButton(true, animated: true)被调用。

extension UISearchBar {

var cancelButton : UIButton? {
    if let view = self.subviews.first {
        for subView in view.subviews {
            if let cancelButton = subView as? UIButton {
                return cancelButton
            }
        }
    }
    return nil
}
}
于 2012-04-11T15:00:38.500 回答
20

更改“取消”按钮的标题:

[[UIButton appearanceWhenContainedIn:[UISearchBar class], nil] setTitle:@"newTitle" forState:UIControlStateNormal];

斯威夫特等效:

   let cancelButton = UIButton.appearance(whenContainedInInstancesOf: [UISearchBar.self])
   cancelButton?.setTitle("cancel".localized, for: .normal)
于 2012-10-01T10:26:15.857 回答
20

在 iOS 5.0+ 中,您可以使用appearnce代理。

在显示搜索栏之前:

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];

如果您使用[UIButton appearanceWhenContainedIn:[UISearchBar class], nil],它会影响其他按钮(例如清除按钮)。所以,你最好不要使用UIButton'sappearnce。试试UIBarButtonItem

于 2012-12-21T08:17:47.687 回答
17

尽管这可能与原始问题不完全相关,但在尝试自定义 UISearchBar 中的取消按钮的更大意义上,该解决方案仍然适用。认为这将帮助其他陷入这种情况的人。

我的情况是更改取消按钮的标题,但有一个转折,其中我不想默认显示取消按钮,但只想在用户进入搜索模式时显示它(通过在搜索文本字段内单击)。此时,我希望取消按钮带有“完成”的标题(“取消”给我的屏幕赋予了不同的含义,因此进行了自定义)。

不过,这就是我所做的(结合了 caelavel 和 Arenim 的解决方案):

使用以下两种方法将 UISearchBar 子类化为 MyUISearchBar:

-(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];

}

在使用此 Searchbar 的视图控制器中,以下代码负责显示取消按钮并自定义其标题:

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

奇怪的是,当退出搜索模式时,我不需要做任何事情来隐藏取消按钮,因为默认情况下它是隐藏的。

于 2010-10-03T15:27:38.057 回答
13

你想做的事情非常艰难。没有内置的钩子可以获取取消按钮。

但是,如果您愿意打开引擎盖,有几个选择。

首先,UISearchBar 是一个 UIView,而 Cancel 按钮也是一个视图,正如您所期望的那样,它作为子视图添加到搜索栏中。

我做了一些实验,可以告诉你,当按钮在屏幕上时,它的大小为 48,30。

因此,在 viewWillAppear 中,您可以执行以下操作:

  1. 通过查找大小为 48,30 的视图,在 [searchBar subviews] 中找到取消按钮视图。(似乎只有一个——这可能会改变......)您可以加倍小心并寻找一个大致正确位置的(横向和纵向不同)。

  2. 将子视图添加到取消按钮。

  3. 子视图应该是 UIControl(以便您可以设置启用 = NO,以确保触摸事件到达实际的取消按钮)

  4. 它需要有正确的颜色和圆角;由于我还不明白的原因,您需要捏造尺寸(55,30 似乎有效)

  5. 如果 searchBar.showsCancelButton 始终为 YES,这将起作用;如果您希望它在不编辑搜索字符串时消失,则需要在每次出现取消按钮时找到一个挂钩来添加覆盖。

  6. 如您所见,这是一些丑陋的修补。睁大眼睛去做。

于 2009-07-29T15:00:19.053 回答
7

您可以通过遍历搜索栏的子视图并检查类类型(而不是大小)来找到取消按钮:

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

然后更改色调颜色:

[cancelButton setTintColor:[UIColor colorWithRed:145.0/255.0 green:159.0/255.0 blue:179.0/255.0 alpha:1.0]];
于 2010-05-11T23:17:28.363 回答
7

如果你想配置你的取消按钮,UISearchBar你应该UIButton从你的对象中获取UISearchBar对象。下面的例子

UISearchBar *s_bar = [[UISearchBar alloc] initWithFrame:CGRectMake(50,20,300,30)];
s_bar.delegate = self;
s_bar.barStyle = UIBarStyleDefault;
s_bar.showsCancelButton = YES;
UIButton *cancelButton;
for (id button in s_bar.subviews)
{
    if ([button isKindOfClass:[UIButton class]])
    {
        cancelButton=(UIButton*)button;
        break;
    }
}
于 2011-03-03T11:08:11.143 回答
4

自定义 UISearchBar 和覆盖方法 -addSubview:

- (void) addSubview:(UIView *)view {
    [super addSubview:view];

    if ([view isKindOfClass:UIButton.class]) {
        UIButton *cancelButton = (UIButton *)view;
        [cancelButton setBackgroundImage:[UIImage imageNamed:@"xxxx.png"] forState:UIControlStateNormal];
        [cancelButton setBackgroundImage:[UIImage imageNamed:@"yyyy.png"] forState:UIControlStateHighlighted];
    }
}
于 2011-09-21T09:03:44.400 回答
4

我将给出关于 UIAppearance 技术的详细回答。首先,您需要了解取消按钮是一个私有的 UINavigationButton:UIButton。经过一番检查,UINavigationButton 似乎会响应那些 UIAppearance 选择器:

// 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:)

巧合的是,这些选择器与 UIBarButtonItem 的选择器相匹配。这意味着诀窍是使用两个单独的 UIAppearance 来处理私有类 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:...];

这将使您可以根据需要自定义取消按钮。

于 2013-09-11T16:59:00.557 回答
1

初始化 UISearchBar 后,您可以探究它的子视图并自定义每个子视图。例子:

for (UIView *view in searchBar.subviews) {

    //if subview is the button
    if ([[view.class description] isEqualToString:@"UINavigationButton"]) {

        //change the button images and text for different states
        [((UIButton *)view) setEnabled:YES];
        [((UIButton *)view) setTitle:nil forState:UIControlStateNormal];
        [((UIButton *)view) setImage:[UIImage imageNamed:@"button image"] forState:UIControlStateNormal];
        [((UIButton *)view) setBackgroundImage:[UIImage imageNamed:@"button"] forState:UIControlStateNormal];
        [((UIButton *)view) setBackgroundImage:[UIImage imageNamed:@"button_pressed"] forState:UIControlStateSelected];
        [((UIButton *)view) setBackgroundImage:[UIImage imageNamed:@"button_pressed"] forState:UIControlStateHighlighted];

    //if the subview is the background
    }else if([[view.class description] isEqualToString:@"UISearchBarBackground"]) {

        //put a custom gradient overtop the background
        CAGradientLayer *gradient = [CAGradientLayer layer];
        gradient.frame = view.bounds;
        gradient.colors = [NSArray arrayWithObjects:(id)[[some uicolor] CGColor], (id)[[another uicolor] CGColor], nil];
        [view.layer insertSublayer:gradient atIndex:0];

    //if the subview is the textfield
    }else if([[view.class description] isEqualToString:@"UISearchBarTextField"]){

        //change the text field if you wish

    }

}

对我来说效果很好!尤其是渐变:)

于 2011-10-14T14:15:12.927 回答
1

斯威夫特 2.1.1:

没有简单的方法来连接和设置搜索栏的样式,您需要从搜索栏中手动获取子视图,然后应用您的更改。

var cancelButton: UIButton
let topView: UIView = self.customSearchController.customSearchBar.subviews[0] as UIView
for subView in topView.subviews {
 if subView.isKindOfClass(NSClassFromString("UINavigationButton")!) {
    cancelButton = subView as! UIButton
    cancelButton.enabled = true
    cancelButton.setTitle("TestTitle", forState: UIControlState.Normal) // Change to set the title
    cancelButton.setBackgroundImage(UIImage(named: "ImageName"), forState: .Normal) // Change this to set a custom cancel button image, set the title to "" to remove 'Cancel' text
   }
}
于 2016-05-23T02:31:55.103 回答
1

首先,我要感谢https://stackoverflow.com/a/37381821/1473144中的@Eliott

我必须对他的回答进行一些调整,才能在下面的规格中发挥作用。请,我要求 OP 更新已接受的答案,因为它已经过时了。

斯威夫特 3、iOS 10 和 Xcode 8.2.1

searchBar.showsCancelButton = true
var cancelButton: UIButton
let topView: UIView = self.searchBar.subviews[0] as UIView
for subView in topView.subviews {
    if let pvtClass = NSClassFromString("UINavigationButton") {
        if subView.isKind(of: pvtClass) {
            cancelButton = subView as! UIButton

            cancelButton.setTitle("", for: .normal)
            cancelButton.tintColor = UIColor.black
            cancelButton.setImage(#imageLiteral(resourceName: "searchX"), for: .normal)
        }
    }

}
于 2017-01-05T17:02:27.843 回答
0

嗯,这里是函数,它可以改变Cancel的按钮标签。修改它,如果你愿意。用法是:

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];
  }
 }
}
于 2010-08-19T08:08:00.187 回答
0
- (void) searchBarTextDidBeginEditing:(UISearchBar *)theSearchBar 
{        
    NSArray *arr = [theSearchBar subviews];
    UIButton *cancelButton = [arr objectAtIndex:3];
    [cancelButton setTitle:@"yourtitle" forState:UIControlStateNormal];    
}

只需记录 arr amd 即可查看索引控制位于哪个位置。以同样的方式你可以设置UITextField属性:

    NSArray *arr = [searchbar subviews];
    UITextField *searchfield = [arr objectAtIndex:2];
    [searchfield setTextAlignment:UITextAlignmentRight];
于 2012-08-08T06:05:40.053 回答
0

我的应用程序中有很多 UISearchBar 项目,所以我编写了这个类别来添加一个属性,以便您可以访问mySearchBar.cancelButton. (如果您不熟悉类别,请在此处阅读有关使用类别扩展对象的更多信息。)

请记住,您应该只在 Cancel 按钮可见时访问它,因为 UISearchBar 似乎每次显示时都会创建一个新按钮对象。不要保存指向cancelButton的指针,只需要在需要的时候获取即可:

@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
于 2012-12-17T19:30:04.780 回答
0

愚蠢的方式

for(id cc in [SearchBar subviews])
{
    if([cc isKindOfClass:[UIButton class]])
    {
        UIButton *btn = (UIButton *)cc;
        ......
        Do whatever you want
        .......        
    }
}
于 2013-03-29T08:38:29.237 回答
0
extension UISearchBar {
var cancelButton : UIButton? {
    let topView: UIView = self.subviews[0] as UIView

    if let pvtClass = NSClassFromString("UINavigationButton") {
        for v in topView.subviews {
            if v.isKind(of: pvtClass) {
                return v as? UIButton
            }
        }
    }

    return nil
}
}
于 2017-02-26T12:05:33.170 回答
0
UISearchBar *searchBar;
[searchBar setShowsCancelButton:YES animated:YES];

UIButton *cancelButton = 
YES == [searchBar respondsToSelector:NSSelectorFromString(@"cancelButton")] ? 
[searchBar valueForKeyPath:@"_cancelButton"] : nil;

cancelButton.titleEdgeInsets = UIEdgeInsetsMake(0, -10, 0, 10);
[cancelButton setTitle:@"New :)" forState:UIControlStateNormal];
于 2017-03-18T18:37:46.663 回答
0

对于 iOS 11 和 Swift 4。创建 UISearchController 的子类。覆盖方法:

override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        print("layout")
        if let btn = searchBar.subviews[0].subviews[2] as? UIButton {
            btn.frame = CGRect(x: 306, y: 20, width: 53, height: 30)
        }
}
于 2017-11-13T18:24:27.857 回答
0

对于 iOS 10 及更高版本,请使用以下方法

[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UISearchBar class]]] setTintColor:[UIColor blackColor]];
于 2019-10-03T10:25:33.770 回答
0

最好的风格cancelButton是不使用UIAppearance就像这样,它也是最Swift5 iOS13适合的UISearchResultController.searchBar

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
        }
    }
}

设置serachBar tintColor将设置tintColor所有项目的 ,包括 ,cancelButton但是这样 中的闪光灯searchField也将设置为相同tintColor,因此找到textfield并设置tintColor它将解决闪光灯问题

于 2019-12-17T08:19:11.710 回答