web-dev-qa-db-ja.com

GameCenterのようなUITableViewの上部にUISearchBarをロックする

Game CenterのUITableViewsとこの上部にある検索バーには、この素晴らしい機能があります。検索バーがテーブルヘッダービューに配置されるアプリ(標準のテーブルセルとしてカウントされる)とは異なり、代わりに、その上の親ナビゲーションバーにボルトで固定されているようです。したがって、テーブルをスクロールすると、検索バーは実際に移動しますが、テーブルの境界を超えてスクロールした場合、検索バーはナビゲーションバーへの接触を停止しません。

これがどのように行われたかを誰かが知っていますか? Apple多分検索バーとテーブルの両方を親スクロールビューに配置するのではないかと思っていましたが、それよりも簡単なのではないかと思っています。

27
TiM

ボブの答えは逆になります:MIN(0、scrollView.contentOffset.y)である必要があります。

また、サイズ変更(回転時に発生する)を適切にサポートするために、他のフレーム値を再利用する必要があります。

-(void)scrollViewDidScroll:(UIScrollView *)scrollView 
{
    UISearchBar *searchBar = searchDisplayController.searchBar;
    CGRect rect = searchBar.frame;
    rect.Origin.y = MIN(0, scrollView.contentOffset.y);
    searchBar.frame = rect;
}
28
friedenberg

SearchBarをテーブルヘッダーに配置し、-(void)scrollViewDidScroll:(UIScrollView *)scrollViewデリゲートメソッドをtableViewに実装できます。このようなことをすることはうまくいくはずです:

-(void) scrollViewDidScroll:(UIScrollView *)scrollView {
    searchBar.frame = CGRectMake(0,MAX(0,scrollView.contentOffset.y),320,44);
} 

SearchDisplayControllerを使用した場合は、self.searchDisplayController.searchbarを使用して検索バーにアクセスします。

7
Bob Vork

In Swift 2.1およびiOS 9.2.1

    let searchController = UISearchController(searchResultsController: nil)

        override func viewDidLoad() {
        /* Search controller parameters */
            searchController.searchResultsUpdater = self  // This protocol allows your class to be informed as text changes within the UISearchBar.
            searchController.dimsBackgroundDuringPresentation = false  // In this instance,using current view to show the results, so do not want to dim current view.
            definesPresentationContext = true   // ensure that the search bar does not remain on the screen if the user navigates to another view controller while the UISearchController is active.

            let tableHeaderView: UIView = UIView.init(frame: searchController.searchBar.frame)
            tableHeaderView.addSubview(searchController.searchBar)
            self.tableView.tableHeaderView = tableHeaderView

        }
        override func scrollViewDidScroll(scrollView: UIScrollView) {
           let searchBar:UISearchBar = searchController.searchBar
           var searchBarFrame:CGRect = searchBar.frame
           if searchController.active {
              searchBarFrame.Origin.y = 10
           }
           else {
             searchBarFrame.Origin.y = max(0, scrollView.contentOffset.y + scrollView.contentInset.top)

           }
           searchController.searchBar.frame = searchBarFrame
       }
5
Muhammad Qasim

Game Centerの検索バーを完全にエミュレートする場合は、もう1つ手順があります。

friedenberg's優れた回答、およびコメントに記載されているiOS 6+のfollowben'sの変更から始める場合、検索バー自体がアクティブなときに機能を調整する必要があります。

Game Centerでは、下にスクロールすると検索バーがテーブルと共にスクロールしますが、テーブルの境界を超えて上にスクロールしようとすると、ナビゲーションバーの下に固定されたままになります。ただし、検索バーがアクティブで検索結果が表示されている場合、検索バーはテーブルと共にスクロールしなくなります。 ナビゲーションバーの下に固定されたままです

これを実装するための完全なコード(iOS 6以降用)を以下に示します。 (iOS 5以下を対象としている場合、UISearchBarをUIViewでラップする必要はありません)

CustomTableViewController.m

- (void)viewDidLoad
{
    UISearchBar *searchBar = [[UISearchBar alloc] init];
    ...
    UIView *tableHeaderView = [[UIView alloc] initWithFrame:searchBar.frame];
    [tableHeaderView addSubview:searchBar];

    [self.tableView setTableHeaderView:tableHeaderView];
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    UISearchBar *searchBar = self.tableView.tableHeaderView.subviews.lastObject;
    CGRect searchBarFrame = searchBar.frame;

    /*
     * In your UISearchBarDelegate implementation, set a boolean flag when
     * searchBarTextDidBeginEditing (true) and searchBarTextDidEndEditing (false)
     * are called.
     */

    if (self.inSearchMode)
    {
        searchBarFrame.Origin.y = scrollView.contentOffset.y;
    }
    else
    {
        searchBarFrame.Origin.y = MIN(0, scrollView.contentOffset.y);
    }

    searchBar.frame = searchBarFrame;
}

- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
    self.inSearchMode = YES;
}

- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
    self.inSearchMode = NO;
}

ほら!これで、検索バーが非アクティブの場合、テーブルとともに移動し、テーブルの境界を超えて移動しようとしても修正されたままになります。アクティブな場合、Game Centerと同様に、固定されたままになります。

