Cách tốt nhất để kiểm tra xem UITableViewCell có hoàn toàn hiển thị hay không


100

Tôi có một UITableView với các ô có độ cao khác nhau và tôi cần biết khi nào chúng có thể nhìn thấy hoàn toàn hay không.

Hiện tại, tôi đang lặp qua từng ô trong danh sách các ô hiển thị để kiểm tra xem nó có hiển thị hoàn toàn không mỗi khi cuộn chế độ xem. Đây có phải là cách tiếp cận tốt nhất?

Đây là mã của tôi:

- (void)scrollViewDidScroll:(UIScrollView *)aScrollView {

    CGPoint offset = aScrollView.contentOffset;
    CGRect bounds = aScrollView.bounds;    
    NSArray* cells = myTableView.visibleCells;

    for (MyCustomUITableViewCell* cell in cells) {

        if (cell.frame.origin.y > offset.y &&
            cell.frame.origin.y + cell.frame.size.height < offset.y + bounds.size.height) {

            [cell notifyCompletelyVisible];
        }
        else {

            [cell notifyNotCompletelyVisible];
        }
    }
}

Biên tập:

Xin lưu ý rằng * - (NSArray ) opensCells trả về các ô có thể nhìn thấy được cả hiển thị hoàn toàn và hiển thị một phần.

Chỉnh sửa 2:

Đây là mã sửa đổi sau khi kết hợp các giải pháp từ cả lnafzigerVadim Yelagin :

- (void)scrollViewDidScroll:(UIScrollView *)aScrollView {
    NSArray* cells = myTableView.visibleCells;
    NSArray* indexPaths = myTableView.indexPathsForVisibleRows;

    NSUInteger cellCount = [cells count];

    if (cellCount == 0) return;

    // Check the visibility of the first cell
    [self checkVisibilityOfCell:[cells objectAtIndex:0] forIndexPath:[indexPaths objectAtIndex:0]];

    if (cellCount == 1) return;

    // Check the visibility of the last cell
    [self checkVisibilityOfCell:[cells lastObject] forIndexPath:[indexPaths lastObject]];

    if (cellCount == 2) return;

    // All of the rest of the cells are visible: Loop through the 2nd through n-1 cells
    for (NSUInteger i = 1; i < cellCount - 1; i++)
        [[cells objectAtIndex:i] notifyCellVisibleWithIsCompletelyVisible:YES];
}

- (void)checkVisibilityOfCell:(MultiQuestionTableViewCell *)cell forIndexPath:(NSIndexPath *)indexPath {
    CGRect cellRect = [myTableView rectForRowAtIndexPath:indexPath];
    cellRect = [myTableView convertRect:cellRect toView:myTableView.superview];
    BOOL completelyVisible = CGRectContainsRect(myTableView.frame, cellRect);

    [cell notifyCellVisibleWithIsCompletelyVisible:completelyVisible];
}

3
Chỉ là một lưu ý phụ, bạn nên chuyển đến tất cả các câu hỏi trước đây của mình và chấp nhận câu trả lời của những người đã giúp bạn.
Baub

4
Cảm ơn đã cho tôi biết! Tôi đã cho họ +1 nhưng lại quên mất tính năng câu trả lời được chấp nhận.
RohinNZ

Mã của bạn trông đúng với tôi, và mặc dù nó phức tạp, nhưng nó hoạt động. Đừng sửa những gì chưa hỏng, hả?
CodaFi

Câu trả lời:


125

Bạn có thể lấy trực tiếp của một ô với rectForRowAtIndexPath:phương thức và so sánh nó với trực tiếp giới hạn của tableview bằng cách sử dụng CGRectContainsRecthàm.

Lưu ý rằng điều này sẽ không khởi tạo ô nếu nó không được nhìn thấy, và do đó sẽ khá nhanh.

Nhanh

let cellRect = tableView.rectForRowAtIndexPath(indexPath)
let completelyVisible = tableView.bounds.contains(cellRect)

Ob-C

CGRect cellRect = [tableView rectForRowAtIndexPath:indexPath];
BOOL completelyVisible = CGRectContainsRect(tableView.bounds, cellRect);

Tất nhiên điều này sẽ không liên quan đến việc chế độ xem bảng bị cắt bởi một siêu chiếu hoặc bị che khuất bởi một chế độ xem khác.


Cảm ơn bạn! Tôi đã kết hợp mã này với giải pháp của lnafziger.
RohinNZ

11
Ngày thứ hai nghĩ rằng nó có thể được chỉCGRectContainsRect(tableView.bounds, [tableView rectForRowAtIndexPath:indexPath])
Vadim Yelagin

1
tại sao lại là origin.x = 0, origin.y = 0, width = 0, height = 0 của cellRect của tôi? mặc dù trên giao diện người dùng không phải tất cả đều là 0, tôi đang sử dụng bố cục tự động, có ý kiến ​​gì không?
RainCast

7
Swift 3:let completelyVisible = tableView.bounds.contains(tableView.rectForRow(at: indexPath))
cbartel

Làm cách nào chúng ta có thể xử lý chức năng tương tự cho UICollectionView?
Satyam

64

Tôi sẽ thay đổi nó như thế này:

- (void)checkVisibilityOfCell:(MyCustomUITableViewCell *)cell inScrollView:(UIScrollView *)aScrollView {
    CGRect cellRect = [aScrollView convertRect:cell.frame toView:aScrollView.superview];

    if (CGRectContainsRect(aScrollView.frame, cellRect))
        [cell notifyCompletelyVisible];
    else
        [cell notifyNotCompletelyVisible];
}

