简体   繁体   English

UITableViewCell 内的 UIScrollView 触摸检测

[英]UIScrollView inside UITableViewCell touch detect

I have a tableview with 8 custom cells.我有一个带有 8 个自定义单元格的表格视图。 in the 8th cell I added a scrollView with paging enabled so I can show page 1 and page 2 (or 3, 4... 10) without have a very high cell.在第 8 个单元格中,我添加了一个启用分页的滚动视图,这样我就可以显示第 1 页和第 2 页(或 3、4...10)而没有非常高的单元格。

The problem is with the scrollView I can't use didSelectRowAtIndexPath because the cell is behind the scrollView so I'm trying to detect scrollView tap (not swipe).问题出在滚动视图上,我无法使用 didSelectRowAtIndexPath,因为单元格位于滚动视图后面,所以我试图检测滚动视图的点击(而不是滑动)。

I played with touchesBegan and touchesEnded but they are never called (I know touches work with UIView only, but maybe.....)我玩过 touchesBegan 和 touchesEnded 但它们从未被调用(我知道 touches 仅适用于 UIView,但也许......)

Any help is very appreciated.非常感谢任何帮助。

Thanks, Max谢谢,马克斯

There is a trick Apple recommends to use in this case, in theirs WWDC 2014 session "Advanced scrollviews" (See Demo starting from 8:10):在这种情况下,Apple 建议使用一个技巧,在他们的WWDC 2014 session “高级滚动视图”中(参见 8:10 开始的演示):

[cell.contentView addSubview:_scrollView];
[_scrollView setUserInteractionEnabled:NO];
[cell.contentView addGestureRecognizer:_scrollView.panGestureRecognizer];

That's all what needs to be done, no need to override touchesBegan: , touchesMoved: and others.这就是所有需要做的,不需要覆盖touchesBegan:touchesMoved:和其他。


I used solution based on overriding of touchesBegan: , touchesMoved: , touchesEnded: and touchesCancelled: previously, but sometimes it caused a weird behaviour: when select a certain cell, method -tableView:didSelectRowAtIndexPath: was called for cell with different indexPath.我使用了基于覆盖touchesBegan:touchesMoved:touchesEnded:touchesCancelled:的解决方案,但有时它会导致奇怪的行为:当 select 某个单元格时,方法-tableView:didSelectRowAtIndexPath:被调用用于具有不同 indexPath 的单元格。

Solution from Apple has no side effects so far and looks more elegant. Apple的解决方案到目前为止没有副作用,看起来更优雅。

There is also an elegant resolution:还有一个优雅的解决方案:

Create a SubClass from UIScrollView and override the following methods从 UIScrollView 创建一个子类并覆盖以下方法

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [[self superview]touchesBegan:touches withEvent:event];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    [[self superview]touchesMoved:touches withEvent:event];
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [[self superview]touchesCancelled:touches withEvent:event];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [[self superview]touchesEnded:touches withEvent:event];
}

Passing every touch to the superview of the scroll view and then the didSelectRowAtIndexPath will be called.将每次触摸传递给滚动视图的超级视图,然后将调用 didSelectRowAtIndexPath。

Solved subclassing both uitableviewcell and uiscrollview.解决了 uitableviewcell 和 uiscrollview 的子类化问题。

It worked for my needs.它满足了我的需求。 Hope it can help.希望它可以提供帮助。

Max最大限度


myScrollView.h myScrollView.h


#import <UIKit/UIKit.h>
@interface myScrollView : UIScrollView {
}

@end

myScrollView.m我的滚动视图.m


#import "myScrollView.h"
@implementation myScrollView


- (id)initWithFrame:(CGRect)frame {

    return [super initWithFrame:frame];
}

- (void) touchesEnded: (NSSet *) touches withEvent: (UIEvent *) event 
{   
    NSLog(@"touch scroll");
    // If not dragging, send event to next responder
    if (!self.dragging) 
        [self.nextResponder touchesEnded: touches withEvent:event]; 
    else
        [super touchesEnded: touches withEvent: event];
}

myCell.h myCell.h


#import <UIKit/UIKit.h>


@interface myCell : UITableViewCell {

}

@end

myCell.m我的细胞.m



#import "myCell.h"


@implementation myCell


