When trying to connect a Navigation Bar Button to the Exit item of a ViewController in Xcode 6 (not really sure if it's an Xcode 6 problem but worth mentioning as it is in beta) it does not find the Swift function in the custom class.
The function it should be finding:
#IBAction func unwindToList(segue: UIStoryboardSegue) {
}
I made another button on the view just to make sure I could get an IBAction working with Swift and that I was writing it correctly. This works fine:
#IBAction func test(sender: AnyObject) {
NSLog("Test")
}
I have seen this question that seems like the same issue but according to the answers there this should be working.
Xcode 6 is in beta and, of course, Swift is very new, but wanted to see if anyone has come across this before considering it a potential bug.
This is a known issue with Xcode 6:
Unwind segue actions declared in Swift classes are not recognized by Interface Builder
In order to get around it you need to:
Change class MyViewController to #objc(MyViewController) class MyViewController
Create an Objective-C header file with a category for MyViewController that redeclares the segue action.
#interface MyViewController (Workaround)
- (IBAction)unwindToMyViewController: (UIStoryboardSegue *)segue;
#end
In the storyboard, select the instance of MyViewController, clear its custom class, then
set it back to MyViewController.
After these steps you are able to connect buttons to the exit item again.
Xcode 6 Release Notes PDF, Page 10
Instead of using the Objective-C workaround, Xcode 6 Beta 4, which can now be installed, supports the connection of unwind segues in the Interface Builder. You can update now from the iOS Dev center. Control-click and drag from the UI item you want to trigger the segue to the exit icon, and select the function unwindToSegue after having put the following code in the destination view controller.
#IBAction func unwindToSegue (segue : UIStoryboardSegue) {}
I was able to finally get it to work; the xcode6 IB is really fragile right now (crashes a lot too). I had to restart the IDE before I could connect the nav bar button item to the exit item. I ended up re-creating my test project and following the above suggestion (Xcode 6 Release Notes PDF, Page 10) to get it to work. In addition, when adding the .h file, I made sure to select my project target, which was unchecked by default. I also created my controller swift stub via the Cocoa Touch Class template (vs empty swift file). I used a modal segue in my nav controller.
ListTableViewController.h
#import <UIKit/UIKit.h>
#interface ListTableViewController
- (IBAction)unwindToList: (UIStoryboardSegue *)segue;
#end
ListTableViewController.swift
import UIKit
#objc(ListTableViewController) class ListTableViewController: UITableViewController {
#IBAction func unwindToList(s:UIStoryboardSegue) {
println("hello world");
}
}
hope that helps
In Xcode 6 Beta 4 which is available for download, unwind segues and interface builder is supported. I have tested it by myself in a little project.
In Swift 2.3 I found the external name of the parameter must be "withUnwindSegue":
#IBAction func unwindToThisView(withUnwindSegue unwindSegue: UIStoryboardSegue) {
...
}
It appears that Xcode 6.1 has fixed this issue. You can now set up unwind segues in Swift with the following code:
#IBAction func unwindToList(segue: UIStoryboardSegue) {
// Nothing needed here, maybe a log statement
// print("\(segue)")
}
This method - which can remain empty - needs to have a method signature with the UIStoryboardSegue type and not AnyObject or Interface Builder will not see it.
For more detail check the TechNote 2298
I had the same problem, also with Xcode Beta 4 at the beginning.. till I found out, that I simply forgot to add the #IBOutlet for the Cancel and Save Buttons in the respective controller. After this, I could connect the buttons with the Exit-Icon :))
If it's always the same presenting view controller that you'd like to unwind to, you can always just do:
self.navigationController?.popViewControllerAnimated(true)
You may want to verify that the original controller destination that you're trying to unwind to is not embedded inside a Container object. Xcode 6 ain't having that.
The answers above rely on ObjC to fix the issue, I have found a pure Swift solution. While adding the segue handler in Swift allowed me to create the unwind segue in Interface Builder (Xcode 6.3), the handler was not being called.
#IBAction func unwindToParent(sender: UIStoryboardSegue) {
dismissViewControllerAnimated(true, completion: nil)
}
So after digging in, the canPerformUnwindSegueAction:fromViewController:withSender from the super class returns false. So I've overridden the implementation, and it works:
override func canPerformUnwindSegueAction(action: Selector, fromViewController: UIViewController, withSender sender: AnyObject) -> Bool {
return action == Selector("unwindToParent:")
}
Update
The code above is incorrect, as I resolved the issue without overriding canPerformUnwindSegueAction:fromViewController:withSender. The fundamental error was to make the distinction between the presenting viewcontroller and the presented viewcontroller.
When an unwind segue is initiated, it must first locate the nearest view controller in the navigation hierarchy which implements the unwind action specified when the unwind segue was created. This view controller becomes the destination of the unwind segue. If no suitable view controller is found, the unwind segue is aborted.
source: Technical Note TN2298
So, define the #IBAction on the presenting viewcontroller, not on the presented view controller. That way the segue will have meaningful values for the properties destinationViewController and sourceViewController as well, being respectively the presenting and presented viewcontroller.
Xcode --version 6.4
Swift 1.2
#IBAction func backButton(sender: AnyObject) {
dismissViewControllerAnimated(true, completion: nil)
}
Related
I am use to performing unwinds to the easy what that is like this:
First set this on the destination view controller:
#IBAction func unwindToThisViewController(segue: UIStoryboardSegue) {}
Finally drag and drop and then pick the wanted unwind:
But till Xcode updates to 8.2.1 if you have the destination view controller with some extensions it doesn't appear on the list (Manual Segue). I didn't update to 8.3 because I'm still using Swift 2.3 so I don't know in further versions this bug is solved.
Instead of having to move all the extensions in the same file, which it will be a chaos find anything there. I am wondering if there is any other way to perform this action without using the storyboard step.
I am currently using navigation controller, maybe popToViewController will do it? I don't know honestly.
Thank you very much for the help.
In the storyboard, give your unwind segue an identifier.
Then, in your code do:
performSegue(withIdentifier: "SegueID", sender: nil)
Source: https://spin.atomicobject.com/2014/12/01/program-ios-unwind-segue/
My app's navigation flow looks a bit like this:
UINavigationController - MasterViewController > DetailViewController > InfoViewController
MasterViewController contains the following method:
#IBAction func unwindToMaster(with segue: UIStoryboardSegue) {}
In DetailViewController, there is a similar method:
#IBAction func unwindToDetail(with segue: UIStoryboardSegue) {}
I use these methods, along with UIButtons, to allow the user to advance forward and back through the navigation hierarchy. In interface builder, I see the method under DetailViewController when I right click on "Exit" in InfoViewController, but when I right click on "Exit" in DetailViewController, no other unwind segues are listed.
I have consulted multiple online sources (Ray Wenderlich, relevant StackOverflow questions) instructing the correct way to produce unwind segues in interface builder, but none of these have helped solve the issue. Right now, I need help figuring out what the problem is in the first place. As development usually goes, it's probably something staring me square in the face.
I am running Xcode 8.1 using Swift 3. Thank you.
To re-iterate: not only an Action for a Storyboard Unwind Segue has to be placed in the same source file as class definition for an unwind-to (destination) View Controller (e.g., #IBAction func prepareForUnwind(segue: UIStoryboardSegue), detected as prepareForUnwind[With]Segue in a "Presenting Segues" list), but also that View Controller cannot have ANY extensions in ANY secondary source files. You have to merge class definition and all extensions into a single source file.
(As of Xcode 8.2.1.)
In my case, I had a complicated inheritance in my view controller and it was a reason why Interface Builder did not see my unwind. So this workaround works for me well:
Go to YouCollViewController.swift file and delete all classes, protocols, generics, etc. your view controller implements in view controller's declaration
Inherit your view controller from UIViewController
Go to the storyboard and connect your action with the unwind, it should appear in unwinds list
Go back to YouCollViewController.swift file and discard all the changes
I consulted Apple's Developer Library (specifically the page "Using Unwind Segues"). There, the example definition of an unwind action is:
#IBAction func unwindToMainMenu(sender: UIStoryboardSegue) {
let sourceViewController = sender.sourceViewController
// Pull any data from the view controller which initiated the unwind segue.
}
Applying this example in my code, I changed my unwind action declarations to:
#IBAction func unwindToMaster(sender: UIStoryboardSegue) {
print("Unwinded to master.")
}
and
#IBAction func unwindToDetail(sender: UIStoryboardSegue) {
print("Unwinded to detail.")
}
I also made sure that each method was contained within the same file as MasterViewController's class declaration. After further testing, I found that all extensions of MasterViewController had to exist in the same file for interface builder to find and recognize the unwind segue.
Now, in storyboard, the exit menu shows both unwind segues. My conclusion is that by fiddling around with where the methods are placed and how they are declared, a configuration that interface builder can recognize will be found. I hope it will be less touchy in the future, as my current method organization is very long and difficult to navigate.
Because none of these answers helped me and I didn't want to mess around with my code that much. Here another solution you can give a try.
Starting from the point where you have already added the unwind function to your MasterViewController.
First I have gone ahead and added a new ViewController to my storyboard as well as a new Cocoa Touch Class File of type UIViewController and connected it with my Storyboard one (I called it the HelperViewController).
Then you can add the same unwind function, you already have inside your MasterVC to your newly created HelperVC.
#IBAction func unwindToMaster(with segue: UIStoryboardSegue) {}
Now connect the Helper to your DetailVC. Your Storyboard should look somewhat like mine. You should get the same option as me if you ctrl + drag and drop to the exit of your DetailVC. When the connection has been established and you hover over the newly created segue both the Master and the HelperVC should be highlighted. Now you can delete the HelperVC again and everything should work as expected.
I hope that helped!
With swift 4 XCode 9.2 I found that you need to drag the reference from the yellow icon at the top of the viewController to the "Exit" reference of the target viewController in the left details panel. Hope this helps!
Sometimes XCode restart makes the actions appear.
When trying to connect a Navigation Bar Button to the Exit item of a ViewController in Xcode 6 (not really sure if it's an Xcode 6 problem but worth mentioning as it is in beta) it does not find the Swift function in the custom class.
The function it should be finding:
#IBAction func unwindToList(segue: UIStoryboardSegue) {
}
I made another button on the view just to make sure I could get an IBAction working with Swift and that I was writing it correctly. This works fine:
#IBAction func test(sender: AnyObject) {
NSLog("Test")
}
I have seen this question that seems like the same issue but according to the answers there this should be working.
Xcode 6 is in beta and, of course, Swift is very new, but wanted to see if anyone has come across this before considering it a potential bug.
This is a known issue with Xcode 6:
Unwind segue actions declared in Swift classes are not recognized by Interface Builder
In order to get around it you need to:
Change class MyViewController to #objc(MyViewController) class MyViewController
Create an Objective-C header file with a category for MyViewController that redeclares the segue action.
#interface MyViewController (Workaround)
- (IBAction)unwindToMyViewController: (UIStoryboardSegue *)segue;
#end
In the storyboard, select the instance of MyViewController, clear its custom class, then
set it back to MyViewController.
After these steps you are able to connect buttons to the exit item again.
Xcode 6 Release Notes PDF, Page 10
Instead of using the Objective-C workaround, Xcode 6 Beta 4, which can now be installed, supports the connection of unwind segues in the Interface Builder. You can update now from the iOS Dev center. Control-click and drag from the UI item you want to trigger the segue to the exit icon, and select the function unwindToSegue after having put the following code in the destination view controller.
#IBAction func unwindToSegue (segue : UIStoryboardSegue) {}
I was able to finally get it to work; the xcode6 IB is really fragile right now (crashes a lot too). I had to restart the IDE before I could connect the nav bar button item to the exit item. I ended up re-creating my test project and following the above suggestion (Xcode 6 Release Notes PDF, Page 10) to get it to work. In addition, when adding the .h file, I made sure to select my project target, which was unchecked by default. I also created my controller swift stub via the Cocoa Touch Class template (vs empty swift file). I used a modal segue in my nav controller.
ListTableViewController.h
#import <UIKit/UIKit.h>
#interface ListTableViewController
- (IBAction)unwindToList: (UIStoryboardSegue *)segue;
#end
ListTableViewController.swift
import UIKit
#objc(ListTableViewController) class ListTableViewController: UITableViewController {
#IBAction func unwindToList(s:UIStoryboardSegue) {
println("hello world");
}
}
hope that helps
In Xcode 6 Beta 4 which is available for download, unwind segues and interface builder is supported. I have tested it by myself in a little project.
In Swift 2.3 I found the external name of the parameter must be "withUnwindSegue":
#IBAction func unwindToThisView(withUnwindSegue unwindSegue: UIStoryboardSegue) {
...
}
It appears that Xcode 6.1 has fixed this issue. You can now set up unwind segues in Swift with the following code:
#IBAction func unwindToList(segue: UIStoryboardSegue) {
// Nothing needed here, maybe a log statement
// print("\(segue)")
}
This method - which can remain empty - needs to have a method signature with the UIStoryboardSegue type and not AnyObject or Interface Builder will not see it.
For more detail check the TechNote 2298
I had the same problem, also with Xcode Beta 4 at the beginning.. till I found out, that I simply forgot to add the #IBOutlet for the Cancel and Save Buttons in the respective controller. After this, I could connect the buttons with the Exit-Icon :))
If it's always the same presenting view controller that you'd like to unwind to, you can always just do:
self.navigationController?.popViewControllerAnimated(true)
You may want to verify that the original controller destination that you're trying to unwind to is not embedded inside a Container object. Xcode 6 ain't having that.
The answers above rely on ObjC to fix the issue, I have found a pure Swift solution. While adding the segue handler in Swift allowed me to create the unwind segue in Interface Builder (Xcode 6.3), the handler was not being called.
#IBAction func unwindToParent(sender: UIStoryboardSegue) {
dismissViewControllerAnimated(true, completion: nil)
}
So after digging in, the canPerformUnwindSegueAction:fromViewController:withSender from the super class returns false. So I've overridden the implementation, and it works:
override func canPerformUnwindSegueAction(action: Selector, fromViewController: UIViewController, withSender sender: AnyObject) -> Bool {
return action == Selector("unwindToParent:")
}
Update
The code above is incorrect, as I resolved the issue without overriding canPerformUnwindSegueAction:fromViewController:withSender. The fundamental error was to make the distinction between the presenting viewcontroller and the presented viewcontroller.
When an unwind segue is initiated, it must first locate the nearest view controller in the navigation hierarchy which implements the unwind action specified when the unwind segue was created. This view controller becomes the destination of the unwind segue. If no suitable view controller is found, the unwind segue is aborted.
source: Technical Note TN2298
So, define the #IBAction on the presenting viewcontroller, not on the presented view controller. That way the segue will have meaningful values for the properties destinationViewController and sourceViewController as well, being respectively the presenting and presented viewcontroller.
Xcode --version 6.4
Swift 1.2
#IBAction func backButton(sender: AnyObject) {
dismissViewControllerAnimated(true, completion: nil)
}
I've much the same problem as in this question but these solutions don't work. Currently running xcode 7.3.1.
I've a split view controller app, that modally presents a save record view controller. On the save VC, there are a save and cancel button that should (in both cases) unwind back to the master view.
Within the Master View Controller, I have the following code:
#IBAction func unwindToSegue (segue: UIStoryboardSegue) { }
BUT, there's a compiler error: Only instance methods can be declared #IBAction. There is the suggestion to delete #IBAction, but while this removes the error I can't connect to this action in the child VC.
With or without #IBAction in the unwind method, I get the same issue described in the referenced problem - dragging a line from my UIButton to the Exit button in Storyboard IB, does not show any action to connect with.
You need to put the function inside the UIViewController.
class MasterViewController: UIViewController {
#IBAction func unwindToSegue(segue: UIStoryboardSegue) { }
}
I got this error simply when I had an extra parenthesis in my code... it was positioned before the IBAction method, so it essentially cut it off from being inside the UIViewController class, as Tomas suggests. So, check that all of your parenthesis are in place.
#IBAction is very picky with regard to syntax including white spaces between the string. for an example, nameA.txt ="" is not acceptable.
you need to have equal white space contiguous to string such as
nameA.text = ""
I am struggling with this problem for a few hours now.
I have a view with a tableView and custom cell in it. What I am trying to do is to perform a segue which is in my viewController from the custom cell. The first thing I did was to create a variable called "parent" in my cell.swift with the type "viewController".
weak var parent : ViewController!
After that, I'm just trying to perform the segue like this :
parent.performSegueWithIdentifier("eventDetails", sender: parent)
And I have the following error : Receiver (<Deevent.ViewController: 0x7fb99074f5d0>) has no segue with identifier 'eventDetails'
So I tried something else... I created a button in my ViewController and connected a segue to the next view (everything from the storyboard file)
In my viewController.swift I created the following function
func viewEventDetails() {
performSegueWithIdentifier("eventDetails", sender: self)
}
and this one in my cell.swift :
func viewEvent(sender: AnyObject) {
parent.viewEventDetails()
}
So when I call it from the cell, it crashes with the same error than before but when I click on the button it's working. I even tried to click on the button programmatically btnDetails.sendActionsForControlEvents(UIControlEvents.TouchUpInside) and I had the exact same error.
I've already tried to clean my project and delete it from the simulator. I'm really missing something here... hope somebody can help me.
Thanks !
It sounds like you need to give your segue an Identifier.
A view controller can have multiple segues, and so you need to define which one you want to use. In your case, in your code you are using the Identifier "eventDetails".
Assuming you are using a Storyboard, you need to click on the segue arrow between the two view controllers, and in the Attributes Inspector on the right, set the Identifier value to "eventDetails".