- (void)scrollViewDidScroll:(UIScrollView *)aScrollView { 
    NSArray* cells = myTableView.visibleCells;

    NSUInteger cellCount = [cells count];
    if (cellCount == 0)
        return;

    // Check the visibility of the first cell
    [self checkVisibilityOfCell:[cells firstObject] inScrollView:aScrollView];
    if (cellCount == 1)
        return;

    // Check the visibility of the last cell
    [self checkVisibilityOfCell:[cells lastObject] inScrollView:aScrollView];
    if (cellCount == 2)
        return;

    // All of the rest of the cells are visible: Loop through the 2nd through n-1 cells
    for (NSUInteger i = 1; i < cellCount - 1; i++)
        [[cells objectAtIndex:i] notifyCompletelyVisible];
}

<Và> trong câu lệnh if phải là <= hoặc> =, tôi sẽ sửa điều này trong câu trả lời ...
Aardvark

12

Bạn có thể thử một cái gì đó như sau để xem có bao nhiêu phần trăm hiển thị:

-(void)scrollViewDidScroll:(UIScrollView *)sender
{
    [self checkWhichVideoToEnable];
}

-(void)checkWhichVideoToEnable
{
    for(UITableViewCell *cell in [tblMessages visibleCells])
    {
        if([cell isKindOfClass:[VideoMessageCell class]])
        {
            NSIndexPath *indexPath = [tblMessages indexPathForCell:cell];
            CGRect cellRect = [tblMessages rectForRowAtIndexPath:indexPath];
            UIView *superview = tblMessages.superview;

            CGRect convertedRect=[tblMessages convertRect:cellRect toView:superview];
            CGRect intersect = CGRectIntersection(tblMessages.frame, convertedRect);
            float visibleHeight = CGRectGetHeight(intersect);

            if(visibleHeight>VIDEO_CELL_SIZE*0.6) // only if 60% of the cell is visible
            {
                // unmute the video if we can see at least half of the cell
                [((VideoMessageCell*)cell) muteVideo:!btnMuteVideos.selected];
            }
            else
            {
                // mute the other video cells that are not visible
                [((VideoMessageCell*)cell) muteVideo:YES];
            }
        }
    }
}

Nếu chế độ xem bảng có thể hiển thị 2 ô với video (ví dụ: trên iPad), cả hai video sẽ phát với mã trên?
Jerome

@Catalin Tôi đã thử giải pháp của bạn nhưng chế độ xem bảng của tôi bị chậm lại. Bất kỳ cách nào để cải thiện khả năng cuộn?
Rahul Vyas

@RahulVyas xin lỗi nhưng không có :( kiểm tra các yếu tố bên trong của bạn có thể bố trí có thể được tối ưu hóa một chút vì nó liên quan đến lớp / lớp con
Catalin

5

Từ các tài liệu:

displayCells Trả về các ô bảng hiển thị trong bộ thu.

- (NSArray *)visibleCells

Giá trị trả về Một mảng chứa các đối tượng UITableViewCell, mỗi đối tượng đại diện cho một ô có thể nhìn thấy trong chế độ xem bảng nhận.

Tính khả dụng Có sẵn trong iOS 2.0 trở lên.

Xem thêm - indexPathsForVosystemRows


4
Xin lỗi có lẽ tôi đã không đủ rõ ràng. Tôi chỉ quan tâm đến những ô hoàn toàn có thể nhìn thấy được. - (NSArray *) hiddenCells và indexPathsForVosystemRows đều trả về các ô có thể nhìn thấy hoàn toàn và hiển thị một phần. Như bạn có thể thấy trong đoạn mã của tôi, tôi đang sử dụng - (NSArray *) opensCells để tìm những cái có thể nhìn thấy hoàn toàn. Cảm ơn dù sao.
RohinNZ

4

Nếu bạn cũng muốn tính đến contentInset và không muốn dựa vào superview (khung xem bảng trong superview có thể là một cái gì đó khác hơn 0,0), đây là giải pháp của tôi:

extension UITableView {

    public var boundsWithoutInset: CGRect {
        var boundsWithoutInset = bounds
        boundsWithoutInset.origin.y += contentInset.top
        boundsWithoutInset.size.height -= contentInset.top + contentInset.bottom
        return boundsWithoutInset
    }

    public func isRowCompletelyVisible(at indexPath: IndexPath) -> Bool {
        let rect = rectForRow(at: indexPath)
        return boundsWithoutInset.contains(rect)
    }
}

1
- (BOOL)checkVisibilityOfCell{
    if (tableView.contentSize.height <= tableView.frame.size.height) {
        return YES;
    } else{
        return NO;
    }
}

0
UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath];
CGRect frame = cell.frame;
if (CGRectContainsRect(CGRectOffset(self.collectionView.frame, self.collectionView.contentOffset.x, self.collectionView.contentOffset.y), frame))
{
    // is on screen
}

0

Ngay cả khi bạn nói rằng bạn muốn kiểm tra nó mỗi khi bạn cuộn, bạn cũng có thể sử dụng

-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
       CGRect cellRect = [tableView convertRect:cell.frame toView:tableView.superview];
       if (CGRectContainsRect(tableView.frame, cellRect)){
            //Do things in case cell is fully displayed
        }

}

0

Có thể đối với vấn đề này, tốt hơn nên sử dụng chức năng tiếp theo từ UITableViewDelegate

func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath)

Điều này sẽ không hoạt động. Từ một tài liệutells the delegate that the specified cell was removed from the table.
anatoliy_v

0

Đoạn mã dưới đây sẽ cho phép bạn kiểm tra xem ô chế độ xem bộ sưu tập có hoàn toàn hiển thị hay không thông qua các thuộc tính bố cục của chế độ xem bộ sưu tập.

guard let cellRect = collectionView.layoutAttributesForItem(at: indexPath)?.frame else { return } let isCellCompletelyVisible = collectionView.bounds.contains(cellRect)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.