如何正确子类化 UIControl?

发布于 2024-07-30 08:50:18 字数 728 浏览 10 评论 0原文

我不需要 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使用 UIViewinitWithFrame: 中的指定初始值设定项进行实例化。

我能找到的所有示例始终使用 UIButtonUISlider 作为子类化的基础,但我想更接近 UIControl 因为那是我想要什么:快速且无延迟的触摸坐标。

I don't want UIButton or anything like that. I want to subclass UIControl directly and make my own, very special control.

But for some reason, none of any methods I override get ever called. The target-action stuff works, and the targets receive appropriate action messages. However, inside my UIControl subclass I have to catch touch coordinates, and the only way to do so seems to be overriding these guys:

- (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;
}

They get never called, even though the UIControl is instantiated with the designates initializer from UIView, initWithFrame:.

All examples I can find always use a UIButton or UISlider as base for subclassing, but I want to go closer to UIControl since that's the source for what I want: Fast and undelayed Touch coordinates.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

独自←快乐 2024-08-06 08:50:18

我知道这个问题很古老,但我也遇到了同样的问题,我想我应该给我 2 美分。

如果您的控件有任何子视图,则 beginTrackingWithTouchtouchesBegan 等可能不会被调用,因为这些子视图正在吞噬触摸事件。

如果您不希望这些子视图处理触摸,可以将 userInteractionEnabled 设置为 NO,这样子视图就会简单地传递事件。 然后您可以覆盖 touchesBegan/touchesEnded 并在那里管理所有触摸。

希望这可以帮助。

I know this question is ancient, but I had the same problem and I thought I should give my 2 cents.

If your control has any subviews at all, beginTrackingWithTouch, touchesBegan, etc might not get called because those subviews are swallowing the touch events.

If you don't want those subviews to handle touches, you can set userInteractionEnabled to NO, so the subviews simply passes the event through. Then you can override touchesBegan/touchesEnded and manage all your touches there.

Hope this helps.

忆离笙 2024-08-06 08:50:18

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 语句来显示事件是如何被调用的。 在这种情况下,代码将简化为

    导入 UIKit 
      类 MyCustomControl: UIControl { 
    
          override func beginTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) ->;   布尔{ 
              print("开始跟踪") 
              返回真 
          } 
    
          override func continueTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) ->;   布尔{ 
              让点 = touch.locationInView(self) 
              print("x: \(点.x), y: \(点.y)") 
              返回真 
          } 
    
          覆盖 func endTrackingWithTouch(touch: UITouch?, withEvent event: UIEvent?) { 
              print("追踪结束") 
          } 
      } 
      

方法 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 那样实现委托模式。

Swift

These is more than one way to subclass UIControl. When a parent view needs to react to touch events or get other data from the control, this is usually done using (1) targets or (2) the delegate pattern with overridden touch events. For completeness I will also show how to (3) do the same thing with a gesture recognizer. Each of these methods will behave like the following animation:

enter image description here

You only need to choose one of the following methods.


Method 1: Add a Target

A UIControl subclass has support for targets already built in. If you don't need to pass a lot of data to the parent, this is probably the method you want.

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

Notes

  • There is nothing special about the action method names. I could have called them anything. I just have to be careful to spell the method name exactly like I did where I added the target. Otherwise you get a crash.
  • The two colons in didDragInsideControl:withEvent: means that two parameters are being passed to the didDragInsideControl method. If you forget to add a colon or if you don't have the correct number of parameters, you will get a crash.
  • Thanks to this answer for help with the TouchDragInside event.

Passing other data

If you have some value in your custom control

class MyCustomControl: UIControl {
    var someValue = "hello"
}

that you want to access in the target action method, then you can pass in a reference to the control. When you are setting the target, add a colon after the action method name. For example:

