在UIButton中初始化目标的位置? 特别关心IBDesignable
我有一个
@IBDesignable class Fancy:UIButton
我要
addTarget(self, action:#selector(blah), forControlEvents: UIControlEvents.TouchUpInside)
那么UIButton应该在哪里做?
addTarget
最好的地方在哪里?
1 – 我见过layoutSubviews
build议 – 是吗?
注意 – 实验表明layoutSubviews
存在一个问题,当然,只要事情四处移动,就可以经常调用它。 多次添加目标是一个糟糕的主意。
2 – didMoveToSuperview
是另一个build议。
3 – inits(其中之一)的某个地方?
注意 – 如果你在Init内部进行实验,实验会显示一个令人着迷的问题。 在Init期间,IBInspectablevariables还没有真正设置! (因此,例如,我根据IBInspectable设置的控件的“样式”进行了分支;它不会像@IBInspectable:在运行时不起作用!)
4 – 别的地方?
我试图在初始化,它运作良好。 但它打破了编辑工作的devise。
通过颠簸,我想出了这个(由于某种原因,都必须包括在内?)
@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) }
我不知道为什么会这样,我不明白为什么会有两个不同的init例程。
在UIButton中包含addTarget
的正确方法是什么?
如何执行awakeFromNib
并在那里做?
在Interface Builder的上下文中运行代码时,还可以有条件地运行(或不运行)代码:
#if !TARGET_INTERFACE_BUILDER // this code will run in the app itself #else // this code will execute only in IB #endif
您不应该将生成该操作的对象添加为目标。
目标和callback应该是另一个对象,通常是视图控制器。
有2个inits方法,因为button可以通过调用init或从nib / xib的反序列化( NSCoder
)进程来实例化。 由于您可能将button添加到故事板,所以称为init?(_: NSCoder)
的init方法。
[UPDATE]
我同意你在评论中的意见,但我认为action-target模式应该用于与其他对象进行通信,我使用条件性的,因为据我所知,我从来没有见过像你在苹果公司写的东西代码或其他库。 如果你想拦截并在button内做一些动作,你应该重写UIControl
暴露的一些方法。
关于可devise性,你再次正确。 如果以编程方式创buildbutton,则调用init(frame)
,如果button来自xib,则调用init(coder)
。
方法init(frame)
在devise过程中也被调用。 在这一点上,我认为最好的select是直接debugging你的视图。
- 在你的UIButton子类中放置一些断点
- select故事板中的视图
- 转到编辑器 – >debugging选定的视图
现在你应该能够理解问题所在。
TL;博士
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") } } }
为什么不使用addTarget
addTarget
方法是被认为是“ 公共 ”的动作 – 目标接口的一部分。 任何与你的button相关的东西都可以说, 删除它的所有动作 ,有效地打破它。 它优先使用一些“ 保护 ”的手段,例如endTrackingWithTouch
只能被覆盖 ,不能直接调用。 这样就不会干扰任何使用动作目标机制的外部对象。
(我知道在ObjC / UIKit中没有严格的“公共”或“保护”,但是这个概念依然存在)
你的方式
如果你想完全按照你的方式来做,那么你的例子是好的,只需init(frame:CGRect)
调用复制到init(frame:CGRect)
。
或者你可以把addTarget
放在awakeFromNib
(不要忘记超级),而不是init?(coder decoder: NSCoder)
,但是你将被迫执行init的编码器,所以…
layoutSubviews
和didMoveToSuperView
都是可怕的想法。 两者都可能发生不止一次,导致再次增加目标行动。 然后blah
将被多次调用一次点击。
顺便一提
苹果的方式
通过Cocoa MVC (由UIKit类implmentation实施),您应该将该动作分配给控制该button的对象,animation或不animation。 大多数情况下,对象将是cocoaMVC “控制器” – UIViewController
。
如果以编程方式创buildbutton,则UIViewController
应该在重载的loadView
或viewDidLoad
为其自身分配目标。 当从nib加载button时,优先的方式是在xib本身中分配目标动作。
老好MVC
正如这里所提到的,在真正的MVC视图中不会向自己发送操作。 在UIKit中最接近实际的MVC控制器的是UIGestureRecognizer
。
被警告说,用UIKit类设置真正的MVC是非常困难的。
你的初始化方法是不正确的,这将工作:
“`迅速
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) }
“`