vee
vee

Reputation: 739

How to track changes in an array and collectionview.reloaddata()

i'm experiencing issues with reloading data in my collectionview. I have an array populating the collectionview. However whenever i add more items to the array it doesn't show up unless I reloaddata(). What is the best way to have the collectionview reloaddata whenever it sees changes in the array, whether is removing or adding new elements? Thanks in advance! Edit: Added Code.

class CheckoutController: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {

var inventoryTabController: InventoryTabController?

override init(frame: CGRect){
    super.init(frame: frame)
    setupViews()

}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}


func loadItems() -> [Item]? {
    return NSKeyedUnarchiver.unarchiveObject(withFile: Item.ArchiveURL.path) as? [Item]
}

func saveItems() {
    let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(checkout, toFile: Item.ArchiveURL.path)
    if !isSuccessfulSave {
        print("Failed to save items...")
    }
}

func addItem(item: Item) {
    items.append(item)
    collectionView.reloadData()
}

func editItem(item: Item, index: Int) {
    items[index] = item
    collectionView.reloadData()
}

func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    // Identify which segue is occuring.
    if segue.identifier == "ShowDetail" {
        let itemDetailViewController = segue.destination as! AddItemController

        // Get the cell that generated this segue.
        if let selectedItemCell = sender as? InventoryCell {
            let indexPath = collectionView.indexPath(for: selectedItemCell)!
            let selectedItem = items[indexPath.row]
            itemDetailViewController.item = selectedItem
        }
    }
    else if segue.identifier == "AddItem" {
        print("Adding new meal.")
    }
}

lazy var collectionView: UICollectionView = {
    let layout = UICollectionViewFlowLayout()
    let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)

    cv.dataSource = self
    cv.delegate = self

    cv.backgroundColor = UIColor.rgb(r: 247, g: 247, b: 247)
    return cv
}()

let cellId = "cellId"
let paynow = "paynow"
func setupViews() {
    backgroundColor = .brown

    addSubview(collectionView)

    addConstraintsWithFormat("H:|[v0]|", views: collectionView)
    addConstraintsWithFormat("V:|[v0]|", views: collectionView)
    collectionView.indicatorStyle = UIScrollViewIndicatorStyle.white

    collectionView.register(PayNowCell.self, forCellWithReuseIdentifier: paynow)

    collectionView.register(CheckoutCell.self, forCellWithReuseIdentifier: cellId)
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return checkout.count //init number of cells
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

    if indexPath.item == 0 {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "paynow", for: indexPath) as! PayNowCell //init cells
    return cell
    }else{
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! CheckoutCell //init cells
    print("Printing this \(checkout.count)")
    cell.item = checkout[indexPath.item]
    return cell
    }

}

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    //let item = items[indexPath.item]
    //inventoryController?.showItemDetailForItem(item: item, index: indexPath.item)
    print("selected")
    print(indexPath.item)
    collectionView.reloadData()
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    if indexPath.item == 0 {
        return CGSize(width:frame.width, height: 100) //each cell dimensions
    }else{
        return CGSize(width:frame.width, height: 150) //each cell dimensions
    }
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return 0
}

}

Upvotes: 0

Views: 1530

Answers (1)

JustinM
JustinM

Reputation: 2222

There are many ways to do this but without seeing any code I can't recommend a specific way in particular. The most generic would be setting up a didSet listener on your array, and call collectionView.reloadData() when it changes.

 var dataArr = [1, 2, 3, 4] {
       didSet {
         //collectionView.reloadData()
         print("Changed from \(oldValue) to \(dataArr)")
       }  
}

dataArr.append(5)
//prints - Changed from [1, 2, 3, 4] to [1, 2, 3, 4, 5]
dataArr.removeFirst()
//prints - Changed from [1, 2, 3, 4, 5] to [2, 3, 4, 5]

Edit below based on comment regarding global variable

You should really try to stay away from global variables. Since your persisting the list you can retrieve it when necessary. I'm not going to get into the details but there's plenty of stuff on SO regarding the reasons why. Now that I mentioned that I'll go ahead and give you a way to go about handling this situation.

You can use NotificationCenter to post an app wide notification when the array values change. Then subscribe to the notifications in any viewController you wish.

extension Notification.Name {
    static let arrayValueChanged = Notification.Name("arrayValueChanged")
}

var dataArr = [1, 2, 3, 4] {
    didSet {
        NotificationCenter.default.post(name: .arrayValueChanged, object: nil)
    }
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(forName: .arrayValueChanged, object: nil, queue: OperationQueue.main) { [weak self] (notif) in
            self?.collectionView.reloadData()
        }
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}

Upvotes: 3

Related Questions