myCustomControl.addTarget(self, action: #selector(touchedDown), forControlEvents: UIControlEvents.TouchDown) 

Notice that it is touchedDown: (with a colon) and not touchedDown (without a colon). The colon means that a parameter is being passed to the action method. In the action method, specify that the parameter is a reference to your UIControl subclass. With that reference, you can get data from your control.

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

Subclassing UIControl gives us access to the following methods:

  • beginTrackingWithTouch is called when the finger first touches down within the control's bounds.
  • continueTrackingWithTouch is called repeatedly as the finger slides across the control and even outside of the control's bounds.
  • endTrackingWithTouch is called when the finger lifts off the screen.

If you need special control of the touch events or if you have a lot of data communication to do with the parent, then this method may work better then adding targets.

Here is how to do it:

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

This is how the view controller is set up to be the delegate and respond to touch events from our custom control.

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

Notes

  • To learn more about the delegate pattern, see this answer.
  • It is not necessary to use a delegate with these methods if they are only being used within the custom control itself. I could have just added a print statement to show how the events are being called. In that case, the code would be simplified to

    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

Adding a gesture recognizer can be done on any view and it also works on a UIControl. To get similar results to the example at the top, we will use a UIPanGestureRecognizer. Then by testing the various states when an event is fired we can determine what is happening.

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

Notes

  • Don't forget to add the colon after the action method name in action: "gestureRecognized:". The colon means that parameters are being passed in.
  • If you need to get data from the control, you could implement the delegate pattern as in method 2 above.
冰火雁神 2024-08-06 08:50:18

我花了很长时间努力寻找解决这个问题的方法,但我认为没有。 但是,仔细检查文档后,我认为应该调用 begintrackingWithTouch:withEvent:continueTrackingWithTouch:withEvent: 可能是一种误解...

< code>UIControl 文档说:

您可能想扩展UIControl
子类化有两个基本原因:

观察或修改调度
向目标发送的行动消息
特定事件要执行此操作,请重写
sendAction:to:forEvent:,评估
传入的选择器、目标对象或
“记下”位掩码并继续
必填。

提供自定义跟踪行为
(例如,更改突出显示
外观)为此,请覆盖一个
或以下所有方法:
beginTrackingWithTouch:withEvent:,
continueTrackingWithTouch:withEvent:,
endTrackingWithTouch:withEvent:

其中的关键部分(在我看来不是很清楚)是它说您可能想要扩展 UIControl 子类 - 而不是您可能想要直接扩展 UIControl 。 可能不应该调用 beginTrackingWithTouch:withEvent:continuetrackingWithTouch:withEvent: 来响应触摸,而应该调用 UIControl 直接子类调用它们,以便它们的子类可以监视跟踪。

因此,我的解决方案是重写 touchesBegan:withEvent:touchesMoved:withEvent: 并从那里调用它们,如下所示。 请注意,此控件未启用多点触摸,并且我不关心触摸结束和触摸取消事件,但如果您想完整/彻底,您可能也应该实现这些事件。

- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
    [super touchesBegan:touches withEvent:event];
    // Get the only touch (multipleTouchEnabled is NO)
    UITouch* touch = [touches anyObject];
    // Track the touch
    [self beginTrackingWithTouch:touch withEvent:event];
}

- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
    [super touchesMoved:touches withEvent:event];
    // Get the only touch (multipleTouchEnabled is NO)
    UITouch* touch = [touches anyObject];
    // Track the touch
    [self continueTrackingWithTouch:touch withEvent:event];
}

请注意,您还应该使用 sendActionsForControlEvents: 发送与您的控件相关的任何 UIControlEvent* 消息 - 这些消息可能会从超级方法中调用,我还没有测试过。

I have looked long and hard for a solution to this problem and I don't think there is one. However, on closer inspection of the documentation I think it might be a misunderstanding that begintrackingWithTouch:withEvent: and continueTrackingWithTouch:withEvent: are supposed to be called at all...

UIControl documentation says:

You may want to extend a UIControl
subclass for two basic reasons:

To observe or modify the dispatch of
action messages to targets for
particular events To do this, override
sendAction:to:forEvent:, evaluate the
passed-in selector, target object, or
“Note” bit mask and proceed as
required.

To provide custom tracking behavior
(for example, to change the highlight
appearance) To do this, override one
or all of the following methods:
beginTrackingWithTouch:withEvent:,
continueTrackingWithTouch:withEvent:,
endTrackingWithTouch:withEvent:.

The critical part of this, which is not very clear in my view, is that it says you may want to extend a UIControl subclass - NOT you may want to extend UIControl directly. It's possible that beginTrackingWithTouch:withEvent: and continuetrackingWithTouch:withEvent: are not supposed to get called in response to touches and that UIControl direct subclasses are supposed to call them so that their subclasses can monitor tracking.

So my solution to this is to override touchesBegan:withEvent: and touchesMoved:withEvent: and call them from there as follows. Note that multi-touch is not enabled for this control and that I don't care about touches ended and touches canceled events, but if you want to be complete/thorough you should probably implement those too.

- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
    [super touchesBegan:touches withEvent:event];
    // Get the only touch (multipleTouchEnabled is NO)
    UITouch* touch = [touches anyObject];
    // Track the touch
    [self beginTrackingWithTouch:touch withEvent:event];
}

- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
    [super touchesMoved:touches withEvent:event];
    // Get the only touch (multipleTouchEnabled is NO)
    UITouch* touch = [touches anyObject];
    // Track the touch
    [self continueTrackingWithTouch:touch withEvent:event];
}

Note that you should also send any UIControlEvent* messages that are relevant for your control using sendActionsForControlEvents: - these may be called from the super methods, I haven't tested it.

遇到 2024-08-06 08:50:18

我认为您忘记添加 [super] 调用 TouchesBegan/touchesEnded/touchesMoved。
如果您像这样重写 TouchBegan / TouchEnded 之类的方法

(BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event    
(BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event

将不起作用:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
   NSLog(@"Touches Began");
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
   NSLog(@"Touches Moved");
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
   NSLog(@"Touches Ended");
}

但是! 如果方法如下,则一切正常:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
   [super touchesBegan:touches withEvent:event];
   NSLog(@"Touches Began");
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
   [super touchesMoved:touches withEvent:event];
     NSLog(@"Touches Moved");
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
   [super touchesEnded:touches withEvent:event];
   NSLog(@"Touches Ended");
}

