迅速
这些是子类化的不止一种方式UIControl
。当父视图需要对触摸事件做出反应或从控件获取其他数据时,通常使用 (1) 目标或 (2) 具有覆盖触摸事件的委托模式来完成。为了完整起见,我还将展示如何 (3) 使用手势识别器做同样的事情。这些方法中的每一个都将类似于以下动画:

您只需要选择以下方法之一。
方法一:添加目标
子UIControl
类支持已内置的目标。如果您不需要将大量数据传递给父类,这可能是您想要的方法。
MyCustomControl.swift
import UIKit
class MyCustomControl: UIControl {
// You don't need to do anything special in the control for targets to work.
}
ViewController.swift
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var myCustomControl: MyCustomControl!
@IBOutlet weak var trackingBeganLabel: UILabel!
@IBOutlet weak var trackingEndedLabel: UILabel!
@IBOutlet weak var xLabel: UILabel!
@IBOutlet weak var yLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Add the targets
// Whenever the given even occurs, the action method will be called
myCustomControl.addTarget(self, action: #selector(touchedDown), forControlEvents: UIControlEvents.TouchDown)
myCustomControl.addTarget(self, action: #selector(didDragInsideControl(_:withEvent:)),
forControlEvents: UIControlEvents.TouchDragInside)
myCustomControl.addTarget(self, action: #selector(touchedUpInside), forControlEvents: UIControlEvents.TouchUpInside)
}
// MARK: - target action methods
func touchedDown() {
trackingBeganLabel.text = "Tracking began"
}
func touchedUpInside() {
trackingEndedLabel.text = "Tracking ended"
}
func didDragInsideControl(control: MyCustomControl, withEvent event: UIEvent) {
if let touch = event.touchesForView(control)?.first {
let location = touch.locationInView(control)
xLabel.text = "x: \(location.x)"
yLabel.text = "y: \(location.y)"
}
}
}
笔记
- 动作方法名称没有什么特别之处。我可以给他们打电话。我只需要小心地拼写方法名称,就像我在添加目标时所做的那样。否则你会崩溃。
- 中的两个冒号
didDragInsideControl:withEvent:
表示将两个参数传递给该didDragInsideControl
方法。如果您忘记添加冒号或没有正确数量的参数,您将遇到崩溃。
- 感谢此答案对
TouchDragInside
活动的帮助。
传递其他数据
如果您的自定义控件有一些价值
class MyCustomControl: UIControl {
var someValue = "hello"
}
您想在目标操作方法中访问,然后您可以传入对控件的引用。设置目标时,在操作方法名称后添加一个冒号。例如:
myCustomControl.addTarget(self, action: #selector(touchedDown), forControlEvents: UIControlEvents.TouchDown)
请注意,它是touchedDown:
(带冒号)而不是touchedDown
(不带冒号)。冒号表示正在将参数传递给操作方法。在 action 方法中,指定参数是对您的UIControl
子类的引用。通过该引用,您可以从控件中获取数据。
func touchedDown(control: MyCustomControl) {
trackingBeganLabel.text = "Tracking began"
// now you have access to the public properties and methods of your control
print(control.someValue)
}
方法 2:委托模式和覆盖触摸事件
子类UIControl
化使我们可以访问以下方法:
beginTrackingWithTouch
当手指第一次在控件的范围内向下触摸时调用。
continueTrackingWithTouch
当手指滑过控件甚至超出控件的边界时会重复调用。
endTrackingWithTouch
当手指离开屏幕时调用。
如果您需要对触摸事件进行特殊控制,或者如果您需要与父级进行大量数据通信,那么这种方法可能比添加目标更有效。
这是如何做到的:
MyCustomControl.swift
import UIKit
// These are out self-defined rules for how we will communicate with other classes
protocol ViewControllerCommunicationDelegate: class {
func myTrackingBegan()
func myTrackingContinuing(location: CGPoint)
func myTrackingEnded()
}
class MyCustomControl: UIControl {
// whichever class wants to be notified of the touch events must set the delegate to itself
weak var delegate: ViewControllerCommunicationDelegate?
override func beginTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) -> Bool {
// notify the delegate (i.e. the view controller)
delegate?.myTrackingBegan()
// returning true means that future events (like continueTrackingWithTouch and endTrackingWithTouch) will continue to be fired
return true
}
override func continueTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) -> Bool {
// get the touch location in our custom control's own coordinate system
let point = touch.locationInView(self)
// Update the delegate (i.e. the view controller) with the new coordinate point
delegate?.myTrackingContinuing(point)
// returning true means that future events will continue to be fired
return true
}
override func endTrackingWithTouch(touch: UITouch?, withEvent event: UIEvent?) {
// notify the delegate (i.e. the view controller)
delegate?.myTrackingEnded()
}
}
ViewController.swift
这就是将视图控制器设置为委托并响应我们自定义控件的触摸事件的方式。
import UIKit
class ViewController: UIViewController, ViewControllerCommunicationDelegate {
@IBOutlet weak var myCustomControl: MyCustomControl!
@IBOutlet weak var trackingBeganLabel: UILabel!
@IBOutlet weak var trackingEndedLabel: UILabel!
@IBOutlet weak var xLabel: UILabel!
@IBOutlet weak var yLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
myCustomControl.delegate = self
}
func myTrackingBegan() {
trackingBeganLabel.text = "Tracking began"
}
func myTrackingContinuing(location: CGPoint) {
xLabel.text = "x: \(location.x)"
yLabel.text = "y: \(location.y)"
}
func myTrackingEnded() {
trackingEndedLabel.text = "Tracking ended"
}
}
笔记
- 要了解有关委托模式的更多信息,请参阅此答案。
如果仅在自定义控件本身中使用这些方法,则无需使用委托和这些方法。我本可以添加一个print
语句来显示事件是如何被调用的。在这种情况下,代码将简化为
import UIKit
class MyCustomControl: UIControl {
override func beginTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) -> Bool {
print("Began tracking")
return true
}
override func continueTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) -> Bool {
let point = touch.locationInView(self)
print("x: \(point.x), y: \(point.y)")
return true
}
override func endTrackingWithTouch(touch: UITouch?, withEvent event: UIEvent?) {
print("Ended tracking")
}
}
方法 3:使用手势识别器
添加手势识别器可以在任何视图上完成,它也适用于UIControl
. 为了获得与顶部示例相似的结果,我们将使用UIPanGestureRecognizer
. 然后通过测试触发事件时的各种状态,我们可以确定发生了什么。
MyCustomControl.swift
import UIKit
class MyCustomControl: UIControl {
// nothing special is required in the control to make it work
}
ViewController.swift
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var myCustomControl: MyCustomControl!
@IBOutlet weak var trackingBeganLabel: UILabel!
@IBOutlet weak var trackingEndedLabel: UILabel!
@IBOutlet weak var xLabel: UILabel!
@IBOutlet weak var yLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// add gesture recognizer
let gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(gestureRecognized(_:)))
myCustomControl.addGestureRecognizer(gestureRecognizer)
}
// gesture recognizer action method
func gestureRecognized(gesture: UIPanGestureRecognizer) {
if gesture.state == UIGestureRecognizerState.Began {
trackingBeganLabel.text = "Tracking began"
} else if gesture.state == UIGestureRecognizerState.Changed {
let location = gesture.locationInView(myCustomControl)
xLabel.text = "x: \(location.x)"
yLabel.text = "y: \(location.y)"
} else if gesture.state == UIGestureRecognizerState.Ended {
trackingEndedLabel.text = "Tracking ended"
}
}
}
笔记
- 不要忘记在
action: "gestureRecognized:"
. 冒号表示正在传入参数。
- 如果您需要从控件中获取数据,您可以按照上面的方法 2 实现委托模式。