Вложенный CollectionView неправильно настраивает источник данных или делегирование? - PullRequest
0 голосов
/ 11 июня 2018

У меня есть настройка collectionView следующим образом:

class TagViewController: UIViewController,  UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, SNPostViewCellDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.delegate = self
        timeline.delegate = self
        UserService.posts(for: User.current,tagString: self.tagTitle!) { (postStuff) in
            let posts = postStuff.0
            self.postIds = postStuff.1
            self.posts = posts.sorted(by: {
                $0.timeStamp.compare($1.timeStamp) == .orderedAscending
            })
            self.timeline.dataSource = self
            self.collectionView.dataSource = self
            //self.timeline.collectionViewLayout.invalidateLayout()
        }
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        if collectionView == self.collectionView {
            return posts.count
        } else if collectionView == self.timeline {
            let first = posts.first?.timeStamp
            let last = posts.last?.timeStamp
            let months = last?.months(from: first!) ?? 0
            print("no of months",months)

            if let diff = last?.months(from: first!), diff <= 5 {
                return months + 5-diff
            } else {
                return months + 1
            }
        } else {
            preconditionFailure("Unknown collection view!")
        }
    }
  func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        if collectionView == self.collectionView {
            let post = posts[indexPath.row]
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! SNPostViewCell
            cell.isVideo = post.isVideo
            cell.delegate = self
            cell.notes.text = post.notes
            cell.thumbnailURL = URL(string: post.thumbnailURL)
            let formatter = DateFormatter()
            formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
            cell.timeStampLabel.text = formatter.string(from: post.timeStamp)
            cell.mediaURL = URL(string: post.mediaURL)
            cell.notes.topAnchor.constraint(equalTo: cell.thumbnail.bottomAnchor,constant: 0.0).isActive = true
            return cell
        } else if collectionView == self.timeline {
            let index = indexPath.row
            print(index,"index")
            let calendar = Calendar.current
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "MMM"
            let firstPost = posts.first?.timeStamp
            let month = calendar.date(byAdding: .month, value: index, to: firstPost!)
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SNMonthViewCell", for: indexPath) as! SNMonthViewCell
            cell.monthLabel.text = dateFormatter.string(from: month!)
            cell.monthLabel.textAlignment = .center
            return cell
        } else {
            preconditionFailure("Unknown collection view!")
        }
    }

Как вы можете видеть, как только мои сообщения загружены, я установил dataSource для моей временной коллекции collectionView - временная шкала - это то, что меня интересует, так как это то, чтосодержит вложенный subcollectionView.В основном, как это работает, каждый SNMonthViewCell помечается месяцем - например, ян, затем внутри ячейки вложенная подколлекция содержит ячейку для каждого дня января.Теперь, чтобы установить делегаты / источники данных для этого дня collectionView в ячейке месяца, я делаю:

func collectionView(_ collectionView: UICollectionView,
                             willDisplay cell: UICollectionViewCell,
                             forItemAt indexPath: IndexPath){
    if collectionView == self.timeline{
        guard let monthViewCell = cell as? SNMonthViewCell else  {
            return
        }
        let index = indexPath.item
        let firstPost = self.posts.first?.timeStamp
        let monthDate = Calendar.current.date(byAdding: .month, value: index, to: firstPost!)
        let monthInt = Calendar.current.component(.month, from: monthDate!)
        let yearInt = Calendar.current.component(.year, from: monthDate!)
        let postDates = dates(self.posts, withinMonth: monthInt, withinYear: yearInt)
        let dayDelegatesInstance = dayCellDelegates(firstDay: (monthDate?.startOfMonth())!, monthPosts:postDates)
        monthViewCell.setCollectionViewDataSourceDelegate(dataSourceDelegate: dayDelegatesInstance)
    }
}

class dayCellDelegates: NSObject,UICollectionViewDataSource, UICollectionViewDelegate {
    let firstDay: Date
    let monthPosts: [Post]

    init(firstDay: Date, monthPosts: [Post]){
        self.firstDay = firstDay
        self.monthPosts = monthPosts
    }

    func collectionView(_ collectionView: UICollectionView,
                        numberOfItemsInSection section: Int) -> Int {
        print("how many cells?")
        let range = Calendar.current.range(of: .day, in: .month, for: self.firstDay)!
        return range.count
    }

