Найкращий спосіб перевірити, чи повністю видно 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, так і Вадима Єлагіна :

- (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
Як бічна примітка, ви повинні перейти до всіх своїх попередніх питань і прийняти відповіді тих, хто вам допоміг.
Бауб

4
Дякуємо, що розповіли! Я вже дав їм +1, але забув про функцію прийнятої відповіді набору.
RohinNZ

Ваш код мені здається правильним, і хоча він складний, він працює. Не виправте, що не зламалося, так?
CodaFi

Відповіді:


125

Ви можете отримати прямої клітинки за rectForRowAtIndexPath:методом і порівняти її з прямою межею табличного перегляду за допомогою 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);

Звичайно, це не стосуватиметься перегляду таблиць, відсіканого за допомогою перегляду або затьмареного іншим поданням.


Дякую! Я поєднав цей код з рішенням lnafziger.
RohinNZ

11
З другої думки це може бути простоCGRectContainsRect(tableView.bounds, [tableView rectForRowAtIndexPath:indexPath])
Вадим Єлагін

1
чому це походження мій cellRect.x = 0, origin.y = 0, ширина = 0, висота = 0? хоча в інтерфейсі вони не всі 0, я використовую автоматичний макет, будь-які ідеї?
RainCast

7
Свіфт 3:let completelyVisible = tableView.bounds.contains(tableView.rectForRow(at: indexPath))
cbartel

Як ми можемо обробляти подібну функціональність для UICollectionView?
Сатьям

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 Я спробував ваше рішення, але мій перегляд таблиці набуває уповільнення. Будь-який спосіб покращити продуктивність прокрутки?
Рахул Вяс

@RahulVyas вибачте, але ні :( перевірте свої внутрішні елементи, можливо, макет можна трохи оптимізувати, що стосується шарів / підшарів
Catalin

5

З документів:

visibleCells Повертає комірки таблиці, які видно у приймачі.

- (NSArray *)visibleCells

Повернене значення Масив, що містить об'єкти UITableViewCell, кожен з яких представляє видиму комірку у поданні таблиці прийому.

Доступність Доступно в iOS 2.0 та новіших версіях.

Дивіться також - indexPathsForVisibleRows


4
Вибачте, можливо, я був недостатньо зрозумілий. Мене цікавлять лише клітини, які повністю видні. - (NSArray *) visibleCells та indexPathsForVisibleRows обидва повертають комірки, які повністю видимі та частково видимі. Як ви бачите в моєму коді, я вже використовую - (NSArray *) visibleCells, щоб знайти ті, які повністю видно. Спасибі все одно.
RohinNZ

4

Якщо ви також хочете взяти до уваги contentInset, і не хочете покладатися на перегляд (кадр подання таблиці в супервізові може бути чимось іншим, ніж 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)
    }
}

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

Навіть якщо ви сказали, що хочете перевіряти це кожного разу, коли ви прокручуєте, ви також можете використовувати

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.