iPhone:デフォルトでUITableView検索バーを非表示


85

Interface Builderを使用してテーブルビューを作成し、それにライブラリの検索バーと検索表示コントローラーを追加して検索機能を追加しました。ただし、IBは、ビューが最初に表示されたときに画面の上部にバーが表示されるように設定しました。

検索バーをデフォルトで非表示にし、テーブルビューでスクロールできるようにする方法を知りたいです(例については、Appleのメールアプリケーションを参照してください)。私は、呼び出し試したscrollRectToVisible:animated:中でviewDidLoadテーブルビューをスクロールダウンするが、無駄に。デフォルトで検索バーを非表示にするための好ましい方法は何ですか?

回答:


116

まず、UISearchBarをUITableViewのtableHeaderViewに追加して、テーブルのコンテンツとともにスクロールされ、ビューの上部に固定されないようにします。

検索バーはテーブルビューの行としてカウントされないため、テーブルビューの上部を最初の行までスクロールすると、検索バーが「非表示」になります。

[yourTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];

またはSwiftで:

yourTableView.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0), atScrollPosition: UITableViewScrollPosition.Top, animated: false)

データが含まれる前にテーブルビューをスクロールしないようにしてください(scrollToRowAtIndexPath指定されたindexPathが有効な行を指していない場合(つまり、テーブルビューが空の場合)は例外が発生します)。


12
実際、画面を埋めるのに十分な行がない場合、テーブルビューがスクロールしないという問題が発生しています。1行または2行のテーブルでは、このコードは何もしません。それは予想される動作ですか?どうにかして回避できますか?
ティム

18
方法を見つけました。インデックスパスにスクロールする代わりに、テーブルのスクロールビューのコンテンツオフセットを0.0、44.0のCGRectに変更します
Tim

107
ティム、それが進むべき道です!self.tableView.contentOffset = CGPointMake(0、self.searchDisplayController.searchBar.frame.size.height);を使用しました。
Joe D'Andrea

1
ところで-[tableViewreloadData]を呼び出した後にこのコードを入力すると、テーブルが2行から通常のスクロールを可能にするのに十分な行に移動したときに、contentOffsetメソッドを使用すると検索の表示と非表示がぎくしゃくすることがわかりましたscrollToRowAtIndexPathメソッドを使用しているときのボックスには、同じジャーキーがありませんでした。
クリスR

1
これは、どのような状況でも機能する唯一の究極の方法です。contentOffset複雑なビューコントローラレイアウトを使用している場合、任意の値で設定すると機能しなくなります。私はそれをお勧めしません。
eonil 2014年

45

UISearchBarをUITableViewのサブビューとして追加しないでください。これは必要ありません。

UITableViewには、これに最適なtableHeaderViewプロパティがあります。

- (void) viewDidLoad {
    [super viewDidLoad]; 
    self.searchBar = [[[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)] autorelease];
    self.searchBar.showsCancelButton = YES;    
    self.searchBar.delegate = self;
    self.tableView.tableHeaderView = self.searchBar;     
}

デフォルトで表示したくない場合:

- (void) viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.tableView setContentOffset:CGPointMake(0, 44)];
}

また、キャンセルボタンを表示して、もう一度非表示にします。

- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
    [self.tableView setContentOffset:CGPointMake(0, 44) animated:YES];
    [self.searchBar resignFirstResponder];
}

私にとって、これは、HIGと一致しているように感じられる非表示のUISearchBar実装を生成した唯一のソリューションでした。テーブルビューの上部に「スタック」している他のソリューションは、不自然に感じます。
kurtzmarc 2012

1
このソリューションが最も効果的でした。これが私の意見の答えになるはずです。
darwindeeds 2013

この解決策は素晴らしい(+1)です。ただし、tableViewが検索バーを適切に非表示にしない場合があります(私の場合:tableviewコントローラーが別のViewコントローラーからプッシュバックされたとき)。この問題についての質問があります:stackoverflow.com/questions/15222186/…しかし、この質問の提供された答えはあまり良くないと思います。@bandejapaisa、何かアイデアはありますか?
ブライアン

3
これは私にとってはうまくいきましたが、全体を通して44をハードコーディングする代わりに、viewWillAppearでself.tableView.tableHeaderView.frame.size.heightを使用して、Appleが将来検索バーの高さに変更できるようにすることをお勧めします。
ジョンスティーブン

これを入れるviewWillAppearことは、ビューに戻るたびに実行されるため、お勧めできません。これは、テーブルビューがゆっくりと縮小することを意味します。イライラするバグ!
サイレン

