ios UIScrollView 内的 UITableView 滚动后未收到第一次点击
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21677268/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
UITableView inside UIScrollView not receiving first tap after scrollling
提问by Angel G. Olloqui
Brief
简短的
I am having an issue with a UITableView
inside a UIScrollView
. When I scroll the external scrollView
, the table
does not receive the willSelect/didSelect
event on the first touch, but it does on the second one. What is even more strange, the cell itself gets the touches and the highlighted state, even when the delegate
does not.
我在 aUITableView
内部遇到问题UIScrollView
。当我滚动 external 时scrollView
,第一次触摸时table
不会收到willSelect/didSelect
事件,但第二次触摸时会收到。更奇怪的是,单元格本身获得了触摸和突出显示状态,即使delegate
没有。
Detailed explanation
详细说明
My view hierarchy:
我的视图层次结构:
UIView
- UIScrollView (outerscroll)
- Some other views and buttons
- UITableView (tableView)
Inside the scroll view I have some extra views that get expanded/closed dynamically. The table view needs to get "fixed" on top, together with some other elements of the view, so that is why I created this layout, that allows me to easily move elements in a similar way than Apple recommends by the use of transformations when the scroll happens.
在滚动视图中,我有一些额外的视图可以动态展开/关闭。表格视图需要与视图的其他一些元素一起“固定”在顶部,所以这就是我创建这个布局的原因,它允许我以类似于 Apple 推荐的方式轻松移动元素,当使用转换时滚动发生。
The table View is transformed with a translation effect when the outerscroll moves like this:
当outerscroll像这样移动时,table View会转换成平移效果:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView == self.outerScrollView) {
CGFloat tableOffset = scrollView.contentOffset.y - self.fixedHeaderFrame.origin.y;
if (tableOffset > 0) {
self.tableView.contentOffset = CGPointMake(0, tableOffset);
self.tableView.transform = CGAffineTransformMakeTranslation(0, tableOffset);
}
else {
self.tableView.contentOffset = CGPointMake(0, 0);
self.tableView.transform = CGAffineTransformIdentity;
}
// Other similar transformations are done here, but not involving the table
}
In my cell, if I implement these methods:
在我的单元格中,如果我实现这些方法:
- (void)setSelected:(BOOL)selected {
[super setSelected:selected];
if (selected) {
NSLog(@"selected");
}
}
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
[super setHighlighted:highlighted animated:animated];
if (highlighted) {
NSLog(@"highlighted");
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
NSLog(@"touchesBegan");
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
NSLog(@"touchesEnded");
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesCancelled:touches withEvent:event];
NSLog(@"touchesCancelled");
}
Y can see this output when fails (first tap):
Y 可以在失败时看到此输出(第一次点击):
2014-02-10 13:04:40.940 MyOrderApp[5588:70b] highlighted
2014-02-10 13:04:40.940 MyOrderApp[5588:70b] touchesBegan
2014-02-10 13:04:40.978 MyOrderApp[5588:70b] touchesEnded
And this one when works (second tap):
这个在工作时(第二次点击):
2014-02-10 13:05:30.359 MyOrderApp[5588:70b] highlighted
2014-02-10 13:05:30.360 MyOrderApp[5588:70b] touchesBegan
2014-02-10 13:05:30.487 MyOrderApp[5588:70b] touchesEnded
2014-02-10 13:05:30.498 MyOrderApp[5588:70b] expanded
No other frame change, animation or any other view interaction is done between the first and the second tap. Also, only when scrolling large amounts the bug appears, but with scrollings of just a few pixels everything keeps working as expected.
在第一次和第二次点击之间没有进行其他帧更改、动画或任何其他视图交互。此外,只有在大量滚动时才会出现错误,但滚动只有几个像素时,一切都会按预期工作。
I experimented changing some properties as well, but with no luck. Some of the things I did:
我也尝试过改变一些属性,但没有运气。我做过的一些事情:
- Remove
userInteractionEnabled
from views other than the scroll and table - Add a call to
setNeedsLayout
on the table, scroll and main view whenscrollViewDidScroll
occurs. - Remove the transformations from the table (still happens)
userInteractionEnabled
从滚动和表格以外的视图中删除setNeedsLayout
在scrollViewDidScroll
发生时添加对表、滚动和主视图的调用。- 从表中删除转换(仍然发生)
I have seen some comments about the unexpected behaviour of embedding UITableViews
inside UIScrollViews
but I can not see such a warn in the official documentation by Apple, so I am expecting it to work.
我看到了一些关于嵌入UITableViews
内部的意外行为的评论,UIScrollViews
但我在 Apple 的官方文档中看不到这样的警告,所以我期待它能够工作。
The app is iOS7+ only.
该应用程序仅适用于 iOS7+。
Questions
问题
Has anyone experienced similar issues? Why is this and how can I solve it? I think that I could be able to intercept the tap gesture on the cell and pass it with a custom delegate or similar, but I would like the table to receive the proper eventsand so my UITableViewDelegate
receives it as expected.
有没有人遇到过类似的问题?为什么会这样,我该如何解决?我认为我可以拦截单元格上的点击手势并使用自定义委托或类似方式传递它,但我希望表格接收正确的事件,因此我UITableViewDelegate
可以按预期接收它。
Updates
更新
- I tried disabling cell reuse as suggested in a comment but it still happens in the same way.
- 我尝试按照评论中的建议禁用单元格重用,但它仍然以相同的方式发生。
回答by Rémy Virin
From Apple Documentation, you shouldn't embed a UITableView
inside a UIScrollView
.
从Apple 文档中,您不应该UITableView
在UIScrollView
.
Important: You should not embed UIWebView or UITableView objects in UIScrollView objects. If you do so, unexpected behavior can result because touch events for the two objects can be mixed up and wrongly handled.
重要提示:您不应在 UIScrollView 对象中嵌入 UIWebView 或 UITableView 对象。如果这样做,可能会导致意外行为,因为两个对象的触摸事件可能会混淆并错误处理。
Your problem is really related to what your UIScrollView
does.
你的问题真的与你的UIScrollView
工作有关。
But if it's just to hide the tableview when needed (that was my case), you can just move the UITableView
in its superview.
但是,如果只是在需要时隐藏 tableview(这就是我的情况),您可以UITableView
在其超级视图中移动。
I wrote a small example here : https://github.com/rvirin/SoundCloud/
我在这里写了一个小例子:https: //github.com/rvirin/SoundCloud/
回答by Tom Abraham
leave the inner UITableView's scrollEnabled
property set as YES
. this lets the inner UITableView
know to handle scroll-related touches on the UIScrollView
correctly.
将内部 UITableView 的scrollEnabled
属性设置为YES
. 这让内部UITableView
知道正确处理与滚动相关的触摸UIScrollView
。
回答by Oren
I ran into this same problem and figured out a solution!!
我遇到了同样的问题并找到了解决方案!!
You need to set the delaysTouchesBegan
to true on your scrollview so that the scrollview sends its failed scrolled-gesture (i.e. the tap) to its children.
您需要在delaysTouchesBegan
滚动视图上将设置为 true,以便滚动视图将其失败的滚动手势(即点击)发送给其子项。
var delaysTouchesBegan: Bool
-
A Boolean value determining whether the receiver delays sending touches in a begin phase to its view.
var delaysTouchesBegan: Bool
-
一个布尔值,确定接收器是否在开始阶段延迟向其视图发送触摸。
When the value of the property is YES, the window suspends delivery of touch objects in the UITouchPhaseBegan phase to the view. If the gesture recognizer subsequently recognizes its gesture, these touch objects are discarded. If the gesture recognizer, however, does not recognize its gesture, the window delivers these objects to the view in a touchesBegan:withEvent: message (and possibly a follow-up touchesMoved:withEvent: message to inform it of the touches' current locations).
当该属性的值为YES时,窗口暂停将UITouchPhaseBegan阶段中的触摸对象传递给视图。如果手势识别器随后识别出其手势,则丢弃这些触摸对象。但是,如果手势识别器无法识别其手势,则窗口将这些对象通过 touchesBegan:withEvent: 消息(可能还有后续的 touchesMoved:withEvent: 消息通知它触摸的当前位置)传递给视图.
But there's a catch...it doesn't work if you do it directly on the scrollview!
但是有一个问题......如果你直接在滚动视图上做它就行不通!
// Does NOT work
self.myScrollview.delaysTouchesBegan = true
Apparently this is an iOS bug where setting this property doesn't work (thank you apple). However there's a simple workaround: set the property directly on the scrollview's pan gesture. Sure enough, this worked for me perfectly:
显然,这是一个 iOS 错误,设置此属性不起作用(谢谢苹果)。但是有一个简单的解决方法:直接在滚动视图的平移手势上设置属性。果然,这对我来说非常有效:
// This works!!
self.myScrollview.panGestureRecognizer.delaysTouchesBegan = true
回答by Nicolas Bonnet
It seems that your UiTableView doesn't recognize your tap. Did you try to use that :
您的 UiTableView 似乎无法识别您的水龙头。您是否尝试使用它:
- (BOOL)gestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UISwipeGestureRecognizer *)otherGestureRecognizer
{
if ([otherGestureRecognizer.view isKindOfClass:[UITableView class]]) {
return YES;
}
return NO;
}
Note from apple:
苹果的注意事项:
called when the recognition of one of gestureRecognizer or otherGestureRecognizer would be blocked by the other. return YES to allow both to recognize simultaneously. the default implementation returns NO (by default no two gestures can be recognized simultaneously)
当对gestureRecognizer 或otherGestureRecognizer 之一的识别被另一个阻止时调用。返回 YES 以允许两者同时识别。默认实现返回 NO(默认情况下不能同时识别两个手势)
note: returning YES is guaranteed to allow simultaneous recognition. returning NO is not guaranteed to prevent simultaneous recognition, as the other gesture's delegate may return YES
注意:返回 YES 保证允许同时识别。返回 NO 不能保证防止同时识别,因为另一个手势的代表可能返回 YES
Hope that will help.
希望这会有所帮助。
回答by Wladek Surala
Gesture recognizers won't work correctly for two embedded scroll views or subclasses.
对于两个嵌入式滚动视图或子类,手势识别器将无法正常工作。
Try a workaround:
尝试解决方法:
Use transparent, custom, and overlaying everything in cell
UIButton
with proper tag, or subclassUIButton
and add a index path property and overwrite each time in reused cell.Add this button as a property to your custom cell.
Add target for desired
UIControlEvent
(one or more) that points to your UITableViewDelegate protocol adopting class.Disable selecting in IB, and manually manage the selection from code.
使用透明、自定义和覆盖单元格中的所有内容,并
UIButton
使用适当的标签或子类UIButton
添加索引路径属性并在重复使用的单元格中每次覆盖。将此按钮作为属性添加到您的自定义单元格。
为
UIControlEvent
指向您的 UITableViewDelegate 协议采用类的所需(一个或多个)添加目标。在 IB 中禁用选择,并从代码手动管理选择。
This solution requires attention for cases of single/multi selection.
此解决方案需要注意单选/多选的情况。
回答by aleh
I've encountered a UITableView with scrollEnabled being NO within a UIScrollView in some legacy code. I have not been able to change the existing hierarchy easily nor enable scrolling, but come up with the the following workaround for the first tap problem:
我在一些遗留代码中遇到了一个 UITableView,其 scrollEnabled 在 UIScrollView 中为 NO。我无法轻松更改现有层次结构,也无法启用滚动,但为第一个点击问题提出了以下解决方法:
@interface YourOwnTableView : UITableView
@end
@implementation YourOwnTableView
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesCancelled:touches withEvent:event];
// Note that this is a hack and it can stop working at some point.
// Looks like a table view with scrollEnabled being NO does not handle cancellation cleanly,
// so let's repeat begin/end touch sequence here hoping it'll reset its own internal state properly
// but won't trigger cell selection (the touch passed is in its cancelled phase, perhaps there is a part
// of code inside which actually checks it)
[super touchesBegan:touches withEvent:event];
[super touchesEnded:touches withEvent:event];
}
@end
Again, this is just a workaround working in my specific case. Having a table view within a scroll view is still a wrong thing.
同样,这只是在我的特定情况下工作的解决方法。在滚动视图中使用表格视图仍然是错误的。
回答by shubham
- Drop a UIButton over your UITableViewCell and create the outlet as "btnRowSelect".
In your view controller put this code in cellForRowAtIndexPath
cell.btnRowSelect.tag = indexPath.row cell.btnRowSelect.addTarget(self, action: Selector("rowSelect:"), forControlEvents: .TouchUpInside)
Add this function to your viewController as well-
func rowSelect (sender:UIButton) { // "sendet.tag" give you the selected row // do whatever you want to do in didSelectRowAtIndexPath }
- 将 UIButton 放在 UITableViewCell 上,并将插座创建为“btnRowSelect”。
在您的视图控制器中将此代码放入 cellForRowAtIndexPath
cell.btnRowSelect.tag = indexPath.row cell.btnRowSelect.addTarget(self, action: Selector("rowSelect:"), forControlEvents: .TouchUpInside)
将此功能也添加到您的 viewController 中 -
func rowSelect (sender:UIButton) { // "sendet.tag" give you the selected row // do whatever you want to do in didSelectRowAtIndexPath }
This function "rowSelect" will work as didSelectRowAtIndexPath where you get the row"indexPath.row" as "sender.tag"
此函数“rowSelect”将作为 didSelectRowAtIndexPath 工作,其中您将行“indexPath.row”作为“sender.tag”
回答by Stepan Hruda
The scrollview is trying to figure out whether the user's intention is to scroll or not, so it's delaying the initial touch on purpose. You can turn this off by setting delaysContentTouches
to NO
.
scrollview 试图弄清楚用户的意图是否要滚动,因此它故意延迟了最初的触摸。您可以通过设置delaysContentTouches
为关闭此功能NO
。
回答by Duc Minh Trinh
I have the same problem with nested UITableView and have found a work-around for this:
我对嵌套的 UITableView 有同样的问题,并为此找到了解决方法:
innerTableView.scrollEnabled = YES;
innerTableView.alwaysBounceVertical = NO;
You'll need to set the height of the inner table view to match with the total height of its cells so that it'll not scroll when user scrolling the outer view.
您需要将内部表格视图的高度设置为与其单元格的总高度相匹配,以便在用户滚动外部视图时它不会滚动。
Hope this helps.
希望这可以帮助。
回答by LunaCodeGirl
My mistake was implementing the delegate method:
我的错误是实现委托方法:
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
instead of the one I meant to implement:
而不是我打算实施的:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Hence only being called on the second cell being tapped, because that was when the first cell would be deselected. Stupid mistake made with the help of autocomplete. Just a thought for those of you who may wander here not realizing you've made the same mistake too.
因此只在被点击的第二个单元格上被调用,因为那是第一个单元格将被取消选择的时候。在自动完成的帮助下犯了愚蠢的错误。对于那些可能在这里徘徊却没有意识到自己也犯了同样错误的人来说,这只是一个想法。