UISlider 返回两个 Touch Up Inside 事件,为什么会发生这种情况?

发布于 2024-07-25 23:50:58 字数 443 浏览 2 评论 0原文

我有一个滑块,用于在某处设置浮点值。 我将 Value Changed 连接到 viewController 中的方法。 那部分工作正常。

我需要知道用户何时开始触摸控件,但不一定是滑块更改的每个瞬间(我为此收到了 Value Changed 事件)。 因此,我将 Touch Up Inside 事件连接到 viewController 中的另一个方法。

问题是,当用户触摸 UISlider 控件时,该方法会被调用两次。 搞什么? 对于UIButtons或其他触摸事件(例如Touch Down)来说,这种方式不起作用。

我想我可以解决这个问题,但这似乎是滑块控件处理触摸方式的一个错误。 有谁知道为什么会发生?

顺便说一句:即使内部触摸是唯一连接的事件,双触摸事件也会发生。

I have a slider that I'm using to set a float value somewhere. I connect Value Changed to a method in my viewController. That part works fine.

I need to know when the user starts touching the control but not necessarily every single instant that the slider changes (I receive the Value Changed events for that). So I connected a Touch Up Inside event to another method in the viewController.

The problem it, that method gets called twice when the user touches the UISlider control. WTF? It doesn't work that way with UIButtons or other touch events like Touch Down.

I can work around it, I think, but it seems like a bug in the way the slider control handles touches. Does anybody know why it happens?

BTW: the double touch event happens even when Touch Up Inside is the only connected event.

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

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

发布评论

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

评论(9

寻找我们的幸福 2024-08-01 23:50:58

这样就不会因未来的更新而中断

[customSlider addTarget:self action:@selector(sliderDown:) forControlEvents:UIControlEventTouchDown];

[customSlider addTarget:self action:@selector(sliderAction:) forControlEvents:UIControlEventTouchUpInside];



- (void)sliderDown:(id)sender
{

    sliderused = NO;

}

- (void)sliderAction:(id)sender
{
    if(sliderused){
    return;
}

sliderused = YES;

//Your Code Here
}

Just so it doesn't break with future updates

[customSlider addTarget:self action:@selector(sliderDown:) forControlEvents:UIControlEventTouchDown];

[customSlider addTarget:self action:@selector(sliderAction:) forControlEvents:UIControlEventTouchUpInside];



- (void)sliderDown:(id)sender
{

    sliderused = NO;

}

- (void)sliderAction:(id)sender
{
    if(sliderused){
    return;
}

sliderused = YES;

//Your Code Here
}
萝莉病 2024-08-01 23:50:58

如果您愿意对滑块进行子类化,则可以通过实现 endTrackingWithTouch 而不执行任何操作来轻松解决此问题:

- (void) endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{}

这里唯一的警告是某些特定的 UIControlEvents 将无法正确触发。 我相信它们是以下这些,但我没有进行具体测试来看看哪些有效,哪些无效。 然而,触摸改变和内部修饰肯定可以工作,而无需重复触发。

   UIControlEventTouchDragInside     = 1 <<  2,
   UIControlEventTouchDragOutside    = 1 <<  3,
   UIControlEventTouchDragEnter      = 1 <<  4,
   UIControlEventTouchDragExit       = 1 <<  5,

if you are willing to sub-class the slider, you can easily solve this by implementing endTrackingWithTouch and doing nothing:

- (void) endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{}

the only caveat here is that some specific UIControlEvents will not fire properly. i believe they are the following ones, but i didn't do specific testing to see which ones work and which don't. however touch changed and touch up inside definitely do work without duplicate firing.

   UIControlEventTouchDragInside     = 1 <<  2,
   UIControlEventTouchDragOutside    = 1 <<  3,
   UIControlEventTouchDragEnter      = 1 <<  4,
   UIControlEventTouchDragExit       = 1 <<  5,
泛滥成性 2024-08-01 23:50:58

我也遇到了这个问题,我的解决方案是添加一个布尔值“allowEvent”。 然后我将 UIControlEventTouchDown 连接到将“allowEvent”设置为 YES 的函数。

在 UIControlEventTouchUpInside 的选择器中,我检查了allowEvent 是否为YES,然后执行该事件并将allowEvent 设置为NO。

那么即使事件触发两次,也只会执行一次。

希望能帮助到你!

I had this problem as well, my solution was to add a boolean "allowEvent". Then I connected the UIControlEventTouchDown to a function that set "allowEvent" to YES.

In the selector for the UIControlEventTouchUpInside, i checked if allowEvent is YES, then execute the event and set allowEvent to NO.

Then the event will only be executed once even if it fires twice.

Hope it helps!

风情万种。 2024-08-01 23:50:58

我认为最简单的解决方案是使用上面提到的标志解决方案。

我创建了一个名为 (BOOL)pressed 的标志并将其添加到 TouchUpInside 和 TouchDown 事件中。
所以,你只需要检查是否按下== YES。

// On down event
pressed = YES;

// On up event
if (pressed)
{
  pressed = NO;
  // code here

}

我希望它有帮助。

哈密

I think the simplest solution is to use a flag solution mentioned above.

I've created an flag called (BOOL)pressed and added to TouchUpInside and TouchDown events.
So, you just need to check if pressed == YES.

// On down event
pressed = YES;

// On up event
if (pressed)
{
  pressed = NO;
  // code here

}

I hope it helps.

Hami

冷默言语 2024-08-01 23:50:58

谢谢你们。 我有同样的问题。 这是一些为我解决这个问题的整数数学:

    static UInt32 counter = 0;

counter++;

if((counter/2)*2 != counter) return;

Thank you all. I have the same issue. Here is some integer math that solves it for me:

    static UInt32 counter = 0;

counter++;

if((counter/2)*2 != counter) return;
金橙橙 2024-08-01 23:50:58

我刚刚尝试过,我注意到同样的行为。 它从两个地方调用,首先从UISlider的endTrackingWithTouch:withEvent:方法(由UIControl的touchesEnded:withEvent:方法调用),然后从同一个UIControl的touchesEnded:withEvent:方法(首先调用endTrakingWithTouch:withEvent:方法) 。

此外,当触摸在滑块之外结束时,该方法仅由 endTrackingWithTouch:withEvent: 方法调用一次。 touchesEnded:withEvent: 方法调用此方法,但不会像触摸在控件内结束时那样调用回调。

无论如何,为什么要连接 Touch Up Inside? 您想知道用户何时开始触摸控件吗? 我认为你应该连接 Touch Down 而不是 Touch Up Inside。

编辑:我发现您需要更改后的值,并且更改后的值被调用得太频繁。 我刚刚重新运行测试,并注意到以下内容。 更改的值在两次 touch up 内部调用之间调用。 也许你可以用它做点什么?

I just tried, and I notice the same behavior. It get's called from two places, first from UISlider's endTrackingWithTouch:withEvent: method (which is called by UIControl's touchesEnded:withEvent: method), and then from the same UIControl's touchesEnded:withEvent: method (which first called the endTrakingWithTouch:withEvent: method).

Also, when the touch ended outside the slider, the method gets called only once by the endTrackingWithTouch:withEvent: method. The touchesEnded:withEvent: method calls this, but then does not call the callback as it did when the touch ended inside the control.

Anyhow, why do you connect Touch Up Inside? You want to know when the user starts touching the control right? I think you should then connect Touch Down instead of Touch Up Inside.

Edit: I see you need the changed value, and that value changed is called too often. I just re-ran the test, and noticed the following. The value changed is called between the two touch up inside calls. Maybe you can do something with that?

冰雪之触 2024-08-01 23:50:58

由于我之前的评论没有得到很好的接受,因此我决定针对该问题提出一个简单的解决方案,以防有人感兴趣。

我知道这不是世界上最有效的解决方案,但它的工作原理是一样的。

我创建了两个方法:sliderUp 和 sliderChanged,分别链接到 TouchUp 和 ValueChanged。 我还创建了一个全局 int 变量(timesFired,如果你愿意)设置为初始设置为零

在 sliderUp 方法中,我放置了我不介意触发两次的任何代码。 就我而言,如果滑块不在顶部(如滑动解锁功能),我会将滑块射回到底部。 我还在延迟后将 timesFired 重置回零。 以允许调用其他方法。 万一。 (performSelector: withObject: afterDelay: 如果你不知道该怎么做)

在 sliderChanged 方法中,我首先有一个 if 语句: if(timesFired < 1) { }
(我还有一个 if 语句来确保我的滑块处于最大值),然后包含我只想在滑块达到最大值时发生一次的所有代码。 为了确保该代码只被触发一次,我还增加了 timesFired。

一旦用户抬起手指,timesFired 就会被设置回零,整个过程就可以重新开始。

现在,一旦我能弄清楚如何在滑块到达顶部后冻结滑块,我就会对这个解决方案感到满意。 (简单地禁用它会起作用吗?)

Since my previous comment was not well received, I decided to come up with a simple work around for the problem in case anyone is interested.

I know that it isn't the most efficient solution in the world, but it works just the same.

I created two methods: sliderUp and sliderChanged linked to TouchUp and ValueChanged, respectively. I also created a global int variable (timesFired, if you will) set to initially set to zero

In the sliderUp method I put any code that I didn't mind firing twice. In my case I shoot the slider back down to the bottom if it is not at the top (like the slide to unlock function). I also reset the timesFired back to zero after a delay. to allow for the other method to be called. just in case. (performSelector: withObject: afterDelay: if you don't know how to do that)

In the sliderChanged method I first have an if statement: if(timesFired < 1) { }
(I also have an if statement to make sure my slider is at the maximum) and then include all of the code that I want to happen only once, when the slider reaches the maximum value. to make sure that that code only gets fired once, i also increment timesFired.

As soon as the user lifts up their finger on the timesFired will be set back to Zero and the whole process can start anew.

Now, as soon as I can figure out how to freeze the slider after they get to the top, I will actually be happy with this solution. (will simply disabling it work?)

居里长安 2024-08-01 23:50:58

该错误已于 4.2 版本修复。
我现在使用 Dan 的代码如下:

static UInt32 counter = 0;

if ([[UIDevice currentDevice].systemVersion compare: @"4.2" options: NSNumericSearch] == NSOrderedAscending) {
    counter++;
    if((counter/2)*2 != counter) return;
}

This bug has been fixed as of 4.2.
I am now using Dan's code as follows:

static UInt32 counter = 0;

if ([[UIDevice currentDevice].systemVersion compare: @"4.2" options: NSNumericSearch] == NSOrderedAscending) {
    counter++;
    if((counter/2)*2 != counter) return;
}
痴骨ら 2024-08-01 23:50:58

一个简单的解决方案,如果苹果将来修复此功能(?),也不会破坏该解决方案
是添加一个属性来存储自上次交互以来的时间。
如果自上次交互以来已经过了不可能的短时间,则退出。

(在下面的代码中,latestTimeMark,最初在viewDidLoad中设置为当前时间)

NSDate *now = [NSDate date];
 double nowDouble = [now timeIntervalSince1970];
 double difference = nowDouble - self.latestTimeMark;
 self.latestTimeMark = nowDouble;
 //NSLog(@"difference is now: %f", difference);
 if(difference<0.01f) return;

A simple solution, that won't break if Apple fix this ...feature (?) in the future
is to add a property that stores the time since last interaction.
and exit if it is an impossible short time since last interaction.

(in the code below, latestTimeMark, initially set to current time in viewDidLoad)

NSDate *now = [NSDate date];
 double nowDouble = [now timeIntervalSince1970];
 double difference = nowDouble - self.latestTimeMark;
 self.latestTimeMark = nowDouble;
 //NSLog(@"difference is now: %f", difference);
 if(difference<0.01f) return;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文