web-dev-qa-db-ja.com

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

28
Chu Yeow

やりたいことはかなり大変です。キャンセルボタンを取得するための組み込みのフックはありません。

ただし、ジミーがフードを開けて構わない場合は、いくつかのオプションがあります。

まず、UISearchBarはUIViewであり、[キャンセル]ボタンもビューです。これは、予想どおり、サブビューとして検索バーに追加されます。

少し実験してみましたが、ボタンが画面上にあるときのサイズは48、30であることがわかります。

したがって、viewWillAppearでは、次のようなことができます。

  1. サイズが48,30の[searchBarサブビュー]でキャンセルボタンビューを探します。 (1つしかないようです。これは変更される可能性があります...)二重に注意して、ほぼ正しい位置にあるものを探します(横向きと縦向きでは異なります)。

  2. サブビューをキャンセルボタンに追加します。

  3. サブビューはUIControlにする必要があります(タッチイベントが実際のキャンセルボタンに到達するようにするために、enabled = NOに設定できます)。

  4. 正しい色と丸い角が必要です。私がまだ理解していない理由でサイズを変更する必要があります(55、30は動作するようです)

  5. これはsearchBar.showsCancelButtonが常にYESの場合に機能します。検索文字列を編集していないときに非表示にしたい場合は、キャンセルボタンが表示されるたびにオーバーレイを追加するフックを見つける必要があります。

  6. ご覧のとおり、これは見苦しい作業です。目を大きく開いてください。

13
Amagrammer

UIAppearanceを使用すると、UISearchBarのサブビューを繰り返さなくてもキャンセルボタンのスタイルを設定できますが、現在 UIButtonヘッダーには_UI_APPEARANCE_SELECTOR_

編集:キャンセルボタンが表示されるまでサブビューをドリルダウンします

しかし、これは通常、searchBar.setShowsCancelButton(true, animated: true)が呼び出されるまでnilを返します。

_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
}
}
_
27
lemnar

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をお試しください。

20
Yiming Tang

[キャンセル]ボタンのタイトルを変更します。

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

対応するスウィフト:

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

これは元の質問に正確に関連しているわけではないかもしれませんが、UISearchBarの[キャンセル]ボタンをカスタマイズしようとするより広い意味で、解決策は依然として適用可能です。これはそのようなシナリオで行き詰まっている他の人を助けるだろうと思った。

私の状況は、キャンセルボタンのタイトルを変更することでしたが、ユーザーが検索モードに入ったときに(検索テキストフィールド内をクリックして)キャンセルボタンをデフォルトでは表示せず、表示するだけでした。 )。この瞬間、私はキャンセルボタンに「完了」というキャプションを付けたかったのです(「キャンセル」は画面に異なる意味を与えていたため、カスタマイズされました)。

それにもかかわらず、ここに私がやったことがあります(CaelavelとArenimのソリューションの組み合わせ):

次の2つのメソッドを使用して、MyUISearchBarとしてサブクラス化されたUISearchBar:

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

奇妙なことに、検索モードが終了したとき、デフォルトでは非表示になっているので、キャンセルボタンを非表示にするために何もする必要はありませんでした。

17
Sara

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

キャンセルボタンを見つけるには、検索バーのサブビューをループして、(サイズではなく)クラスタイプを確認します。

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

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のセレクターと一致します。トリックの意味は、2つの個別の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:...];

これにより、[キャンセル]ボタンを好きなだけカスタマイズできます。

4
Cœur

カスタム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];
    }
}
4
iwill

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

    }

}

私のためにうまくいきました!特に勾配:)

Swift 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
   }
}
1
Elliott Davies

まず第一に、これから@Eliottに感謝したいと思います https://stackoverflow.com/a/37381821/1473144

以下の仕様で機能するように、彼の回答を少し調整する必要がありました。承認された回答は非常に古いため、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)
        }
    }

}
1
gaskbr

アプリ全体に多くの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
0
Aaron Brager

さて、これがキャンセルのボタンラベルを変更できる関数です。必要に応じて変更してください。使い方は:

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

AMDのログを取得して、AMDがどのインデックスコントロールがあるかを確認します。同様に、uはUITextFieldプロパティを設定できます。

    NSArray *arr = [searchbar subviews];
    UITextField *searchfield = [arr objectAtIndex:2];
    [searchfield setTextAlignment:UITextAlignmentRight];
0
Abhishek Singh
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];
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)
        }
}
0
mazy
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
}
}
0
Abhishek Bedi

愚かな方法

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