UITableView не обрабатывает contentOffset правильно при прокрутке / подкачке

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

текущим решением этих требований является скользящее окно над массивом данных. Когда пользователь прокручивает, я добавляю ячейки с одного конца и удаляю с другого конца. Я использую положение прокрутки (глядя на то, какие ячейки идут на экране), чтобы определить, пришло ли время для загрузки новые данные.

обычно, когда вы называете tableView.deleteRows(at:with:) и удалить ячейки из начала таблицы, tableView регулирует его contentOffset свойство, поэтому пользователь по-прежнему видит те же ячейки, что и до операции.

однако, когда tableView замедляется после прокрутки, его contentOffset не настраивается при обновлениях, и это приводит к загрузке новых страниц снова и снова, пока замедление не будет завершено. Затем, при первом обновлении после замедления,contentOffset фиксируется tableView и загрузки останавливает.

то же самое происходит при прокрутке назад и добавлении значений в начале таблицы с tableView.insertRows(at:with:).

как я могу заставить UITableView правильно настроить его contentOffset?

или есть другие способы преодолеть эту ошибку-сохранение возможности загружать произвольную часть в середине массива данных и прокручивать ее?

Я сделал крошечный проект, иллюстрирующий баг:

https://github.com/wsb9/TableViewExample

2 ответов


из вашего примера проекта я могу понять, что вы пытаетесь реализовать бесконечную прокрутку концепции содержимого окна, чтобы вы всегда могли иметь фиксированное количество строк (пути индекса ,скажем, 100), так что когда окно прокручивается вниз/вверх - вид таблицы удалить indexPaths сверху/снизу соответственно. И даже если у вас больше элемента источника данных, вы всегда можете иметь tableView indexPaths 100

в основном вы имеете дело с двумя проблема:

  1. ContentOffset

  2. динамическая высота

предположим, что высота фиксирована (44), а таблица не перевернута. Чтобы реализовать окно для бесконечной прокрутки, вам нужно сделать следующее:

override func scrollViewDidScroll(_ scrollView: UIScrollView) {

        let bottom: CGFloat = scrollView.contentSize.height - scrollView.frame.size.height
        let buffer: CGFloat = 3 * 44
        let scrollPosition = scrollView.contentOffset.y

        if (scrollPosition > bottom - buffer) {
            dataSource.expose(dataSource.exposedRange.shift(by: 25))
            self.tableView.contentOffset.y -= self.dataSource.deltaHeightToRemove
        }
    }
  • решите, сколько высоты буфера вам нужно сохранить, когда прокрутка идет вниз. Этот буфер высоты-это высота, после которой вы решите вставить еще один элемент (25) в источник.
  • на данный момент Вы теперь должны удалить элементы из верхней части
  • когда вы удаляете элемент сверху, вы в основном говорите scrollView, чтобы уменьшить его контент-смещение на ту же высоту.
  • таким образом, общий размер контента будет решена каждый раз

    надеюсь, это поможет.

    EDIT: - Вот модифицированный код, который на самом деле делает бесконечную прокрутку внизу вид таблицы с динамической высотой ячейки. Этот не увеличивает количество строк более 100. но все равно загружает данные в скользящее окно. ссылке


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

  • одна вещь, которую вы хотите увеличить производительность вашего представления таблицы, загружая только несколько ячеек за раз
  • ваша вторая проблема иногда вы хотите загрузить представление таблицы с данными, которые случайным образом размещены в массиве источника данных

Я проверил ваш код и вы реализовали свой sliding-window over data-source model очень интересно. Проблема, вызванная тем, что ты пытался сделать tableview эффективно путем извлекать и читать клетки.

на самом деле Dequeuing ячейка должна повторно использовать ячейку уже в памяти. Пожалуйста, взгляните на документацию apple,

по соображениям производительности источник данных табличного представления должен повторное использование объектов UITableViewCell при назначении ячеек строкам в метод tableView(_:cellForRowAt:). Представление таблицы поддерживает очередь или список объектов UITableViewCell, которые имеет источник данных маркировано для повторное использование. Вызовите этот метод из объекта источника данных при запросе укажите новую ячейку для представления таблицы. Этот метод извлекает собой существующая ячейка, если она доступна или создает новую, используя класс или файл nib, который вы ранее зарегистрировали. Если ячейка недоступна для повторное использование и вы не зарегистрировали класс или файл nib, этот метод возвращает nil.

хорошая новость sliding-window over data-source model работает отлично, как только я удалил ваш механизм удаления строк и чтения. Вот ваш рабочий код,

https://drive.google.com/file/d/0B2y_JJbzjRA6dDR3QzRMUzExSGs/view?usp=sharing