วิธีที่ดีที่สุดในการตรวจสอบว่า UITableViewCell มองเห็นได้อย่างสมบูรณ์หรือไม่


100

ฉันมี UITableView ที่มีเซลล์ที่มีความสูงต่างกันและฉันจำเป็นต้องรู้ว่าเมื่อใดที่มองเห็นได้อย่างสมบูรณ์หรือไม่

ในขณะนี้ฉันกำลังวนรอบแต่ละเซลล์ในรายการเซลล์ที่มองเห็นได้เพื่อตรวจสอบว่ามองเห็นได้อย่างสมบูรณ์ทุกครั้งที่เลื่อนมุมมองหรือไม่ นี่เป็นแนวทางที่ดีที่สุดหรือไม่?

นี่คือรหัสของฉัน:

- (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];
        }
    }
}

แก้ไข:

โปรดทราบว่า * - (NSArray ) visibleCellsจะส่งคืนเซลล์ที่มองเห็นได้ซึ่งทั้งมองเห็นได้อย่างสมบูรณ์และมองเห็นได้บางส่วน

แก้ไข 2:

นี่คือรหัสที่แก้ไขหลังจากรวมโซลูชันจากทั้งlnafzigerและVadim 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
คุณควรไปที่คำถามก่อนหน้านี้ทั้งหมดและยอมรับคำตอบของผู้ที่ช่วยคุณ
Baub

4
ขอบคุณที่บอกฉัน! ฉันได้ให้ +1 แก่พวกเขาแล้ว แต่ลืมเกี่ยวกับคุณสมบัติคำตอบที่ยอมรับในชุดไปแล้ว
RohinNZ

รหัสของคุณดูถูกต้องสำหรับฉันและแม้ว่าจะซับซ้อน แต่ก็ใช้งานได้ อย่าแก้ไขสิ่งที่ไม่พังใช่มั้ย?
CodaFi

คำตอบ:


126

คุณสามารถรับ rect ของเซลล์ด้วยrectForRowAtIndexPath:วิธีการและเปรียบเทียบกับขอบเขตของ tableview rect โดยใช้CGRectContainsRectฟังก์ชัน

โปรดทราบว่าสิ่งนี้จะไม่สร้างอินสแตนซ์ของเซลล์หากมองไม่เห็นและจะค่อนข้างเร็ว

รวดเร็ว

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

Obj-C

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

แน่นอนว่าสิ่งนี้จะไม่ถือว่ามุมมองตารางถูกตัดโดย superview หรือถูกบดบังด้วยมุมมองอื่น


ขอบคุณ! ฉันได้รวมรหัสนี้กับโซลูชันของ lnafziger แล้ว
RohinNZ

11
ในความคิดที่สองอาจเป็นเพียงCGRectContainsRect(tableView.bounds, [tableView rectForRowAtIndexPath:indexPath])
Vadim Yelagin

1
ทำไม cellRect ของฉันเป็น origin.x = 0, origin.y = 0, width = 0, height = 0? แม้ว่าใน UI จะไม่ใช่ 0 ทั้งหมด แต่ฉันกำลังใช้รูปแบบอัตโนมัติมีแนวคิดใด ๆ
RainCast

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

เราจะจัดการฟังก์ชันที่คล้ายกันสำหรับ UICollectionView ได้อย่างไร?
Satyam

64

ฉันจะเปลี่ยนเป็นแบบนี้:

- (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];
}

<และ> ในคำสั่ง if ควรเป็น <= หรือ> = ฉันจะแก้ไขสิ่งนี้ในคำตอบ ...
Aardvark

12

คุณสามารถลองสิ่งนี้เพื่อดูว่าเปอร์เซ็นต์ที่มองเห็นได้:

-(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];
            }
        }
    }
}

หากมุมมองตารางสามารถแสดง 2 เซลล์พร้อมวิดีโอ (เช่นบน iPad) วิดีโอทั้งสองจะเล่นด้วยรหัสด้านบน?
เจอโรม

@Catalin ฉันได้ลองใช้วิธีแก้ปัญหาของคุณแล้ว แต่ tableview ของฉันช้าลง วิธีใดในการปรับปรุงประสิทธิภาพการเลื่อน?
Rahul Vyas

@RahulVyas ขอโทษ แต่ไม่ :( ตรวจสอบองค์ประกอบภายในของคุณบางทีเลย์เอาต์สามารถปรับให้เหมาะสมได้เล็กน้อยตามที่เกี่ยวกับเลเยอร์ / ชั้นย่อย
Catalin

5

จากเอกสาร:

visibleCells ส่งคืนเซลล์ตารางที่มองเห็นได้ในเครื่องรับ

- (NSArray *)visibleCells

Return Value อาร์เรย์ที่มีออบเจ็กต์ UITableViewCell แต่ละเซลล์แสดงเซลล์ที่มองเห็นได้ในมุมมองตารางที่รับ

ความพร้อมใช้งานมีให้ใน iOS 2.0 และใหม่กว่า

ดูเพิ่มเติม - indexPathsForVisibleRows


4
ขออภัยบางทีฉันไม่ชัดเจนพอ ฉันสนใจเฉพาะเซลล์ที่มองเห็นได้อย่างสมบูรณ์ - (NSArray *) visibleCells และ indexPathsForVisibleRows ส่งคืนเซลล์ที่มองเห็นได้อย่างสมบูรณ์และมองเห็นได้บางส่วน อย่างที่คุณเห็นในรหัสของฉันฉันใช้ - (NSArray *) visibleCells เพื่อค้นหาเซลล์ที่มองเห็นได้อย่างสมบูรณ์ ขอบคุณอย่างไรก็ตาม
RohinNZ

5

หากคุณต้องการคำนึงถึง contentInset และไม่ต้องการพึ่งพา superview (กรอบมุมมองตารางใน superview อาจเป็นอย่างอื่นที่ไม่ใช่ 0,0) นี่คือวิธีแก้ปัญหาของฉัน:

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)
    }
}


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

แม้ว่าคุณจะบอกว่าต้องการตรวจสอบทุกครั้งที่เลื่อนคุณก็สามารถใช้ได้เช่นกัน

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

บางทีสำหรับปัญหานี้ควรใช้ฟังก์ชันถัดไปจาก UITableViewDelegate

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

สิ่งนี้จะไม่ทำงาน จาก doctells the delegate that the specified cell was removed from the table.
anatoliy_v

0

โค้ดด้านล่างนี้จะช่วยให้คุณตรวจสอบได้ว่าเซลล์มุมมองคอลเลกชันสามารถมองเห็นได้อย่างสมบูรณ์ผ่านแอตทริบิวต์เค้าโครงของมุมมองคอลเลกชันหรือไม่

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.