UICollectionView не будет scrollToItem (at: IndexPath, at: position) - PullRequest
1 голос
/ 04 ноября 2019

Я использую этот метод для прокрутки представления моей коллекции.

import UIKit
extension UICollectionView {
    func scrollToLast() {
        guard numberOfSections > 0 else {
            print("number of sections < 0")
            return
        }
        let lastSection = numberOfSections - 1
        guard numberOfItems(inSection: lastSection) > 0 else {
            print("number of items < 0")
            return
        }
        let lastItemIndexPath = IndexPath(item: numberOfItems(inSection: lastSection) - 1,
                                          section: lastSection)
        self.scrollToItem(at: lastItemIndexPath, at: .centeredVertically, animated: true)
        print("last Item (section, item #): \(lastItemIndexPath)")
    }
}

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

У меня есть следующее предупреждение / ошибка в консоли, но, похоже, оно не совпадает с моментом прокруткивызывается, поэтому не уверен, что это уместно:

2019-11-04 06: 01: 13.017384-0800 AppName [17598: 4494554] [CollectionView] Попытка подготовить макет во время вызова prepareLayoutбыл уже в процессе (т. е. повторный входящий вызов) был проигнорирован. Пожалуйста, отправьте сообщение об ошибке. Экземпляр UICollectionView: (; layer =; contentOffset: {0, 0}; contentSize: {0, 0}; AdjustContentInset: {0, 0, 0, 0}; макет:; dataSource: appBundle.instance

Пример того, как я это называю: (это в viewDidLoad)

DataService.instance.messageListener(roomId: room.id, { (result) in
        self.messageArr.append(result)
        self.messageArr = self.messageArr.sorted(by: { $0.date < $1.date})
        self.collection.reloadData()
        self.collection.scrollToLast()
        self.messageTextArea.text = ""
})

Редактировать: я удалил все вызовы в основную очередь, и поведение изменилось. Видтеперь будет прокручиваться к вершине представления коллекции, когда метод messageListener запущен, но при использовании того же метода scrollToLast() в другом месте, представление прокручивается до последнего элемента, как и должно - но только после представленияпрокручивается вручную до дна. Я также пытался переместить код на viewDidAppear в ответе ниже, но эффекта не было.

Ответы [ 3 ]

1 голос
/ 04 ноября 2019

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

1 голос
/ 04 ноября 2019

Это не относится к вашему вопросу, но у меня есть несколько рекомендаций:
1) Вам не нужно вызывать DispatchQueue.main.async перед вызовом self.scrollToItem, потому что вы уже переключаетесь на основной поток в DataService.instance. .messageListener.
2) У вас есть потенциальные проблемы с многопоточностью с messageArr, потому что вы редактировали его из потока DataService messageListener completeHandlers (и он мог быть в нем в потоке). Как я понимаю, messageArr является источником данных для представления вашей коллекции. Поэтому, когда вы прокручиваете представление коллекции или перезагружаете ее, collectionView обращается к messageArr из основного потока. Это может привести к сбою или неопределенному поведению, потому что вы получаете доступ к массиву messageArr из двух потоков без блокировки. Одним из решений является просто асинхронизация всего обработчика завершения DataService.instance.messageListener в главной очереди.

    DataService.instance.messageListener(roomId: room.id, { (result) in
        DispatchQueue.main.async {
            self.messageArr.append(result)
            self.messageArr = self.messageArr.sorted(by: { $0.date < $1.date })
            self.collection.reloadData()
            self.collection.scrollToLast()
            self.messageTextArea.text = ""
        }
    })
0 голосов
/ 06 ноября 2019

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

Вот обходной путь:

extension UICollectionView {
    func scrollToBottomOfContent() {
        let contentHeight: CGFloat = self.contentSize.height
        let heightAfterInserts: CGFloat = self.frame.size.height - (self.contentInset.top + self.contentInset.bottom)
        if contentHeight > heightAfterInserts {
            self.setContentOffset(CGPoint(x: 0, y: self.contentSize.height - self.frame.size.height), animated: true)
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...