UIScrollView中的UITableView在滚动后没有收到第一次点击

时间:2014-02-10 12:28:33

标签: ios objective-c uitableview uiscrollview

我在UITableView内遇到UIScrollView的问题。当我滚动外部scrollView时,table在第一次触摸时不会收到willSelect/didSelect事件,但会在第二次触摸时收到delegate事件。更奇怪的是,即使UIView - UIScrollView (outerscroll) - Some other views and buttons - UITableView (tableView) 没有,单元格本身也能获得触摸和突出显示的状态。

详细说明

我的视图层次结构:

- (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

}

在滚动视图中,我有一些额外的视图可以动态扩展/关闭。表格视图需要与视图中的其他一些元素一起“固定”在顶部,这就是我创建此布局的原因,这使我可以通过使用转换以类似于Apple推荐的方式轻松移动元素。滚动发生了。

当outerscroll像这样移动时,表视图将使用转换效果进行转换:

- (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");
}

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

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

Y失败时可以看到此输出(第一次点击):

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

这个工作时(第二次点击):

userInteractionEnabled

在第一次和第二次点击之间没有进行其他帧更改,动画或任何其他视图交互。此外,只有当滚动大量时才会出现错误,但只需几个像素的滚动,一切都会按预期工作。

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

  • 从滚动和表格以外的其他视图中删除setNeedsLayout
  • 在桌面上添加对scrollViewDidScroll的调用,在UITableViews发生时滚动并显示主视图。
  • 从表中删除转换(仍然会发生)

我已经看到了一些关于在UIScrollViews中嵌入UITableViewDelegate的意外行为的评论,但我在Apple的官方文档中看不到这样的警告,所以我希望它可以正常工作。

该应用仅限iOS7 +。

问题

有没有人遇到类似问题?为什么会这样,我该如何解决??我认为我可以截取单元格上的点击手势并使用自定义委托或类似方式传递它,但我希望表格能够接收正确的事件,所以我的{{1}按预期收到它。

更新

  • 我尝试按照评论中的建议禁用单元格重用,但它仍然以相同的方式发生。

14 个答案:

答案 0 :(得分:22)

Apple Documentation开始,您不应在UITableView内嵌入UIScrollView

  

重要提示:您不应该嵌入UIWebView或UITableView对象   UIScrollView对象。如果这样做,可能会导致意外行为   因为两个对象的触摸事件可能混淆错误   处理。

您的问题与UIScrollView的问题有关。

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

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

答案 1 :(得分:19)

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

答案 2 :(得分:14)

我遇到了同样的问题并找到了解决方案!

您需要在滚动视图上将data['records'].delete_if{ |h| (h['title'] == 'Val1') || (h['title'] == 'Val2') } 设置为true,以便滚动视图将失败的滚动手势(即点击)发送给其子项。

delaysTouchesBegan - 一个布尔值,用于确定接收器是否将开始阶段的触摸延迟到其视图。

  

当属性值为YES时,窗口暂停传递   将UITouchPhaseBegan阶段中的对象触摸到视图。如果   手势识别器随后识别其手势,这些触摸   对象被丢弃。但是,如果手势识别器没有   识别它的手势,窗口将这些对象传递给视图   在touchesBegan:withEvent:message(可能还有一个跟进   touchesMoved:withEvent:消息告知它当前的触摸   位置)。

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

但是有一个问题......如果你直接在scrollview上做它就不行了!

var delaysTouchesBegan: Bool

显然这是一个iOS错误,设置此属性不起作用(谢谢苹果)。但是有一个简单的解决方法:直接在scrollview的平移手势上设置属性。果然,这对我很有用:

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

答案 3 :(得分:3)

您的UiTableView似乎无法识别您的点按。你尝试过使用它吗?

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

来自苹果的说明:

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

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

希望这会有所帮助。

答案 4 :(得分:3)

手势识别器无法正确处理两个嵌入式滚动视图或子类。

尝试解决方法:

  1. 使用透明,自定义,并使用正确的标记或子类UIButton覆盖单元格UIButton中的所有内容,并添加索引路径属性并在重用的单元格中重写。

  2. 将此按钮添加为自定义单元格的属性。

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

  4. 禁用在IB中选择,并从代码中手动管理选择。

  5. 此解决方案需要注意单/多选的情况。

答案 5 :(得分:2)

在某些遗留代码中,我遇到了一个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

同样,这只是我在特定情况下的解决方法。在滚动视图中拥有表视图仍然是错误的。

答案 6 :(得分:1)

我建议您在实际滚动外部滚动视图时不要让您的单元格处于突出显示状态,这是非常容易处理的推荐方法。你可以通过采用布尔值并在下面的方法中切换它来做到这一点

- (void)scrollViewDidScroll:(UIScrollView *)scrollView 

答案 7 :(得分:1)

滚动视图试图弄清楚用户是否要滚动,因此它会故意延迟初始触摸。您可以将delaysContentTouches设置为NO来启用此功能。

答案 8 :(得分:1)

我在嵌套的UITableView中遇到了同样的问题,并为此找到了解决办法:

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

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

希望这有帮助。

答案 9 :(得分:1)

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

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

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

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

因此,仅在第二个单元被调用时被调用,因为那是第一个单元格被选中 de 的时候。在自动完成的帮助下犯了愚蠢的错误。只是为那些可能在这里徘徊的人而不是意识到你也犯了同样的错误。

答案 10 :(得分:1)

  1. 将UIButton放在UITableViewCell上,然后将其设为“btnRowSelect”。
  2. 在视图控制器中将此代码放在cellForRowAtIndexPath

    .home
        background:
          image: url(https://i.stack.imgur.com/XZDsP.jpg), url(https://img.clipartfest.com/d840c9cfc1786dc7013443ac7638dde0_halloween-clip-art-free-spider-spider-web-clipart-png_500-463.png)
          blend-mode: color-dodge
          repeat: no-repeat
          size: cover, contain
          position: 0, center
        height: 100vh
    
  3. 将此功能添加到viewController中 -

    setOption
  4.   

    此函数“rowSelect”将作为didSelectRowAtIndexPath在其中工作   你得到“indexPath.row”行作为“sender.tag”

答案 11 :(得分:0)

正如其他答案所说,你不应该在滚动视图中放置一个tableview。 UITableView无论如何都继承自UIScrollView,所以我想这就是令人困惑的地方。在这种情况下我一直做的是:

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

2)在父UIViewController中创建容器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;
}

现在你有一个表格视图,顶部有一堆其他的shizzle。

享受!

答案 12 :(得分:0)

如果触摸表视图它将正常工作。也可以在同一视图控制器中使用滚动视图。

tableview.scrollEnabled = true;

答案 13 :(得分:0)

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

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

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

这是工作