25

contentOffsetは、TimとJoe D'Andreaのコメントに記載されていますが、検索バーを非表示にするアニメーションを追加することで、少し拡張されています。検索バーが消えるのは少し意外なことに気づきました。

iOS 4.0以降をターゲットにしたい人のためのちょっとしたアップデートは、これを試してみてください:

[UIView animateWithDuration:0.4 animations:^{
    self.tableView.contentOffset = CGPointMake(0, self.searchDisplayController.searchBar.frame.size.height);
 } completion:nil];

前の答え:

[UIView beginAnimations:@"hidesearchbar" context:nil];
[UIView setAnimationDuration:0.4];
[UIView setAnimationBeginsFromCurrentState:YES];

self.tableView.contentOffset = CGPointMake(0, self.searchDisplayController.searchBar.frame.size.height);

[UIView commitAnimations];


9
アニメーションがこのソリューションの鍵であることがわかりました。アニメーションなしでコンテンツオフセットを設定しようとしても、何もしないように見えます。ただし、このサンプルではアニメーションメソッド(またはブロック)を使用するのではなく、[self.tableView setContentOffset:CGPointMake(0,44)animated:TRUE]を呼び出すだけで十分に機能することがわかりました。
クイックタイム2011年

アニメーションなしでは全然動かないようです。@quickthymeも同じ観察をしました。
Thilo 2011

5
viewDidAppear:代わりにを実装すれば、アニメーションを必要とせずにこれで成功しましたviewDidLoad
wbyoung 2011年

8

この解決策には多くの答えがありますが、私にとってはどれもうまくいきませんでした。

これは完全に機能します。アニメーションはなく、-viewDidLoad

 - (void)viewDidLoad {
     [super viewDidLoad];
     self.tableView.contentOffset = CGPointMake(0,  self.searchBar.frame.size.height - self.tableView.contentOffset.y);
 }

注意:

コードはsearchBar @property、検索バーにリンクされているを持っていることを前提としています。そうでない場合は、self.searchDisplayController.searchBar代わりに使用できます


これは私のために働いた唯一のものです!、しかし、それはviewdidloadを解除するだけです...そのため、最初は非表示になりますが、アプリの実行中は非表示になりません。そして、私はそれをviewwillappearとviewdidappearに入れてみましたが、うまくいきません。ありがとう!
fguespe 2014

@fguespeそれは奇妙です..viewDidLoadは、ビューがインスタンス化されるたびに呼び出される必要があります。別のビューから戻った後、UISearchBarが表示されることは私には思いもよらなかった。
Matej 2014

タブ付きアプリケーションを使用しています。多分それ?
fguespe 2014

@fguespeそれが問題になる可能性があると思います。残念ながら、それ-viewWillAppearが機能しない場合、どのように解決するのかわかりません。タブコントローラーがビューに切り替わったときに呼び出されますか?
Matej 2014

viewwillappearとviewdidappearが呼び出されますが、両方を試してみましたが、ビューが間違ってオフセットされているようです。期待した結果は返されません。つまり、何かがしますが、それは悪いことです。
fguespe 2014

7

Swift3 +の場合

これは私がしました:

これを宣言しますvar

    var searchController = UISearchController()

そしてそのviewDidLoad()方法で

    searchController = UISearchController(searchResultsController: nil)
    searchController.searchResultsUpdater = self
    searchController.hidesNavigationBarDuringPresentation = false
    searchController.dimsBackgroundDuringPresentation = true
    searchController.searchBar.placeholder = NSLocalizedString("Search", comment: "")
    definesPresentationContext = true
    tableView.tableHeaderView = searchController.searchBar

    tableView.contentOffset = CGPoint(x: 0, y: searchController.searchBar.frame.size.height)

5

私の目標は、View Controllerがロードされたときに、ストーリーボードでプレーンなTableViewのスタイルのtableHeaderViewに追加された検索バーを非表示にし、ユーザーがUITableViewの上部で下にスクロールしたときにバーを表示することでした。だから、私はSwiftを使用していて、拡張機能を追加しました:

extension UITableView {
    func hideSearchBar() {
        if let bar = self.tableHeaderView as? UISearchBar {
            let height = CGRectGetHeight(bar.frame)
            let offset = self.contentOffset.y
            if offset < height {
                self.contentOffset = CGPointMake(0, height)
            }
        }
    }
}

のviewDidLoad()だけを呼び出します:

self.tableView.hideSearchBar()

5

