Swift 4 Unwind segue not working on navigation controller - ios

I am working on an app in Swift 4 and I am trying to unwind a segue. I have a Table View Controller, When the user selects a cell, the segue happens to a navigation controller which has a relationship root view controller with another controller I have UIViewController. In the UIViewController I have a UIButton inside Navigation Items, I have unwind set with the UIButton by selecting the button and dragging to exit and I have the unwind code inside the UITableViewController like so:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segueRSVP" {
}
}
#IBAction override func unwind(for unwindSegue: UIStoryboardSegue, towardsViewController subsequentVC: UIViewController) {
print("Here")
}
But nothing happens, it doesn't even get triggered.
Here are my screenshots:

Add this unwind to your RSVP controller:
#IBAction func unwindToThisViewController(segue: UIStoryboardSegue) {
print("IM BACK")
}
Then control + drag the button in your detail controller up to the exit and select the method: unwindToThisViewController
Just tested this on xcode swift 4 and it should work
I'm not sure why, but when I "overrode" unwind, it didn't work for me either, so I set up my own ibaction

In my case, was tagging the swift unwind func with #objc keyword.
#objc func unwindToThisViewController(segue: UIStoryboardSegue) { ... }
Hope this works.

Related

Having trouble "reaching" my override prepare function to programmatically change views in a tab bar controller

I have a view controller with a container view that has a tab bar controller embedded in it. Im using this to display 3 different view controllers based on what is pressed from a segmented control in the main vc. I have been following this solution: https://stackoverflow.com/a/38283669/11536234
My problem is that when I change the segmented control index (by pressing a different segment) I can't figure out how to "reach" the prepare function.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("prepare reached")
//super.prepare(for: segue, sender: sender)
//switch(segue.identifier ?? "") {
//case "TabBar":
guard let TabController = segue.destination as? UITabBarController else {
fatalError("Unexpected destination: \(segue.destination)")
}
TabController.selectedIndex = toggle.selectedSegmentIndex
//default:
//fatalError("Unexpected Segue Identifier; \(segue.identifier)")
//}
}
#IBAction func toggleAction(_ sender: UISegmentedControl) {
print("toggle is now at index: ", toggle.selectedSegmentIndex)
//performSegue(withIdentifier: "TabBar", sender: sender)
//container.bringSubview(toFront: views[sender.selectedSegmentIndex])
}
So far i have tried placing a performsegue function in an action function linked to the segmented control. This doesn't work, however, because it essentially adds another embedded tab bar programmatically or calls the embed segue again and I receive this error statement: "There are unexpected subviews in the container view. Perhaps the embed segue has already fired once or a subview was added programmatically?"
*The commented lines of code are there to show what I've tried that hasn't worked vs where I'm at.
When you embed a view controller to a container view from another view controller(MainVC), the segue is performed only once when MainVC loads. To pass values to the embedded UIViewController/UITabBarController, you need to get the child view controller and send the data
class MainVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func segmentControlAction(_ sender: UISegmentedControl) {
if let tabBarVC = self.children.first(where: { $0 is UITabBarController }) as? UITabBarController {
tabBarVC.selectedIndex = sender.selectedSegmentIndex
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//called before mainvc viewDidLoad
let destinationVC = segue.destination as? UITabBarController
destinationVC?.selectedIndex = 1
}
}
You can't do what you are trying to do.
With embed segues the segue fires when the host view controller is first loads. That invokes your prepare(for:sender) method, once and only once, before the embedded view controller's views are loaded. It doesn't get called again.
What you need to do is to save a pointer to your child view controller in an instance variable. Define a protocol that the parent uses to talk to the child, and then use that protocol to send a message to the child when the user selects a different segment in your segmented control.
Then the child (presumably the tab bar controller) can switch selected tabs in response to the message you define.

View controller with 2 destinations

I have 2 buttons in a view controller. If button 1 is clicked, then go to WebView controller. If button 2 is clicked then go to ArtView controller.
How to make override func prepare(for segue: UIStoryboardSegue, sender: Any?) have 2 destinations depending on the button clicked?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? WebViewController {
// your code
}
if let destination = segue.destination as? ArtViewController {
// your code
}
}
Hope it will help! :)
Have each button invoke a different segue, either by control-dragging segues directly from the button, or by calling performSegue(withIdentifier:sender:) from your buttons' IBAction code.
Then use code in prepare(for:sender:) to figure out which destination is being invoked.
You can:
Check the identifier on the segue (not recommended - fragile)
Check the class of the destination view controller (better than 1)
Have each view controller implement a protocol and check the protocol of the destination:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.destination {
case let webView as WebViewProtocol:
//Your code for a web view
case let artView as ArtViewProtocol:
//Your code for an Art View
}
}
Option 3 is reliable while providing loose coupling (each case can trigger any view controller that conforms to the desired protocol. All it needs to know is that the destination understands the desired protocol.)

Navigation bar disappears when using custom Segue animation

i'm trying to use custom segue animations in my app, however whenever i perform the segue the navigation bar disappears in the following VC. The animation and segue does work though
//calling prepare function and using .growscale animation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue is CustomSegue {
(segue as! CustomSegue).animationType = .growScale
}
}
//button performs Segue
#IBAction func exerciseLibrary(_ sender: UIButton) {
self.performSegue(withIdentifier: "customSegue", sender: self)
}
I set the segue to custom as well, and i hope this is of any help but the VC that i am segueing to is a Collection View Controller
Update: Storyboard storyboard-main

prepare(for segue: UIStoryboardSegue, sender: Any?) Not firing in Xcode 8.1 swift 3 ios 10

I have a tabbed view controller that is associated with two view controllers.
I need to pass an array from the first View Controller to the Second View controller.
In Order to do this I have the following code in my first VC:
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "AddItem"
{
if let destinationVC = segue.destination as? SecondViewController
{
destinationVC.toDoList = toDoList
}
}
}
However this is not getting fired when I switch two the second VC using the tab button.
Any ideas as to why this is not getting fired?
This is how my main storyboard looks like:
Main Storyboard
You cannot transfer objects between tab views through segue because the VC is not actually making a direct segue connection rather you can do it with a delegate or a notificationCenter post and observe to transfer
You have to put that segue code in Tabbar controller class. You shouldn't put that thing in firstVC. Because there is no segue is going on from first VC to Second VC.

Know recent performed/popped segue?

Am I able to know which segue has just been performed/popped?
For example I have 2 controllers:
View Controller A: Have 1 button that navigates to View B.
View Controller B: Have back button to View A.
I want in View A can detect that the View B just been popped out from stack, so I can do something in ViewWillAppear.
Can I do it?
You can name the segue and then implement prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) method, where you check the identifier if the segue. If it's the segue from ControllerA to ControllerB, just setup some flag that you will check in the viewWillAppear method.
Something like this:
var pushedB = false
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "mySegueName" {
pushedB = true
}
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if pushedB {
pushedB = false
// TODO your action
}
}

Resources