Si está desarrollando un producto para el mercado masivo, lo más probable es que las personas con baja visión lo usen. Si se esfuerza por crear interfaces fáciles de usar, debe hacerlo convenientemente para todos los clientes, incluidas las personas con baja visión. Creo que a menudo nos olvidamos de eso. Y es hora de arreglarlo.

Ingresé la consulta "entrega de pizza" en la búsqueda en la App Store, descargué las primeras 24 aplicaciones y verifiqué cuál de ellas proporciona una interfaz para personas con baja visión.
2 de 24 . Y parece que uno de los dos lo hizo por accidente: a medida que aumenta el tamaño de la fuente, toda la interfaz "flota" y se vuelve más difícil de usar. Es triste
550,000 personas usan la aplicación Dodo Pizza para iOS cada mes. Incluso si el 1% de nuestros usuarios tienen una fuente ampliada, son 5500 personas las que se sienten incómodas al usar nuestra aplicación. Lo corregiremos
Agregar soporte de tipo dinámico
- Utilizamos estilos de texto de sistema dinámico en lugar de estilos estáticos.
- Opcionalmente, habilite la marca de verificación Ajusta automáticamente la fuente en las etiquetas de los guiones gráficos. O, si la etiqueta en el botón o se crea a través del código,
adjustsFontForContentSizeCategory
su parámetro adjustsFontForContentSizeCategory
. - Enseñamos la interfaz para estirar a diferentes tamaños de fuente:
- Utilizamos el cálculo automático de tamaños de celda donde podemos.
- Donde no podemos: obtenemos la configuración de tamaño de fuente actual y respondemos a los cambios en el método traitCollectionDidChange
. - Obtenemos una interfaz que es imposible de usar.

Cambiamos la interfaz para que pueda usarse
Retrocedemos y empezamos a pensar cómo hacer todo bien.
Usa correctamente el lugar en el menú
Ahora hay mucho espacio vacío debajo de la imagen de la pizza. Tratemos de poner una imagen encima del nombre: de esta manera se agrandará y el espacio vacío desaparecerá. Para hacer esto,
UIStackView
imagen y veremos
UIStackView
contenedor con todo lo demás en
UIStackView
, y luego
UIStackView
la dirección de la vista de pila si es necesario.

No tenemos separadores entre los elementos del menú, por lo que cuando el tamaño es grande, las celdas comienzan a "pegarse" y el precio de la pizza está demasiado cerca de la imagen de la próxima pizza. Intentemos agregar un separador.

No es eso En primer lugar, se ve más o menos. En segundo lugar, será difícil de ver para las personas con baja visión. Incluso si se vuelve a pintar de gris a negro.
Lo retiramos y tratamos de aumentar Internet entre las células.

Ahora entonces.
Subtotal: usa más espacio, los ojos saltan menos de línea en línea, la lectura se ha vuelto más fácil.
Mejoramos estiramiento y remoción
Ahora puede aumentar el ancho del botón Agregar al carrito, de lo contrario, flotará hacia la izquierda y no estará debajo de su dedo, aunque hay mucho espacio vacío a la derecha. Puede, por supuesto, simplemente moverlo hacia el lado derecho, pero luego será inconveniente para las personas zurdas y, en general, hagamos mejores gestos que los no comprimidos. Y haremos que el trazo sea más grueso, y ahora no es consistente con la fuente.

Miro todo esto y entiendo que la foto de la pizza, por supuesto, es muy grande. Tratemos de ocultarlo, tal vez sin imágenes que pueda vivir.

En general, un menú sin fotos no ha perdido mucha información, pero ahora un elemento del menú casi siempre interfiere con la pantalla de un iPhone 6S. Pero se ha vuelto menos atractivo, Drool no fluye al desplazarse. Tal Por ahora, dejémoslo así, piense detenidamente y tal vez luego devuelva la imagen.
No te olvides de marcar "en vivo"
Ahora categorías. En general, incluso con el primer enfoque resultó tolerablemente. Enciende uno nuevo.

Traté de navegar por el menú y cambiar entre categorías. Sin embargo, resultó mal: todo se desmorona en acción. Al desplazarse por el menú, las categorías cambian automáticamente, y en pines tan grandes atrae demasiada atención.
Reemplacemos
UICollectionView
con un botón que llamará a
UIActionSheet
.