I think that you forgot to add [super] calls to touchesBegan/touchesEnded/touchesMoved.
Methods like

(BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event    
(BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event

aren't working if you overriding touchesBegan / touchesEnded like this :

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
   NSLog(@"Touches Began");
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
   NSLog(@"Touches Moved");
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
   NSLog(@"Touches Ended");
}

But! All works fine if methods will be like :

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
   [super touchesBegan:touches withEvent:event];
   NSLog(@"Touches Began");
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
   [super touchesMoved:touches withEvent:event];
     NSLog(@"Touches Moved");
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
   [super touchesEnded:touches withEvent:event];
   NSLog(@"Touches Ended");
}
夜夜流光相皎洁 2024-08-06 08:50:18

我经常使用的最简单的方法是扩展 UIControl,但利用继承的 addTarget 方法来接收各种事件的回调。 关键是监听发送者和事件,以便您可以找到有关实际事件的更多信息(例如事件发生的位置)。

因此,只需简单地子类化 UIControl,然后在 init 方法中(如果使用 nib,请确保还设置了 initWithCoder),添加以下内容:

[self addTarget:self action:@selector(buttonPressed:forEvent:) forControlEvents:UIControlEventTouchUpInside];

当然,您可以选择任何标准控件事件,包括 UIControlEventAllTouchEvents。 请注意,选择器将传递两个对象。 首先是控制。 第二个是有关事件的信息。 下面是使用触摸事件根据用户是否按下左侧和右侧按钮来切换按钮的示例。

- (IBAction)buttonPressed:(id)sender forEvent:(UIEvent *)event
{
    if (sender == self.someControl)
    {        
        UITouch* touch = [[event allTouches] anyObject];
        CGPoint p = [touch locationInView:self.someControl];
        if (p.x < self. someControl.frame.size.width / 2.0)
        {
            // left side touch
        }
        else
        {
            // right side touch
        }
    }
}

当然,这适用于非常简单的控件,您可能会达到这样的程度,这不会为您提供足够的功能,但这已经满足了我对自定义控件的所有目的,并且非常易于使用,因为我通常关心相同的内容UIControl 已支持的控件事件(触摸、拖动等...)

此处自定义控件的代码示例:自定义 UISwitch (注意:这不会向 buttonPressed:forEvent: 选择器注册,但您可以从上面的代码中看出)

The simplest approach that I often use is to extend UIControl, but to make use of the inherited addTarget method to receive callbacks for the various events. The key is to listen for both the sender and the event so that you can find out more information about the actual event (such as the location of where the event occurred).

So, just simply subclass UIControl and then in the init method (make sure your initWithCoder is also setup if you are using nibs) , add the following:

[self addTarget:self action:@selector(buttonPressed:forEvent:) forControlEvents:UIControlEventTouchUpInside];

Of course, you can choose any of the standard control events including UIControlEventAllTouchEvents. Notice that the selector will get passed two objects. The first is the control. The second is the info about the event. Here's an example of using the touch event to toggle a button depending on if the user pressed on the left and right.

- (IBAction)buttonPressed:(id)sender forEvent:(UIEvent *)event
{
    if (sender == self.someControl)
    {        
        UITouch* touch = [[event allTouches] anyObject];
        CGPoint p = [touch locationInView:self.someControl];
        if (p.x < self. someControl.frame.size.width / 2.0)
        {
            // left side touch
        }
        else
        {
            // right side touch
        }
    }
}

Granted, this is for pretty simplistic controls and you may reach a point where this will not give you enough functionality, but this has worked for all my purposes for custom controls to this point and is really easy to use since I typically care about the same control events that the UIControl already supports (touch up, drag, etc...)

Code sample of custom control here: Custom UISwitch (note: this does not register with the buttonPressed:forEvent: selector, but you can figure that out from the code above)

绝情姑娘 2024-08-06 08:50:18

我遇到了 UIControl 无法响应 beginTrackingWithTouchcontinueTrackingWithTouch 的问题。

我发现我的问题是当我执行 initWithFrame:CGRectMake() 时,我将框架做得很小(反应区域),并且只有几个点可以工作。 我将框架设置为与控件相同的大小,然后每当我按下控件中的任何位置时,它都会做出响应。

I was having trouble with a UIControl not responding to beginTrackingWithTouch and continueTrackingWithTouch.

I found my problem was when I did initWithFrame:CGRectMake() I made the frame to small (the area that reacts) and had only a couple point spot where it did work. I made the frame the same size as the control and then anytime I pressed on anywhere in the control it responded.

情释 2024-08-06 08:50:18

Obj C

我找到了 2014 年的相关文章 https://www.objc.io/issues /13-架构/行为/

有趣的是,它的方法是利用 IB 并将事件处理逻辑封装在指定对象内(他们称之为行为),从而从视图/视图控制器中删除逻辑,使其更轻。

Obj C

I found a related article from 2014 https://www.objc.io/issues/13-architecture/behaviors/.

The interesting thing about it is that its approach is to make use of IB and encapsulate the event handling logic inside a designated object (they call it behaviors), thus removing logic from your view/viewController making it lighter.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文