Reputation: 179
High level summary of my issue:
I have a UITableViewController (TVC1) that does a performSegue to invoke a static UITableViewController (TVC2) so the user can enter details. After pressing a button called "Add another" on TVC2, I want TVC2 to save its data and to be invoked again to add another record.
Details:
I have a tableViewController (TVC1) which implements this data model:
- Category <=> maps to tableview sections
- Group <=> maps to tableview rows
- Detail <=> maps to tableview rows
A display could look like this:
- Section 0: Category 1
- Row 0: Group A
- Row 1: Detail x1
- Row 2: Detail y2
- Row 3: Detail z3
- Row 4: Group B
- Row 5: Detail x2
- Row 6: Detail y2
- Row 7: Detail y3
When I want to edit a detail by clicking on detail row 5 for example, I do a performSegue
in the didSelectRowAt
.
In the TVC1.prepareForSegue
I provide TVC1 as the delegate for TVC2 where that detail can be added/edited. I also provide the data of the detail to be edited to properties of TVC2.
On TVC2 - after providing the detail - the user can click a save button in the toolbar, which will save the data and take him back to TVC1. Alternatively, at the end of the form where the details can be entered, I have a button:
The idea is that the user is directly taken to a new screen where they can add another detail for the same group. So basically after TVC2 disappears, it should appear again to add new details.
In TVC2, I linked this button to the unwindFrom
method on TVC1. Then in the prepareForSegue
of TVC2, I call a protocol method on the delegate (TVC1) to provide back the edited details and provide a structure called NextAction
to inform the root TVC1 what its next action should be. In this case it should add another detail in the same group.
Then in the unwindFrom
method of TVC1 I look at the nextAction
and initiate the next performSegue
.
I don't know of another place to do this. For me that is the last hook where I can present the next action, which is TVC2 again.
This results in weird behaviour. It appears to work once, but not a second time.
Debugging shows (=====> lines are my own print debug lines):
========> TVC2 prepare start (means the add more details button was pressed)
========> TVC1 newDetailValues begin (TVC2 calling the delegate protocol method to provide back edited data - including NextAction)
========> TVC1 newDetailValues end
========> TVC2 prepare end (I am not sure if TVC2 is now closed/inaccessible)
========> TVC1 unwindFromAddEdit begin (here nextAction is evaluated and a performSegue done)
========> TVC1 prepare begin (initiated by the performSegue)
========> TVC1 prepare end
'Presenting view controllers on detached view controllers is discouraged <Project.TVC1: 0x.........>.'
========> TVC1 unwindFromAddEdit end
Note the error/warning:
'Presenting view controllers on detached view controllers is discouraged <Project.TVC1: 0x.........>.'
But I do see that detail TVC2. But when I click on the button again, it displays the root TVC1 and not TVC2. xcode now says:
'Warning: Attempt to present <UINavigationController: 0x.........> on <Project.TVC1: 0x.........> whose view is not in the window hierarchy!'
I got the feeling this has to to with threading - possibly where the TVC1.unwindFrom
is running in the scope of TVC2.
When I edit TVC1.unwindFrom
and replace:
performSegue(withIdentifier: "AddDetails", sender: self)
... with:
DispatchQueue.main.async {
performSegue(withIdentifier: "AddDetails", sender: self)
}
Everything works just fine.
But:
Who can shine some light into what is happening?
Upvotes: -1
Views: 31
Reputation: 179
Answer was provided by Paulw11 in the comments:
Best way is not to unwind after each invocation of TVC2 when the "add another" button is pressed.
Instead, the "add another" button should be control-dragged to the same navigation controller and do a present modally.
In the prepareForSegue the details that TVC2 needs can be copied from the current ViewController to the new instance of itself.
At some point the user uses either the CANCEL or SAVE button in the toolbar, which unwinds everything to TVC1.
Doing it this way also makes the UI look cleaner, as the unwind to TVC1 is not visible.
Upvotes: 0