Swift
Это более одного способа подкласса UIControl
. Когда родительское представление должно реагировать на события касания или получать другие данные от элемента управления, это обычно делается с использованием (1) целей или (2) шаблона делегата с переопределенными событиями касания. Для полноты картины я также покажу, как (3) сделать то же самое с распознавателем жестов. Каждый из этих методов будет вести себя как следующая анимация:
Вам нужно только выбрать один из следующих методов.
Метод 1: Добавить цель
Подкласс 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
(без двоеточия). Двоеточие означает, что параметр передается методу действия. В методе действия укажите, что параметр является ссылкой на ваш подкласс 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 выше.