在哪里初始化 UIButton 中的目标?特别关心 IBDesignable
Where to initialize target in a UIButton? Particularly caring for IBDesignable
我有一个
@IBDesignable
class Fancy:UIButton
我想
addTarget(self, action:#selector(blah),
forControlEvents: UIControlEvents.TouchUpInside)
那么应该在 UIButton 的什么地方完成?
addTarget
最好的地方在哪里?
1 - 我看到 layoutSubviews
建议 - 是吗?
注意 - 实验表明 layoutSubviews
的一个问题是,当然,只要事情四处移动,它就可以经常调用。多次“添加目标”是个坏主意。
2 - didMoveToSuperview
是另一个建议。
3 - Inits(之一)中的某处?
注意 - 如果您在 Init 中进行实验,就会发现一个有趣的问题。在 Init 期间,IBInspectable 变量尚未实际设置! (因此,例如,我根据 IBInspectable 设置的控制“样式”进行分支;它显然不起作用,因为 @IBInspectable:在 运行 时不起作用!)
4 - 其他地方???
我尝试在 Init 中执行此操作,并且效果很好。但它会破坏可设计对象在编辑器中的工作。
经过折腾,我想到了这个(出于某种原因两者都必须包括在内?)
@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
的正确方法是什么?
您不应将产生动作的同一对象添加为目标。
目标及其回调应该是另一个对象,通常是视图控制器。
有 2 个 inits 方法,因为可以通过调用 init 或从 nib/xib 反序列化 (NSCoder
) 的过程来实例化按钮。由于您可能已将按钮添加到故事板,因此调用的 init 方法是 init?(_: NSCoder)
。
[更新]
我同意你在评论中所说的内容,但我认为动作目标模式应该用于与其他对象进行通信,我正在使用条件,因为据我所知我从未见过像你在 Apple 中写的那样的东西代码或其他一些库。如果您想拦截按钮并在按钮内进行一些操作,您可能应该重写 UIControl
.
中公开的一些方法
关于可设计的,你是,再一次,正确的。如果您以编程方式创建按钮,则调用 init(frame)
,如果按钮来自 xib,则调用 init(coder)
。
方法init(frame)
在设计过程中也会被调用。在这一点上,我认为最好的选择是直接调试你的视图。
- 在 UIButton 子类中放置一些断点
- Select 故事板中的视图
- 转到编辑器 -> 调试所选视图
现在你应该明白问题出在哪里了
您的初始化方法不正确,这将起作用:
```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)
}
```
实施 awakeFromNib
并在那里实施如何?
您还可以有条件地 运行(或不 运行)在 Interface Builder 的上下文中 运行 编码:
#if !TARGET_INTERFACE_BUILDER
// this code will run in the app itself
#else
// this code will execute only in IB
#endif
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, effectively breaking it. It is preffered to use some of 'protected' means, for instance endTrackingWithTouch
which is accessible only to be overriden,而不是直接调用。这样它就不会干扰任何使用动作-目标机制的外部对象。
(我知道ObjC/UIKit中没有严格的'public'或'protected',但概念依然存在)
你的方式
如果您想按照自己的方式进行操作,那么您的示例就很好,只需将 addTarget
调用复制到 init(frame:CGRect)
。
或者你可以把 addTarget
放在 awakeFromNib
中(不要忘记 super)而不是 init?(coder decoder: NSCoder)
,但是无论如何你将被迫用编码器实现 init,所以.. .
layoutSubviews
和 didMoveToSuperView
都是糟糕的想法。两者都可能发生不止一次,导致再次添加 blah
目标操作。然后blah
会被一次点击调用多次
顺便说一句
Apple 方式
通过 Cocoa MVC(由 UIKit classes 实现强制执行),您应该将该操作分配给控制该按钮的对象,无论是否有动画。大多数情况下,该对象将是 Cocoa MVC 'Controller' - UIViewController
.
如果您以编程方式创建按钮 UIViewController
应该在覆盖 loadView
或 viewDidLoad
中将目标分配给自身。当从 nib 加载按钮时,首选方法是在 xib 本身中分配目标操作。
旧的好 MVC
正如 中提到的 here 真正的 MVC 视图不会向自己发送操作。 UIKit 中最接近 real MVC Controller 的是 UIGestureRecognizer
.
请注意,使用 UIKit class 集很难提取 真正的 MVC。
我有一个
@IBDesignable
class Fancy:UIButton
我想
addTarget(self, action:#selector(blah),
forControlEvents: UIControlEvents.TouchUpInside)
那么应该在 UIButton 的什么地方完成?
addTarget
最好的地方在哪里?
1 - 我看到 layoutSubviews
建议 - 是吗?
注意 - 实验表明 layoutSubviews
的一个问题是,当然,只要事情四处移动,它就可以经常调用。多次“添加目标”是个坏主意。
2 - didMoveToSuperview
是另一个建议。
3 - Inits(之一)中的某处?
注意 - 如果您在 Init 中进行实验,就会发现一个有趣的问题。在 Init 期间,IBInspectable 变量尚未实际设置! (因此,例如,我根据 IBInspectable 设置的控制“样式”进行分支;它显然不起作用,因为 @IBInspectable:在 运行 时不起作用!)
4 - 其他地方???
我尝试在 Init 中执行此操作,并且效果很好。但它会破坏可设计对象在编辑器中的工作。
经过折腾,我想到了这个(出于某种原因两者都必须包括在内?)
@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
的正确方法是什么?
您不应将产生动作的同一对象添加为目标。
目标及其回调应该是另一个对象,通常是视图控制器。
有 2 个 inits 方法,因为可以通过调用 init 或从 nib/xib 反序列化 (NSCoder
) 的过程来实例化按钮。由于您可能已将按钮添加到故事板,因此调用的 init 方法是 init?(_: NSCoder)
。
[更新]
我同意你在评论中所说的内容,但我认为动作目标模式应该用于与其他对象进行通信,我正在使用条件,因为据我所知我从未见过像你在 Apple 中写的那样的东西代码或其他一些库。如果您想拦截按钮并在按钮内进行一些操作,您可能应该重写 UIControl
.
中公开的一些方法
关于可设计的,你是,再一次,正确的。如果您以编程方式创建按钮,则调用 init(frame)
,如果按钮来自 xib,则调用 init(coder)
。
方法init(frame)
在设计过程中也会被调用。在这一点上,我认为最好的选择是直接调试你的视图。
- 在 UIButton 子类中放置一些断点
- Select 故事板中的视图
- 转到编辑器 -> 调试所选视图
现在你应该明白问题出在哪里了
您的初始化方法不正确,这将起作用:
```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)
}
```
实施 awakeFromNib
并在那里实施如何?
您还可以有条件地 运行(或不 运行)在 Interface Builder 的上下文中 运行 编码:
#if !TARGET_INTERFACE_BUILDER
// this code will run in the app itself
#else
// this code will execute only in IB
#endif
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, effectively breaking it. It is preffered to use some of 'protected' means, for instance endTrackingWithTouch
which is accessible only to be overriden,而不是直接调用。这样它就不会干扰任何使用动作-目标机制的外部对象。
(我知道ObjC/UIKit中没有严格的'public'或'protected',但概念依然存在)
你的方式
如果您想按照自己的方式进行操作,那么您的示例就很好,只需将 addTarget
调用复制到 init(frame:CGRect)
。
或者你可以把 addTarget
放在 awakeFromNib
中(不要忘记 super)而不是 init?(coder decoder: NSCoder)
,但是无论如何你将被迫用编码器实现 init,所以.. .
layoutSubviews
和 didMoveToSuperView
都是糟糕的想法。两者都可能发生不止一次,导致再次添加 blah
目标操作。然后blah
会被一次点击调用多次
顺便说一句
Apple 方式
通过 Cocoa MVC(由 UIKit classes 实现强制执行),您应该将该操作分配给控制该按钮的对象,无论是否有动画。大多数情况下,该对象将是 Cocoa MVC 'Controller' - UIViewController
.
如果您以编程方式创建按钮 UIViewController
应该在覆盖 loadView
或 viewDidLoad
中将目标分配给自身。当从 nib 加载按钮时,首选方法是在 xib 本身中分配目标操作。
旧的好 MVC
正如 中提到的 here 真正的 MVC 视图不会向自己发送操作。 UIKit 中最接近 real MVC Controller 的是 UIGestureRecognizer
.
请注意,使用 UIKit class 集很难提取 真正的 MVC。