dank_muffin
dank_muffin

Reputation: 263

How to add item to a UICollectionViewController from a separate UIViewController without using Storyboard segue?

I have a UICollectionViewController displaying an array of data called countdowns. I have a UIViewController that is presented modally where the user can create a new countdown. When the user taps "Add" (a UIBarButtonItem), I want to add the new countdown from UIViewController to the array (and thus the collection view) on UICollectionViewController.

I am building my app completely programmatically (no storyboard files). Every article/stack overflow post/YouTube tutorial I can find involves using a UIStoryBoardSegue and/or is about pushing data from a CollectionView to another ViewController, but not the other way around. I have not found a way to do this 100% programmatically.

During my hunt I've read is generally bad to create and use segues purely programmatically. Is this true? If so, do I just need to use a delegate? How would I implement that?

Here's some code:

Countdown Class: (has other properties, but for sake of simplicity I'm only including the title property)

class Countdown {

    var title: String!

    init?(title: String!) {

        // Fail initialization if string is blank
        if title.isEmpty {
            return nil
        }

        // Initializing property
        self.title = title

    }

}

CountdownCollectionViewController: (this is the main view of the app)

class CountdownCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {

    var countdowns = [Countdown]()

    override func viewDidLoad() {
        super.viewDidLoad()

        loadSampleCountdowns()

    }

    private func loadSampleCountdowns() {

        // created 3 sample countdowns here

        countdowns += [countdown1, countdown2, countdown3]
    }

UIViewController: (presented modally from CountdownsCollectionViewController)

class ViewController: UIViewController {

    var countdown: Countdown?

    // here I have a textfield property where the user enters a title for a new countdown 

    override func viewDidLoad() {
        super.viewDidLoad()


        self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Add", style: .done, target: self, action: #selector(addTapped))

    }


    @objc func addTapped(_ sender: UIBarButtonItem) {

        let title = titleTextField.text ?? ""

        countdown = Countdown(title: title)

        // here is where I want an action to send the newly created countdown to CountdownCollectionViewController

}

I have tried using this function in CountdownCollectionViewController that uses a segue, but since I do not have storyboards it of course does not work. However, I think I'm headed in the right direction with the code that is inside of it:

func unwindToCountdownList(_ sender: UIStoryboardSegue) {

    if let sourceViewController = sender.source as? ViewController, let countdown = sourceViewController.countdown {

        // Add a new countdown:
        let newIndexPath = IndexPath(row: countdowns.count, section: 0)

        // Append the new countdown from UIViewController to the array:
        countdowns.append(countdown) 

        // Add new item to collectionView:
        collectionView.insertItems(at: [newIndexPath])
}

Upvotes: 0

Views: 139

Answers (1)

Sweeper
Sweeper

Reputation: 273213

I would use a delegate here.

Create a ViewControllerDelegate:

protocol ViewControllerDelegate: class {
    func didCreateCountDown(vc: ViewController, countDown: CountDown)
}

Add a delegate property to ViewController and call it at the appropriate time:

weak var delegate: ViewControllerDelegate?
@objc func addTapped(_ sender: UIBarButtonItem) {

    let title = titleTextField.text ?? ""

    countdown = Countdown(title: title)

    delegate.didCreateCountDown(vc: self, countDown: countDown)

    dismiss(animated: true, completion: nil)

}

Make CountdownCollectionViewController conform to ViewControllerDelegate:

extension CountdownCollectionViewController : ViewControllerDelegate {
    func didCreateCountDown(vc: ViewController, countDown: CountDown) {
        // Add a new countdown:
        let newIndexPath = IndexPath(row: countdowns.count, section: 0)

        // Append the new countdown from UIViewController to the array:
        countdowns.append(countdown) 

        // Add new item to collectionView:
        collectionView.insertItems(at: [newIndexPath])
    }
}

Although you did not show this, there should be a place in CountdownCollectionViewController that you call present to present ViewController:

let vc = ViewController()
...
present(vc, animated: true, completion: nil)

Just before present, you can set self as the delegate:

vc.delegate = self

Upvotes: 1

Related Questions