如何正确子类UIControl?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何正确子类UIControl?相关的知识,希望对你有一定的参考价值。

我不想UIButton或类似的东西。我想直接继承UIControl,让我自己的,很特别的控制。

但由于某些原因,没有我重写任何方法得到不断呼吁。目标 - 动作东西的作品,以及指标得到适当行动的消息。然而,我UIControl子类中,我要赶触摸坐标,这样做的唯一途径似乎是压倒一切的这些家伙:

- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
    NSLog(@"begin touch track");
    return YES;
}

- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
    NSLog(@"continue touch track");
    return YES;
}

他们得到从来不叫,即使UIControl被实例化与UIView的候任初始化,initWithFrame:

所有的例子,我总能找到使用UIButtonUISlider为基地的子类,但我想去接近UIControl,因为那是源我想要什么:快速,无迟滞的触摸坐标。

答案

我知道这个问题是古老的,但我有同样的问题,我想我应该给我的2美分。

如果你的控件有任何子视图可言,beginTrackingWithTouchtouchesBegan等可能不会被调用,因为这些子视图吞咽触摸事件。

如果你不希望这些子视图来处理触摸,你可以设置userInteractionEnabledNO,这样子视图简单地通过事件。然后你就可以覆盖touchesBegan/touchesEnded和管理所有接触那里。

希望这可以帮助。

另一答案

Swift

这是继承UIControl不止一种方法。当父视图需要反应以从控制触摸事件或让其他数据,这通常是使用(1)目标或(2)的委托模式与重写的触摸事件来完成。为了完整起见我还将展示如何(3)做同样的事情用一个手势识别。每种方法都将像下面的动画:

enter image description here

你只需要选择下列方法之一。


Method 1: Add a Target

一个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方法。如果您忘记添加一个冒号,或者如果你没有的参数的正确数量,你会得到一个崩溃。
  • 由于this answer为与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)
}

Method 2: Delegate Pattern and Override Touch Events

子类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"
    }
}

笔记

  • 要了解更多关于委托模式,见this answer
  • 这是没有必要使用委托这些方法,如果他们只被自定义的控件本身内使用。我可能只是增加了一个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") } }

Method 3: Use a Gesture Recognizer

添加一个手势识别器可以在任何视图来完成,它也适用于一个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 gest

以上是关于如何正确子类UIControl?的主要内容,如果未能解决你的问题,请参考以下文章

UIControl 子类延迟响应

突出显示 UIControl 子类

使用 IB 中的设计重用 UIControl 子类

UIControl 子类 - 如何停止触摸事件流?

如何正确子类化从 Swift 中的 xib 加载的视图类?

iOS子类化UIControl没有响应