I am getting a strange error with iOS13 when performing a Segue and I can't figure out what it means, nor can I find any documentation for this error. The problem is that this seems to cause a lot of lag (a few seconds) until the segue is performed.
2019-09-11 22:45:38.861982+0100 Thrive[2324:414597] [TableView] Warning once only: UITableView was told to layout its visible cells
and other contents without being in the view hierarchy (the table view
or one of its superviews has not been added to a window). This may
cause bugs by forcing views inside the table view to load and perform
layout without accurate information (e.g. table view bounds, trait
collection, layout margins, safe area insets, etc), and will also
cause unnecessary performance overhead due to extra layout passes.
Make a symbolic breakpoint at
UITableViewAlertForLayoutOutsideViewHierarchy to catch this in the
debugger and see what caused this to occur, so you can avoid this
action altogether if possible, or defer it until the table view has
been added to a window. Table view: ; layer = ; contentOffset: {0, 0}; contentSize: {315, 118};
adjustedContentInset: {0, 0, 0, 0}; dataSource: >
I am using Hero but I tried disabling it and using a regular Segue and this hasn't stopped the lag.
The code to initiate the segue is didSelectRowAt
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
selectedCell = realIndexFor(activeGoalAt: indexPath)
performSegue(withIdentifier: "toGoalDetails", sender: nil)
} else if indexPath.section == 1 {
selectedCell = indexPath.row
performSegue(withIdentifier: "toIdeaDetails", sender: nil)
} else {
selectedDecision = indexPath.row
hero(destination: "DecisionDetails", type: .zoom)
}
}
And then none of the code in viewDidLoad or viewWillAppear from the destination VC affects this in any way (I tried commenting it all out with no difference.
Any idea what's causing this? I can share whatever other details are needed.
Thank you.
It happened to me because I registered the device for change orientation notification in the viewWillAppear(:) method.
I moved the registration in the viewDidAppear(:) and Xcode it's not stopping at the breakpoint anymore.
What I can say is that layout changes might be run when the view is already visible...
For people using DiffableDataSource, set animatingDifferences to false and warning will be gone.
dataSource.apply(snapshot, animatingDifferences: false)
Like #joe-h, I was getting this error and was also surprised as the unwind approach he shows is one used by lots of developers + is in some significant Apple iOS sample code.
The triggering line in my code (#joe-h, I'm guessing likely in yours, too) is a tableView.reloadRows at the selectedIndexPath (which is an unwrapped tableView.indexPathForSelectedRow):
tableView.reloadRows(at: [selectedIndexPath], with: .automatic)
Unfortunately commenting out the row isn't an option if you are unwinding after updating the value in an existing tableView row (which is an approach in the Apple FoodTracker tutorial mentioned above, as well as one used in Apple's Everyone Can Code series). If you don't reload the row(s) then your change won't show in the tableView. After commenting out the reload in the unwind, I added a viewDidAppear with the following code and this seems to fix things:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let selectedIndexPath = tableView.indexPathForSelectedRow {
tableView.reloadRows(at: [selectedIndexPath], with: .automatic)
}
}
I'd welcome comments on whether this is a sound approach, but for now, this seems to be working.
I had the same error on my Project; A tableView with a diffable datasource. Been bugging on it for hours. Problem lies in updating the snapshot, more specifically on a background thread (default). Forcing the update of the datasource on the main thread got rid of the problem! Hope this helps someone out there!
func updateData(on annotations: [Annotation]) {
var snapshot = NSDiffableDataSourceSnapshot<AnnotationType, Annotation>()
//Append available sections
AnnotationType.allCases.forEach { snapshot.appendSections([$0]) }
//Append annotations to their corresponding sections
annotations.forEach { (annotation) in
snapshot.appendItems([annotation], toSection: annotation.type as AnnotationType)
}
//Force the update on the main thread to silence a warning about tableview not being in the hierarchy!
DispatchQueue.main.async {
self.dataSource.apply(snapshot, animatingDifferences: true)
}
}
This warning can happen du to updating table view or collection view while it is not visible, for example when it is on the parent view controller. To solve that, first, I created a property in the view controller, containing the table view to check if the view controller is visible or not, as bellow:
var isVisible: Bool = false
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.isVisible = true
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidAppear(animated)
self.isVisible = false
}
Then in the data source delegate, before reacting to changes, first check if the view controller is visible. If it was not, do not do any updates. For example
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
guard isVisible else { return }
tableView.beginUpdates()
}
You should check that visibility before doing any changes in the tableView. For example, in case of NSFetchedResultsController, it must be done in all delegate callbacks which we have implemented.
UPDATE
I recently found that if you update the table view with animation false, even when it is not visible, there won't be any warnings.
I'm new to Xcode/Swift so this may or may not help anyone. I started getting this error after updating to iOS 13 and Xcode 11 within the app when going back to a list from a detail view.
I found that I was doing a tableView.reloadRows and tableView.insertRows in the unwind(as suggested by Apple in one of their tutorials)
#IBAction func unwindToMealList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.source as? MealViewController, let meal = sourceViewController.meal {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update an existing meal.
meals[selectedIndexPath.row] = meal
tableView.reloadRows(at: [selectedIndexPath], with: .none)
}
else {
// Add a new meal.
let newIndexPath = IndexPath(row: meals.count, section: 0)
meals.append(meal)
tableView.insertRows(at: [newIndexPath], with: .automatic)
}
}
}
)
I commented out that section of code and it went away.
Oddly enough leaving the sort and self.tableView.reloadData() didn't give me the error.
In viewDidDisappear method I declare tableView.setContentOffset(CGPoint(x: 0, y: 0), animated: false) function. Some of you says it's not important but it affected tableView delegate methods. For example viewForHeader function is not called when I get this warning.
I found the most robust and safe way is to wait for the didMoveToWindow of the table view / collection view
as even in viewWillAppear the view may NOT be attached to a window and puting your code in viewDidAppear may cause unwanted graphical glitches
class MyTableViewOrCollectionView: UITableView {
var didMoveToWindowCallback: (()->())? = nil
override func didMoveToWindow() {
super.didMoveToWindow()
didMoveToWindowCallback?()
didMoveToWindowCallback = nil
}
}
and than you can
override func viewDidLoad() {
super.viewDidLoad()
tableView.didMoveToWindowCallback = { [weak self] in
self?.setupInitialContent()
}
}
iPadOS 13.2.3 swift 5.2 Xcode 11.2.1
Just ran into this issue only when starting the app while the device was landscape.
I was calling the detail seque in the viewDidLoad func of the master controller to make sure the detail view was setup correctly.
override func viewDidLoad() {
super.viewDidLoad()
...
self.performSegue(withIdentifier: "showDetail", sender: self)
}
When I removed the performSeque the warning not longer appeared, however,
the left bar buttons on the detail controller no longer worked properly, again only when starting the app while the device was landscape. The left most button would activate the next button to the right instead of what the first button was suppose to do.
The fix for the bar buttons was to add to the viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
...
self.splitViewController?.preferredDisplayMode = UISplitViewController.DisplayMode.allVisible
}
Then execute
override func viewWillAppear(_ animated: Bool) {
self.splitViewController?.preferredDisplayMode = UISplitViewController.DisplayMode.automatic
super.viewWillAppear(animated)
}
I have no explanation why this worked!
This app had worked flawlessly until iPados 13 was loaded.
I am getting similar breakpoint with SwiftUI, without even dealing with viewDidLoad or viewDidappear
//
// ContentView.swift
// DD
//
// Created by Roman Emperor on 3/29/20.
// Copyright © 2020 Emperors. All rights reserved.
//
import Combine
import SwiftUI
// Defining a class Booking of type Bindable Object [changed to ObservableObject]
class Booking: ObservableObject {
var didChange = PassthroughSubject<Void, Never>()
// Array of types to work with
static let types = ["Consultation", "Tooth Pain", "Cleaning", "Brases", "Dental Implant" ]
// Setting instance varibale type
var type = 0 { didSet { update() } }
func update () {
didChange.send(())
}
}
struct ContentView: View {
#ObservedObject var booking = Booking() //bindableObject in old swift version
var body: some View {
NavigationView {
Form {
Section {
Picker(selection: $booking.type, label: Text("Select a Booking Type")) {
ForEach(0 ..< Booking.types.count){
Text(Booking.types[$0]).tag($0)
}
}
}
}
.navigationBarTitle(Text("Darpan Dental Home"))
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
The Complete output Log is here:
*> 2020-03-29 09:22:09.626082+0545 DD[1840:76404] [TableView] Warning
once only: UITableView was told to layout its visible cells and other
contents without being in the view hierarchy (the table view or one of
its superviews has not been added to a window). This may cause bugs by
forcing views inside the table view to load and perform layout without
accurate information (e.g. table view bounds, trait collection, layout
margins, safe area insets, etc), and will also cause unnecessary
performance overhead due to extra layout passes. Make a symbolic
breakpoint at UITableViewAlertForLayoutOutsideViewHierarchy to catch
this in the debugger and see what caused this to occur, so you can
avoid this action altogether if possible, or defer it until the table
view has been added to a window.*
**where is this UITableViewAlertForLayoutOutsideViewHierarchy in SwiftUI ? **
extension UIView {
func rootView() -> UIView {
var view = self
while view.superview.isNotNil {
view = view.superview!
}
return view
}
var isOnWindow: Bool {
return self.rootView() is UIWindow
}
}
then you just need to check if your tableView isOnWindow like...
if self.tableView.isOnWindow {
/// do stuff
}
Disclaimer: as the documentation explains, you may need to defer the call which means that there is no warranty your method will be called again so it's your responsibility to perform your update when isOnWindow is true.
Had the same issue, removing tableView.reloadSections fixed it. This was the line of code causing the warning:
iOS 13:
tableView.reloadSections(IndexSet(integer: 0), with: .automatic)
in iOS 14, removing tableView.reloadSections did not fix the warning.
Or maybe your code (like mine) has nothing wrong with it and this message just randomly starts popping up. In that case, do a clean on your project, restart Xcode and watch the message magically go away!
Please check following function
override func viewWillLayoutSubviews()
For anyone that has this issue with a UISplitViewController and a UITableView inside the detail view controller, you can try subclassing and override layoutSubviews like this (From this thread):
class CustomTableView: UITableView {
override func layoutSubviews() {
if (self.window == nil) {
return
}
super.layoutSubviews()
}
}
Instead of reloading the rows inside viewDidAppear, this is what it worked for me:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
self.tableView.reloadRows(at: [indexPath], with: .none)
}
Also if you are using DiffableDataSource and you are selecting an indexPath manually for example, you need to do it on the completion block of the apply snapshot method:
dataSource.apply(snapshot, to: section, animatingDifferences: false, completion: {
// select the indexPath programmatically or do UITableView UI stuff here.
})
... the table view or one of its superviews has not been added to a window ...
To resolve the issue we need to check tableView.window property:
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
updateTableViewRows()
}
func dataChanged() {
updateTableViewRows()
}
func updateTableViewRows() {
if tableView.window == nil {
// TODO: just update data source
} else {
tableView.performBatchUpdates {
// TODO: update data source
// TODO: update table view cells
}
}
}
}
The idea is to not call performBatchUpdates and related functions while tableView.window is nil.
I have a UITabBarController (as initial view controller) which checks connectivity status of the device. Everytime the connectivity status changes, a checkmark in a child UITableViewController cell (.accessoryType) should be set (.checkmark) or removed (.none)
Code in Tab Bar Controller:
let monitor = NWPathMonitor()
monitor.pathUpdateHandler = { path in
let tvc = InfoTableViewController()
if path.status == .satisfied {
// set .checkmark in UITableViewController
let cell = tvc.statusOnlineCell
print("cell :", cell)
} else {
// set .none in UITableViewController
}
}
let queue = DispatchQueue.global(qos: .background)
monitor.start(queue: queue)
Outlet in UITableViewController:
#IBOutlet weak var statusOnlineCell: UITableViewCell!
I can change the accessory type from inside the UITableViewController class using .checkmark and .none.
statusOnlineCell.accessoryType = .checkmark
statusOnlineCell.accessoryType = .none
All fine so far!
However as soon as I try to access the UITableView.statusOnlineCell from UITabBar, I get nil. Hence, I cannot change its property outside the UITableViewController.
I saw 3 possible approaches:
A global variable, which reflects the online status. I could use the UITableView.viewDidAppear() method to change the statusOnlineCell accessory type. This works, but only if UITableView is not shown (only if another than UITableView is shown). If the UITableView is shown and I change the connectivity status, the view is not reloaded and I didn't find any way to achieve this. Is this possible?
Find a possibility to change the accessory type of UITableView.statusOnlineCell from the UITabBarController. Accessing the outlook returned in nil. Why is that? On top, after the accessory type would have changed, I would need to reload the view (for the case that the UITableView was active while changing connectivity status).
Is there any kind of (unknown to me) method which fires, when an object's property changed (à la needReload())? This would be too good to be true I believe.
To summarize - I need code to change the accessory type of a tableview cell, depending on the connectivity status, even whith this tableview being visible.
I read some tutorials and stackexchange articles, Google, ... but none did the job.
This was my top candidate, but I didn't manager to apply these examples to my situation.
https://learnappmaking.com/pass-data-between-view-controllers-swift-how-to/#back-delegation
I didn't want to use notifications since not really appropriate.
Any hint would be sufficient. Thanks in advance.
----- EDIT (14:52 UTC) ----- (requested by #vadian)
I added the (testing) code in the UITabBarController. The result of cell shows nil. So I cannot directly address the cell in UITableView from UITabBarController.
The UITableView doesn't have any related code yet since I directly address the property statusOnlineCell from UITabBarController in order to change its accessory type.
This approach is option 2. (of my 3 possibilities mentioned above).
I didn't find a solution, but a workaround.
Why no solution : the cell object was always nil if called from another class. Hence no chance to change it's property to .checkmark or .none. This was basically my key problem and unknown to me.
The workaround : a delegate! I declared the class, holding the cell, as delegate for the connectivity status change. Like that, the checkmark gets set or is removed from the cell, independantly whether the page is in view or not.
This runs when the app starts (UITabBarController):
protocol StatusOnlineChangedDelegate {
func updateStatusOnline(_ online: Bool)
}
class TabBarController: UITabBarController {
var socDelegate: StatusOnlineChangedDelegate?
override func viewDidLoad() {
super.viewDidLoad()
let monitor = NWPathMonitor()
monitor.pathUpdateHandler = { path in
if path.status == .satisfied {
self.socDelegate?.updateStatusOnline(true)
} else {
self.socDelegate?.updateStatusOnline(false)
}
}
let queue = DispatchQueue.global(qos: .background)
monitor.start(queue: queue)
}
}
The UITableViewController holding the cell looks like this:
class InfoTableViewController: UITableViewController, StatusOnlineChangedDelegate {
#IBOutlet weak var statusOnlineCell: UITableViewCell!
override func viewDidLoad() {
super.viewDidLoad()
TabBarController().socDelegate = self
}
func updateStatusOnline(_ online: Bool) {
DispatchQueue.main.async {
self.statusOnlineCell.accessoryType = (online ? .checkmark : .none)
}
}
}
Runs perfectly as desired.
I have been debugging for a day and decided I have no idea what is causing the error in my app. It would be awesome if anyone could help me out figure it out.
So I created a custom UIView from a Nib File with class name ManualScreen. xibsetup() basically is in UIView extension which just loads from the Nib file. I want to send the button tap from my view to ViewController. I directly did not add this view to the ViewController because I need to remove this ManualScreen view and add another view in its place when Segment Control is moved to another option.
class ManualScreen: UIView {
var mManualViewListener:ManualViewListener!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: CGRect.zero)
xibSetup()
}
#IBOutlet weak var counterLabel: UILabel!{
didSet {
print("labelView: \(String(describing: counterLabel))")
}
}
#IBAction func addButton(_ sender: UIButton) {
if(mManualViewListener != nil){ ->>>this is always nil for some reason
print("insdie the listener counting")
mManualViewListener.addCount()
}else{
print("listener is nil")
}
}
func addListener(manualViewListener:ManualViewListener){
print("adding listener")
mManualViewListener = manualViewListener
}
}
This UIView is initilized in the Viewcontroller and this Viewcontroller also implements my delegate protocol. When I initalized my customView in the Viewcontroller, I add this Viewcontroller as the delegate by doing
var manualScreen = ManualScreen()
manualScreen.addListener(manualViewListener: self)
My delegate protocol is
protocol ManualViewListener {
func addCount()
}
Once listener is set, I should be able to send some event (here button touch) from my view to the ViewController using manualViewListener.addcount(). But it says my manualViewListener is nil always.
I have just written a small portion of my code here as writing everything will be not feasible. If anyone wants to see the whole app, here is the GitHub link to the thing I am working. https://github.com/Rikenm/Auto-Counter-iOS
It doesn't look pretty for now. I am just working on the functionality right now.
And finally thank you for the help.
Your problem is here
override init(frame: CGRect) {
super.init(frame: CGRect.zero)
xibSetup() // this is the problem maker
}
you add a new view of the same class above it and for sure it's listener object is nil with the the screen view that you instantiate here
mManualScreen = ManualScreen()
mManualScreen.addListener(manualViewListener: self)
//
extension UIView{
func xibSetup() {
let view = loadFromNib()
addSubview(view)
stretch(view: view)
}
// 2. Loads the view from the nib in the bundle
/// Method to init the view from a Nib.
///
/// - Returns: Optional UIView initialized from the Nib of the same class name.
func loadFromNib<T: UIView>() -> T {
let selfType = type(of: self)
let bundle = Bundle(for: selfType)
let nibName = String(describing: selfType)
let nib = UINib(nibName: nibName, bundle: bundle)
guard let view = nib.instantiate(withOwner: self, options: nil).first as? T else {
fatalError("Error loading nib with name \(nibName) ")
}
return view
}
}
Instead you need
var mManualViewListener:ManualViewListener!
static func loadFromNib() -> ManualScreen {
let view = Bundle.main.loadNibNamed("ManualScreen", owner: self, options: nil)?.first as! ManualScreen
return view
}
with
mManualScreen = ManualScreen.loadFromNib()
mManualScreen.addListener(manualViewListener: self)
The problem is that you're creating 2 separate ManualScreen instances. Your method xibSetup creates and returns another ManualScreen instance and adds it as a subview of your first ManualScreen, which is attached to your detail view controller. If you set a breakpoint within addManualScreen() in your DetailViewController and inspect mManualScreen's subviews, you'll see another one.
Hence, you're setting the mManualViewListener delegate property to a ManualScreen, but the extra ManualScreen (which you shouldn't be creating) added as a subview from xibSetup() is intercepting the action, and that view doesn't have an mManualViewListener attached to it.
You should fix your view instantiation to only create one instance of ManualScreen and you will fix the problem.
I tried adding a couple of breakpoints to your code. It seems the way you're adding the view is a little (a lot?) off.
Settings Breakpoints
First off, I added a breakpoint to your addManualScreen method in line 89:
containerView.addSubview(mManualScreen)
And another breakpoint in your ManualScreen itself, the function addButton, line 51:
if(mManualViewListener != nil){
First Breakpoint Hit
OK, breakpoint one hit. What is mManualScreen at this point?
po mManualScreen
gives us amongst other things the object ID Auto_Counter.ManualScreen: 0x7fcfebe018d0
is the delegate set?
po mManualScreen.mManualViewListener
indeed it is: some : <Auto_Counter.DetailViewController: 0x7fcfeb837fb0>
Second Breakpoint Hit
OK, second breakpoint hit when I tap the + button. Is the mManualListener still set?
po mManualViewListener
Nope, we get nil
Lets take a look at the object itself then
po self
gives us
Auto_Counter.ManualScreen: 0x7fcfe8d4b300
Hang on, that's not the same object ID!
The Problem
Now take a look at xibSetup
func xibSetup() {
let view = loadFromNib()
addSubview(view)
stretch(view: view)
}
Here is where your second/inner view is created! And this is the view that reacts to your #IBAction.
Solution
You should rethink how you create your manual view, I can't really come up with the correct solution as it seems a bit convoluted at the moment, but you need to use either the nib creation method...or create it manually.
Update It seems others has found the correct solution. I hope my answer helps you in how to diagnose these kinds of problems another time at least then so you can reduce the frustration period from a day to maybe just half a day :)
Hope that helps.
I'm trying to wrap my head around what could be causing an error I'm seeing. Here's how the general flow looks: when we first hit this feature, we set our tableHeaderView to a custom segmentedcontrol view, then make a service call to fetch some data, when that is complete, we reload the table. This data is all cached, so when the user leaves and comes back, no service call is made, is simply returns back the cached data. However, when coming back, it doesn't seem to recognize that a header view is on the table, and instead places the tableView at the same origin as its header view, so the first cell is covered by the headerView. Here's some relevant methods:
override func viewDidLoad() {
super.viewDidLoad()
self.useAdaptableFeedbackFooter = true
self.tableView.registerReusableCell(DCManagePaymentsTransactionCell.self)
self.tableView.tableHeaderView = self.segmentedControlView
self.segmentedControlView.selectedSegmentIndex = self.selectedSegment.rawValue
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
// TODO: Remove once we have other segments implemented
makeServiceCall()
}
private lazy var segmentedControlView: DMSegmentedControlTableHeaderView = {
let view = DMSegmentedControlTableHeaderView.loadableViewFromNib()
let segmentTitles = [DCManagePaymentsString.Summary.localized, DCManagePaymentsString.History.localized, DCManagePaymentsString.Pending.localized]
view.segmentTitles = segmentTitles
view.delegate = self
return view
}()
private func makeServiceCall() {
self.businessService.getData {
(result: DMServiceResult<DCPendingPayments>) -> Void in
switch result {
case .Success (let data):
if data.isHAMode {
self.data.payment = data.pendingPayments.first
self.tableView.dataSource = self.HADataSource
self.tableView.delegate = self.HADataSource
} else {
self.data.pendingPayments = data.pendingPayments
self.tableView.dataSource = self.dataSource
self.tableView.delegate = self.dataSource
}
self.tableView.reloadData()
case .Failure(let errorInfo):
self.navigationController?.handleError(errorInfo, withCompletion: nil)
}
}
}
The only difference I can see is how quickly we eventually call reloadData(). When I set an explicit reloadData() call in viewDidAppear it sets the tableView up correctly, but there's a jerky animation to that.
It seems that I may be setting my table up too fast for it to recognize that there's a header view? Although it doesn't make sense that that's what's causing this. Any ideas or suggestions? I can post more code if necessary.
Edit: I've added two screenshots, first is what the header looks like on first load, second is after you return and it just gets cached data (the Pending Payments is my first cell, and it gets completely covered by the header segmented control in the cached data load):
I have been pulling my hair out trying to get this 'Delegate' thing to work in Swift for an App I am working on.
I have two files: CreateEvent.swift and ContactSelection.swift, where the former calls the latter.
CreateEvent's contents are:
class CreateEventViewController: UIViewController, ContactSelectionDelegate {
/...
var contactSelection: ContactSelectionViewController = ContactSelectionViewController()
override func viewDidLoad() {
super.viewDidLoad()
/...
contactSelection.delegate = self
}
func updateInvitedUsers() {
println("this finally worked")
}
func inviteButton(sender: AnyObject){
invitedLabel.text = "Invite"
invitedLabel.hidden = false
toContactSelection()
}
/...
func toContactSelection() {
let contactSelection = self.storyboard?.instantiateViewControllerWithIdentifier("ContactSelectionViewController") as ContactSelectionViewController
contactSelection.delegate = self
self.navigationController?.pushViewController(contactSelection, animated: true)
}
ContactSelection's contents are:
protocol ContactSelectionDelegate {
func updateInvitedUsers()
}
class ContactSelectionViewController: UITableViewController {
var delegate: ContactSelectionDelegate?
override func viewDidLoad() {
super.viewDidLoad()
self.delegate?.updateInvitedUsers()
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// Stuff
self.delegate?.updateInvitedUsers()
}
}
What am I doing wrong? I am still new and don't fully understand this subject but after scouring the Internet I can't seem to find an answer. I use the Back button available in the Navigation Bar to return to my CreateEvent view.
var contactSelection: ContactSelectionViewController = ContactSelectionViewController()
This is instantiating a view controller directly, and the value never gets used. Since it looks like you're using storyboards, this isn't a good idea since none of the outlets will be connected and you'll get optional unwrapping crashes. You set the delegate of this view controller but that's irrelevant as it doesn't get used.
It also isn't a good idea because if you do multiple pushes you'll be reusing the same view controller and this will eventually lead to bugs as you'll have leftover state from previous uses which might give you unexpected outcomes. It's better to create a new view controller to push each time.
In your code you're making a brand new contactSelection from the storyboard and pushing it without setting the delegate.
You need to set the delegate on the instance that you're pushing onto the navigation stack.
It's also helpful to pass back a reference in the delegate method which can be used to extract values, rather than relying on a separate reference in the var like you're doing.
So, I'd do the following:
Remove the var contactSelection
Add the delegate before pushing the new contactSelection object
Change the delegate method signature to this:
protocol ContactSelectionDelegate {
func updateInvitedUsers(contactSelection:ContactSelectionViewController)
}
Change your delegate calls to this:
self.delegate?.updateInvitedUsers(self)