Voooot Ahora puede tomar el panel superior, donde se encuentra la ciudad, las existencias, la dirección y el código promocional.
No te olvides de las líneas muy largas.
Primero, hagamos una elección de la ciudad. No hay nada complicado con la fuente en el botón, pero es interesante enseñar a los "triángulos" a crecer con la fuente. En nuestro caso, el triángulo se convirtió en un icono en el botón, que se mueve hacia el lado derecho a través de
CGAffineTransform
. Otra opción es recopilar
NSAttributedString
del texto y el ícono del triángulo, y luego alimentarlo todo al botón. Para normalizar el icono, puede usar una imagen vectorial, que necesariamente debe estar en activos con la marca de verificación Conservar datos vectoriales.

El icono del triángulo es negro y está pintado de blanco a través del código. Y por alguna razón, con el tamaño de texto estándar, aparecen artefactos en forma de bordes negros. Es gracioso En realidad no Se curó poniendo un ícono que originalmente era blanco en los activos.
Ahora estiramos los dodo-rublos, todo es simple:

Y ahora la pregunta es: ¿qué pasará si el nombre de la ciudad resulta ser largo y tenemos muchos dodo-rublos? En teoría, debes acortar el nombre de la ciudad. ¿Recuerdas lo que dije sobre la segunda opción para agregar dicho ícono a un botón, a través de
NSAttributedString
? Lo intenté y ahora hay un problema de que cuando reducimos el título, el ícono del triángulo desaparece, porque ahora es parte del título. Stosh Tendremos que devolver la lógica de mover el icono a través de transformaciones.
Si conoce una forma conveniente de mover el icono en el botón hacia el lado derecho y escalarlo junto con la fuente en el encabezado, deséchelo en los comentarios, por favor.
Meter en
Por fin existencias. Aquí necesitas sentarte y pensar. El título puede ser largo e incluso ahora a veces no cabe en una línea. En un tamaño grande, no le quedará bien en absoluto. Si hace que el panel naranja superior sea de goma y permita que el título de la acción en un tamaño grande ocupe varias líneas, entonces el bloque superior ocupará la mitad de la pantalla incluso en iPhones grandes, y no tendrá que recordar acerca de 4S. Este no es el caso. Puedes jugar con un diseño dentro del cuadro de acción: haz que la imagen sea cuadrada y toma el lugar vacante como título. Pero las imágenes de las existencias se personalizan en un formato específico y no se mostrarán correctamente en otro. Esto es imposible
Complicado
Entonces, pero puedes volver a eliminar completamente las imágenes y ocupar todo el lugar con un título.

Si lo es. Las manos pican para colorear el fondo bajo el título de la acción, pero esto afectará negativamente la legibilidad. Y nosotros, como, estamos tratando de mejorarlo. Por lo tanto, no pintamos nada y pasamos a los dos botones restantes sobre la dirección y los códigos promocionales.
Trabajamos con restricciones estrictas.
Los encabezados en estos botones son irreductibles. Pero si no se reducen, los botones se deslizarán unos sobre otros. Y sí, no puedes ocultar estos botones.
Cuando rehice el stock, no quise aumentar la altura del panel naranja superior. Parece que tiene que hacerlo. Es bueno que no lo aumentaron en ese momento, de lo contrario ahora habría aduha. En general, voy a seleccionar una línea para cada botón.

Ufff, eso es todo. En cuanto a las fotos apagadas en el menú, todavía no estoy seguro. Alternativamente, puede mostrar solo la mitad de las imágenes de pizza en lugar de un círculo completo, pero tenemos media pizza recta en el menú, por lo que no funcionará, podemos confundir a los usuarios.
Comparemos el primer enfoque con el resultado final:

Ahora compare el "antes" y el "después" con una simulación de visión deficiente:

No tenga miedo de cambiar la interfaz y los controles. No hay nada de malo en que alguien vea otro botón o, por ejemplo, un control deslizante. Y no es fatal si alguien no ve algo o si el título es diferente.
Y no tocamos el UITabBarController
, ya que con un tamaño de texto grande, al salir de la caja con un toque prolongado puede mostrar el icono y el título de la pestaña de la misma manera que iOS muestra el cambio en el volumen.
Mostramos cómo funciona todo dentro
Cada componente lógico de IU en la aplicación Dodo Pizza para iOS se asigna a un
UIViewController
separado. Cada uno de estos controladores tiene una
UIView
asignada a un archivo separado. Puede leer más sobre esto en nuestros artículos:
Controlador, ¡tómalo con calma! Sacamos el código en UIViewControlador de cebolla. Rompemos las pantallas en partesLa eliminación de componentes lógicos de la interfaz de usuario en un
UIViewController
separado
UIViewController
simplificado enormemente la tarea de modificar las interfaces a diferentes estados. Le recomendamos que pruebe este enfoque, incluso si no planea agregar soporte para Dynamic Type: es más fácil controlar el estado de las pantallas: responder a cambios en la autorización, derechos, roles, etc.
Entonces aquí. Agregamos una capa adicional entre dicho componente de la interfaz de usuario y su contenedor principal. Lo tenemos llamado
StateViewController
.

