UIScrollView里面的UITableView在滚动后没有收到第一个水龙头

简要

我在UIScrollView里有一个UITableView的问题。 当我滚动外部滚动scrollViewtable在第一次触摸时不会接收到willSelect/didSelect事件,但它在第二次触摸时会发生。 更奇怪的是,即使delegate没有,单元本身也会得到触动和突出显示的状态。

详细的解释

我的视图层次:

 UIView - UIScrollView (outerscroll) - Some other views and buttons - UITableView (tableView) 

在滚动视图中,我有一些额外的视图dynamic扩展/closures。 表视图需要与视图的其他元素一起“固定”在顶部,所以这就是为什么我创build了这个布局,这使我能够以类似的方式轻松地移动元素,而不是像Apple推荐的那样通过使用转换滚动发生。

当outerscroll像这样移动时,表格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 } 

在我的单元格中,如果我实现这些方法:

 - (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可以看到这个输出(第一次敲击):

 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 

而这个作品(第二次):

 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 

在第一个和第二个水龙头之间没有其他的框架改变,animation或任何其他的视图交互。 另外,只有当滚动大量的错误出现,但只有几个像素的滚动一切保持按预期工作。

我尝试改变一些属性,但没有运气。 我做的一些事情:

  • 从滚动和表格以外的视图中移除userInteractionEnabled
  • scrollViewDidScroll发生时,在表上添加对setNeedsLayout的调用,滚动和主视图。
  • 从表中删除转换(仍然发生)

我已经看到了在UIScrollViews中embeddedUITableViews的意外行为的一些意见,但我不能看到在苹果的官方文档中这样的警告,所以我期待它的工作。

该应用程序仅适用于iOS7 +。

问题

有没有人遇到类似的问题 ? 为什么是这个, 如何解决呢 ? 我认为我可以截获单元格上的点按手势,并将其传递给自定义委托或类似的,但我希望表能够接收正确的事件 ,所以我的UITableViewDelegate按预期接收它。

更新

  • 我试着按照评论中的build议禁用单元重用,但它仍然以同样的方式发生。

从Apple文档中 ,您不应该在UIScrollViewembeddedUITableView

重要说明:您不应该在UIS​​crollView对象中embeddedUIWebView或UITableView对象。 如果这样做,可能会导致意外的行为,因为两个对象的触摸事件可能会混淆并被错误地处理。

你的问题真的和你的UIScrollView有关。

但是,如果只是在需要时隐藏tableview(这是我的情况),你可以在它的超级视图中移动UITableView

我在这里写了一个小例子: https : //github.com/rvirin/SoundCloud/

将内部UITableView的scrollEnabled属性设置为YES 。 这让内部的UITableView知道正确处理UIScrollView上的滚动相关的触摸。

我遇到了同样的问题,并找出了解决办法!

您需要将delaysTouchesBegan设置为true,以便滚动视图将其失败的滚动手势(即水龙头)发送给其子项。

var delaysTouchesBegan: Bool一个布尔值,用于确定接收方是否延迟将开始阶段的触摸发送到其视图。

当属性的值为YES时,窗口将UITouchPhaseBegan阶段中的触摸对象的传递暂停到视图。 如果手势识别器随后识别其手势,则丢弃这些触摸对象。 如果手势识别器无法识别其手势,则窗口将这些对象传递给touchesBegan:withEvent:消息中的视图(可能还会跟随touchesMoved:withEvent:消息通知其触摸的当前位置) 。

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIGestureRecognizer_Class/index.html#//apple_ref/occ/instp/UIGestureRecognizer/delaysTouchesBegan

但有一个问题……如果你直接在scrollview上执行的话,这是行不通的!

 // Does NOT work self.myScrollview.delaysTouchesBegan = true 

显然这是一个iOS的错误,其中设置此属性不起作用(谢谢你苹果)。 但是有一个简单的解决方法:直接在滚动视图的平移手势上设置属性。 果然,这对我完美的工作:

 // This works!! self.myScrollview.panGestureRecognizer.delaysTouchesBegan = true 

看来你的UiTableView不能识别你的水龙头。 你有没有尝试使用:

 - (BOOL)gestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UISwipeGestureRecognizer *)otherGestureRecognizer { if ([otherGestureRecognizer.view isKindOfClass:[UITableView class]]) { return YES; } return NO; } 

苹果的注意事项:

当对其中一个gestureRecognizer或otherGestureRecognizer的识别被另一个识别时调用。 返回YES以允许两者同时识别。 默认实现返回NO(默认情况下,不能同时识别两个手势)

