Home » Guías » Lecciones por prototipos (III): barras de búsqueda (UISearchBar)
UISearchBar

Lecciones por prototipos (III): barras de búsqueda (UISearchBar)

En nuestra primera lección por prototipos, que podéis leer pulsando aquí, vimos cómo era una vista de tabla (o Table View). Uno de los tipos de interfaz más comunes en iOS. Supimos qué parámetros la definen, cómo responder a sus eventos y cómo enviarle los datos necesarios para que se visualicen. Y todo ello, usando un Playground con lo que podemos usar tanto Xcode como la app Swift Playgrounds de iOS para probar dicha lección.

En esta ocasión vamos a ampliar un poco más la funcionalidad, y ver cómo podemos implementar una barra de búsqueda en una vista de tabla. Para ello vamos a usar otro ejemplo similar al usado la otra vez, aunque más simple.

Una cosa es la barra, otra la búsqueda

Lo primero que tenemos que entender, es que una barra de búsqueda no tiene funcionalidad implementada. No es más que un campo con una propiedad .text a la que podemos acceder mediante una delegación cada vez que el usuario escriba un carácter en la misma o detectar (y actuar) cuando se interactúa con ella.

Vamos a crear nuestra tabla para ver a qué nos referimos:

Usando este código en nuestro playground (en Mac o iOS) tendremos una tabla con una serie de podcast de tecnología que actualmente existen en iTunes. Nada más. Si queremos poner una barra de búsqueda y añadirla, tenemos que usar la clase UISearchController y añadirla a nuestra tabla. Para ello vamos a usar la propiedad tableHeaderView de la propia clase tableView que tenemos en nuestro controlador, como hueco donde situarla. Esta propiedad espera una vista que colocar en la cabecera y ahí le vamos a enviar el objeto UISearchController que es otro controlador en sí mismo. Pero claro, no podemos enviarle este tal cual: enviamos la propiedad searchBar que es la vista de la propia barra dentro del controlador.

Modificamos entonces nuestro viewDidLoad() para incluir la barra, quedando el método completo de esta forma, además de incluir una propiedad a nivel de clase de tipo opcional para nuestro controlador:

Creamos un objeto UISearchController que puede ser inicializado indicándole otro controlador más, en este caso uno que gestiona los resultados de la búsqueda. Pero como no es el caso, ya que solo queremos incluir la barra en nuestra tabla, lo inicializamos con este parámetro a nil. Acto seguido, hacemos que nuestra clase delegue a la propiedad searchBar que es donde tenemos la propia barra. Obviamente, tenemos un error: hay que conformarse al protocolo correspondiente.

Cambiamos nuestra primera línea para que la clase tableView se conforme con el protocolo UISearchBarDelegate que nos ofrece unos cuantos métodos interesantes. El clave de todos ellos es el que se dispara cuando el texto de la propia barra cambia, que veremos más adelante.

La siguiente propiedad obscuresBackgroundDuringPresentation, es la que dicta si el fondo se oscurecerá cuando la búsqueda esté activa, la cual ponemos a false. Históricamente se usaba otra propiedad llamada dimsBackgroundDuringPresentation, pero Apple recomienda usar la que hemos puesto en su lugar con la última versión de iOS.

Luego fijamos la propiedad placeholder de nuestra barra de búsqueda, que será el texto que informará al usuario de qué ha de hacer en dicha barra. Por último ponemos la propiedad de nuestro controlador de tabla definesPresentationContext a true. Con esto conseguimos que cuando estemos en un contexto de navegación y podamos movernos a otro controlador de vista diferente, la barra de búsqueda no se moverá junto a la navegación y quedará unida a nuestro actual controlador de tabla.

Cuando ejecutamos, tenemos un resultado muy vistoso, como podemos ver:

UISearchBar implementada

Implementando el filtro

Si escribimos veremos que no hace nada. Es lo que hemos comentado ya: la barra de búsqueda es un elemento funcional que no aporta funcionalidad a la app de por sí, solo a sí misma. Ahora tenemos que implementar la búsqueda: ¿cómo?. Haciendo que cuando la barra de búsqueda esté activa, en vez de recuperar la fuente de datos de nuestro actual array datos, lo haga de una copia filtrada de resultados. Esto quiere decir que tenemos que modificar nuestras funciones base de la delegación de fuente de datos de la tabla: numberOfRowsInSection y cellForRowAt. Además, hemos de incluir un par de propiedades a nuestra clase principal.

