Bloquear un UISearchBar en la parte superior de un UITableView como Game Center

Hay una característica interesante en UITableViews en Game Center y las barras de búsqueda que tienen en su parte superior. A diferencia de las aplicaciones en las que la barra de búsqueda se coloca en la vista del encabezado de la tabla (por lo que count como una celda de tabla estándar), en su lugar parece atornillarse a la barra de navigation principal que se encuentra encima de ella. Por lo tanto, al desplazarse por la tabla, la barra de búsqueda realmente se mueve, pero si se desplaza por encima de los límites de la tabla, la barra de búsqueda nunca deja de tocar la barra de navigation.

¿Alguien sabe cómo se pudo haber hecho esto? Me preguntaba si Apple tal vez colocó la barra de búsqueda y la tabla en una vista de desplazamiento principal, pero me pregunto si puede ser más simple que eso.

La respuesta de Bob está invertida: debe ser MIN (0, scrollView.contentOffset.y).

Además, con el fin de soportar adecuadamente el cambio de tamaño (que ocurriría al girar), los otros valores del marco deberían reutilizarse.

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

Podría colocar la barra de búsqueda en el encabezado de la tabla e implementar el método (scroll) de desplazamientoViewDidScroll: (UIScrollView *) delegado para el tableView. Hacer algo así debería funcionar:

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

Si usó el searchDisplayController, accedería a la barra de búsqueda utilizando la barra de búsqueda self.searchDisplayController.search.

En Swift 2.1 e 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 } 

Hay un paso más si desea emular completamente las barras de búsqueda en Game Center.

Si comienza con la excelente respuesta de Friedenberg , así como la modificación de followben para iOS 6+ mencionada en los comentarios, aún necesita ajustar la funcionalidad cuando la barra de búsqueda esté activa.

En Game Center, las barras de búsqueda se desplazarán con la tabla mientras se desplaza hacia abajo, pero se mantendrá fija debajo de la barra de navigation cuando intente desplazarse más allá de los límites de la tabla. Sin embargo, cuando la barra de búsqueda está activa y se muestran los resultados de búsqueda, la barra de búsqueda ya no se desplaza con la tabla; permanece fijo en el lugar debajo de la barra de navigation .

Aquí está el código completo (para iOS 6+) para implementar esto. (Si te orientas a iOS 5 o less, no necesitas ajustar UISearchBar en una 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; } 

Voilà! Ahora, cuando la barra de búsqueda está inactiva, se moverá con la tabla y permanecerá fija cuando intente moverse más allá de los límites de la tabla. Cuando está activo, permanecerá fijo en su lugar, al igual que en Game Center.

Todas las otras respuestas aquí me proporcionaron información útil, pero ninguna funcionó con iOS 7.1. Aquí hay una versión simplificada de lo que funcionó para mí:

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 

Nota: Si está utilizando "desplegable para actualizar" en su list, necesitará replace scrollView.contentInset.top en scrollViewDidScroll: con una constante para permitir que la barra de búsqueda se desplace sobre la animation de actualización.

Si bien otras respuestas parecen útiles y hacen parcialmente el trabajo, no resuelve el problema de la barra de búsqueda que no recibe los toques del usuario porque se mueve fuera de los límites de su vista principal a medida que cambia su marco.

Lo peor es que, onclick en la barra de búsqueda para convertirlo en el primer respondedor, es muy probable que el delegado de tableView:didSelectRowAtIndexPath: llame a tableView:didSelectRowAtIndexPath: en la celda que se muestra debajo de la barra de búsqueda.

Para abordar los problemas descritos anteriormente, debe ajustar la barra de búsqueda en una UIView simple, una vista que es capaz de procesar los toques que se produjeron fuera de sus límites. De esta manera, puede transmitir esos toques a la barra de búsqueda.

Entonces hagamos eso primero:

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

En este momento, tenemos una subclass UIView llamada SearchBarView que es capaz de recibir toques que se produjeron fuera de sus límites.

En segundo lugar, debemos colocar la barra de búsqueda en esa nueva vista mientras el controller de vista carga su vista:

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

Por último, deberíamos actualizar el marco de la barra de búsqueda a medida que el usuario se desplaza hacia abajo en la vista de la tabla para que permanezca fijo en la parte superior:

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

Aquí está el resultado:

introduzca la descripción de la imagen aquí

Nota importante: si la vista de la tabla tiene secciones, es probable que oculte la barra de búsqueda, por lo que debe colocar la barra de búsqueda encima cada vez que se actualicen los bounds la vista de la tabla.

introduzca la descripción de la imagen aquí

viewDidLayoutSubviews es un buen lugar para hacer eso:

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

Espero que esto ayude. Puede download el proyecto de ejemplo desde aquí .

Si su objective de implementación es iOS 9 y superior, entonces puede usar anclajes y establecer UISearchBar y UITableView mediante progtwigción:

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

Supongo que crea UISearchBar y UITableView desde el código, no en el guión gráfico.