- (id)initWithFrame:(CGRect)frame {

    return [super initWithFrame:frame];
}

- (void) touchesEnded: (NSSet *) touches withEvent: (UIEvent *) event 
{   
    NSLog(@"touch cell");
    // If not dragging, send event to next responder
    [super touchesEnded: touches withEvent: event];
}

RootViewController.h RootViewController.h



#import <UIKit/UIKit.h>

@class myCell;
@class myScrollView;

@interface RootViewController : UITableViewController {

    myCell *cell;
    myScrollView *scrollView;
}

@end

RootViewController.m根视图控制器.m



#pragma mark -
#pragma mark Table view data source

// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}


// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 3;
}


// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell";

    // my custom cell
    cell = [[myCell alloc] init];
    if (cell == nil) {
        cell = [[[myCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }


    // the custom scroll view
    scrollView = [[myScrollView alloc] initWithFrame:cell.frame];
    scrollView.contentSize = CGSizeMake(640, 40);
    [cell.contentView addSubview:scrollView];

    //something to add in scrollView
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 150, 20)];
    label.text = @"some text";
    [scrollView addSubview:label];

    // Configure the cell.

    return cell;
}

The selected answer is correct, but I updated the code based on a bug I was getting.选择的答案是正确的,但我根据我得到的错误更新了代码。

In the subclassed scroll view add the following code.在子类滚动视图中添加以下代码。

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    if (self.dragging) {
        [super touchesMoved:touches withEvent:event];
    } else {
        if ([self.delegate isKindOfClass:[UITableViewCell class]]) {
            [(UITableViewCell *)self.delegate touchesCancelled:touches withEvent:event];
        }

        [self.superview touchesMoved:touches withEvent:event];
    }
}

If your self.delegate is not the UITableViewCell , than replace that property with a property to your cell.如果您的self.delegate不是UITableViewCell ,则将该属性替换为您的单元格的属性。

The cell needs to retrieve the cancel touch event during movement to prevent the undesired results.单元格需要在移动过程中检索取消触摸事件,以防止出现不希望的结果。 It can be easily reproducible as follows.它可以很容易地重现如下。

  • Highlight the cell (assuming the scroll view is over the whole cell, if not highlight the scroll view)突出显示单元格(假设滚动视图在整个单元格上,如果不突出显示滚动视图)
  • While the cell is highlighted, drag the table view当单元格突出显示时,拖动表格视图
  • Select any other cell and now the previously highlighted cell will retrieve the didSelectCell state Select 任何其他单元格,现在之前突出显示的单元格将检索didSelectCell state

Another point to mention is that order matters!还有一点要提的是顺序很重要! If the self.delegate is not called before the self.superview then the highlighted state wont happen.如果self.delegateself.superview之前没有被调用,那么突出显示的 state 不会发生。

I found the simplest solution for my needs:我找到了满足我需求的最简单的解决方案:

subclass UIScrollView touchesEnded method and post a notification.子类 UIScrollView touchesEnded 方法并发布通知。

In the UITableview add an observer in viewdidAppear (remove it in viewdiddisappear) to call a function that call tableview didSelectRowForIndexPath.在 UITableview 中,在 viewdidAppear 中添加一个观察者(在 viewdiddisappear 中将其删除)以调用调用 tableview didSelectRowForIndexPath 的 function。

Something like this (swift version)像这样的东西(快速版)

// myScrollView.swift

import UIKit

class myScrollView: UIScrollView {
    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
            NSNotificationCenter.defaultCenter().postNotificationName("selectTVRow", object: nil)
    }
}

In your tableView:在您的表格视图中:

// ItemsList.swift

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "selectFourthRow", name: "selectTVRow", object: nil)
    }

    override func viewDidDisappear(animated: Bool) {
        super.viewDidDisappear(animated)
        NSNotificationCenter.defaultCenter().removeObserver(self, name: "selectfourthrow", object: nil)
    }

    func selectFourthRow() {
        let rowToSelect:NSIndexPath = NSIndexPath(forRow: 4, inSection: 0);
        self.tableView(self.tableView, didSelectRowAtIndexPath: rowToSelect);
    }

    /*
    .... rest of your tableview Datasource and Delegate methods...
    numberOfSectionsInTableView, numberOfRowsInSection, cellForRowAtIndexPath
    */

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM