51

iPhoneの連絡先アプリで検索語を入力してから[検索]ボタンをタップすると、キーボードが非表示になりますが、キャンセルボタンは有効になっています。私のアプリでは、resignFirstResponderを呼び出すと、キャンセルボタンが無効になります。

キャンセルボタンを有効な状態に保ちながらキーボードを非表示にする方法を知っている人はいますか?

私は次のコードを使用します:

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
    [searchBar resignFirstResponder];
}

キーボードがスライドして見えなくなりますが、検索テキストフィールドの右側にある[キャンセル]ボタンが無効になっているため、検索をキャンセルできません。連絡先アプリは、キャンセルボタンを有効な状態に維持します。

おそらく1つの解決策は、検索バー自体ではなく、searchBarオブジェクトに飛び込んで、実際のテキストフィールドでresignFirstResponderを呼び出すことだと思います。

任意の入力をいただければ幸いです。

4

22 に答える 22

29

この方法はiOS7で機能しました。

- (void)enableCancelButton:(UISearchBar *)searchBar
{
    for (UIView *view in searchBar.subviews)
    {
        for (id subview in view.subviews)
        {
            if ( [subview isKindOfClass:[UIButton class]] )
            {
                [subview setEnabled:YES];
                NSLog(@"enableCancelButton");
                return;
            }
        }
    }
}

(また、[_ searchBar resignFirstResponder]を使用した後は、必ずどこかで呼び出してください。)

于 2013-08-09T15:53:47.103 に答える
22

これを試して

for(id subview in [yourSearchBar subviews])
{
    if ([subview isKindOfClass:[UIButton class]]) {
        [subview setEnabled:YES];
    }
}
于 2012-04-01T22:33:56.330 に答える
10

[検索]ボタンをタップする代わりにテーブルのスクロールを開始すると、受け入れられたソリューションは機能しません。その場合、「キャンセル」ボタンは無効になります。

これは、KVOを使用して無効にするたびに[キャンセル]ボタンを再度有効にする私のソリューションです。

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    // Search for Cancel button in searchbar, enable it and add key-value observer.
    for (id subview in [self.searchBar subviews]) {
        if ([subview isKindOfClass:[UIButton class]]) {
            [subview setEnabled:YES];
            [subview addObserver:self forKeyPath:@"enabled" options:NSKeyValueObservingOptionNew context:nil];
        }
    }
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    // Remove observer for the Cancel button in searchBar.
    for (id subview in [self.searchBar subviews]) {
        if ([subview isKindOfClass:[UIButton class]])
            [subview removeObserver:self forKeyPath:@"enabled"];
    }
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    // Re-enable the Cancel button in searchBar.
    if ([object isKindOfClass:[UIButton class]] && [keyPath isEqualToString:@"enabled"]) {
        UIButton *button = object;
        if (!button.enabled)
            button.enabled = YES;
    }
}
于 2012-09-13T12:21:14.187 に答える
9

iOS 6以降、ボタンはUIButtonではなくUINavigationButton(プライベートクラス)のように見えます。

上記の例を次のように調整しました。

for (UIView *v in searchBar.subviews) {
    if ([v isKindOfClass:[UIControl class]]) {
        ((UIControl *)v).enabled = YES;
    }
}

ただし、内部をいじくり回しているため、これは明らかに脆弱です。ボタン以上のものを有効にすることもできますが、より良い解決策が見つかるまでは機能します。

これを公開するようにAppleに依頼する必要があります。

于 2012-10-26T00:03:37.237 に答える
8

これは私にとってはうまくいくようでした(viewDidLoadで):

__unused UISearchDisplayController* searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];

UISearchDisplayControllerを適切に使用する必要があることはわかっていますが、これは現在の実装の簡単な修正でした。

于 2013-10-01T21:14:33.087 に答える
7

ランタイムAPIを使用して、キャンセルボタンにアクセスできます。

UIButton *btnCancel = [self.searchBar valueForKey:@"_cancelButton"];
[btnCancel setEnabled:YES];
于 2015-07-13T10:45:40.103 に答える
5

UISearchBarに単純なカテゴリとしてこれを実装することにより、他の人がすでに投稿したものを拡張しました。

UISearchBar + alwaysEnableCancelButton.h

#import <UIKit/UIKit.h>

@interface UISearchBar (alwaysEnableCancelButton)

@end

UISearchBar + alwaysEnableCancelButton.m

#import "UISearchBar+alwaysEnableCancelButton.h"

@implementation UISearchBar (alwaysEnableCancelButton)

- (BOOL)resignFirstResponder
{
    for (UIView *v in self.subviews) {
        // Force the cancel button to stay enabled
        if ([v isKindOfClass:[UIControl class]]) {
            ((UIControl *)v).enabled = YES;
        }

        // Dismiss the keyboard
        if ([v isKindOfClass:[UITextField class]]) {
            [(UITextField *)v resignFirstResponder];
        }
    }

    return YES;
}
@end
于 2013-01-25T19:29:24.937 に答える
4

