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

时间:2016-05-15 18:49:13

标签: ios swift uibutton initializer

我有一个

@IBDesignable
class Fancy:UIButton

我想

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

那么UIButton应该在哪里完成?

addTarget的最佳位置在哪里?

1 - 我看到layoutSubviews建议 - 是吗?

注意 - 实验表明layoutSubviews的一个问题是,当事情四处移动时,它可以经常被调用。 " addTarget"是一个坏主意。不止一次。

2 - didMoveToSuperview是另一个建议。

3 - Inits中的某个地方?

注意 - 如果你在Init中进行实验,那么实验会显示一个引人入胜的问题。在Init期间,IBInspectable变量尚未实际设置! (例如,我的分支取决于IBInspectable设置的"样式和#34;它显然不起作用@IBInspectable:在运行时不会工作!)

4 - 其他地方???

我试图在Init中做到这一点,并且效果很好。但它打破了编辑工作中的可设计性。

enter image description here

通过晃来晃去,我想出了这个(出于某种原因,两者都必须被包括在内?)

@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)
        }

我不知道为什么会有效,而且我不明白为什么会有两种不同的初始化例程。

在UIButton中包含addTarget的正确方法是什么?

4 个答案:

答案 0 :(得分:3)

您不应将生成动作的同一对象添加为目标 目标及其回调应该是另一个对象,通常是视图控制器 有2个inits方法,因为可以通过调用init或来自nib / xib的deserializion(NSCoder)进程来实例化按钮。由于您可能已将按钮添加到故事板,因此调用的init方法为init?(_: NSCoder)
[UPDATE]
我同意你在评论中所说的内容,但我认为动作目标模式应该用于与其他对象进行通信,我使用条件,因为据我所知,我从未见过像你在Apple中写的那样代码或其他一些库。如果你想拦截并在按钮内部做一些动作,你应该覆盖UIControl中公开的一些方法。
关于可设计,你再次是正确的。如果您以编程方式创建按钮,则会调用init(frame),如果按钮来自xib,则会调用init(coder)。 在可设计的过程中也调用方法init(frame)。在这一点上,我认为最好的选择是直接调试你的观点。

  • 在UIButton子类
  • 中放置一些断点
  • 在故事板中选择视图
  • 转到编辑器 - >调试选定的视图

现在你应该能够理解问题所在。

答案 1 :(得分:3)

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方法是动作目标界面的一部分,被视为' public '。任何参考你的按钮的东西都可以说是remove all of its actions,有效地打破它。它优先使用一些 受保护的'意味着,例如endTrackingWithTouch只能被overriden访问,而不是直接调用。这样它就不会使用动作目标机制干扰任何外部对象。

(我知道在ObjC / UIKit中没有严格的公共'或#39;保护'但概念仍然存在)

你的方式

如果您想完全按自己的方式行事,那么您的示例一切都很好,只需将addTarget电话复制到init(frame:CGRect)即可。

或者您可以将addTarget放入awakeFromNib(不要忘记超级)而不是init?(coder decoder: NSCoder),但无论如何您将被迫使用编码器实现init,所以..

layoutSubviewsdidMoveToSuperView都是糟糕的想法。两者都可能不止一次发生,导致再次添加blah目标操作。然后,只需单击一次即可多次调用blah

顺便说一下

Apple方式

通过Cocoa MVC(由UIKit类实施强制执行),您应该将该操作分配给控制该按钮的对象,动画与否。大多数情况下,该对象将是 Cocoa MVC ' Controller' - UIViewController

如果您以编程方式创建按钮UIViewController,则应在覆盖的loadViewviewDidLoad中将目标指定给自己。当从nib加载按钮时,优先的方法是在xib本身中分配目标动作。

Old Good MVC

如上所述真实MVC 视图中的here不会向自己发送动作。与UIKit中真实MVC 控制器最接近的是UIGestureRecognizer

请注意,使用UIKit类集设置真实MVC 非常困难。

答案 2 :(得分:2)

您的初始化方法不正确,这将有效:

```swift

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)
}

```

答案 3 :(得分:2)

如何实施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/