I am working on a camera app where the camera views are shown modally. After I am done with cropping. I perform an unwind segue to the MainPageViewController
. (
I'm guessing the performSegue is not firing because the unwind segue has not yet finished. The only thing I can think of at the moment, is to delay calling the performSegue using dispatch_after
. This seems very "hacky" to me though.
@IBAction func unwindToMainMenu(segue: UIStoryboardSegue) {
dispatch_after(1, dispatch_get_main_queue()) { () -> Void in
self.performSegueWithIdentifier("Categories", sender: self)
}
}
A bit late to the party but I found a way to do this without using state flags
Note: this only works with iOS 9+, as only custom segues support class names prior to iOS9 and you cannot declare an exit segue as a custom segue in storyboards
class UIStoryboardSegueWithCompletion: UIStoryboardSegue {
var completion: (() -> Void)?
override func perform() {
super.perform()
if let completion = completion {
completion()
}
}
}
note: the action for this segue should be unwindToMainMenu
to match the original question
@IBAction func unwindToMainMenu(segue: UIStoryboardSegue) {
if let segue = segue as? UIStoryboardSegueWithCompletion {
segue.completion = {
self.performSegueWithIdentifier("Categories", sender: self)
}
}
}
Your code will now execute after the exit segue completes its transition
Taking forward this answer (I only had Objective-C code)
Subclass UIStoryBoardSegue
#import <UIKit/UIKit.h>
@interface MyStoryboardSegue : UIStoryboardSegue
/**
This block is called after completion of animations scheduled by @p self.
*/
@property (nonatomic, copy) void(^completion)();
@end
And call this completion block after completion of animations.
@implementation MyStoryboardSegue
- (void)perform {
[super perform];
if (self.completion != nil) {
[self.destinationViewController.transitionCoordinator
animateAlongsideTransition:nil
completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
if (![context isCancelled]) {
self.completion();
}
}];
}
}
@end
Taking forward the previous two answers, there is a bit more detail about the objective c version here (I too only had Objective-C code)
Subclass UIStoryboardSegue with UIStoryboardSegueWithCompletion
class UIStoryboardSegueWithCompletion: UIStoryboardSegue { var completion: (() -> Void)?
override func perform() {
super.perform()
if let completion = completion {
completion()
}
}
}
UIStoryboardSegueWithCompletion.h
#import <UIKit/UIKit.h>
@interface MyStoryboardSegue : UIStoryboardSegueWithCompletion
@property (nonatomic, copy) void(^completion)();
@end
UIStoryboardSegueWithCompletion.m
#import "UIStoryboardSegueWithCompletion.h"
@implementation UIStoryboardSegueWithCompletion
- (void)perform {
[super perform];
if (self.completion != nil) {
[self.destinationViewController.transitionCoordinator
animateAlongsideTransition:nil
completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
if (![context isCancelled]) {
self.completion();
}
}];
}
}
@end
note: the action for this segue should be unwindToMainMenu to match the original question [image showing segue ui][1] [image showing segue ui 2][2]
Select exit segue from storyboard Add custom class
-(IBAction)unwindToMainMenu(UIStoryboardSegue *)segue {
if([segue isKindOfClass:[UIStoryboardSegueWithCompletion class]]){
UIStoryboardSegueWithCompletion *segtemp = segue;// local prevents warning
segtemp.completion = ^{
NSLog(@"segue completion");
[self performSegueWithIdentifier:@"Categories" sender:self];
};
}
}
Your code will now execute after the exit segue completes its transition
The exit segue IBAction method happens before the actual unwind segue is finished. I had the same issue and resolved it this way (if you don't mind my paraphrasing of your code). It avoids the extra time and animations from relying on ViewDidAppear.
@IBAction func unwindToMainMenu(segue: UIStoryboardSegue) {
let categoriesTable = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("CategoryTableViewController")
self.navigationController?.viewControllers.append(categoriesTable)
self.navigationController?.showViewController(categoriesTable, sender: self)
}
Hope this is helpful for anyone else who runs into this and just wants an instantaneous transition!
Updated @moride's answer for Swift 5. The transition coordinator is now optional, so we run completion immediately if this is the case.
class UIStoryboardSegueWithCompletion: UIStoryboardSegue {
var completion: (() -> Void)?
override func perform() {
super.perform()
guard let completion = completion else { return }
guard let coordinator = destination.transitionCoordinator else {
completion()
return
}
coordinator.animate(alongsideTransition: nil) { context in
guard !context.isCancelled else { return }
completion()
}
}
}