0

I have a collection view in my UIViewController class and use an array of Booleans to see which indices are expanded (refer to this answer).

var isExpanded = [Bool]()

So when I click a button, I am able to reload the cell with:

@objc func topButtonTouched(indexPath: IndexPath) {
    print(indexPath)
    isExpanded[indexPath.row] = !isExpanded[indexPath.row]
    UIView.animate(withDuration: 0.6, delay: 0.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 1.0, options: UIView.AnimationOptions.transitionCurlUp, animations: {
        self.listCollectionView.reloadItems(at: [indexPath])
    }, completion: { success in
        print("success")
    })
}

So far so good. However, the issue I am having appears when the cells are reordered. I am not reordering with beginInteractiveMovementForItem(), updateInteractiveMovementTargetPosition(), etc.

I started using this function (from this repository) below to create a scaling animation (of the snapshot of the cell) thinking that it would reorder the cells as well:

@objc func longPressRecognized(_ recognizer: UILongPressGestureRecognizer) {
    let location = recognizer.location(in: collectionView)
    let indexPath = collectionView.indexPathForItem(at: location)

    switch recognizer.state {
    case UIGestureRecognizer.State.began:
        guard let indexPath = indexPath else { return }

        let cell = cellForRow(at: indexPath)
        snapshotView = cell.snapshotView(afterScreenUpdates: true)
        collectionView.addSubview(snapshotView!)
        cell.contentView.alpha = 0.0

        UIView.animate(withDuration: 0.2, animations: {
            self.snapshotView?.transform = CGAffineTransform(scaleX: 1.1, y: 1.1)
            self.snapshotView?.alpha = 0.9
        })
        snapshotPanPoint = location
        snapshotIndexPath = indexPath
    case UIGestureRecognizer.State.changed:
        guard let snapshotPanPoint = snapshotPanPoint else { return }

        let translation = CGPoint(x: location.x - snapshotPanPoint.x, y: location.y - snapshotPanPoint.y)
        snapshotView?.center.y += translation.y
        self.snapshotPanPoint = location

        guard let indexPath = indexPath else { return }
        collectionView.moveItem(at: snapshotIndexPath!, to: indexPath)
        snapshotIndexPath = indexPath
    default:
        guard let snapshotIndexPath = snapshotIndexPath else { return }
        let cell = cellForRow(at: snapshotIndexPath)
        UIView.animate(
            withDuration: 0.2,
            animations: {
                self.snapshotView?.center = cell.center
                self.snapshotView?.transform = CGAffineTransform.identity
                self.snapshotView?.alpha = 1.0
        },
            completion: { finished in
                cell.contentView.alpha = 1.0
                self.snapshotView?.removeFromSuperview()
                self.snapshotView = nil
        })
        self.snapshotIndexPath = nil
        self.snapshotPanPoint = nil
    }
}

I've tried changing the isExpanded array in the .changed section of the switch but had no luck. I also tried this answer.

Gif of my problem:

Gif Screen Shot

Edit: Before when I had a single array for bools, it was like this:

@objc func topButtonTouched(indexPath: IndexPath) {
isExpanded[indexPath.item] = !isExpanded[indexPath.item]

and now with the main array of objects, I am trying to do this:

@objc func topButtonTouched(indexPath: IndexPath) {

    var names = mainData.map({ $0.isExpanded })
    names[indexPath.row] = !names[indexPath.row]

and then update the indexpaths.

  • 1
    What happens when you change `isExpanded` array right before calling `collectionView.moveItem()`? to test, try setting `isExpanded` to be all false, just to see if there is any effect at all. – Yonat Dec 06 '18 at 06:28
  • That was what i tried in the beginning but I guess I was doing it wrong. I posted a temporary solution. –  Dec 06 '18 at 14:33

1 Answers1

0

In general, using multiple data sources in a single UITableView brings bad luck, when you're doing it while planning on changing the order of items- that's just suicide :)

So I would recommend you to change your data source from two arrays- [String] and [Bool] to a single [ListItem] -

(Here I'm using a class but you may use a struct, just keep in mind classes are passed by reference while structs are passed by value)

class ListItem{
    var title: String
    var isExpended: Bool

    init(title: String, isExpended: Bool){
        self.title = title
        self.isExpended = isExpended
    }
}

Then, when the user completed reordering, you need to commit those changes on the list- removing and inserting the element to a different index will do the job.

Niv
  • 517
  • 5
  • 18
  • Thank you. I will try and implement it later when I have the chance. –  Dec 06 '18 at 15:38
  • I made the custom class and then at the top of my controller class I made an test array of ListItems. The labels and initial heights are there as they should be but the only problem I have is expanding the cells. for the expanding function that is called when the button is clicked, i used the map function to get the array of heights and then I set it to the opposite of whatever it was but it doesnt seem to change. –  Dec 06 '18 at 16:54
  • You may add the part of your code that doesn’t work out to your question so I could take a look – Niv Dec 06 '18 at 17:13
  • I solved it by doing self.mainData[indexPath.item].isExpanded = !self.mainData[indexPath.item].isExpanded. Would there be a preferred way? –  Dec 06 '18 at 17:42
  • It's good, you may also use `mainData[indexPath.item].isExpended.toggle() `. BTW if this answer helped you please mark it as accepted for the upcoming users :) – Niv Dec 06 '18 at 18:07