0

I need to store a view to use as a UICollectionView header. I don't want it to cycle out of memory though, because it needs to keep its state/data, etc.

With a table view you can just do tableView.tableHeaderView = view.

Here's what I'm trying:

func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {

        switch kind {
        case MagazineLayout.SupplementaryViewKind.sectionHeader:
            if let t = headerView { //headerView is an instance var
                return t
            } else {
                let view = collectionView.dequeueReusableSupplementaryView(ofKind: MagazineLayout.SupplementaryViewKind.sectionHeader, withReuseIdentifier: "MyHeaderView", for: indexPath) as! MyHeaderView
                view.titleLabel.text = "test"
                view.switch.addAction(for: .valueChanged, { [weak self] in
                    self?.switchValueChanged()
                })
                headerView = view
                return view
            }
        ...
    }

I don't want to re-create it every time the user scrolls it away and then back, so I'm trying to store a reference to it. This isn't working though. Hard to explain but the view it displays is cut off and the switch isn't responsive. If I comment out the "if" part and just create a new one every time, it looks correct but state is lost (i.e. the switch gets turned off) What's the best way to do this?

soleil
  • 12,133
  • 33
  • 112
  • 183
  • it ins't re-created each time the user scrolls and then go back. you just need to change the text, re-add action for switch. But for your issue, if you want to keep reference to it, I think you shouldn't call `collectionView.dequeueReusableSupplementaryView`, and replace it with `MyHeaderView.init` – nghiahoang Jul 07 '20 at 04:44
  • That doesn't quite work. You'll get an error - `was not retrieved by calling -dequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath: or is nil` – soleil Jul 07 '20 at 04:52
  • oh, I see. But your implementation doesn't work because that `when user scrolls and then go back`, the header was marked as reusable, was called with `prepareForReuse` method etc... – nghiahoang Jul 07 '20 at 05:03

1 Answers1

0

Since you're keeping the reference and not letting it deallocate when it scrolls out of the view, remove the register and dequeuing entirely. It worked fine for me, here's how:

let view = MyHeaderView()
override func viewDidLoad() {
    super.viewDidLoad()
    view.titleLabel.text = "test"
    view.switch.addAction(for: .valueChanged, { [weak self] in
        self?.switchValueChanged()
    })
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
    switch kind {
    case MagazineLayout.SupplementaryViewKind.sectionHeader:
        return view
        //...
    }
}
Frankenstein
  • 15,732
  • 4
  • 22
  • 47
  • Unfortunately I get this: `*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'the view returned from -collectionView:viewForSupplementaryElementOfKind:atIndexPath (MagazineLayoutSupplementaryViewKindSectionHeader, {length = 2, path = 0 - 0}) was not retrieved by calling -dequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath` so it seems like it's forcing you to use `dequeueReusableSupplementaryViewOfKind` – soleil Jul 07 '20 at 22:22