Project Information:
I have three views: 'Main, AddTask, and TaskTable' and three view controllers: 'MainVC, AddTaskVC, TaskTableVC'. My 'Main' view has an embedded 'UITableView' which is 'TaskTable'. My 'Main' view also has a button that presents a modal view which is 'AddTask'. My 'AddTask' view has a button that dismisses itself. My 'TaskTableVC' has an object named 'taskTable' which is the 'UITableView'
Goal:
I have been trying to call reloadData
on taskTable
when the modal view AddTask
is dismissed. However, no matter how I tackle this I cannot call reloadData
on the taskTable
because it always seems to be nil after AddTask
appears for the first time.
Problem Research:
The first thing I did was directly call reloadData()
on my taskTable
object using completion
when the AddTask
was dismissed :
self.dismiss(animated: true, completion: {
TaskTableVC().taskTable.refreshData()
})
but I quickly learned that I cannot call it directly because the TaskTable
view was not loaded and I was creating an instance.
Next, I put print statements on my viewDidLoad
for my three views (see Project Information). On startup, the first thing that loads is my TaskTable
and then my Main
. When I click a button on my Main
my AddTask
loads. So far so good... However, when I dismiss my AddTask
, nothing loads. I thought my TaskTable
and Main
would get loaded but that was not the case. So I tried calling a function refreshData()
that was part of MainVC
on completion
of dismiss
in AddTaskVC
The function would load the TaskTable
view using loadViewIfNeeded
:
func refreshData() {
TaskTableVC().loadViewIfNeeded()
}
Then in my TaskTableVC
under my viewDidLoad
I have:
override func viewDidLoad() {
super.viewDidLoad()
print("table view loaded")
self.taskTable.reloadData()
}
Yet the error I continue to get is: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
I believe this is because I called TaskTableVC.loadViewIfNeeded()
via an instance? Whatever the case may be, I cannot get this to work for the life of me.
TL;DR
I need to call reloadData
on an embedded UITableView
when I call dismiss on a modal view.
//MainVC:
class MainVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
print("main view loaded")
}
func refreshData() {
TaskTableVC().loadViewIfNeeded()
}
}
//AddTaskVC
class AddTaskVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
print("AddTask view loaded")
}
//...
@IBAction func addTask(_ sender: Any) {
//...
self.dismiss(animated: true, completion: {
MainVC().refreshData()
})
}
}
//TaskTableVC
class TaskTableVC : UITableViewController {
@IBOutlet var taskTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
print("table view loaded")
self.taskTable.reloadData() //error here
}
//...
}
There are several options, for example you could use Delegate pattern to communicate between those ViewControllers, there are also some other options like Observer pattern or RxSwift, you could also keep some reference directly to those embeded ViewControllers, but I don't recommend to do that.
Since there are segue relationships between those embeded ViewControllers we can use prepare(for segue
methot to assing those delegates.
class MainVC: UIViewController, TasksUpdateDelegate {
weak var tasksTableDelegate: TaskTableDelegate?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let taskTableVC = segue.destination as? TaskTableVC {
tasksTableDelegate = taskTableVC
} else if let addTaskVC = segue.destination as? AddTaskVC {
addTaskVC.delegate = self
}
}
func tasksChanged() {
tasksTableDelegate?.reloadTasks()
}
}
TasksUpdateDelegate
informs MainVC
about any changes in tasks.
protocol TasksUpdateDelegate: class {
func tasksChanged()
}
class AddTaskVC: UIViewController {
weak var delegate: TasksUpdateDelegate?
@IBAction func addTask(_ sender: Any) {
dismiss(animated: true) { [weak self] in
self?.delegate?.tasksChanged()
}
}
}
TasksTableDelegate
is used in MainVC to inform TaskTableVC
that tasksTable
should be reloaded.
protocol TaskTableDelegate: class {
func reloadTasks()
}
class TaskTableVC: UITableViewController, TaskTableDelegate {
@IBOutlet var taskTable: UITableView!
func reloadTasks() {
taskTable.reloadData()
}
}