web-dev-qa-db-ja.com

UISearchBarはキャンセルボタンの自動無効化を無効にします

UISearchBarをテーブルビューに実装しましたが、1つの小さなことを除いて、ほとんどすべてが機能しています。テキストを入力してからキーボードの検索ボタンを押すと、キーボードが消え、検索結果だけがテーブルに表示されます。テキストはUISearchBarに残りますが、キャンセルボタンは無効になります。

私は自分のリストをApple連絡先アプリの機能にできるだけ近づけようとしていますが、そのアプリで検索を押してもキャンセルボタンは無効になりません。

UISearchBarヘッダーファイルを調べたところ、_searchBarFlags構造体の下にautoDisableCancelButtonのフラグがあることに気付きましたが、これはプライベートです。

UISearchBarをセットアップするときに欠けているものはありますか?

37
rplankenhorn

私は解決策を見つけました。このforループを使用して、検索バーのサブビューをループし、キーボードの検索ボタンが押されたときに有効にすることができます。

for (UIView *possibleButton in searchBar.subviews)
{
    if ([possibleButton isKindOfClass:[UIButton class]])
    {
        UIButton *cancelButton = (UIButton*)possibleButton;
        cancelButton.enabled = YES;
        break;
    }
}
51
rplankenhorn

IOS 7で機能させるには、これを少し調整する必要がありました。

- (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;
            }
        }
    }
}
15
David Douglas

これを簡単に実現するには2つの方法があります

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{
    //  The small and dirty
    [(UIButton*)[searchBar valueForKey:@"_cancelButton"] setEnabled:YES];

    // The long and safe
     UIButton *cancelButton = [searchBar valueForKey:@"_cancelButton"];
    if ([cancelButton respondsToSelector:@selector(setEnabled:)]) {
         cancelButton.enabled = YES;
    }
}

2番目のものを使用する必要があります。Appleがバックグラウンドで変更する場合、アプリケーションはクラッシュしません。

ところで、iOS 4.0から8.2までテストしましたが、変更はありません。また、ストアで承認されたアプリケーションでも問題なく使用しています。

5
Laszlo

これが私にとってiOS6で動作するようになった理由です:

searchBar.showsCancelButton = YES;
searchBar.showsScopeBar = YES;
[searchBar sizeToFit];
[searchBar setShowsCancelButton:YES animated:YES];
3
scurioni

ここでの私の答え に従って、これを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);
    });
}
3
followben

これが私のソリューションです。これは、iOSのすべてのバージョンのすべての状況で機能します。

IE、他のソリューションは、ユーザーがスクロールビューをドラッグしたためにキーボードが閉じられたときに処理しません。

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

// This will handle whenever the text field is resigned non-programatically
// (IE, because it's set to resign when the scroll view is dragged in your storyboard.)
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
    [self performSelector:@selector(enableCancelButton:) withObject:searchBar afterDelay:0.001];
}

// Also follow up every [searchBar resignFirstResponder];
// with [self enableCancelButton:searchBar];
3
ArtOfWarfare

これがSwift 3のソリューションで、拡張機能を利用してキャンセルボタンを簡単に取得します。

extension UISearchBar {
    var cancelButton: UIButton? {
        for subView1 in subviews {
            for subView2 in subView1.subviews {
                if let cancelButton = subView2 as? UIButton {
                    return cancelButton
                }
            }
        }
        return nil
    }
}

今使用のために:

class MyTableViewController : UITableViewController, UISearchBarDelegate {

    var searchBar = UISearchBar()

    func viewDidLoad() {
        super.viewDidLoad()
        searchBar.delegate = self
        tableView.tableHeaderView = searchBar
    }

    func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
        DispatchQueue.main.async {
            if let cancelButton = searchBar.cancelButton {
                cancelButton.isEnabled = true
                cancelButton.isUserInteractionEnabled = true
            }
        }
    }
}
2
Colin Basnett

答えはどれも私にはまったく役に立たなかった。 iOS 7をターゲットにしていますが、答えが見つかりました。

私が試しているのは、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を調べて、そのサブビューを公開しました。常に1つだけあり、タイプはUIView(私の変数searchBarSubview)です。

enter image description here

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

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

2
Matthew Quiros

MonotouchまたはXamariniOSの場合、iOS7およびiOS8で機能する次のC#ソリューションがあります。

foreach(UIView view in searchBar.Subviews)
{
    foreach(var subview in view.Subviews)
    {
        //Console.WriteLine(subview.GetType());
        if(subview.GetType() == typeof(UIButton))
        {
            if(subview.RespondsToSelector(new Selector("setEnabled:")))
            {
                UIButton cancelButton = (UIButton)subview;
                cancelButton.Enabled = true;
                Console.WriteLine("enabledCancelButton");
                return;
            }
        }
    }
}

この答えは David Douglas ソリューションに基づいています。

1
testing

より完全な答え:

  • iOS 7以降、searchBarの下に追加レベルのサブビューがあります
  • キャンセルボタンを有効にするのに適した場所はsearchBarTextDidEndEditingです。

extension MyController: UISearchBarDelegate {
  public func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
    DispatchQueue.main.async {
    // you need that since the disabling will
    // happen after searchBarTextDidEndEditing is called
      searchBar.subviews.forEach({ view in
        view.subviews.forEach({ subview in
          // ios 7+
          if let cancelButton = subview as? UIButton {
            cancelButton.isEnabled = true
            cancelButton.isUserInteractionEnabled = true
            return
          }
        })
        // ios 7-
        if let cancelButton = subview as? UIButton {
          cancelButton.isEnabled = true
          cancelButton.isUserInteractionEnabled = true
          return
        }
      })
    }
  }
}
1
Guig