para descargar curso apple ios swift

ios - descargar - swift download



Subclase Swift UIView (6)

Ejemplo de subclase de UIView personalizada

Por lo general, creo aplicaciones de iOS sin usar guiones gráficos o plumillas. Compartiré algunas técnicas que he aprendido para responder sus preguntas.

Ocultar métodos de init no deseados

Mi primera sugerencia es declarar una base UIView para ocultar los inicializadores no deseados. He discutido este enfoque en detalle en mi respuesta a "Cómo ocultar Storyboard y Nib Inicializadores específicos en subclases de UI" . Nota: Este enfoque supone que no usará BaseView o sus descendientes en guiones gráficos o plumillas, ya que intencionalmente hará 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 de UIView personalizada debe heredar de BaseView . Debe llamar a super.init () en su inicializador. No necesita implementar init(coder:) . Esto se demuestra en el siguiente ejemplo.

Agregar un UITextField

Creo propiedades almacenadas para subvistas referenciadas fuera del método init . Normalmente lo haría para un UITextField. Prefiero crear instancias de subvistas dentro de la declaración de la propiedad de subvista de esta manera: let textField = UITextField() .

UITextField no estará visible a menos que lo agregue a la lista de subvista de la vista personalizada llamando a addSubview(_:) . Esto se demuestra en el siguiente ejemplo.

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 Diseño automático) dentro del método layoutSubviews . layoutSubviews() se llama inicialmente y cada vez 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 todo el ancho en varios tamaños de iPhones y iPads y se ajusta para la rotación, debe acomodar muchos tamaños iniciales y cambiar su tamaño dinámicamente.

Puede consultar frame.height y frame.width dentro de layoutSubviews() para obtener las dimensiones de CustomView como referencia. Esto se demuestra en el siguiente ejemplo.

Ejemplo de subclase UIView

Una subclase de UIView personalizada que contiene un UITextField que no necesita 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 suelo hacer esto, no mostraré un ejemplo. Puede encontrar ejemplos de implementación del diseño automático en el código de 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 en el que estoy interesado pero que no he probado es LayoutKit . Fue escrito por el equipo de desarrollo y 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 en init(coder:)

Al crear subclases de UIView que nunca se usarán en un guión gráfico o plumín, puede introducir inicializadores con diferentes parámetros y requisitos de inicialización que el método init(coder:) no podría init(coder:) . Si no falló init (codificador :) con un fatalError , podría provocar problemas muy confusos en el fatalError si se usa accidentalmente en un guión gráfico / plumín. 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 creó en código o en un guión gráfico / plumín, entonces podría 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 } }

Acabo de empezar a usar Swift. Leí el libro y aprendo mejor mientras lo hacía, comencé algo simple.

Quiero subclasificar UIView y mostrar un inicio de sesión como vista. He creado esto en Objective-C pero ahora quiero portarlo a Swift. No uso guiones gráficos, por lo que creo toda mi interfaz de usuario en código.

Pero el primer problema es que debo implementar initWithCoder . Le di una implementación predeterminada ya que no se llamará. Ahora, cuando ejecuto el programa, se bloquea porque tengo que implementar initWithFrame también. Ahora tengo esto:

override init() { super.init() println("Default init") } override init(frame: CGRect) { super.init(frame: frame) println("Frame init") } required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) println("Coder init") }

Mi pregunta es ¿dónde debería crear mi campo de texto, etc ... y si nunca implemento el marco y el codificador, ¿cómo puedo "ocultar" esto?


Aquí hay un ejemplo de cómo suelo construir mis subclases (UIView). Tengo el contenido como variables para que pueda accederse y modificarse tal vez más tarde en alguna otra clase. También he mostrado cómo uso el diseño automático y agrego contenido.

Por ejemplo, en un ViewController tengo esta vista inicializada en ViewDidLoad () ya que solo se llama una vez cuando la vista es visible. Luego uso estas funciones que hago aquí addContentToView() y luego addContentToView() activateConstraints() para construir el contenido y establecer restricciones. Si más adelante en un ViewController quiero que el color de un botón sea rojo, solo hago eso en esa función específica en ese ViewController. Algo así como: func tweaksome(){ self.customView.someButton.color = UIColor.red}

class SomeView: UIView { var leading: NSLayoutConstraint! var trailing: NSLayoutConstraint! var bottom: NSLayoutConstraint! var height: NSLayoutConstraint! var someButton: UIButton = { var btn: UIButton = UIButton(type: UIButtonType.system) btn.setImage(UIImage(named: "someImage"), for: .normal) btn.translatesAutoresizingMaskIntoConstraints = false return btn }() var btnLeading: NSLayoutConstraint! var btnBottom: NSLayoutConstraint! var btnTop: NSLayoutConstraint! var btnWidth: NSLayoutConstraint! var textfield: UITextField = { var tf: UITextField = UITextField() tf.adjustsFontSizeToFitWidth = true tf.placeholder = "Cool placeholder" tf.translatesAutoresizingMaskIntoConstraints = false tf.backgroundColor = UIColor.white tf.textColor = UIColor.black return tf }() var txtfieldLeading: NSLayoutConstraint! var txtfieldTrailing: NSLayoutConstraint! var txtfieldCenterY: NSLayoutConstraint! override init(frame: CGRect){ super.init(frame: frame) self.translatesAutoresizingMaskIntoConstraints = false } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) //fatalError("init(coder:) has not been implemented") } /* // Only override draw() if you perform custom drawing. // An empty implementation adversely affects performance during animation. override func draw(_ rect: CGRect) { // Drawing code } */ func activateConstraints(){ NSLayoutConstraint.activate([self.btnLeading, self.btnBottom, self.btnTop, self.btnWidth]) NSLayoutConstraint.activate([self.txtfieldCenterY, self.txtfieldLeading, self.txtfieldTrailing]) } func addContentToView(){ //setting the sizes self.addSubview(self.userLocationBtn) self.btnLeading = NSLayoutConstraint( item: someButton, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1.0, constant: 5.0) self.btnBottom = NSLayoutConstraint( item: someButton, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1.0, constant: 0.0) self.btnTop = NSLayoutConstraint( item: someButton, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 0.0) self.btnWidth = NSLayoutConstraint( item: someButton, attribute: .width, relatedBy: .equal, toItem: self, attribute: .height, multiplier: 1.0, constant: 0.0) self.addSubview(self.textfield) self.txtfieldLeading = NSLayoutConstraint( item: self.textfield, attribute: .leading, relatedBy: .equal, toItem: someButton, attribute: .trailing, multiplier: 1.0, constant: 5) self.txtfieldTrailing = NSLayoutConstraint( item: self.textfield, attribute: .trailing, relatedBy: .equal, toItem: self.doneButton, attribute: .leading, multiplier: 1.0, constant: -5) self.txtfieldCenterY = NSLayoutConstraint( item: self.textfield, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1.0, constant: 0.0) } }


Es importante que su UIView se pueda crear mediante el generador de interfaces / guiones gráficos o desde el código. Creo que es útil tener un método de setup para reducir la duplicación de cualquier código de configuración. p.ej

class RedView: UIView { override init (frame: CGRect) { super.init(frame: frame) setup() } required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder)! setup() } func setup () { backgroundColor = .red } }


Esto es mas simple.

override init (frame : CGRect) { super.init(frame : frame) // Do what you want. } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }


Swift 4.0, si desea usar la vista desde el archivo xib, entonces esto es para usted. Creé la clase CustomCalloutView Subclase de UIView. He creado un archivo xib y en IB simplemente seleccione el propietario del archivo y luego seleccione el nombre de la clase del inspector de atributos para CustomCalloutView, luego cree una salida en su clase.

import UIKit class CustomCalloutView: UIView { @IBOutlet var viewCallout: UIView! // This is main view @IBOutlet weak var btnCall: UIButton! // subview of viewCallout @IBOutlet weak var btnDirection: UIButton! // subview of viewCallout @IBOutlet weak var btnFavourite: UIButton! // subview of viewCallout // let nibName = "CustomCalloutView" this is name of xib file override init(frame: CGRect) { super.init(frame: frame) nibSetup() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) nibSetup() } func nibSetup() { Bundle.main.loadNibNamed(String(describing: CustomCalloutView.self), owner: self, options: nil) guard let contentView = viewCallout else { return } // adding main view contentView.frame = self.bounds //Comment this line it take default frame of nib view // custom your view properties here self.addSubview(contentView) } }

// Ahora agregándolo

let viewCustom = CustomCalloutView.init(frame: CGRect.init(x: 120, y: 120, 50, height: 50)) self.view.addSubview(viewCustom)


Usualmente hago algo como esto, es un poco detallado.

class MyView: UIView { override init(frame: CGRect) { super.init(frame: frame) addBehavior() } convenience init() { self.init(frame: CGRect.zero) } required init(coder aDecoder: NSCoder) { fatalError("This class does not support NSCoding") } func addBehavior() { print("Add all the behavior here") } } let u = MyView(frame: CGRect.zero) let v = MyView()

(Editar: he editado mi respuesta para que la relación entre los inicializadores sea más clara)