今天,在我的创作时间里,我做了一些相当全面的研究,了解如何从 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];
}
上面的代码是一个简化版本。我还确保该视图是选取器视图(或其子视图之一),并为手势识别器设置适当的状态,如上所述。我也对取消、结束和移动做了同样的事情。然而,选择器视图仍然没有响应。
在回到正常工作之前,我还尝试了最后一件事。在我广泛的谷歌搜索中,我读到嵌套的 UIScrollViews 从 3.x 开始就神奇地工作了,所以我尝试将我的选择器视图放入嵌套的 UIScrollView 中并在其上设置以下属性:
scrollView.delaysContentTouches = NO;
scrollView.canCancelContentTouches = NO;
正如人们所期望的那样,外部滚动视图对待内部滚动视图的方式与对待选择器视图的方式没有任何不同,因此内部滚动视图没有接收到触摸。我认为这是一个不太可能的尝试,但它很容易实现,所以我认为值得一试。
我所知道的是 UIScrollView 有一个名为的手势识别器UIScrollViewDelayedTouchesBeganGestureRecognizer
拦截触摸并在 150(?)ms 后将它们发送到适当的子视图。我认为我应该能够编写一个类似的识别器,该识别器会导致滚动视图的默认识别器失败,而不是延迟触摸,而是立即将它们发送到选择器视图。因此,如果有人知道如何编写这样的识别器,请告诉我,如果您对问题有任何其他解决方案,也非常欢迎您分享。
感谢您阅读整个问题,即使您不知道答案,您仍然可以对问题进行投票,以便它得到更多关注(希望来自能够回答它的人)。谢谢! :)