在UIButton中初始化目标的位置? 特别关心IBDesignable

我有一个

@IBDesignable class Fancy:UIButton 

我要

 addTarget(self, action:#selector(blah), forControlEvents: UIControlEvents.TouchUpInside) 

那么UIButton应该在哪里做?

addTarget最好的地方在哪里?

1 – 我见过layoutSubviewsbuild议 – 是吗?

注意 – 实验表明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并在那里做?

https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Protocols/NSNibAwaking_Protocol/#//apple_ref/occ/instm/NSObject/awakeFromNib

在Interface Builder的上下文中运行代码时,还可以有条件地运行(或不运行)代码:

 #if !TARGET_INTERFACE_BUILDER // this code will run in the app itself #else // this code will execute only in IB #endif 

(请参阅http://nshipster.com/ibinspectable-ibdesignable/

您不应该将生成该操作的对象添加为目标。
目标和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的编码器,所以…

layoutSubviewsdidMoveToSuperView都是可怕的想法。 两者都可能发生不止一次,导致再次增加目标行动。 然后blah将被多次调用一次点击。

顺便一提

苹果的方式

通过Cocoa MVC (由UIKit类implmentation实施),您应该将该动作分配给控制该button的对象,animation或不animation。 大多数情况下,对象将是cocoaMVC “控制器” – UIViewController

如果以编程方式创buildbutton,则UIViewController应该在重载的loadViewviewDidLoad为其自身分配目标。 当从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) } 

“`