ios uitableview uiscrollview uisearchbar game-center

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



uiscrollview game-center (7)

Esta genial función está en UITableViews en Game Center y en las barras de búsqueda que tienen en la parte superior. A diferencia de las aplicaciones en las que la barra de búsqueda se coloca en la vista de encabezado de la tabla (para que cuente como una celda de tabla estándar), en cambio, parece estar unida a la barra de navegación principal situada 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 navegación.

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


Hay un paso más si quieres 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 a medida que se desplaza hacia abajo, pero permanecerán fijadas debajo de la barra de navegación cuando intente desplazarse hacia arriba 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 su lugar debajo de la barra de navegación .

Aquí está el código completo (para iOS 6+) para implementar esto. (Si su objetivo es iOS 5 o inferior, no es necesario que ajuste 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.


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

Además, para admitir correctamente el cambio de tamaño (que ocurriría cuando se gira), los otros valores de cuadro deben reutilizarse.

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


Puede poner la barra de búsqueda en el encabezado de la tabla e implementar el - (void) scrollViewDidScroll: (UIScrollView *) Método delegar scrollView para tableView. Hacer algo como esto debería funcionar:

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

Si utilizó searchDisplayController, accederá a la barra de búsqueda utilizando self.searchDisplayController.searchbar.


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

Lo que es peor es que, al hacer clic en la barra de búsqueda para que sea el primero en responder, es muy probable que el delegado de tableView:didSelectRowAtIndexPath: llame a tableView:didSelectRowAtIndexPath: en la celda que se encuentra 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 sea capaz de procesar toques ocurridos fuera de sus límites. De esta forma, puede transmitir esos toques a la barra de búsqueda.

Así que hagámoslo 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 subclase UIView llamada SearchBarView que es capaz de recibir toques ocurridos fuera de sus límites.

En segundo lugar, debemos poner la barra de búsqueda en esa nueva vista mientras el controlador de vista está cargando 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, debemos actualizar el marco de la barra de búsqueda a medida que el usuario se desplaza hacia abajo en la vista de tabla para que quede fijo en la parte superior:

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

Aquí está el resultado:

-

Nota importante: si su vista de tabla tiene secciones, probablemente sombrearán su barra de búsqueda, por lo que deberá colocar la barra de búsqueda sobre ellas cada vez que se actualicen los bounds la vista de tabla.

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 descargar el proyecto de ejemplo desde here .


Si su destino de despliegue es iOS 9 y superior, entonces puede usar anclajes y configurar UISearchBar y UITableView mediante programació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.


Todas las otras respuestas aquí me proporcionaron información útil, pero ninguna de ellas 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 lista, deberá reemplazar scrollView.contentInset.top en scrollViewDidScroll: con una constante para permitir que la barra de búsqueda se desplace sobre la animación de actualización.


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