簡體   English   中英

在 UISearchBar 中設置取消按鈕的樣式

[英]Styling the cancel button in a UISearchBar

我有一個 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

您可以使用UIAppearance設置取消按鈕的樣式,而無需迭代UISearchBar子視圖,但UIButton標頭當前沒有任何用UI_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
}
}

更改“取消”按鈕的標題:

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

Swift 等效項:

   let cancelButton = UIButton.appearance(whenContainedInInstancesOf: [UISearchBar.self])
   cancelButton?.setTitle("cancel".localized, for: .normal)

在 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的外觀。 試試UIBarButtonItem

盡管這可能與原始問題不完全相關,但該解決方案在嘗試自定義 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];

}

在使用此搜索欄的視圖控制器中,以下代碼負責顯示取消按鈕並自定義其標題:

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

奇怪的是,當退出搜索模式時,我沒有做任何事情來隱藏取消按鈕,因為它默認是隱藏的。

你想做的事情很艱難。 沒有內置的鈎子可以到達取消按鈕。

但是,如果您願意打開引擎蓋,有幾種選擇。

首先,UISearchBar 是一個 UIView,而 Cancel 按鈕也是一個視圖,正如您所期望的那樣,它作為子視圖添加到搜索欄中。

我進行了一些實驗,可以告訴您,當按鈕在屏幕上時,它的大小為 48,30。

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

  1. 在 [searchBar subviews] 中查找大小為 48,30 的取消按鈕視圖。 (似乎只有一個——這可能會改變……)你可以加倍小心,尋找一個位置大致正確的(橫向和縱向不同)。

  2. 向取消按鈕添加子視圖。

  3. 子視圖應該是一個 UIControl(這樣你就可以設置 enabled = NO,以確保觸摸事件到達實際的取消按鈕)

  4. 它需要有正確的顏色和圓角; 由於我還不明白的原因,您需要捏造大小(55,30 似乎有效)

  5. 如果 searchBar.showsCancelButton 始終為 YES,這將起作用; 如果您希望它在不編輯搜索字符串時消失,則每次出現取消按鈕時都需要找到一個鈎子來添加疊加層。

  6. 如您所見,這是一些丑陋的修補。 睜大眼睛做這件事。

您可以通過循環搜索欄的子視圖並檢查類類型(而不是大小)來找到取消按鈕:

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

如果你想在UISearchBar上配置你的取消按鈕,你應該從你的UISearchBar對象中獲取UIButton對象。 下面的例子

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

我將詳細回答有關 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:...];

這將使您可以根據需要自定義“取消”按鈕。

自定義 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];
    }
}

斯威夫特 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
   }
}

首先,我要感謝來自這個https://stackoverflow.com/a/37381821/1473144 的@Eliott

我必須對他的回答進行一些調整才能在我下面的規范中工作。 請,我要求 OP 更新已接受的答案,因為它已經過時了。

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

}

初始化 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

    }

}

對我來說很棒! 尤其是漸變:)

- (void) searchBarTextDidBeginEditing:(UISearchBar *)theSearchBar 
{        
    NSArray *arr = [theSearchBar subviews];
    UIButton *cancelButton = [arr objectAtIndex:3];
    [cancelButton setTitle:@"yourtitle" forState:UIControlStateNormal];    
}

只需記錄 arr 和查看索引控制所在的日志。 以同樣的方式你可以設置UITextField屬性:

    NSArray *arr = [searchbar subviews];
    UITextField *searchfield = [arr objectAtIndex:2];
    [searchfield setTextAlignment:UITextAlignmentRight];

我的應用程序中有許多 UISearchBar 項目,所以我編寫了這個類別來添加一個屬性,以便您可以訪問mySearchBar.cancelButton (如果您不熟悉類別,請在此處閱讀有關使用類別擴展對象的更多信息。)

請記住,您應該只在“取消”按鈕可見時訪問它,因為 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

愚蠢的方式

for(id cc in [SearchBar subviews])
{
    if([cc isKindOfClass:[UIButton class]])
    {
        UIButton *btn = (UIButton *)cc;
        ......
        Do whatever you want
        .......        
    }
}

嗯,這里是函數,它可以改變取消的按鈕標簽。 如果需要,請修改它。 用法是:

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

對於 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)
        }
}

對於 iOS 10 及更高版本,請使用以下方法

[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UISearchBar class]]] setTintColor:[UIColor blackColor]];

設置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將解決這個問題信號燈

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM