ios - ¿Dónde inicializar el objetivo en un UIButton? Particularmente cuidadoso para IBDesignable
swift initializer (4)
tl; dr
override func endTrackingWithTouch(touch: UITouch?, withEvent event: UIEvent?) {
super.endTrackingWithTouch(touch, withEvent: event)
if let touchNotNil = touch {
if self.pointInside(touchNotNil.locationInView(self), withEvent: event) {
print("it works")
}
}
}
Por qué no usar addTarget
addTarget
método addTarget
es parte de la interfaz acción-objetivo que se considera '' pública ''. Cualquier cosa con referencia a su botón puede, por ejemplo, eliminar todas sus acciones , rompiéndola efectivamente. Se prefiere utilizar algunos de los medios " protegidos ", por ejemplo, endTrackingWithTouch
que solo se puede anular , no endTrackingWithTouch
directamente. De esta forma, no interferirá con ningún objeto externo mediante el mecanismo de acción-objetivo.
(Sé que no hay un estricto ''público'' o ''protegido'' en ObjC / UIKit, pero el concepto permanece)
A tu manera
Si quieres hacerlo exactamente a tu manera, entonces tu ejemplo está bien, solo copia addTarget
call to init(frame:CGRect)
.
O puede poner addTarget
en awakeFromNib
(no olvide super) en lugar de init?(coder decoder: NSCoder)
, pero se verá obligado a implementar init con el codificador de todos modos, por lo que ...
layoutSubviews
y didMoveToSuperView
son ideas terribles. Ambos pueden suceder más de una vez, lo que resulta en blah
target-action agregado nuevamente. Luego se llamará blah
varias veces por un solo clic.
Por cierto
La forma de Apple
Por el Cocoa MVC (que se aplica mediante la implementación de clases de UIKit) debe asignar esa acción al objeto que controla ese botón, las animaciones o no. Muy a menudo ese objeto será Cocoa MVC ''Controller'' - UIViewController
.
Si crea un botón programáticamente, UIViewController
debe asignar el destino a sí mismo en loadView
o viewDidLoad
. Cuando se carga el botón desde la punta, la forma preferida es asignar acción objetivo en xib.
Old Good MVC
Como se menciona aquí en las vistas MVC reales , no se envíen acciones a sí mismos. Lo más parecido al controlador MVC real en UIKit es UIGestureRecognizer
.
Tenga en cuenta que es bastante difícil extraer MVC real con el conjunto de clases UIKit.
tengo un
@IBDesignable
class Fancy:UIButton
quiero
addTarget(self, action:#selector(blah),
forControlEvents: UIControlEvents.TouchUpInside)
Entonces, ¿dónde en UIButton debería hacerse eso?
¿Dónde está el mejor lugar para addTarget
?
1 - He visto el layoutSubviews
sugerido - ¿es así?
Nota: la experimentación muestra que un problema con layoutSubviews
es que, por supuesto, se puede layoutSubviews
menudo, siempre que las cosas se muevan. Sería una mala idea "agregarTarget" más de una vez.
2 - didMoveToSuperview
es otra sugerencia.
3 - ¿En algún lugar en (uno de) los Inits?
Nota: la experimentación muestra un problema fascinante si lo haces dentro de Init. Durante Init, las variables IBInspectables aún no están configuradas. (Entonces, por ejemplo, estaba ramificando dependiendo del "estilo" de control establecido por un IBInspectable; simplemente no funciona como @IBInspectable: ¡no funcionará cuando se ejecute!)
4 - ¿En algún otro lugar?
Intenté hacerlo en Init, y funcionó bien. Pero rompe designables de trabajar en el Editor.
Al dar vueltas, se me ocurrió esto (¿por alguna razón ambos deben incluirse?)
@IBDesignable
class DotButton:UIButton
{
@IBInspectable var mainColor ... etc.
required init?(coder decoder: NSCoder)
{
super.init(coder: decoder)
addTarget(self, action:#selector(blah),
forControlEvents: UIControlEvents.TouchUpInside)
}
override init(frame:CGRect)
{
super.init(frame:frame)
}
No sé por qué funciona, y no entiendo por qué habría dos rutinas de init diferentes.
¿Cuál es la forma correcta de incluir addTarget
en un UIButton?
¿Qué hay de la implementación de awakeFromNib
y hacerlo allí?
También puede ejecutar (o no ejecutar) código de forma condicional cuando se ejecuta en el contexto de Interface Builder:
#if !TARGET_INTERFACE_BUILDER
// this code will run in the app itself
#else
// this code will execute only in IB
#endif
No debe agregar como objetivo el mismo objeto que produce la acción.
El objetivo y su devolución de llamada deberían ser otro objeto, generalmente un controlador de vista.
Hay 2 métodos inits porque el botón se puede crear una instancia invocando init o mediante el proceso de deserialización ( NSCoder
) desde un nib / xib. Dado que probablemente haya agregado el botón a un guión gráfico, el método init llamado es init?(_: NSCoder)
.
[ACTUALIZAR]
Estoy de acuerdo con lo que dices en el comentario, pero creo que el patrón acción-objetivo debe usarse para comunicarme con otros objetos, estoy usando condicional, porque hasta donde sé nunca he visto algo como lo que escribiste en Apple código o alguna otra biblioteca. Si desea interceptar y realizar algunas acciones dentro del botón, probablemente debería anular algunos de los métodos expuestos en UIControl
.
Acerca de designable, usted es, de nuevo, correcto. init(frame)
si está creando un botón mediante programación, init(coder)
si el botón proviene de un xib.
El método init(frame)
también se llama durante el proceso designado. En este momento, creo que la mejor opción es depurar directamente su vista.
- Coloque algunos puntos de interrupción dentro de usted UIButton subclase
- Seleccione la vista en su guión gráfico
- Vaya al Editor -> Depurar vistas seleccionadas
Ahora deberías ser capaz de entender dónde está el problema.
Su método de inicialización no es correcto, esto funcionará:
`` `rápido
override init(frame: CGRect) {
super.init(frame: frame)
self.loadNib()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.loadNib()
}
private func loadNib() {
let nibView = NSBundle(forClass: self.classForCoder).loadNibNamed("yourView", owner: self, options: nil).first as! UIView
nibView.frame = self.bounds
nibView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
self.button.addTarget(self, action: #selector(action), forControlEvents: .TouchUpInside)
self.addSubview(nibView)
}
`` `