La primera es una propiedad que guardará los datos de nuestro array una vez aplicado el filtro que pongamos en el campo de texto de la barra. El segundo, no es otra cosa que un semáforo que nos indique si la barra de búsqueda está o no activa, para saber cuál es la fuente de datos de la que leeremos: datos o datosFilter.

Primer paso: cambiar el conteo de elementos a mostrar en la delegación correspondiente. Actualizamos el código de numberOfRowsInSection al siguiente:

Si la barra de búsqueda está activada, devolverá como total de elementos de la tabla la propiedad count del array datosFilter. Si no, devolverá el total de elementos del array con el que normalmente trabajamos y donde están todos nuestros datos. Actualizamos el método cellForRowAt para que coja el elemento a mostrar de uno u otro lado según el valor de barraActiva, al que hemos hecho con el método anterior para contar valores.

Nada más simple: si barraActiva es false, entonces recupera el valor del array datos, si no, lo hace recuperando desde el array datosFilter.

Es ahora, cuando vamos a implementar las delegaciones que pertenecen al protocolo, UISearchBarDelegate, de forma que así podamos controlar (entre otros) un evento que nos permita actuar cada vez que se escriba algo en la barra de búsquedas (carácter a carácter) y otro que salta cuando pulsamos en la opción Cancelar de la propia barra, para desactivar su funcionalidad.

El primero es el método textDidChange y lo único que hacemos es usar el método filter de programación funcional para pasarle un closure que nos devuelve true cuando la versión en minúscula de la cadena que estamos comparando contiene la versión también en minúscula, del texto escrito en la barra. Convertimos ambos en minúscula con lowercased() con el objetivo que la búsqueda funcione tanto con mayúsculas como minúsculas.

El otro método searchBarCancelButtonClicked salta cuando se pulsa el botón Cancelar en la barra, y lo único que hace es borrar los datos del filtro (conservando la capacidad del array, con objeto que cuando vuelva a llenarse sea más eficiente) y luego recarga la tabla.

Ahora solo nos queda algo esencial: las delegaciones de la barra de búsqueda son asíncronas, por lo que necesitamos que el playground se siga ejecutando por detrás sin parar. Así que añadimos la instrucción que nos permite controlar esto, fuera del ámbito de la clase:

Cuando se ejecute la tabla, veremos cómo al poner un texto se refrescan los datos y afina la búsqueda. No os preocupéis si en algunos casos no se ve el teclado completo (el móvil). Podéis escribir con el teclado convencional del Mac o el iPad. Así podemos comprobar cómo ha funcionado: simplemente un hueco donde escribir, donde controlamos cada vez que se pulsa una nueva tecla a través de una delegación y creamos una copia de nuestros datos, filtrando resultados y cambiando la fuente de la tabla. Siempre, tableView.reloadData() mediante.

Simple y sencillo

Y no hay más, no hay ningún misterio oculto. Simplemente es poner la barra y luego implementar para que muestre los datos filtrados en un función de lo que se escribe en ella y, obviamente, en tiempo real.

Espero que les haya sido útil la lección, no se pierdan las lecciones anteriores y nos vemos en la próxima. Por cierto, ¿alguna sugerencia para la próxima lección por prototipos que queráis veamos? Un saludo y Good Apple Coding.

Acerca de Julio César Fernández

Analista, consultor y periodista tecnológico, desarrollador, empresario, productor audiovisual, actor de doblaje e ingeniero de vídeo y audio.

Otras recomendaciones

Vapor Swift I

Swift Server Side con Vapor (I), instalación y primeros pasos

En esta guía pensada para Linux, como complemento a la anterior de Swift 3 en este sistema, vamos a descubrir qué es Vapor, una de las soluciones de lado servidor más populares. Cómo se instala y cómo desplegar dos ejemplos sencillos. Nuestros primeros pasos en Swift Server Side, sencillos pero que nos permiten ver el potencial que tiene una herramienta que veremos más en profundidad en varias entregas.