Reputation: 2513
I am working on a storyboard whereupon I need to present a data entry view controller. The data entry vc needs a save button and a cancel button. I need to present the data entry vc from each of two other view controllers.
I would like to configure it all via the storyboard so that my storyboard indeed "tells the story". If I do a show segue to the data entry VC then the back button will get me back to the correct source controller (VC1 or VC2). I suppose that I could use the back button to trigger the cancel operation (hmmm ...), but then what about the save operation? If I present the data entry VC modally, via an intermediate navigation controller, then I can add save and cancel bar buttons items and attach unwind segues to them, but there does not seem to be a way to conditionally unwind back to the correct source (VC1 or VC2).
What I have settled on is:
See the code and the storyboard below. (Note: These are not from my actual application. They are from a project that I am using to experiment with).
So, what I have works. However, I cannot help but feel that there is a more elegant technique. What I am really after is to reuse the data entry view and its controller. Would loading the view from a nib whose owner is the data entry vc work? Maybe a UIContainerView? Would someone with more experience please advise me (if even to confirm that what I have is about as good as it gets)? Thanks!
The relevant code in VC1 and VC2:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
func setupButtons(_ view: UIView) {
view.subviews.forEach() { view in
if let button = view as? UIButton, let title = button.title(for: .normal) {
switch title {
case "Save":
button.addTarget(self, action: #selector(save(sender:)), for: .touchUpInside)
case "Cancel":
button.addTarget(self, action: #selector(cancel(sender:)), for: .touchUpInside)
default:
break
}
}
else {
setupButtons(view)
}
}
}
if let vc = segue.destination as? DataEntryViewController {
setupButtons(vc.view)
}
}
func cancel(sender: UIButton) {
process(button: sender, save: false)
}
func save(sender: UIButton) {
process(button: sender, save: true)
}
private func process(button: UIButton, save: Bool) {
let title = button.title(for: .normal) ?? "<unknown>"
print("The \(title) button was pressed")
if save {
if let vc = button.viewController as? DataEntryViewController {
print("The data is \(vc.data)")
}
else {
print("Cannot get the \(title) button's VC")
}
}
button.removeTarget(self, action: nil, for: .touchUpInside)
}
The relevant code in the data entry controller:
@IBAction func cancel(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
@IBAction func save(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
Upvotes: 1
Views: 61
Reputation: 1227
From what I know about unwind segues, this should just work. See Technical Note TN2298 section "How an Unwind Segue Determines its Destination View Controller". Have you really tried implementing the same unwind action in both VC1 and VC2 and wiring up the buttons to the corresponding unwind actions in Interface Builder?
As a side note you should probably use tags when looking up views instead of comparing button titles to fixed strings (internationalization and all).
Edit:
I have just created a test project with exactly what I described. The app is a tab bar controller with two view controllers (FirstViewController
and SecondViewController
) that both present a data entry view controller with a Cancel button and a Save button. These are wired to unwind actions unwindToCancel:
and unwindToSave:
, respectively.
class FirstViewController: UIViewController
{
@IBAction func unwindToSave(_ segue: UIStoryboardSegue)
{
print("FirstViewController unwindToSave")
}
@IBAction func unwindToCancel(_ segue: UIStoryboardSegue)
{
print("FirstViewController unwindToCancel")
}
}
class SecondViewController: UIViewController
{
@IBAction func unwindToSave(_ segue: UIStoryboardSegue)
{
print("SecondViewController unwindToSave")
}
@IBAction func unwindToCancel(_ segue: UIStoryboardSegue)
{
print("SecondViewController unwindToCancel")
}
}
Depending on which view controller I am coming from, the correct message is printed when I tap Cancel or Save.
Upvotes: 1