framework create ios objective-c cocoa-touch uiview

ios - create - uiviewcontroller



¿Práctica adecuada para subclasificar UIView? (4)

Estoy trabajando en algunos controles de entrada personalizados basados ​​en UIView, y estoy tratando de determinar la práctica adecuada para configurar la vista. Cuando se trabaja con un UIViewController, es bastante simple usar loadView y los métodos viewWill , viewDid relacionados, pero cuando se subclasifica un UIView, los metadsds más cercanos que tengo son `awakeFromNib , drawRect y layoutSubviews . (Estoy pensando en términos de configuración y recuperación de llamadas). En mi caso, estoy configurando mi marco y las vistas internas en layoutSubviews , pero no veo nada en la pantalla.

¿Cuál es la mejor manera de asegurar que mi vista tenga la altura y el ancho correctos que quiero que tenga? (Mi pregunta se aplica independientemente de si estoy usando el diseño automático, aunque puede haber dos respuestas). ¿Cuál es la "mejor práctica" adecuada?


Apple definió muy claramente cómo subclasificar a UIView en el documento.

Consulte la lista a continuación, especialmente eche un vistazo a initWithFrame: y layoutSubviews . El primero está destinado a configurar el marco de su UIView mientras que el último está destinado a configurar el marco y el diseño de sus subvistas.

Recuerde también que initWithFrame: se initWithFrame: solo si está instanciando su UIView programación. Si lo está cargando desde un archivo nib (o un guión gráfico), se initWithCoder: Y en initWithCoder: el marco aún no se ha calculado, por lo que no puede modificar el marco que configuró en el Interface Builder. Como se sugiere en esta respuesta , puede pensar en llamar a initWithFrame: desde initWithCoder: para configurar el marco.

Finalmente, si carga su UIView desde un plumín (o un guión gráfico), también tiene la oportunidad de awakeFromNib de realizar inicializaciones de marcos y awakeFromNib personalizados, ya que cuando se llama awakeFromNib se garantiza que cada vista en la jerarquía se haya desarchivado e inicializado.

Del documento de NSNibAwaking

Los mensajes a otros objetos se pueden enviar de forma segura desde awakeFromNib, momento en el que se garantiza que todos los objetos se desarchivan e inicializan (aunque no necesariamente se activan, por supuesto)

También vale la pena señalar que con la configuración automática no debe establecer explícitamente el marco de su vista. En su lugar, se supone que debe especificar un conjunto de restricciones suficientes para que el marco de cálculo lo calcule automáticamente.

Directamente de la documentation :

Métodos para anular

Inicialización

  • initWithFrame: se recomienda implementar este método. También puede implementar métodos de inicialización personalizados además de, o en lugar de, este método.

  • initWithCoder: Implemente este método si carga su vista desde un archivo nib de Interface Builder y su vista requiere una inicialización personalizada.

  • layerClass Implemente este método solo si desea que su vista use una capa de animación Core diferente para su tienda de respaldo. Por ejemplo, si está utilizando OpenGL ES para hacer su dibujo, le conviene anular este método y devolver la clase CAEAGLLayer.

Dibujo e impresión

  • drawRect: Implemente este método si su vista dibuja contenido personalizado. Si su vista no hace ningún dibujo personalizado, evite anular este método.

  • drawRect:forViewPrintFormatter: Implemente este método solo si desea dibujar el contenido de su vista de manera diferente durante la impresión.

Restricciones

  • requiresConstraintBasedLayout Implemente este método de clase si su clase de vista requiere restricciones para que funcione correctamente.

  • updateConstraints Implemente este método si su vista necesita crear restricciones personalizadas entre sus subvistas.

  • alignmentRectForFrame: frameForAlignmentRect: Implemente estos métodos para anular la alineación de sus vistas con otras vistas.

Diseño

  • sizeThatFits: Implemente este método si desea que su vista tenga un tamaño predeterminado diferente del que normalmente tendría durante las operaciones de cambio de tamaño. Por ejemplo, puede usar este método para evitar que su vista se reduzca hasta el punto donde las subvistas no se pueden mostrar correctamente.

  • layoutSubviews Implemente este método si necesita un control más preciso sobre el diseño de sus subvistas que los comportamientos de restricción o de conversión de tamaño proporcionados.

  • didAddSubview: willRemoveSubview: Implemente estos métodos según sea necesario para rastrear las adiciones y eliminaciones de las subvistas.

  • willMoveToSuperview: didMoveToSuperview Implemente estos métodos según sea necesario para rastrear el movimiento de la vista actual en su jerarquía de vistas.

  • willMoveToWindow: didMoveToWindow Implementa estos métodos según sea necesario para rastrear el movimiento de tu vista a una ventana diferente.

Manejo de eventos:

  • touchesBegan:withEvent: touchesMoved:withEvent: touchesEnded:withEvent: touchesCancelled:withEvent: Implemente estos métodos si necesita manejar eventos táctiles directamente. (Para la entrada basada en gestos, use los reconocedores de gestos).

  • gestureRecognizerShouldBegin: Implemente este método si su vista maneja eventos táctiles directamente y puede evitar que los reconocedores de gestos adjuntos desencadenen acciones adicionales.


Esto todavía aparece alto en Google. A continuación se muestra un ejemplo actualizado para swift.

La función didLoad te permite poner todo tu código de inicialización personalizado. Como han mencionado otros, se didLoad a didLoad cuando se crea una vista mediante programación mediante init(frame:) o cuando el deserializador XIB fusiona una plantilla XIB en su vista a través de init(coder:)

Además : layoutSubviews y updateConstraints se updateConstraints varias veces para la mayoría de las vistas. Esto está diseñado para diseños avanzados de múltiples pasos y ajustes cuando cambian los límites de una vista. Personalmente, evito los diseños de paso múltiple cuando es posible porque consumen ciclos de CPU y hacen que todo sea un dolor de cabeza. Además, puse código de restricción en los inicializadores, ya que rara vez los invalido.

import UIKit class MyView: UIView { //----------------------------------------------------------------------------------------------------- //Constructors, Initializers, and UIView lifecycle //----------------------------------------------------------------------------------------------------- override init(frame: CGRect) { super.init(frame: frame) didLoad() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) didLoad() } convenience init() { self.init(frame: CGRectZero) } func didLoad() { //Place your initialization code here //I actually create & place constraints in here, instead of in //updateConstraints } override func layoutSubviews() { super.layoutSubviews() //Custom manually positioning layout goes here (auto-layout pass has already run first pass) } override func updateConstraints() { super.updateConstraints() //Disable this if you are adding constraints manually //or you''re going to have a ''bad time'' //self.translatesAutoresizingMaskIntoConstraints = false //Add custom constraint code here } }


Hay un resumen decente en la documentation Apple, y esto está bien cubierto en el curso gratuito de Stanford disponible en iTunes. Presento mi versión de TL; DR aquí:

Si su clase consiste principalmente en subvistas, el lugar correcto para asignarlas está en los métodos init . Para las vistas, hay dos métodos de init diferentes que pueden recibir llamadas, dependiendo de si su vista está siendo instanciada desde el código o desde una punta / guión gráfico. Lo que hago es escribir mi propio método de setup y luego llamarlo desde los initWithFrame: y initWithCoder: .

Si está haciendo un dibujo personalizado, de hecho desea anular drawRect: en su vista. Sin embargo, si su vista personalizada es principalmente un contenedor para las subvistas, probablemente no necesite hacer eso.

Solo anule el layoutSubViews las layoutSubViews si desea hacer algo como agregar o quitar una subvista, dependiendo de si está en orientación vertical u horizontal. De lo contrario, deberías poder dejarlo solo.


layoutSubviews está destinado a establecer el marco en vistas secundarias, no en la vista en sí.

Para UIView , el constructor designado suele ser initWithFrame:(CGRect)frame y usted debe establecer el marco allí (o en initWithCoder: , posiblemente ignorando el valor de marco pasado. También puede proporcionar un constructor diferente y establecer el marco allí.