この状況ではiOSがインセットを自動的に調整するため、tableViewを満たすのに十分な行がない場合、iOS8では既存のすべてのソリューションが機能しません。(ただし、十分な行がある場合は、既存の回答が適切です)

この問題で6時間ほど無駄にした後、私はついにこの解決策を手に入れました。

つまり、十分なセルがない場合は空のセルをtableViewに挿入する必要があるため、tableViewのコンテンツサイズは十分に大きいため、iOSはインセットを調整しません。

これが私がSwiftでそれをした方法です:

1.)変数minimumCellNumをクラスプロパティとして宣言する

var minimumCellNum: Int?

2.)計算minimumCellNumして設定tableView.contentOffsetするviewWillAppear

let screenHeight = Int(UIScreen.mainScreen().bounds.height)

// 101 = Height of Status Bar(20) + Height of Navigation Bar(44) + Height of Tab Bar(49)
// you may need to subtract the height of other custom views from the screenHeight. For example, the height of your section headers.
self.minimumCellNum = (screenHeight - 103 - heightOfOtherCustomView) / heightOfYourCell
self.tableView.contentOffset = CGPointMake(0, 44)

3.)で tableView(tableView: UITableView, numberOfRowsInSection section: Int))

let numOfYourRows = YOUR LOGIC
if numOfYourRows > minimumCellNum {
    return numOfYourRows
} else {
    return minimumCellNum!
}

4.)ストーリーボードとに、selection属性がNoneである空のセルを登録します。tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)

if indexPath.row < numOfYourRows {
    return YOUR CUSTOM CELL
} else {
    let cell = tableView.dequeueReusableCellWithIdentifier("EmptyCell", forIndexPath: indexPath) as! UITableViewCell
    return cell
}

5.)で tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)

if tableView == self.tableView {
    if numOfYourRows < (indexPath.row + 1) {
        return
    }
    YOUR LOGIC OF SELECTING A CELL
}

これは完璧な解決策ではありませんが、iOS8で実際に機能する唯一の回避策です。より適切な解決策があるかどうか知りたいのですが。


iOS 11でこれを行う必要がありました。contentOffsetの設定だけで、0個以上のセルのiOS 9/10で機能し、8個以上のセルのiOS11でも機能しました。このソリューションは、それほど必要ではありません。理由はありますか?
トニー

この投稿の最初の段落でその理由を説明していると思います。
ブライアン

最初の段落は理解しましたが、それでもiOS11で「壊れた」だけだったのは不思議でした。セルの数が同じ(十分ではない)の場合、iOS 9、10、および11でも同様の動作が予想されます。もう一度ありがとう、古い投稿を持ってきてすみません!
トニー

4

上記のどれも私にはうまくいきませんでしたので、誰かが同じ問題を抱えている場合に備えて。

iOS7のグループ化searchBartableHeaderViewれたテーブルのように設定しました。でviewDidLoad私が持っているtableView.contentOffset = CGPointMake(0, 44);保つためにsearchBar、最初は「隠されました」。ユーザーが下にスクロールするsearchBarと表示されます

目標:キャンセルボタンタップでsearchBarを非表示にする

searchBarCancelButtonClicked(searchBar: UISearchBar!)

これは機能しませんでした: [yourTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];。tableViewが上にスクロールしすぎたため、行0がtoolBarの後ろに移動していました。

これは機能しませんでした: tableView.ContentOffset = CGPointMake(0, 44) -何も起こりません

これは機能しませんでした: tableView.ContentOffset = CGPointMake(0, searchBar.frame.size.height) -何も起こりません

これは機能しませんでした: tableView.setContentOffset(CGPointMake(0, 44), animated: true); ここでも、tableViewが上にスクロールしすぎて、行0がtoolBarの後ろに移動していました。

