UITableView с полноэкранными ячейками — разбивка на страницы прерывается после извлечения дополнительных строк

#ios #swift #uitableview #scroll #pagination

#iOS #swift #uitableview #прокрутка #разбивка на страницы

Вопрос:

Мое приложение использует UITableView для реализации пользовательского интерфейса в стиле TikTok. Каждая ячейка равна высоте всего экрана. Разбивка на страницы включена, и это отлично работает для первой партии из 10 загружаемых записей. Каждая UITableViewCell — это одна «страница». Пользователь может «перелистывать» страницы, проводя пальцем, и изначально каждая «страница» полностью переворачивается, как и ожидалось. Однако, когда я добавляю дополнительные строки, проверяя, является ли видимая в данный момент ячейка последней, а затем загружаю еще 10 строк, разбивка на страницы выходит из строя. В результате прокрутки получается частично «перевернутая» ячейка — части двух ячеек видны одновременно. Я пробовал разные вещи, но я даже не уверен, в чем проблема. Кажется, что TableView теряет геометрию.

Примечание: после сбоя разбивки на страницы я могу полностью вернуться к первой ячейке. В этот момент UITableView, похоже, восстанавливает самообладание, и я снова могу корректно просматривать все загруженные строки, включая новые.

     func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    // Pause the video if the cell is ended displaying
    if let cell = cell as? HomeTableViewCell {
        cell.pause()
    }
    if let indices = tableView.indexPathsForVisibleRows {
        for index in indices {
            if index.row >= self.data.count - 1 {
                self.viewModel!.getPosts()
                break
            }
        }
    }
}
 

Комментарии:

1. Можно попробовать метод TableView.scrollToItem, чтобы исправить положение ячейки.

2. Я столкнулся с той же проблемой, поэтому я отказался от UITableView и использовал gist.github.com/chanonly123/fd58b04b73e930fd0feef97d3aa732fe

3. @ChanOnly123 — спасибо, я попробовал несколько разных способов использования scrollToItem, но пока не нашел ни одного подхода, который решал бы проблему. Большое спасибо за этот альтернативный код — если это окажется фундаментальным ограничением UITableView, я попробую ваш код следующим. Приветствия!

4. @ChanOnly123 — Я задал вопрос в вашем github — не могли бы вы взглянуть, если у вас есть минутка? Спасибо!!

Ответ №1:

Чтобы создать UX в стиле «Tik Tok», я закончил тем, что использовал структуру текстур вместе с поставщиком облачного видео (mux.com ). Теперь работает нормально.

Ответ №2:

Я столкнулся с той же проблемой, и поскольку я не мог найти решение где-либо еще, вот как я решил ее без использования текстуры:

Я использовал UITableViewDataSourcePrefetching протокол для извлечения новых данных для вставки

 extension TikTokTableView: UITableViewDataSourcePrefetching {
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
    viewModel.prefetchRows(at: indexPaths)
}
 

}

prefetchRows выполнит запрос, если видимая ячейка является последней, как в моем случае

 func prefetchRows(at indexPaths: [IndexPath]) {
    if indexPaths.contains(where: isLastCell) {
        getPosts(type: typeOfPosts, offset: posts.count, lastPostId: lastPost)
    }
}

private func isLastCell(for indexPath: IndexPath) -> Bool {
    return indexPath.row == posts.count - 1
}
 

У меня есть weak var view тип делегата TikTokTableViewDelegate в моей модели представления, чтобы иметь доступ к функции insertItems , реализованной my TikTokTableView . Эта функция используется для информирования UITableView о том, куда вставлять входящие сообщения

 self.posts.append(contentsOf: response.posts)
let indexPathsToReload = self.calculateIndexPathToReload(from: response.posts)
self.view?.insertItems(at: indexPathsToReload)


private func calculateIndexPathToReload(from newPosts: [Post]) -> [IndexPath] {
    let startIndex = posts.count - newPosts.count
    let endIndex = startIndex   newPosts.count
    print(startIndex, endIndex)
    return (startIndex..<endIndex).map { IndexPath(row: $0, section: 0) }
}
 

и это insertItems функция, реализованная в TikTokTableView , и вот ключ: если мы попытаемся вставить эти строки, разбивка на страницы таблицы завершится ошибкой и оставит это странное смещение, мы должны сохранить indexPaths в локальном свойстве и вставить их после завершения анимации прокрутки.

 extension TikTokTableView: TikTokTableViewDelegate {
func insertItems(at indexPathsToReload: [IndexPath]) {
        DispatchQueue.main.async {
            // if we try to insert rows in the table, the scroll animation will be stopped and the cell will have a weird offset
            // that's why we keep the indexPaths and insert them on scrollViewDidEndDecelerating(:)
            self.indexPathsToReload = indexPathsToReload
        }
    }
}
 

Поскольку UITableView это подкласс UIScrollView , к которому у нас есть доступ scrollViewDidEndDecelerating , эта функция запускается в конце прокрутки пользователя, и это время, когда мы вставляем новые строки

 func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    if !indexPathsToReload.isEmpty {
        tableView.insertRows(at: indexPathsToReload, with: .none)
        indexPathsToReload = []
    }
}