3
Nick Schneble

他の回答は役に立ち、部分的には機能しますが、フレームを変更すると検索バーが親ビューの境界の外に移動するため、ユーザーのタッチを受け取らないという問題は解決しません。

さらに悪いことに、検索バーをクリックして最初のレスポンダーにすると、tableViewデリゲートが検索バーの下に配置されたセルでtableView:didSelectRowAtIndexPath:を呼び出す可能性が高くなります。

上記の問題に対処するには、検索バーをプレーンなUIViewでラップする必要があります。これは、境界の外側で発生したタッチを処理できるビューです。このようにして、それらのタッチを検索バーに中継できます。

最初にそれを実行しましょう:

class SearchBarView: UIView {
  override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
    for subview in subviews {
      if !subview.userInteractionEnabled { continue }

      let newPoint = subview.convertPoint(point, fromView: self)
      if CGRectContainsPoint(subview.bounds, newPoint) {
        return subview.hitTest(newPoint, withEvent: event)
      }
    }
    return super.hitTest(point, withEvent: event)
  }
}

現在、SearchBarViewという名前のUIViewサブクラスがあり、その境界外で発生したタッチを受け取ることができます。

次に、ビューコントローラがビューを読み込んでいる間に、検索バーをその新しいビューに配置する必要があります。

class TableViewController: UITableViewController {
  private let searchBar = UISearchBar(frame: CGRectZero)
  ...

  override func viewDidLoad() {
    super.viewDidLoad()
    ...
    searchBar.sizeToFit()
    let searchBarView = SearchBarView(frame: searchBar.bounds)
    searchBarView.addSubview(searchBar)

    tableView.tableHeaderView = searchBarView
  }
}

最後に、ユーザーがテーブルビューを下にスクロールするときに検索バーのフレームを更新して、上部に固定されたままにする必要があります。

override func scrollViewDidScroll(scrollView: UIScrollView) {
  searchBar.frame.Origin.y = max(0, scrollView.contentOffset.y)
}

結果は次のとおりです。

enter image description here

-

重要な注意:テーブルビューにセクションがある場合、セクションはおそらく検索バーをシャドウするため、テーブルビューのboundsが更新されるたびに検索バーをその上に置く必要があります。

enter image description here

viewDidLayoutSubviewsはそのための良い場所です:

override func viewDidLayoutSubviews() {
  super.viewDidLayoutSubviews()
  ...
  if let tableHeaderView = tableView.tableHeaderView {
    tableView.bringSubviewToFront(tableHeaderView)
  }
}

-

お役に立てれば。サンプルプロジェクトは here からダウンロードできます。

1
ozgur

展開ターゲットがiOS 9以降の場合は、アンカーを使用して、UISearchBarとUITableViewをプログラムで設定できます。

    private let tableView = UITableView(frame: .zero, style: .plain)
    private let searchBar = UISearchBar(frame: CGRect .zero)

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.delegate = self
        tableView.dataSource = self
        tableView.contentInset = UIEdgeInsets(top: 44.0, left: 0.0, bottom: 0.0, right: 0.0)

        searchBar.delegate = self
        view.addSubview(tableView)
        view.addSubview(searchBar)
        NSLayoutConstraint.activate([
            searchBar.heightAnchor.constraint(equalToConstant: 44.0),
            searchBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            searchBar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            searchBar.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor)
            ])
    }

ストーリーボードではなく、コードからUISearchBarとUITableViewを作成するとします。

0
Filipp Ignatov

ここにある他のすべての回答から役立つ情報が得られましたが、iOS 7.1を使用して動作するものはありませんでした。これは私のために働いたものの簡単なバージョンです:

MyViewController.h:

@interface MyViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, UISearchDisplayDelegate> {
}
@end

MyViewController.m:

@implementation MyViewController {

    UITableView *tableView;
    UISearchDisplayController *searchDisplayController;
    BOOL isSearching;
}

-(void)viewDidLoad {
    [super viewDidLoad];

    UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
    searchBar.delegate = self;

    searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
    searchDisplayController.delegate = self;
    searchDisplayController.searchResultsDataSource = self;
    searchDisplayController.searchResultsDelegate = self;

    UIView *tableHeaderView = [[UIView alloc] initWithFrame:searchDisplayController.searchBar.frame];
    [tableHeaderView addSubview:searchDisplayController.searchBar];
    [tableView setTableHeaderView:tableHeaderView];

    isSearching = NO;
}

-(void)scrollViewDidScroll:(UIScrollView *)scrollView {

    UISearchBar *searchBar = searchDisplayController.searchBar;
    CGRect searchBarFrame = searchBar.frame;

    if (isSearching) {
        searchBarFrame.Origin.y = 0;
    } else {
        searchBarFrame.Origin.y = MAX(0, scrollView.contentOffset.y + scrollView.contentInset.top);
    }

    searchDisplayController.searchBar.frame = searchBarFrame;
}

- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
    isSearching = YES;
}

-(void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
    isSearching = NO;
}

@end

注:リストで「プルダウンして更新」を使用している場合は、scrollView.contentInset.topscrollViewDidScroll:を定数に置き換えて、検索バーで更新アニメーションをスクロールできるようにする必要があります。

0
Brian