wake-up-neo.com

Sperren einer UISearchBar an den Anfang einer UITableView wie Game Center

In den UITableViews im Game Center und in den Suchleisten, die sich oben befinden, gibt es diese coole Funktion. Im Gegensatz zu Apps, bei denen die Suchleiste in der Tabellenkopfansicht platziert ist (also als Standardzellenzelle gilt), scheint sie mit der übergeordneten Navigationsleiste zu verschrauben. Wenn Sie die Tabelle scrollen, bewegt sich die Suchleiste zwar tatsächlich, aber wenn Sie über die Tabellengrenzen blättern, berührt die Suchleiste niemals die Navigationsleiste.

Weiß jemand, wie das gemacht wurde? Ich habe mich gefragt, ob Apple möglicherweise die Suchleiste und die Tabelle in einer übergeordneten Bildlaufansicht platziert hat, aber ich frage mich, ob es einfacher ist als das.

27
TiM

Bobs Antwort ist umgekehrt: Sie sollte MIN sein (0, scrollView.contentOffset.y).

Um die Größenänderung korrekt zu unterstützen (die beim Drehen auftreten würde), sollten die anderen Rahmenwerte erneut verwendet werden.

-(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

Sie können die Suchleiste in den Tabellenheader einfügen und die - (void) scrollViewDidScroll: (UIScrollView *) - ScrollView-Delegat-Methode für die tableView implementieren. So etwas zu tun, sollte funktionieren:

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

Wenn Sie den searchDisplayController verwendet haben, können Sie über self.searchDisplayController.searchbar auf die Suchleiste zugreifen.

7
Bob Vork

In Swift 2.1 und 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

Es gibt einen weiteren Schritt, wenn Sie die Suchleisten in Game Center vollständig emulieren möchten.

Wenn Sie mit friedenbergs ausgezeichneter Antwort sowie der in den Kommentaren erwähnten followben - Modifikation für iOS 6+ beginnen, müssen Sie die Funktionalität noch anpassen, wenn die Suchleiste selbst aktiv ist.

In Game Center werden die Suchleisten mit der Tabelle gescrollt, während Sie nach unten scrollen. Sie bleiben jedoch unterhalb der Navigationsleiste fixiert, wenn Sie versuchen, über die Tabellengrenzen hinaus zu blättern. Wenn jedoch die Suchleiste aktiv ist und Suchergebnisse angezeigt werden, scrollt die Suchleiste nicht mehr mit der Tabelle. bleibt unterhalb der Navigationsleiste fixiert.

Hier ist der vollständige Code (für iOS 6+), um dies zu implementieren. (Wenn Sie auf iOS 5 oder eine niedrigere Version zielen, müssen Sie die UISearchBar nicht in eine UIView einbinden.)

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;
}

Voilà! Wenn die Suchleiste jetzt inaktiv ist, wird sie mit der Tabelle verschoben und bleibt fest, wenn Sie versuchen, über die Tabellengrenzen hinaus zu gehen. Wenn es aktiv ist, bleibt es wie in Game Center fixiert.

3
Nick Schneble

Während andere Antworten hilfreich erscheinen und teilweise die Arbeit erledigen, wird das Problem der Suchleiste, die die Berührungen des Benutzers nicht empfängt, nicht gelöst, da sie sich beim Ändern des Rahmens außerhalb der Grenzen der übergeordneten Ansicht befindet.

Schlimmer ist, dass wenn Sie auf die Suchleiste klicken, um ihn zum ersten Antwortdienst zu machen, es sehr wahrscheinlich ist, dass der tableView-Delegat tableView:didSelectRowAtIndexPath: für eine Zelle aufrufen wird, die unter der Suchleiste angeordnet ist.

Um die oben beschriebenen Probleme zu beheben, müssen Sie die Suchleiste in eine einfache UIView einbetten, eine Ansicht, die Berührungen verarbeiten kann, die außerhalb ihrer Grenzen liegen. Auf diese Weise können Sie diese Berührungen an die Suchleiste weiterleiten. 

Also machen wir das zuerst:

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)
  }
}

Im Moment haben wir eine UIView-Unterklasse namens SearchBarView, die Berührungen außerhalb ihrer Grenzen empfangen kann. 

Zweitens sollten wir die Suchleiste in diese neue Ansicht einfügen, während der View-Controller seine Ansicht lädt:

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
  }
}

Zum Schluss sollten wir den Rahmen der Suchleiste aktualisieren, wenn der Benutzer die Tabellenansicht nach unten scrollen soll, damit er oben fixiert bleibt:

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

Hier ist das Ergebnis:

 enter image description here

-

Wichtiger Hinweis: Wenn Ihre Tabellenansicht Abschnitte enthält, spiegeln sie wahrscheinlich Ihre Suchleiste, sodass Sie die Suchleiste jedes Mal nach oben bringen müssen, wenn die bounds der Tabellenansicht aktualisiert wird. 

 enter image description here

viewDidLayoutSubviews ist dafür ein guter Ort:

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

-

Hoffe das hilft. Sie können das Beispielprojekt von hier herunterladen.

1
ozgur

Wenn Ihr Implementierungsziel iOS 9 oder höher ist, können Sie Anker verwenden und UISearchBar und UITableView programmgesteuert festlegen:

    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)
            ])
    }

Ich gehe davon aus, dass Sie UISearchBar und UITableView aus Code erstellen, nicht im Storyboard.

0
Filipp Ignatov

Alle anderen Antworten hier lieferten hilfreiche Informationen, aber keine davon funktionierte mit iOS 7.1. Hier ist eine vereinfachte Version dessen, was für mich funktioniert hat:

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

Hinweis: Wenn Sie in Ihrer Liste "Zum Aktualisieren ziehen" verwenden, müssen Sie scrollView.contentInset.top in scrollViewDidScroll: durch eine Konstante ersetzen, damit die Suchleiste über die Aktualisierungsanimation blättern kann.

0
Brian