    func collectionView(_ collectionView: UICollectionView,
                        cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        print("looking for cell!")
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "dayCell",
                                                      for: indexPath as IndexPath)
        let components: Set<Calendar.Component> = [.day]
        //let contained = self.postDates.reduce(false,{Calendar.current.dateComponents(components, from: $0).day == indexPath.item})
        let filtered = self.monthPosts.filter { (post) -> Bool in
            Calendar.current.dateComponents(components, from: post.timeStamp).day == indexPath.item
        }
        if filtered.isEmpty == true {
            cell.backgroundColor = UIColor(red:0.15, green:0.67, blue:0.93, alpha:1.0)
        }
        return cell
    }
}

Вы можете видеть, что я использую свой внешний класс dayCellDelegates для установки делегатов для этого subcollectionView.Теперь в SNMonthViewCell, о котором я говорил ранее, я устанавливаю источник данных и делегирую так:

class SNMonthViewCell: UICollectionViewCell {

    @IBOutlet private weak var dayTicks: UICollectionView!

    @IBOutlet weak var monthLabel: UILabel!

    func setCollectionViewDataSourceDelegate
        <D: UICollectionViewDataSource & UICollectionViewDelegate>
        (dataSourceDelegate: D) {
        dayTicks.delegate = dataSourceDelegate
        dayTicks.dataSource = dataSourceDelegate
        dayTicks.reloadData()
    }
}

Удивительно ... ОДНАКО методы делегирования для моего subcollectionView никогда не вызываются.Откуда мне знать?Поскольку те первые операторы печати, которые я вставил в первые два метода, никогда не вызываются ....

Как выглядит моя временная шкала?

enter image description here

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

1 Ответ

0 голосов
/ 13 июня 2018

Давайте посмотрим, что происходит, когда вы устанавливаете dataSource и делегируете SNMonthViewCell ячейку

func collectionView(_ collectionView: UICollectionView,
    willDisplay cell: UICollectionViewCell,
    forItemAt indexPath: IndexPath){
    if collectionView == self.timeline{
        ...
        let dayDelegatesInstance = dayCellDelegates(firstDay: (monthDate?.startOfMonth())!, monthPosts:postDates)
        monthViewCell.setCollectionViewDataSourceDelegate(dataSourceDelegate: dayDelegatesInstance)
    }
}

, вы создаете экземпляр dayCellDelegates объекта, устанавливаете его в свою ячейку и все.dayCellDelegates будет освобожден при выходе из функции.

Простое, что вам нужно сделать, это сохранить ссылки на ваши dayCellDelegates объекты, чтобы они не были освобождены.Вы можете сделать это по-разному, но я предлагаю использовать Dictionary.

Во-первых, вам нужно создать свойство private var dataSources: [IndexPath : dayCellDelegates] = [:] в вашем классе TagViewController.Теперь, когда будет вызван метод willDisplay, вам необходимо сохранить объект dayCellDelegates в словарь

func collectionView(_ collectionView: UICollectionView,
    willDisplay cell: UICollectionViewCell,
    forItemAt indexPath: IndexPath){
    if collectionView == self.timeline{
        guard let monthViewCell = cell as? SNMonthViewCell else  {
            return
        }
        let index = indexPath.item
        let firstPost = self.posts.first?.timeStamp
        let monthDate = Calendar.current.date(byAdding: .month, value: index, to: firstPost!)
        let monthInt = Calendar.current.component(.month, from: monthDate!)
        let yearInt = Calendar.current.component(.year, from: monthDate!)
        let postDates = dates(self.posts, withinMonth: monthInt, withinYear: yearInt)
        let dayDelegatesInstance = dayCellDelegates(firstDay: (monthDate?.startOfMonth())!, monthPosts:postDates)
        dataSources[indexPath] = dayDelegatesInstance
        monthViewCell.setCollectionViewDataSourceDelegate(dataSourceDelegate: dayDelegatesInstance)
    }
}

Однако вы будете переписывать свой делегат каждый раз, когда будет вызываться метод.Чтобы это исправить, вам нужно проверить, был ли dayCellDelegates ранее сохранен

if let dayDelegatesInstance = dataSources[indexPath] {
    monthViewCell.setCollectionViewDataSourceDelegate(dataSourceDelegate: dayDelegatesInstance)
} else {
    let dayDelegatesInstance = dayCellDelegates(firstDay: (monthDate?.startOfMonth())!, monthPosts:postDates)
        dataSources[indexPath] = dayDelegatesInstance
        monthViewCell.setCollectionViewDataSourceDelegate(dataSourceDelegate: dayDelegatesInstance)
}
...