Ejemplo personalizado de subclase de UIView
Normalmente creo aplicaciones de iOS sin usar guiones gráficos o plumillas. Compartiré algunas técnicas que he aprendido para responder a sus preguntas.
Ocultar init
métodos no deseados
Mi primera sugerencia es declarar una base UIView
para ocultar inicializadores no deseados. He discutido este enfoque en detalle en mi respuesta a "Cómo ocultar los inicializadores específicos del guión gráfico y la punta en las subclases de la interfaz de usuario" . Nota: Este enfoque asume que no usará BaseView
o sus descendientes en guiones gráficos o plumillas, ya que provocará intencionalmente que la aplicación se bloquee.
class BaseView: UIView {
// This initializer hides init(frame:) from subclasses
init() {
super.init(frame: CGRect.zero)
}
// This attribute hides `init(coder:)` from subclasses
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
}
Su subclase personalizada de UIView debe heredar BaseView
. Debe llamar a super.init () en su inicializador. No es necesario implementarlo init(coder:)
. Esto se demuestra en el ejemplo siguiente.
Agregar un UITextField
Creo propiedades almacenadas para subvistas a las que se hace referencia fuera del init
método. Normalmente lo haría para un UITextField. Yo prefiero subvistas crear una instancia dentro de la declaración de la propiedad subvista como esto: let textField = UITextField()
.
UITextField no será visible a menos que lo agregue a la lista de subvista de la vista personalizada llamando a addSubview(_:)
. Esto se demuestra en el ejemplo siguiente.
Diseño programático sin diseño automático
UITextField no será visible a menos que establezca su tamaño y posición. A menudo hago el diseño en código (sin usar Auto Layout) dentro del método layoutSubviews . layoutSubviews()
se llama inicialmente y siempre que ocurre un evento de cambio de tamaño. Esto permite ajustar el diseño según el tamaño de CustomView. Por ejemplo, si CustomView aparece en el ancho completo en varios tamaños de iPhones y iPads y se ajusta para la rotación, debe adaptarse a muchos tamaños iniciales y cambiar el tamaño de forma dinámica.
Puede consultar frame.height
y frame.width
dentro layoutSubviews()
para obtener las dimensiones de CustomView como referencia. Esto se demuestra en el ejemplo siguiente.
Ejemplo de subclase de UIView
Una subclase de UIView personalizada que contiene un UITextField que no es necesario implementar init?(coder:)
.
class CustomView: BaseView {
let textField = UITextField()
override init() {
super.init()
// configure and add textField as subview
textField.placeholder = "placeholder text"
textField.font = UIFont.systemFont(ofSize: 12)
addSubview(textField)
}
override func layoutSubviews() {
super.layoutSubviews()
// Set textField size and position
textField.frame.size = CGSize(width: frame.width - 20, height: 30)
textField.frame.origin = CGPoint(x: 10, y: 10)
}
}
Diseño programático con diseño automático
También puede implementar el diseño utilizando el diseño automático en el código. Como no hago esto a menudo, no mostraré un ejemplo. Puede encontrar ejemplos de implementación de diseño automático en código en Stack Overflow y en otros lugares de Internet.
Marcos de diseño programático
Existen marcos de código abierto que implementan el diseño en código. Uno que me interesa pero que no he probado es LayoutKit . Fue escrito por el equipo de desarrollo de LinkedIn. Desde el repositorio de Github: "LinkedIn creó LayoutKit porque descubrimos que el diseño automático no tiene el rendimiento suficiente para jerarquías de vista complicadas en vistas desplazables".
¿Por qué poner fatalError
eninit(coder:)
Al crear subclases de UIView que nunca se usarán en un guión gráfico o plumilla, puede introducir inicializadores con diferentes parámetros y requisitos de inicialización que el init(coder:)
método no podría llamar . Si no falló init (codificador :) con a fatalError
, podría generar problemas muy confusos en el futuro si se usa accidentalmente en un guión gráfico / nib. El fatalError afirma estas intenciones.
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
Si desea ejecutar algún código cuando se crea la subclase, independientemente de si se crea en código o en un guión gráfico / plumilla, puede hacer algo como lo siguiente (según la respuesta de Jeff Gu Kang )
class CustomView: UIView {
override init (frame: CGRect) {
super.init(frame: frame)
initCommon()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initCommon()
}
func initCommon() {
// Your custom initialization code
}
}