判断一个tablecell是否真的被用户在tableview中看到

Determine if a tablecell is really seen by user in tableview

我正在尝试找到一种方法来检查我的几个 table 单元格是否真的被用户看到了(比方说,如果单元格至少显示 1px,我认为它被看到了).并且还需要确保如果用户上下滚动,我将需要触发该方法。 (这应该很容易,因为我可以将检查放在像 DidScroll 之类的侦听器方法中)

似乎使用 indexPathsForVisibleRows 只能给我在屏幕上几乎 100% 可见的行。

我正在寻找解决方案,看起来使用 rectForRowAtIndexPath 并使用 contentOffset 做一些数学运算可能会给我想要的东西。不知道有没有更好的方法。

有人可以帮忙吗?非常感谢!

“似乎使用 indexPathsForVisibleRows 只能给我在屏幕上几乎 100% 可见的行。”

我不确定这是否正确。快速测试表明,即使只有单元格的第一个点可见,它也包含在内。

但是,您可以通过实施 willDisplay cell:

来完成此操作
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    bSeen[indexPath.row] = @YES;
}

看看这个例子。只需将 UITableViewController 放入导航控制器并将其 class 分配为 SeenRowsTableViewController :

@interface SeenRowsTableViewController ()
{
    NSMutableArray *bSeen;
    NSArray *colors;
    NSInteger nRows;
}

@end

@implementation SeenRowsTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    NSAssert(self.navigationController, @"Example must be in a Navigation Controller");
    
    // series of row background colors
    //  to make it easy to see the last / next row
    colors = @[
        [UIColor colorWithRed:1.00 green:0.75 blue:0.75 alpha:1.0],
        [UIColor colorWithRed:0.75 green:1.00 blue:0.75 alpha:1.0],
        [UIColor colorWithRed:0.75 green:0.75 blue:1.00 alpha:1.0],
        [UIColor cyanColor],
        [UIColor yellowColor],
        [UIColor magentaColor],
    ];
    
    nRows = 500;

    // add a right bar button
    UIBarButtonItem *btn = [[UIBarButtonItem alloc] initWithTitle:@"Check It" style:UIBarButtonItemStyleDone target:self action:@selector(checkIt)];
    [self.navigationItem setRightBarButtonItem:btn];
    
    // init array with number of rows all set to FALSE
    bSeen = [NSMutableArray new];
    for (int i = 0; i < nRows; i++) {
        [bSeen addObject:@NO];
    }
    
    // use default basic cell
    [self.tableView registerClass:UITableViewCell.class forCellReuseIdentifier:@"c"];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [bSeen count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *c = [tableView dequeueReusableCellWithIdentifier:@"c" forIndexPath:indexPath];
    c.textLabel.text = [NSString stringWithFormat:@"Row %ld", (long)indexPath.row];
    c.contentView.backgroundColor = colors[indexPath.row % colors.count];
    return c;
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    bSeen[indexPath.row] = @YES;
}
- (void)checkIt {
    NSUInteger idx = [bSeen indexOfObject:@NO];
    self.title = [NSString stringWithFormat:@"Not Seen: %ld", (long)idx];
}
@end

我们为单元格使用了一系列颜色,以便于查看它们的边框。每次点击“Check It”栏按钮时,我们将导航栏标题更改为第一行未被查看的索引.

因此,在启动时(在 iPhone 8 上),当我们点击“检查”时,我们会得到这个:

缓慢滚动一点后,下一个单元格可见 1 磅,点击“检查”会得到以下信息:

这是底部的 zoomed-in 图片:

如您所见,只有行分隔符和下一个单元格的第一个点可见,但我们的代码已将其正确标记为“已看到”。

这里有一个 Swift 版本,供任何感兴趣的人使用:

class SeenRowsTableViewController: UITableViewController {

    // series of row background colors
    //  to make it easy to see the last / next row
    let colors: [UIColor] = [
        UIColor(red: 1.00, green: 0.75, blue: 0.75, alpha: 1.0),
        UIColor(red: 0.75, green: 1.00, blue: 0.75, alpha: 1.0),
        UIColor(red: 0.75, green: 0.75, blue: 1.00, alpha: 1.0),
        .cyan,
        .yellow,
        .magenta,
    ]
    
    let nRows = 500

    var bSeen: [Bool] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        
        guard navigationController != nil else {
            fatalError("Example must be in a Navigation Controller")
        }
        
        // add a right bar button
        let btn = UIBarButtonItem(title: "Check It", style: .done, target: self, action: #selector(checkIt))
        self.navigationItem.rightBarButtonItem = btn

        // init array with number of rows all set to FALSE
        bSeen = Array(repeating: false, count: nRows)
        
        // use default basic cell
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "c")
    }
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return bSeen.count
    }
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let c = tableView.dequeueReusableCell(withIdentifier: "c", for: indexPath)
        c.textLabel?.text = "Row \(indexPath.row)"
        c.contentView.backgroundColor = colors[indexPath.row % colors.count]
        return c
    }
    override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        bSeen[indexPath.row] = true
    }
    @objc func checkIt() -> Void {
        if let idx = bSeen.firstIndex(of: false) {
            // set NavBar title to first row that has
            //  NOT been seen
            self.title = "Not Seen: \(idx)"
        }
    }
}