如何从UIScrollView窃取触摸?(How to steal touches from UIScrollView?)

编程入门 行业动态 更新时间:2024-10-21 10:36:57
如何从UIScrollView窃取触摸?(How to steal touches from UIScrollView?)

今天在我的创意时间,我做了一些非常全面的研究,关于如何从UIScrollView中窃取触摸,并立即将它们发送到特定的子视图,同时保持其余滚动视图的默认行为。 考虑在UITableView中使用UIPickerView。 默认行为是,如果您将手指拖动到选取器视图上,滚动视图将滚动并且选取器视图将保持不变。

我尝试的第一件事是重写

- (BOOL)touchesShouldCancelInContentView:(UIView *)view

并且不允许UIScrollView取消选取器视图内的接触。 这有效,但它有一个不愉快的副作用。 您希望拾取器视图立即作出响应,因此您将不得不将delaysContentTouches设置为NO。 问题是你不希望表格视图的其余部分立即作出响应,因为如果它在表格视图单元格始终会在滚动开始之前几毫秒高亮显示。

我尝试的第二件事是重写

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

因为我已经读过滚动视图总是返回自身,所以它会从它的子视图中“窃取”触摸,并且如果它们对滚动视图不感兴趣,那么将它们发送到子视图。 但是,这不再是事实。 UIScrollView的hitTest的默认实现:withEvent:实际返回应该接收触摸的子视图。 相反,它使用手势识别器来拦截触摸。

所以我尝试的第三件事是实现我自己的手势识别器,并且如果触摸在选取器视图之外并且否则成功,则导致它失败。 然后,我将所有滚动视图的手势识别器设置为失败,除非我的手势识别器使用以下代码失败:

for (UIGestureRecognizer * gestureRecognizer in self.tableView.gestureRecognizers) { [gestureRecognizer requireGestureRecognizerToFail:myRecognizer]; }

这实际上窃取了滚动视图中的触摸,但拾取器视图从不接收它们。 所以我虽然也许我可以发送我的手势识别器使用此代码接收的所有触摸:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { for (UITouch *touch in touches) [touch.view touchesBegan:touches withEvent:event]; }

上面的代码是一个简化版本。 我还确保视图是一个选择器视图(或其中一个子视图),并为上述手势识别器设置适当的状态。 我也做了同样的取消,结束和移动。 但是,选取器视图仍然没有响应。

在回到我的正常工作之前,我还尝试了最后一件事。 在我广泛的使用google搜索的过程中,我读到了嵌套的UIScrollViews,自3.x开始魔术般地工作,所以我尝试将我的拾取器视图放在嵌套的UIScrollView中并在其上设置以下属性:

scrollView.delaysContentTouches = NO; scrollView.canCancelContentTouches = NO;

正如人们所期望的那样,外层滚动视图不会将内​​层滚动视图与处理选取器视图的任何内容视为不同,因此内层滚动视图不会接收到触摸。 我认为这是一个很长的过程,但实施起来很简单,所以我认为值得一试。

我知道的是,UIScrollView有一个名为UIScrollViewDelayedTouchesBeganGestureRecognizer的手势识别器,截取触摸并在150(?)ms之后将它们发送到适当的子视图。 我在想,我应该能够编写一个类似的识别器,使滚动视图的默认识别器失败,而不是延迟触摸立即将它们发送到选择器视图。 所以,如果有人知道如何编写这样一个识别器,请告诉我,如果您有任何其他解决方案,您也非常欢迎分享。

感谢你阅读整个问题,即使你不知道答案,你仍然可以提出这个问题,以便得到更多的关注(希望有人能够回答)。 谢谢! :)

Today on my creative time I did some quite comprehensive research on how to steal touches from a UIScrollView and send them instantly to a specific subview, while maintaining the default behavior for the rest of the scroll view. Consider having a UIPickerView inside of a UITableView. The default behavior is that if you drag your finger over the picker view, the scroll view will scroll and the picker view will remain unchanged.

The first thing I tried was to override

- (BOOL)touchesShouldCancelInContentView:(UIView *)view

and simply not allow the UIScrollView to cancel touches inside the picker view. This works, but it has an unpleasant side effect. You would like the picker view to respond immediately and thus you will have to set delaysContentTouches to NO. The problem is that you don't want the rest of the table view to respond immediately, because if it does the table view cell will always get highlighted for a few milliseconds before the scrolling starts.

The second thing I tried was to override

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

because I had read that the scroll view always returns itself, so that it will "steal" the touches from its subviews and later send them to the subview if they weren't of interest to the scroll view. However, this isn't true anymore. UIScrollView's default implementation of hitTest:withEvent: actually returns the subview that should receive the touch. Instead it uses gesture recognizers to intercept the touches.

So the third thing I attempted was to implement my own gesture recognizer and cause it to fail if the touch was outside of the picker view and otherwise succeed. Then I set all the scroll view's gesture recognizers to fail unless my gesture recognizer failed using the following code:

for (UIGestureRecognizer * gestureRecognizer in self.tableView.gestureRecognizers) { [gestureRecognizer requireGestureRecognizerToFail:myRecognizer]; }

This does in fact steal the touches from the scroll view, but the picker view never receives them. So I though maybe I could just send all the touches that my gesture recognizer receives using this code:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { for (UITouch *touch in touches) [touch.view touchesBegan:touches withEvent:event]; }

The above code is a simplified version. I also make sure that the view is a picker view (or one of it's subviews) and set the appropriate state for the gesture recognizer as I mentioned above. I also did the same for canceled, ended and moved. However, the picker view was still not responding.

I also tried one last thing before returning to my regular work. During my extensive googling I read that nested UIScrollViews just magically worked since 3.x, so I tried putting my picker view inside a nested UIScrollView and set the following properties on it:

scrollView.delaysContentTouches = NO; scrollView.canCancelContentTouches = NO;

As one would expect the outer scroll view didn't treat the inner scroll view any different than it treated the picker view, so the inner scroll view did not receive the touches. I thought that it was a long shot, but it was simple enough to implement, so I thought it was worth to give it a shot.

What I know is that UIScrollView has a gesture recognizer named UIScrollViewDelayedTouchesBeganGestureRecognizer that intercepts the touches and sends them to the appropriate subview after 150 (?) ms. I'm thinking that I should be able to write a similar recognizer that causes the scroll view's default recognizers to fail and instead of delaying the touches immediately sends them to the picker view. So if anyone knows how to write such a recognizer please let me know and if you have any other solution to the problem, you're very welcome share that as well.

Thank you for reading through the whole question and even if you don't know the answer you could still upvote the question so that it gets more attention (hopefully from someone that can answer it). Thanks! :)

最满意答案

有时你必须先问问题,然后才能找到答案。 丹雷有一个类似的问题,并用一个非常不同的解决方案解决了它。

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event { UIView* result = [super hitTest:point withEvent:event]; if ([result.superview isKindOfClass:[UIPickerView class]]) { self.scrollEnabled = NO; } else { self.scrollEnabled = YES; } return result; }

我测试了代码,它对我来说也很好。 但是,这并不是真的从滚动视图中窃取触摸,所以如果有人知道如何真正窃取那将是伟大的触摸。

来源: UITableView.tableFooterView里面的UIPickerView没有收到拖动触摸

Sometimes you have to ask the question before you can find the answer. Dan Ray had a similar problem and solved it with a very different solution.

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event { UIView* result = [super hitTest:point withEvent:event]; if ([result.superview isKindOfClass:[UIPickerView class]]) { self.scrollEnabled = NO; } else { self.scrollEnabled = YES; } return result; }

I've tested the code and it works fine for me as well. However, this is not really stealing touches from the scroll view, so if anyone knows how to actually steal touches that would be great.

Source: UIPickerView inside UITableView.tableFooterView doesn't receive drag touches

更多推荐

本文发布于:2023-08-07 06:03:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1462658.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:UIScrollView   steal   touches

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!