これは部分的に機能しました: tableView.setContentOffset(CGPointMake(0, 0)しかしtitleForHeaderInSectiontoolBarの後ろに行きました

これはうまくいきました: tableView.setContentOffset(CGPointMake(0, -20)

論理的ではないようですが、-20だけが正しい位置に移動しました


ポイントをハードコーディングするべきではありません。なぜ使用しないのですCGRectGetHeight(searchBar.bounds)か?
Zorayr 2015年

検索バーの高さを取得するためのフレームが設定されていないviewDidLoad()/ init()で実行する必要があると思います。viewDidLayoutSubviews()を試してみてください
Kaushil Ruparelia 2016年

tableViewの読み込みが完了していない場合、どのContentOffsetソリューションも機能しません
Maor

4

コンテンツオフセットは進むべき道ですが、100%は機能しません。

estimatedRowHeight固定数に設定している場合は動作しません。回避策はまだわかりません。私が作成したプロジェクトの問題を示す(複数可)と私が作成したレーダーAppleとします。

すべてを設定する方法の迅速な例が必要な場合は、プロジェクトをチェックしてください。


1
ありがとうございました!これが私が探していたものでした。あなたはずっと上に行く必要があります。
user2387149 2016

3

テーブルビューにデータがない場合、受け入れられた回答はクラッシュすることに注意してください。また、viewDidLoadに追加すると、特定の状況では機能しない場合があります。

[yourTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO]; // crashes if no rows/data

したがって、代わりに次のコード行を追加します。

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

    [yourTableView setContentOffset:CGPointMake(0, self.searchDisplayController.searchBar.frame.size.height)];
}

2

正直なところ、(私にとって)実際に問題を解決した答えはありませんでした。テーブルビューに行がない場合、または行の高さが異なる場合、これらの回答では不十分です。

動作する唯一のもの:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    self.tableView.contentOffset = (CGPoint){.y =  self.tableView.contentOffset.y + self.searchController.searchBar.frame.size.height};
}

オフセット問題の良い解決策。UINavigationControllerを使用するときに私のために働きます。
アンドレアスクラフト

1
行数が画面全体をカバーしていない場合、ソリューションは機能しません-これの回避策を知っていますか?
Zorayr 2015年

これを完全に理解しているかどうかはわかりません。これは、テーブルに数行しかない場合に機能します。しかし、私は以下を使用します:self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
p0lAris 2015

2

scrollToRowAtIndexPathテーブルに行がゼロの場合、このメソッドはクラッシュを引き起こします。

UITableViewは単なるスクロールビューであるため、コンテンツのオフセットを変更するだけです。

これは、tableHeaderViewとして何かがあることを確認し、スクロールして非表示にすると仮定します

    if let searchHeight = tableView.tableHeaderView?.frame.height {
        tableView.setContentOffset(CGPoint.init(x: 0, y:searchHeight ), animated: false)
    }

1

tableViewが空白の場合、「Notes」アプリはアイテムを検索できないことがわかりました。したがって-(void)addSubview:(UIView *)view、最初は空白のテーブルを追加するために使用しているだけだと思います。データソース配列にアイテムを追加すると、別のコードが実行されてtableViewが読み込まれます。

だから私の解決策は:

if([self.arrayList count] > 0)
{
     [self.tableView reloadData];     //use jdandrea's code to scroll view when cell==nil in cellForRowAtIndexPath
     self.tableView.scrollEnabled = YES;
}
else
{
     tableBlank = [[UITableView alloc]initWithFrame:CGRectMake(0,0,320,416) style:UITableViewStylePlain] autorelease];
     tableBlank.delegate = self;
     tableBlank.dataSource = self;
     [self.view addSubview:tableBlank];
}

お役に立てれば :-)


1

tableViewの境界を変更します。これは内部で行うことができviewDidLoad、テーブルが空であるかどうかを心配する必要はありません(例外を回避するため)。

CGRect newBounds = self.tableView.bounds;
newBounds.origin.y = newBounds.origin.y + self.searchBar.bounds.size.height;
self.tableView.bounds = newBounds;

ユーザーがテーブルを下にスクロールすると、検索バーが表示され、正常に動作します


1

この質問に対する他の回答は、私にはまったく機能しなかったか、tableViewの行が0のときに奇妙な動作をしたか、親とネストされたラインアイテム間を行ったり来たりするときにスクロール位置を台無しにしました。これは私にとってはうまくいきました(目標はロード時にUISearchBarを非表示にすることです):

public override func viewWillAppear(animated: Bool) {        
    if self.tableView.contentOffset.y == 0 {
        self.tableView.contentOffset = CGPoint(x: 0.0, y: self.searchBar.frame.size.height) 
    }
}

説明
いつでもはtableViewUISearchBarが(つまり表示されているかどうかを確認するために、このコードをチェックし、表示されるyの位置contentOffset、別名スクロール位置を、です0)。そうである場合は、searchBarの高さを下にスクロールするだけです。searchBarが表示されていない場合(のアイテムのリストが大きいと考えてtableViewください)、tableViewネストされたラインアイテムから戻るときに位置が混乱するため、をスクロールしようとはしません。

補足
このコードを別のメソッドに配置して、単一責任を確保し、コード内でいつでも呼び出すことができるようにすることをお勧めします。


1