注意:返回YES保证允许同时识别。 返回NO不能保证防止同时识别,因为另一手势的代表可能返回YES

希望这会有所帮助。

对于两个embedded的滚动视图或子类,手势识别器无法正常工作。

尝试解决方法:

  1. 使用透明,自定义,并覆盖单元格UIButton与适当的标签,或子类UIButton一切,并添加一个索引path属性,并重写每一次重用单元格。

  2. 将此button作为属性添加到您的自定义单元格。

  3. 为所需的UIControlEvent (一个或多个)添加指向您的UITableViewDelegate协议采用类的目标。

  4. 禁止在IB中select,并手动pipe理代码中的select。

此解决scheme需要关注单个/多个select的情况。

我会build议寻找这样的选项,例如当您实际滚动外部滚动视图时,不要让您的单元格处于高亮显示状态,这是非常容易处理的,并且是推荐的方式。 你可以通过一个布尔值来实现这一点,并在下面的方法中进行切换

 - (void)scrollViewDidScroll:(UIScrollView *)scrollView 

滚动视图试图确定用户的意图是否滚动,因此延迟了初始触摸的目的。 您可以通过将delaysContentTouches设置为NO来closures此function。

我有嵌套的UITableView相同的问题,并find了解决这个问题:

 innerTableView.scrollEnabled = YES; innerTableView.alwaysBounceVertical = NO; 

您需要将内部表格视图的高度设置为与其单元格的总高度相匹配,以便在用户滚动外部视图时不会滚动。

希望这可以帮助。

我的错误是实现委托方法:

 - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath 

而不是我打算实施的那个:

 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 

因此只有在被窃听的第二个单元被调用时,因为那是当第一个单元被选中的时候。 愚蠢的错误与自动完成的帮助下做出的。 只是想到你们这些可能在这里徘徊的人,并没有意识到你们犯了同样的错误。

正如其他答案说,你不应该把一个tableview在滚动视图。 一个UITableViewinheritanceUIScrollView,所以我想这就是事情变得混乱的地方。 我在这种情况下总是做的是:

1)子类UITableViewController并包含一个属性UIView * headView。

2)在父UIViewController中创build容器UIView中的所有顶级的东西

3)初始化您的自定义UITableView,并将tableView的视图添加到视图控制器的完整大小

 [self.view addSubview: self.myTableView.view]; 

4)将headView设置为你的UIView gubbins

 self.tableView.headView = myHeadViewGubbins. 

5)在tableViewController方法

 -(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger *)section; 

做:

 if ( section == 0 ) { return self.headView; } 

现在你有一个桌面视图,顶部有一堆其他的嘶嘶声。

请享用!

我遇到了一个UITableView,scrollEnabled在一些UIScrollView中是一些遗留代码。 我无法轻松更改现有的层次结构,也不能启用滚动function,但为第一个水龙头问题提出了以下解决方法:

 @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 

再次,这只是一个解决scheme,在我的具体情况下工作。 在滚动视图中有一个表视图仍然是一个错误的事情。

那呢,如果碰到桌子视图就会正常工作。 也在相同的视图控制器也滚动视图。

 tableview.scrollEnabled = true; 

我有同样的问题,然后参考“嵌套滚动视图”为lxx说。

https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/UIScrollView_pg/NestedScrollViews/NestedScrollViews.html

横向滚动的例子可以在Stocks应用程序中find。 顶视图是一个表视图,但底视图是使用分页模式configuration的水平滚动视图。 虽然三个子视图中的两个是自定义视图,但第三个视图(包含新闻文章)是UITableView(UIScrollView的子类),它是水平滚动视图的子视图。 水平滚动到新闻视图后,可以垂直滚动其内容。

这是工作

  1. 把一个UIButton放到你的UITableViewCell上,然后创build一个“btnRowSelect”。
  2. 在你的视图控制器把这个代码放在cellForRowAtIndexPath中

     cell.btnRowSelect.tag = indexPath.row cell.btnRowSelect.addTarget(self, action: Selector("rowSelect:"), forControlEvents: .TouchUpInside) 
  3. 把这个函数添加到你的viewController中,

     func rowSelect (sender:UIButton) { // "sendet.tag" give you the selected row // do whatever you want to do in didSelectRowAtIndexPath } 

这个函数“rowSelect”将起作用didSelectRowAtIndexPath,你得到行“indexPath.row”为“sender.tag”