Reputation: 263
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
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