テーブルに常に少なくとも1つの行がある場合は、テーブルの最初の行までスクロールするだけで、検索バーが自動的に非表示になります。

let firstIndexPath = NSIndexPath(forRow: 0, inSection: 0)

self.tableView.selectRowAtIndexPath(firstIndexPath、animated:false、scrollPosition:.Top)

上記のコードをviewDidLoadに配置すると、tableViewがまだロードされていないためエラーがスローされます。この時点で、tableViewはすでにロードされているため、viewDidAppearに配置する必要があります。

あなたがそれをviewDidAppearに置くならばに、tableViewを開くたびに一番上にスクロールします。

UITabBar View Controllerの場合や、セグエを実行してから戻ってきた場合など、tableViewが開いたままの場合は、この動作を望まないかもしれません。初期ロードで一番上にスクロールするだけの場合は、変数を作成して、それが初期ロードであるかどうかを確認し、一度だけ一番上にスクロールするようにすることができます。

最初に、ビューコントローラクラスでisInitialLoadという変数を定義し、それを「true」に設定します。

var isInitialLoad = true

次に、viewDidAppearでisInitialLoadがtrueであるかどうかを確認し、trueである場合は、一番上までスクロールして、isInitialLoad変数をfalseに設定します。

if isInitialLoad {
            let firstIndexPath = NSIndexPath(forRow: 0, inSection: 0)
            self.tableView.selectRowAtIndexPath(firstIndexPath, animated: false, scrollPosition: .Top)

            isInitialLoad = false
        }

1

以下のコードは私のために働いています

self.tableView.contentOffset = CGPointMake(0.0, 44.0);

1

コンテンツオフセットとscrollToIndexpathを設定しようとしましたが、何も問題なく機能しませんでした。viewDidLoadからsearchBarとしてのtableHeaderViewの設定を削除し、以下のようにスクロールビューデリゲートに設定しました

override func scrollViewWillBeginDragging(scrollView: UIScrollView) {
    if scrollView.contentOffset.y < 0 && tableView.tableHeaderView == nil {
        tableView.tableHeaderView = searchController.searchBar
        tableView.setContentOffset(CGPointMake(0, scrollView.contentOffset.y + searchController.searchBar.frame.size.height), animated: false)
    }
}

これは魅力のように機能しました。コンテンツオフセット設定はスムーズなスクロールを目的としています。


0

ステータスバーナビゲーションバーを考慮することを忘れないでください。私のテーブルビューコントローラーは、ナビゲーションコントローラーに埋め込まれていviewDidAppearます。

tableView.contentOffset = CGPointMake(0, -20) // -20 = 44 (search bar height) - 20 (status bar height) - 44 (navigation bar height)

私のために働いた。


0

Swiftの場合:

テーブルビューのコンテンツをyオフセットにするだけです。これは、検索バーの高さのようになります。

self.tableView.contentOffset = CGPointMake(0, self.searchController.searchBar.frame.size.height)

0

スウィフト3

空のテーブルでも動作します!

私の環境では、xibファイルでカスタムセルをロードすると、rowHeightが自動的に設定されます。

    override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        self.tableView.contentOffset = CGPoint(x: 0, y: self.tableView.contentOffset.y + self.searchController.searchBar.bounds.height)
}

0

私も同様の問題を抱えていました。そして、この問題を解決するために私が見つけた唯一の方法は、UITableViewcontentOffsetプロパティでキー値オブザーバーを使用することでした。

いくつかのデバッグの後、テーブルビューのコンテンツサイズがテーブルビューよりも小さい場合に問題が発生することに気付きました。viewWillAppearでKVOを起動します。そして、ビューが表示されてから0.3秒後にディスパッチ停止KVO。contentOffsetが0.0になるたびに、KVOは反応し、検索バーの高さにcontentOffsetを設定します。ビューが表示された後でもビューレイアウトがcontentOffsetをリセットするため、0.3秒後にKVO非同期を停止します。

- (void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];
  [self.tableView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
}

-(void)viewDidAppear:(BOOL)animated{
   __weak typeof (self) weakSelf = self;
   dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [weakSelf.tableView removeObserver:self forKeyPath:@"contentOffset"];});
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    if ([keyPath isEqualToString:@"contentOffset"] && self.tableView.contentOffset.y == 0.0) {
       self.tableView.contentOffset = CGPointMake(0, CGRectGetHeight(self.tableView.tableHeaderView.frame));
    }
}

-(void)dealloc {
    [self.tableView removeObserver:self forKeyPath:@"contentOffset"];
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.