El controlador con el menú integra el controlador de estado y ya incorpora la
collection
, o el controlador de
button
.
Este
StateViewController
muestra este o aquel componente de la IU dependiendo de una situación.
Para hacer esto,
StateViewController
necesita conocer sus estados y cambiarlos si es necesario.
En este ejemplo,
StateViewController
alternará la selección de categorías en el menú de la colección al botón y viceversa. Y en el caso de una pantalla "normal", y en el caso de una pantalla para personas con discapacidad visual, el selector debe ser capaz de hacer lo mismo:
- Mostrar lista de categorías.
- Resalta la categoría seleccionada.
- Actualizar lista de categorías.
- Informe que la categoría "salió".
¿Sientes este maravilloso olor a pequeños troncos frescos? Y, no, este es un equipo de pizza móvil entregada por API. Descanso de 5 minutos.
2 rebanadas después"... Bueno, envolvemos nuestros componentes así para seleccionar categorías en los protocolos, ¡Y SON INMEDIATAMENTE!"
Sugerencia: Inicie el Inspector de accesibilidad para verificar fácilmente cómo responde la interfaz a los cambios en la configuración de mosaico dinámico. Para hacer esto, en Xcode abierto, haga clic en Xcode → Abrir herramienta de desarrollador → Inspector de accesibilidad, seleccione el simulador en el dispositivo y vaya a la última pestaña
Otra sugerencia: saque el control de dinamo taip en el iPhone (no en el simulador) al Centro de control para cambiar fácil y rápidamente el tamaño del texto. Para hacer esto, en un iPhone, vaya a Configuración → Centro de control → Personalizar controles y agregue Tamaño de texto.
Llamamos al selector de categoría habitual
CategoriesCollectionViewController
, y para los discapacitados visuales -
CategoriesButtonViewController
. Su protocolo común se llama
CategoriesPickerProtocol
. El controlador de estado general es
CategoriesStateViewController
.
Describimos los posibles estados en nuestro
CategoriesStateViewController
:
private enum State { case collection, button }
Le enseñamos a mostrar el controlador deseado para cada estado:
private var state: State = .collection { didSet { if state != oldValue { updateViewController(for: state) } } } private func updateViewController(for state: State) { let viewController = self.viewController(for: state) self.updateController(with: viewController) } private func viewController(for state: State) { switch state { case .collection: return CategoriesCollectionViewController.instantiateFromStoryboard() case .button: return CategoriesButtonViewController.instantiateFromStoryboard() } }
instantiateFromStoryboard()
: un método desde una extensión autoescrita a un controlador de vista, crea una instancia de controlador a partir de guiones gráficos si tienen el mismo nombre. El código está en el código fuente al final del artículo.
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) self.updateStateToCurrentContentSize() } private func updateStateToCurrentContentSize() { let contentSize = self.traitCollection.preferredContentSizeCategory self.updateState(to: contentSize) } private func updateState(to contentSize: UIContentSizeCategory) { self.state = contentSize.isAccessibilityCategory ? .button : .collection }
Describimos el protocolo
CategoriesPickerProtocol
, agregando simultáneamente dos protocolos más: para el delegado y para el datasure.
protocol CategoriesPickerProtocol where Self: UIViewController { var datasource: CategoriesDatasource? { get set } var delegate: CategoriesDelegate? { get set } func select(_ category: ProductCategoryModule.ProductCategoryViewModel) func updateCategories() var selectedCategory: ProductCategoryModule.ProductCategoryViewModel? { get } } protocol CategoriesDatasource: class { var categories: [ProductCategoryModule.ProductCategoryViewModel] { get } func index(of category: Product.ProductCategory) -> Int } protocol CategoriesDelegate: class { func productCategoriesView(_ categoriesPicker: CategoriesPickerProtocol, didSelect category: ProductCategoryModule.ProductCategoryViewModel) }
No tiene sentido mostrar la implementación, es solo que cada piker muestra las categorías e informa un cambio ascendente.
Puede encontrar un ejemplo detallado del uso de controladores de estado para taype dinámico en mi
repositorio en GitHub .
→
Por cierto, nos estamos expandiendo