51

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.

Button to Exit with whip

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.

Shruti Thombre
  • 989
  • 4
  • 11
  • 27
skabob11
  • 1,306
  • 1
  • 10
  • 19
  • 2
    Please check the Xcode release notes. – Jack Lawrence Jun 04 '14 at 05:34
  • @JackLawrence Thanks Jack. There was a note in there about this and I was able to figure it out. I'll updated this answer here. – skabob11 Jun 04 '14 at 05:49
  • 2
    Sounds like you're working through apple's ToDo app tutorial in swift, too. Here's a working swift version, in case you get stuck: https://github.com/SimpleAsCouldBe/ios-swift-todo – SimplGy Jun 09 '14 at 06:24
  • @SimpleAsCouldBe Thanks, yeah I am. I'll take a look at your repo. – skabob11 Jun 10 '14 at 13:23
  • Hello! everyone. I'm using Xcode 6.3 and I'm encountering the same problem mentioned above. But for me, the exit can identify the IBAction in view controllers other than my ViewController.swift i.e. the IBAction function if written in other view controllers get shown in exit but not the ones written in ViewController.swift. Any help would be appreciated thanks! – ritvik1512 Apr 24 '15 at 16:14

11 Answers11

55

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:

  1. Change class MyViewController to @objc(MyViewController) class MyViewController
  2. Create an Objective-C header file with a category for MyViewController that redeclares the segue action.

    @interface MyViewController (Workaround)
    - (IBAction)unwindToMyViewController: (UIStoryboardSegue *)segue;
    @end
    
  3. 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

skabob11
  • 1,306
  • 1
  • 10
  • 19
  • 1
    You're right that the documentation does say this is the workaround for the issue, but I can't seem to get it to work. I've added the header file and edited the swift class file (and reset the custom class for the view controller) but nothing seems to work. Is there anything I might be missing that you can think of? – itsmequinn Jun 06 '14 at 22:45
  • @itsmequinn I had the same problem you did. See rajeev's answer below — worked for me. – ehfeng Jun 11 '14 at 00:05
  • That Xcode Release Notes link is not valid anymore. Anyone knows where it is now? – aseba Jun 29 '14 at 17:57
  • @aseba Updated release notes link. – skabob11 Jul 01 '14 at 13:06
  • Does anyone know if this was fixed is today's beta 4 release? – elprl Jul 21 '14 at 23:18
  • 3
    This is fixed in Xcode 6 GM. Dunno if it was working in prior betas. – swilliams Sep 11 '14 at 03:18
  • I'm in XCode 6.2 beta and still need one of these solutions. – Sean Dec 24 '14 at 04:57
51

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) {}
trumpeter201
  • 8,487
  • 3
  • 18
  • 24
  • Hi. I created a little test app with Xcode 6, Beta 4 and the unwind works. However, the reason why I created this app is because it didn't work in another Swift app that I am creating. The unwind setup in both apps is identical, so I cannot explain what is wrong... I am still debugging it and will report back what I've learned. – A Bit of Help Jul 24 '14 at 15:50
  • 20
    One thing that is not clear in the workaround is that the unwind segue function must be in the target view of the unwind segue. i.e. it must be in the view TO WHICH you unwind, not the view FROM WHICH you unwind. That threw me off but once I fixed that it worked. – Laurent Jul 24 '14 at 21:36
  • I've tried it both ways... How is your unwind segue set up? – trumpeter201 Jul 25 '14 at 16:20
  • `@IBAction func unwindToSegue (segue:UIUnwindSegue) {}` – trumpeter201 Jul 25 '14 at 16:22
  • 3
    `@IBAction func unwindToSegue(segue: UIStoryboardSegue) {}` UIUnwindSegue is not available – Haider Ghaleb Jul 27 '14 at 21:00
  • Sorry, yeah, that's what I meant, and what I've been using. – trumpeter201 Jul 27 '14 at 21:27
  • If this still doesn't work, try to make sure the `Module` is set correctly as mentioned in this post: http://stackoverflow.com/questions/24924966/xcode-6-strange-bug-unknown-class-in-interface-builder-file. – Alex Dong Sep 05 '14 at 04:34
  • Thank you very much for this, struggled like crazy to get the unwind to work. The objc header way didnt work at all for me. – thsorens Nov 12 '14 at 14:00
  • 2
    @Laurent Gorse saved me after long long time trying to crack this – vim Dec 27 '14 at 20:19
  • 1
    @LaurentGorse thanks, without you I wouldn't have been able to solve it. – Gaston Sanchez Jan 02 '15 at 23:50
  • @LaurentGorse I was completely stumped until you chimed in. Thank you! – Roshambo Mar 01 '15 at 15:45
  • 1
    its a pain in the ass – Nicolas Manzini Nov 05 '15 at 08:49
16

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

rajeev
  • 161
  • 2
  • 1
    Worked for me as well, what had been missing in the other helps was the fact you need to still declare the @IBAction func unwindToList in your Swift file as well as the .h – Unome Oct 22 '14 at 19:05
11

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.

Fred
  • 421
  • 2
  • 6
5

In Swift 2.3 I found the external name of the parameter must be "withUnwindSegue":

@IBAction func unwindToThisView(withUnwindSegue unwindSegue: UIStoryboardSegue) {
    ...
}
Bob Peterson
  • 636
  • 7
  • 16
  • The Interface Builder in Xcode 8 seems to use Swift 3 syntax (even for 2.3 projects), so if you had `unwindToThisView(unwindSegue:)` it expects `unwindToThisViewWithUnwindSegue:` and not `unwindToThisView:`. I solved it by renaming the method to `unwindToThisView(_ unwindSegue:)` – André Pinto Oct 20 '16 at 15:52
2

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

Cameron Lowell Palmer
  • 21,528
  • 7
  • 125
  • 126
Barlow Tucker
  • 6,229
  • 3
  • 36
  • 42
1

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 :))

1

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)
bkopp
  • 478
  • 1
  • 8
  • 20
1

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.

TMfranken
  • 31
  • 3
0

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.

Bouke
  • 11,768
  • 7
  • 68
  • 102
  • Yes, it is correct to place the unwind in the presenting view controller. Also you don't need the dismissViewControllerAnimated shown above. By virtue of having the unwind segue it handles it automagically. – Cameron Lowell Palmer Aug 12 '16 at 08:59
0

Xcode --version 6.4 Swift 1.2

@IBAction func backButton(sender: AnyObject) { dismissViewControllerAnimated(true, completion: nil) }

cam_271
  • 349
  • 2
  • 8