これは、iOS 7で機能するもう少し堅牢なソリューションです。検索バーのすべてのサブビューを再帰的にトラバースして、すべてのサブビューUIControl([キャンセル]ボタンを含む)が有効になっていることを確認します。

- (void)enableControlsInView:(UIView *)view
{
    for (id subview in view.subviews) {
        if ([subview isKindOfClass:[UIControl class]]) {
            [subview setEnabled:YES];
        }
        [self enableControlsInView:subview];
    }
}

次のように呼び出した直後に、このメソッドを呼び出します[self.searchBar resignFirstResponder]

[self enableControlsInView:self.searchBar];

出来上がり!キャンセルボタンは有効なままです。

于 2013-09-26T23:29:31.460 に答える
3

iOS 12までは、次のように使用できます。-

if let cancelButton : UIButton = self.menuSearchBar.value(forKey: "_cancelButton") as? UIButton{
    cancelButton.isEnabled = true
}

iOS 13の時点で、likeを使用すると(forKey: "_cancelButton")、このプライベートAPIの使用が検出され、残念ながらクラッシュが発生します。

iOS13以降およびSwift5以降の場合

if let cancelButton : UIButton = self.menuSearchBar.value(forKey: "cancelButton") as? UIButton {
    cancelButton.isEnabled = true
}
于 2020-03-03T08:59:26.167 に答える
2

iOS7で動作させるための別のアプローチを見つけました。

私が試しているのは、TwitteriOSアプリのようなものです。[タイムライン]タブの虫眼鏡をクリックUISearchBarすると、[キャンセル]ボタンがアクティブになり、キーボードが表示され、最近の検索画面が表示されます。最近の検索画面をスクロールすると、キーボードは非表示になりますが、[キャンセル]ボタンはアクティブのままになります。

これは私の作業コードです:

UIView *searchBarSubview = self.searchBar.subviews[0];
NSArray *subviewCache = [searchBarSubview valueForKeyPath:@"subviewCache"];
if ([subviewCache[2] respondsToSelector:@selector(setEnabled:)]) {
    [subviewCache[2] setValue:@YES forKeyPath:@"enabled"];
}

テーブルビューのにブレークポイントを設定することで、このソリューションに到達しましたscrollViewWillBeginDragging:。私は自分を調べて、UISearchBarそのサブビューをむき出しにしました。常にタイプUIView(私の変数searchBarSubview)の1つだけがあります。

ここに画像の説明を入力してください

次に、それは呼び出されたものをUIView保持し、3番目の最後の要素がパブリックAPIではなくタイプであることに気付きました。そこで、代わりにKey-Valueコーディングを使用することにしました。に応答するかどうかを確認しましたが、幸いなことに応答します。そこで、プロパティをに設定しました。それ[キャンセル]ボタンであることがわかります。NSArraysubviewCacheUINavigationButtonUINavigationButtonsetEnabled:@YESUINavigationButton

Appleが's内臓の実装を変更することを決定した場合、これは間違いなく壊れますUISearchBarが、一体何なのでしょう。今のところ動作します。

于 2014-02-25T17:26:33.993 に答える
2

David Douglasの回答のSWIFTバージョン(iOS9でテスト済み)

func enableSearchCancelButton(searchBar: UISearchBar){
    for view in searchBar.subviews {
        for subview in view.subviews {
            if let button = subview as? UIButton {
                button.enabled = true
            }
        }
    }
}
于 2015-11-05T06:11:15.700 に答える
2

投稿されたソリューションのほとんどは堅牢ではなく、さまざまな状況で[キャンセル]ボタンが無効になります。

検索バーでより複雑なことをしている場合でも、[キャンセル]ボタンを常に有効にしておくソリューションを実装しようとしました。これは、Swift 4のカスタムUISearchViewサブクラスとして実装されます。value(forKey :)トリックを使用して、キャンセルボタンと検索テキストフィールドの両方を検索し、検索フィールドが編集を終了してキャンセルボタンを再度有効にしたときにリッスンします。また、showsCancelButtonフラグを切り替えるときにキャンセルボタンを有効にします。

UISearchBarの内部の詳細が変更されて機能しなくなった場合に警告する、いくつかのアサーションが含まれています。

import UIKit

final class CancelSearchBar: UISearchBar {
    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setup()
    }

    private func setup() {
        guard let searchField = value(forKey: "_searchField") as? UIControl else {
            assertionFailure("UISearchBar internal implementation has changed, this code needs updating")
            return
        }

        searchField.addTarget(self, action: #selector(enableSearchButton), for: .editingDidEnd)
    }

    override var showsCancelButton: Bool {
        didSet { enableSearchButton() }
    }

    @objc private func enableSearchButton() {
        guard showsCancelButton else { return }
        guard let cancelButton = value(forKey: "_cancelButton") as? UIControl else {
            assertionFailure("UISearchBar internal implementation has changed, this code needs updating")
            return
        }

        cancelButton.isEnabled = true
    }
}
于 2017-12-03T11:32:14.517 に答える
1

