web-dev-qa-db-ja.com

UISearchController:検索バーが空の場合でも結果を表示

私が理解しているように、UISearchControllerのデフォルトの動作は次のとおりです。

  1. 検索バーをタップすると、背景が淡色表示され、「キャンセル」ボタンが表示されます。 SearchResultsControllerはこの時点まで表示されません。
  2. SearchResultsControllerは、検索バーが空でない場合にのみ表示されます。

検索バーが空であるが選択されている場合(つまり、上記のケース1)でもSearchResultsControllerを表示したい。

簡単に言えば、背景を暗くする代わりに、検索結果を表示したいと思います。

これを行う方法はありますか?

詳細説明:

私はUISearchControllerを使用して、それが表示されているビューに表示される結果をフィルター処理していませんが、他の無関係な結果をフィルター処理しています。 Facebookが「ニュースフィード」で行うことのようになります。検索バーをタップすると、最初に検索候補が表示され、その後、編集を開始すると、ニュースフィードに関連しない可能性のある検索結果が表示されます。

30
optimus

SearchBarがアクティブであるがテキストがない場合、基になるtableViewの結果が表示されます。それが組み込みの動作であり、その状態に対してsearchResultsControllerが非表示になっている理由です。

検索がアクティブでフィルタリングが行われていない場合の動作を変更するには、通常はまだ非表示になっているときにsearchResultsControllerを表示する必要があります。

<UISearchResultsUpdating>およびupdateSearchResultsForSearchController:を介してこれを達成する良い方法があるかもしれません。プロトコルを介して解決できる場合は、それが推奨される方法です。

それでも解決しない場合は、組み込みの動作をハッキングする必要があります。私はそれを推奨したり、それに頼ったりはしませんし、壊れやすくなりますが、そのオプションを選択した場合の答えは次のとおりです。

  1. TableViewControllerが<UISearchControllerDelegate>に準拠していることを確認し、追加します

    self.searchController.delegate = self;

  2. willPresentSearchController:を実装します

    - (void)willPresentSearchController:(UISearchController *)searchController
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            searchController.searchResultsController.view.hidden = NO;
        });
    }
    

    これにより、searchResultsControllerが非表示に設定された後にUISearchControllerが表示されます。

  3. didPresentSearchController:を実装します

    - (void)didPresentSearchController:(UISearchController *)searchController
    {
        searchController.searchResultsController.view.hidden = NO;
    }
    

組み込みの動作を回避するより良い方法については、 malhal's answerを参照してください

31
user4151918

単にUISearchResultsUpdatingプロトコルを実装し、結果コントローラービューをupdateSearchResultsForSearchControllerに常に表示するように設定できます。

 func updateSearchResultsForSearchController(searchController: UISearchController) {

   // Always show the search result controller
   searchController.searchResultsController?.view.hidden = false

   // Update your search results data and reload data
   ..
}

これは、テキストなしで検索バーがアクティブになっている場合でもメソッドが呼び出されるため機能します。

32
Ken Toh

PetahChristianソリューションを試しましたが、検索バーに最初にフォーカスしたときにプリロード結果が表示されましたが、何かを入力してクリアすると、プリロード結果は再表示されません。

別の解決策を思いつきました。 SearchResultsControllerにデリゲートを追加し、searchController.searchBar.textが空のときに呼び出すだけです。このようなもの:

SearchResultsController:

protocol SearchResultsViewControllerDelegate {
   func reassureShowingList() -> Void
}

