¿Tienes un gran UIViewController? Para muchos sí. Por un lado, funciona con datos, por otro, con la interfaz.
Las tareas de separar la lógica de la interfaz se describen en cientos de artículos sobre arquitectura: MVP, MVVM, VIPER. Resuelven el problema del flujo de datos, pero no responden a la pregunta de cómo trabajar con la interfaz: en un lugar queda la creación de elementos, diseño, configuración, procesamiento de entrada y animación.
Separemos la vista del controlador y veamos cómo loadView () nos ayuda.

La interfaz de la aplicación para iOS es la jerarquía
UIView
. Las tareas de cada
view
: crear elementos, personalizar, organizar en lugares, animar. Esto se puede ver a partir de los métodos que están en la clase
UIView: addSubview(), drawRect(), layoutSubviews().
Si observa los métodos de la clase
UIViewController
, puede ver que administra la
view:
carga, responde a las pantallas de carga y las acciones del usuario, y muestra nuevas pantallas. A menudo, el código que debe estar en el
UIView
, lo escribimos en subclases del
UIViewController
, esto lo hace demasiado grande. Separarlo
loadView ()
El ciclo de vida de un
UIViewController
comienza con
loadView()
. Una implementación simplificada se ve así:
Podemos anular el método y especificar nuestra clase.
super.loadView()
no es necesario llamar a super.loadView()
!
Implementación CustomView.swift El controlador cargará
CustomView,
agregará a la jerarquía, expondrá
.frame
. La propiedad
.view
será la clase que necesitamos:
Pero mientras el compilador no sabe acerca de la clase y cree que hay una
UIView
normal. Arreglemos esto con una función de conversión de tipo:
Ahora puede ver las variables
CustomView
:
Simplifica con el tipo asociadoRuslan Kavetsky propuso eliminar la duplicación de código mediante la expansión del protocolo:
protocol ViewSpecificController { associatedtype RootView: UIView } extension ViewSpecificController where Self: UIViewController { func view() -> RootView { return self.view as! RootView } }
Para cada nuevo controlador, solo necesita especificar el protocolo y la subclase para su UIView
mediante typealias
:
Código en una subclase de UIView
Crear y configurar controles
Las fuentes, los colores, las constantes y la jerarquía se pueden configurar directamente en el constructor CustomView:
layoutSubviews ()
El mejor lugar para un diseño manual es el método
layoutSubviews()
. Se llama cada vez que se cambia el tamaño de la
view
, por lo que puede confiar en el tamaño de los
bounds
para los cálculos correctos:
Controles privados, propiedades públicas.
Si hay tiempo, entonces hago que los controles de
property
privados, pero los administro a través de variables o funciones públicas "en el campo del conocimiento". Un ejemplo más simple:
La ventaja de la encapsulación: la lógica interna está oculta detrás de la interfaz. Por ejemplo, la validez de un objeto puede estar indicada por el color del área, no por el cuadrado, pero el controlador no sabrá nada al respecto.
¿Qué queda en viewDidLoad ()?
Si usa Interface Builder, a menudo
viewDidLoad()
vacío. Si crea una
view
en código, debe vincular sus acciones a través del patrón de acción de destino, agregar un
UIGestureRecognizer
o conectar delegados.
Personalizable a través de Interface Builder
La subclase para la
view
se puede configurar a través de Interface Builder (en adelante, IB).
Debe seleccionar el objeto de
view
(no el controlador) y establecer su clase. No es necesario escribir su propio
loadView()
, el controlador lo hará por sí mismo. Pero aún debe
UIView
tipo
UIView
.

IBOutlet en UIView
Si selecciona el control dentro de la
view
, el Editor Asistente reconoce la clase
UIView
y la ofrece como el segundo archivo en modo Automático. Para que pueda transferir
IBOutlet
para
view
.

Si no funcionaAbra la clase
CustomView
manualmente, escriba
IBOutlet
. Ahora puede arrastrar por el marcador y colocar el cursor sobre un elemento en IB.

Si crea una interfaz en código, todos los objetos son accesibles después de
init()
, pero cuando se trabaja con IB, el acceso a
IBOutlet
aparece solo después de cargar la interfaz desde
UIStoryboard
en el método
awakeFromNib()
:
IBAction en UIViewController
Para mi gusto, el controlador debe dejar todas las acciones del usuario. De estándar:
- acción objetivo de los controles
- delegar implementación en
UIViewController
- implementación de bloque
- reacción a la
Notification
En este caso, el
UIViewController
controla solo la interfaz. Todo lo relacionado con la lógica empresarial debe sacarse del controlador, pero esta es una opción: MVP, VIPER, etc.
Objetivo-c
En Objective-C, puede reemplazar completamente el tipo
UIView
. Para hacer esto, declare la propiedad con la clase deseada, anule
setter
y
getter
, especificando la clase:
El final
En el ejemplo de GitHub, puede ver la separación de clases para una tarea simple: el color del cuadrado depende de su posición (en el área verde es verde, afuera es rojo).
Cuanto más compleja sea la pantalla, mejor será el efecto: el controlador se reduce, el código se transfiere a su lugar. El código simplemente se transfiere a la
view
, pero la encapsulación facilita la interacción y la lectura del código. A veces, la
view
se puede reutilizar con otro controlador. Por ejemplo, diferentes controladores para iPhone y iPad reaccionan a su manera a la apariencia del teclado, pero esto no cambia el código de
view
.
Usé este código en diferentes proyectos y con diferentes personas, cada vez que el equipo agradeció la simplificación y retomó la práctica. Espero que lo disfrutes también. Todo fácil
UIViewController
!