smileyborgの答えに基づいて、これをsearchBarデリゲートに配置するだけです。

- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{   
    dispatch_async(dispatch_get_main_queue(), ^{
        __block __weak void (^weakEnsureCancelButtonRemainsEnabled)(UIView *);
        void (^ensureCancelButtonRemainsEnabled)(UIView *);
        weakEnsureCancelButtonRemainsEnabled = ensureCancelButtonRemainsEnabled = ^(UIView *view) {
            for (UIView *subview in view.subviews) {
                if ([subview isKindOfClass:[UIControl class]]) {
                [(UIControl *)subview setEnabled:YES];
                }
                weakEnsureCancelButtonRemainsEnabled(subview);
            }
        };

        ensureCancelButtonRemainsEnabled(searchBar);
    });
 }

このソリューションは、iOS7以降でうまく機能します。

于 2014-09-05T07:00:16.100 に答える
1

iOS 10の場合、Swift 3:

for subView in self.movieSearchBar.subviews {
    for view in subView.subviews {
        if view.isKind(of:NSClassFromString("UIButton")!) {
            let cancelButton = view as! UIButton
            cancelButton.isEnabled = true
        }
    }
}
于 2017-01-09T07:47:33.137 に答える
1

iOS 9/10(テスト済み)、Swift 3(短い)の場合:

searchBar.subviews.flatMap({$0.subviews}).forEach({ ($0 as? UIButton)?.isEnabled = true })
于 2017-07-05T10:43:20.733 に答える
1

iOS 11以降の場合、Swift 4-5:

extension UISearchBar {
  func alwaysShowCancelButton() {
    for subview in self.subviews {
      for ss in subview.subviews {
        if #available(iOS 13.0, *) {
          for s in ss.subviews {
            self.enableCancel(with: s)
          }
        }else {
          self.enableCancel(with: ss)
        }
      }
    }
  }
  private func enableCancel(with view:UIView) {
   if NSStringFromClass(type(of: view)).contains("UINavigationButton") {
      (view as! UIButton).isEnabled = true
    }
  }
}

UISearchBarDelegate

func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
    self.searchBar.resignFirstResponder()
    self.searchBar.alwaysShowCancelButton()
  }
于 2020-01-26T16:09:27.617 に答える
0
for (UIView *firstView in searchBar.subviews) {
    for(UIView* view in firstView.subviews) {
        if([view isKindOfClass:[UIButton class]]) {
             UIButton* button = (UIButton*) view;
             [button setEnabled:YES];
        }
    }
}
于 2014-03-04T07:14:16.523 に答える
0

UISearchBarから継承するCustomSearchBarを作成し、次のメソッドを実装できます。

- (void)layoutSubviews {

    [super layoutSubviews];

    @try {
        UIView *baseView = self.subviews[0];

        for (UIView *possibleButton in baseView.subviews)
        {
            if ([possibleButton respondsToSelector:@selector(setEnabled:)]) {
                [(UIControl *)possibleButton setEnabled:YES];
            }
        }
    }
    @catch (NSException *exception) {
        NSLog(@"ERROR%@",exception);
    }
}
于 2015-03-03T20:55:46.830 に答える
0

より良い解決策は

[UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil].enabled = YES;
于 2016-10-20T10:34:06.617 に答える
0

より良い簡単な方法:

[(UIButton *)[self.searchBar valueForKey:@"_cancelButton"] setEnabled:YES];
于 2017-06-22T05:29:18.147 に答える
0

Swift 5&iOS 14

if let cancelButton : UIButton = self.menuSearchBar.value(forKey: "cancelButton") as? UIButton {
    cancelButton.isEnabled = true
}
于 2021-03-13T05:13:21.890 に答える
0

UIKitの変更に対して少し堅牢であり、名前でプライベートなものを参照しない1つの代替方法は、外観プロキシを使用しtagてキャンセルボタンのを設定することです。つまり、セットアップのどこか:

let cancelButtonAppearance = UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self])
cancelButtonAppearance.isEnabled = true
cancelButtonAppearance.tag = -4321

次に、タグを使用できます。ここでは、-4321タグを見つけるためのマジックナンバーです。

extension UISearchBar {
    var cancelButton: UIControl? {
         func recursivelyFindButton(in subviews: [UIView]) -> UIControl? {
             for subview in subviews.reversed() {
                 if let control = subview as? UIControl, control.tag == -4321 {
                     return control
                 }
                 if let button = recursivelyFindButton(in: subview.subviews) {
                     return button
                 }
             }
             return nil
         }
         return recursivelyFindButton(in: subviews)
     }
}

そして最後searchBar.cancelButton?.isEnabled = trueに、デリゲートなど、検索バーがフォーカスを失ったときに使用します。(または、カスタムサブクラスを使用しsetShowsCancelButtonてデリゲートから呼び出す場合は、その関数をオーバーライドして、ボタンが表示されたときにボタンを有効にすることもできます。)

于 2021-04-09T09:49:14.983 に答える