class FullSearchResultsViewController: UIViewController, UISearchResultsUpdating{
   var delegate: SearchResultsViewControllerDelegate?
   ...
   func updateSearchResultsForSearchController(searchController: UISearchController) {
    let query = searchController.searchBar.text?.trim()
    if query == nil || query!.isEmpty {
        ...
        self.delegate?.reassureShowingList()
        ...
    }
    ...
}

また、SearchControllerを含むコントローラーにデリゲートを追加します。

self.searchResultsController.delegate = self
func reassureShowingList() {
    searchController.searchResultsController!.view.hidden = false
}
13
mashix

このようなトリッキーなことで、私はスレッジハンマーアプローチをお勧めします!それは、何かがそれを隠そうとするときを検出し、そうするとき、それを元に戻すことです。これは、KVO(Key Value Observing)を介して実行できます。これは、検索バーのすべての複雑さを処理する必要なく、何があっても機能します。申し訳ありませんが、コードは複雑ですが、KVOは古いスタイルのAPIですが、私のコードは推奨プラクティスに従います。 SearchResultsViewControllerにこれを置きます:

static int kHidden;

@implementation SearchResultsViewController

-(void)viewDidLoad{
    [super viewDidLoad];
    [self.view addObserver:self
                   forKeyPath:@"hidden"
                      options:(NSKeyValueObservingOptionNew |
                               NSKeyValueObservingOptionOld)
                      context:&kHidden];
}

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
    // if it was our observation
    if(context == &kHidden){
        // if the view is hidden then make it visible.
        if([[change objectForKey:NSKeyValueChangeNewKey] boolValue]){
            self.view.hidden = NO;
        }
    }
    else{
        // if necessary, pass the method up the subclass hierarchy.
        if([super respondsToSelector:@selector(observeValueForKeyPath:ofObject:change:context:)]){
            [super observeValueForKeyPath:keyPath
                                 ofObject:object
                                   change:change
                                  context:context];
        }
    }
}

- (void)dealloc
{
    [self.view removeObserver:self forKeyPath:@"hidden"];
}

// Here have the rest of your code for the search results table.

@end

これは、テキストがクリアされているかどうかを含め、すべての場合に機能します。

最後に、検索がアクティブになったときにテーブルが灰色に白くフェードアウトしてから白にフェードアウトしないようにするには、これを使用します:

self.searchController.dimsBackgroundDuringPresentation = NO;
11
malhal

Swift 3バージョン:

searchResultControllerがnilでなく、検索結果を表示するために別のTable View Controllerを使用している場合、そのTable View ControllerをUISearchResultUpdatingおよびupdateSearchResults関数に適合させることができます。ビューを再表示するだけです。

func updateSearchResults(for searchController: UISearchController) {
    view.hidden = false
}

Swift 4バージョン:

func updateSearchResults(for searchController: UISearchController) {
    view.isHidden = false
}
7
Yafi

iOS 13用に更新

IOS13から、この動作のシステムAPIサポートを取得しました。プロパティshowsSearchResultsController = trueを設定できます

WWDC screenshot

iOS 12以下の場合

私は最近UISearchControllerに取り組んでいます。検索バーが空のときにsearchResultsControllerに検索履歴を表示したい。したがって、searchResultsControllerは、UISearchControllerが表示されるたびに表示する必要があります。

ここでは、別のソリューションを使用して、カスタムビューでsearchResultsControllerプロパティをオーバーライドするによってhiddenが常に表示されるようにします。

たとえば、私のsearchResultsControllerUITableViewControllerです。 UITableViewのサブクラスとしてVisibleTableViewを作成し、UITableViewsearchResultsControllerカスタムクラスをVisibleTableViewに変更します。 xibまたはストーリーボードで。このようにして、私のsearchResultsControllerUISearchControllerによって隠されることはありません。

ここの良い点:

  1. KVOよりも実装が簡単です。

  2. searchResultsControllerを表示するための遅延はありません。 「updateSearchResults」デリゲートメソッドの非表示フラグを反転することはできますが、searchResultsControllerを表示するのに遅延があります。

  3. 非表示フラグはリセットされないため、非表示と表示の間にUIのギャップ/ジャンプはありません。

Swift 3サンプルコード

class VisibleTableView: UITableView {
override var isHidden: Bool {
    get {
        return false
    }
    set {
        // ignoring any settings
    }
}
}
3
Simon Wang

Swift 2.3 @malhalのアプローチのバージョン:

class SearchResultsViewController : UIViewController {
    var context = 0

    override func viewDidLoad() {
        super.viewDidLoad()
        // Add observer
        view.addObserver(self, forKeyPath: "hidden", options: [ .New, .Old ], context: &context)
    }

    deinit {
        view.removeObserver(self, forKeyPath: "hidden")
    }

    override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        if context == &self.context {
            if change?[NSKeyValueChangeNewKey] as? Bool == true {
                view.hidden = false
            }
        } else {
            super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
        }
    }
}
3
Niels

非表示にされているのは、検索結果コントローラーのビューです。したがって、非表示になっている場合はいつでも再表示するだけで十分です。検索結果コントローラーで次のように行うだけです。

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.view.isHidden = false
}

func updateSearchResults(for searchController: UISearchController) {
    self.view.isHidden = false
    // ... your other code goes here ...
}

これで、検索バーのテキストが空の場合でも、結果ビュー(つまり、テーブルビュー)が常に表示されます。

ところで、iOS Mailアプリはこのように動作し、それが実装されていると仮定します(Appleが秘密のプライベートUISearchController設定にアクセスできる場合を除く)。

[iOS 10およびiOS 11でテスト済み。以前のシステムではテストしませんでした。]

3
matt

Swift 4バージョンの malhals answer

class SearchController: UISearchController {

    private var viewIsHiddenObserver: NSKeyValueObservation?

    override func viewDidLoad() {
        super.viewDidLoad()

        viewIsHiddenObserver = self.searchResultsController?.view.observe(\.hidden, changeHandler: { [weak self] (view, _) in
            guard let searchController = self else {return}
            if view.isHidden && searchController.searchBar.isFirstResponder {
            view.isHidden = false
            }
        })

    }

}

[weak self]。それ以外の場合は、保持サイクルを導入します。

0
Lausbert

私はサイモン・ワンの答えが本当に好きで、それと一緒に働きました。

カスタムクラスでUISearchControllerをサブクラス化します。

class CustomClass: UISearchController {
  override var searchResultsController: UIViewController? {
    get {
      let viewController = super.searchResultsController
      viewController?.view.isHidden = false
      return viewController
    }
    set {
      // nothing
    }
  }
}

また、コードのどこにもこれがないことを確認してください。

self.resultsSearchController.isActive = true

resultsSearchControllerは私のUISearchControllerです

0
Volhan Salai

結果を暗くしたくない場合は、dimsBackgroundDuringPresentationプロパティをfalseに設定します。

これにより、検索中に基になるコンテンツが淡色表示されないようになります。

SearchTextが空の場合でも結果を返すことを確認する必要があります。そうしないと、空のテーブルビューが表示されます。

0

私はこれに多くの時間を費やし、最終的に私が行った解決策は@malhalsのようなものですが、facebookのKVOControllerを使用することでコードの量が大幅に削減されます: https://github.com/facebook/KVOController 。ここでのもう1つの利点は、searchResultsControllerがUINavigationControllerである場合、@ malhalのコードを追加するためだけにサブクラス化する必要がないことです。

// always show searchResultsController, even if text is empty
[self.KVOController observe:self.searchController.searchResultsController.view keyPath:@"hidden" options:NSKeyValueObservingOptionNew block:^(id observer, UIView* view, NSDictionary *change) {
    if ([change[NSKeyValueChangeNewKey] boolValue] == YES) {
        view.hidden = NO;
    }
}];
self.searchController.dimsBackgroundDuringPresentation = NO;
0
skensell

最も簡単な方法は、この拡張機能でReactiveCocoaを使用することです https://github.com/ColinEberhardt/ReactiveTwitterSearch/blob/master/ReactiveTwitterSearch/Util/UIKitExtensions.Swift

        presentViewController(sc, animated: true, completion: {
            sc.searchResultsController?.view.rac_hidden.modify({ value -> Bool in
                return false
            })
        } )

scはUISearchControllerです

0
Roman Barzyczak