How to close a dialog window in Objective C - ios

I have viewcontroller A and on click of button, i have opened a another viewcontroller with presentation style as Form sheet. In second view controller which is opened have two buttons. Now, when these buttons are clicked, this second view controller should go away and next view controller should open on top of view controller A. But when i am trying below, another view controllers are opening up inside the dialog
i don't know how to unwind this segue and call another scene together.

Nitya, did you try the following piece of code to dismiss current view controller. Use it in the IBAction of the button where you want to dismiss the viewController:-
self.dismissViewControllerAnimated(true, completion: nil)

So you have a root viewcontroller A with a button. If you tap that button, an other viewcontroller (let's name it B) is presented by A as a form sheet. B has two buttons and if you tap one of those buttons, B should be dismissed and after a different viewcontroller (let's say C) shold be presented by A.
Apple don't really recommend this kind of workflow, nevertheless it is possible to implement it. You may shold consider to use navigation controller or master-detail controller instead of this. But if you stick to your original idea here are some possible implementations:
1.
If you are using a storyboard and the button on B triggers an unwind segue to A then you can add this to the end of the unwind seque action:
dispatch_async(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:#"segueThatPresentsC" sender:nil];
});
2.
If you present your viewcontrollers programmatically then:
Before you present B from A you can configure a button of B so that ist target is A and and its action is a method defined in A:
- (void) dismissAndPresentC {
[self dismissViewControllerAnimated:YES completion:^{
[self presentViewController:instanceOfC animated:YES completion:nil];
}];
}
This second is maybe a bit smaller hack, but still a confusing concept.

Related

Segue from screen with several buttons?

I am trying to write an app using UINavigationViewController. My first screen has several buttons on it, and on the click of each button, I want to segue to a UIViewController. I know that I can add a segue on each button, all pointed to the UIViewController that I want to go to, but I was wondering if it is possible to use only one segue that can be fired from each of the buttons.
If that is not possible, I was wondering if it was possible to open the second UIViewController from the first one, on button click, and provide a Back button like the UINavigationView provides. I did manage to get everything on this idea working, except for the back button. I mean I can put a standard button somewhere on the screen and go back, but I'd like the standard back button on the UINavigationView.
Phew! I'm not sure if that makes any sense.
I know that I could also use a tableview, but I'm trying to set this up with buttons.
Thanks
Edit: Thank you to everyone that answered. I now have this working. I would vote up the answers, but I don't have enough posts to do it. I appreciate the answers!
If you need to have separate action functions for each button, suggest that you segue from the main controller to the other controller and create a segue identifier (see xcode procedure below); then, use performSegueWithIdentifier from each of the button action functions. You can also take advantage of the prepareForSegue. To create the segue, control-drag from the left button in the controller in the storyboard to the controller you want to segue to and pick show.
Check the example code in swift that I did for a very similar problem in the SO reference
Linking View Controllers through button
You can embed the main controller in a navigation controller and that will give you the ability to navigate back. If you have multiple layers you can also use unwind segue.
Link each button to one single action (ex. buttonClick) in that ViewController and then perform the appropriate segue using pushViewController method on self.navigationController
-(IBAction)buttonClick:(id)sender {
if(sender.id == self.button1) {
DestinationViewController *vc = [[UIStoryboard storyboardWithName:#"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:#"VC_IDENTIFIER"];
[self.navigationController pushViewController:vc animated:YES];
}
Or if you already have that 1 segue defined in storyboards you can use
[self performSegueWithIdentifier:#"SegueIdentifier" sender:self];
And use that inside the buttonClick method. Using the 1st example, or the second one as long as the segue you setup in the storyboards is a push then you should already get the back button as that is the default behavior for pushing view controllers onto the navigation stack.

UIStoryboard Show (e.g. push) simple inverse action to go back

In storyboard we have great feature that allow us to make Show (e.g. push). So seems the logic is next:
If we don't have navigation controller then view controller will use present modal logic. My question is there any inverse action that I can use with Show?
I have a UIButton that close current view controller screen:
- (IBAction)onTappedCloseButton:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
}
But in case if I don't have navigation controller, how can I simple use inverse action to go back? So my solution is to check if self.navigationController is nil then use dismissing option:
[self dismissViewControllerAnimated:YES completion:nil];
But maybe there is another cool solution like Show (e.g push). But Close (e.g. pop)?
Yes, you can use an unwind segue to go back, and it will be the reverse of whatever the forward segue was.
You have two options on how to do this:
1) The Unwind segue
To make an unwind segue you have to add a method in the view controller you want to "unwind" to with the following format:
-(IBAction)someSelectorName:(UIStoryboardSegue *)sender;
You will then be able to drag from your UIButton up to the "exit" icon in your storyboard.
Wire it up to the selector you just defined and UIKit will figure out how to get back to that view controller without you having to write any code. This can be especially useful as it can figure out when it needs to call -dismissViewControllerAnimated: more than once and can call those methods successfully. It can even unwind from within a view controller embedded in a navigation controller when the view controller you're unwinding to has the navigation controller presented on top of it. (i.e. it will do a dismissViewController instead of a pop to unwind)
2) The Custom unwind method
Say you don't want to or cant trigger this action from a storyboard. There is still an option and its detailed over at this question here:
Whats the programmatic opposite of showViewController:sender:
The gist is you can write your own generic dismiss method by implementing categories on the UIKit container View controllers (or on your own container)

iOS Storyboards: How to segue to the second view with a back button to the first view, but without displaying the first view

Here is my storyboard configuration:
Navigation Controller -> View Controller A -> Push-> View Controller B
^
|
Modal
^
|
View Controller C
What I want to achieve: When a button is pressed in View C, directly View B will be opened modally (No part of View A is to be displayed). Also, View B will have a navigation back button to View A.
To achieve this,
I set up the illustrated storyboard.
I created a segue between View C and the Navigation Controller of View A/B.
In the 'prepareForSegue' method of View Controller C, I get an instance of View Controller A as the first element in the navigation. In this instance, I set a variable like 'directlyProceedToViewB=YES'.
In the viewDidLoad method of View Controller A, I check the variable 'directlyProceedToViewB' and if it is YES, I call 'performSegueWithIdentifier' to segue to View B
The result is so that, first View A is opened modally and after displaying it a very short time, View B is opened with a push animation (View B can navigate back to View A, which is good). But I do not want View A to be displayed any time at all. How can I achieve this?
EDIT:
To better visualize, I'm adding a screenshot with more example cases to support:
Here are some cases I want to support:
We can start with ViewC, click on 'Modally Display B' which opens ViewB, then click 'Back to A' to navigate back to ViewA, then click on 'Dismiss Modal' on ViewA to go back to ViewC
We can start with ViewD, clcik on 'Modally Display A' which opens ViewA, then click on 'PushB' to open ViewB, then go back and forth between A and B and modally dismiss to ViewD.
First of all, some corrections: those are not views but view controllers. And "view A" is not pushed into the UINavigationController but it's the root.
After that, I suggest making the segue in "view C" an unwind segue and implement the IBAction in "view A" by pushing "view B" with [[self navigationController] pushViewController:bViewController animated:NO].
EDIT (adding some details):
I assume that in ViewControllerA's viewWillAppear you present ViewControllerC in a not animated manner.
Implement an unwinding action like (IBAction)unwindAndThenGoToB:(UIStoryboardSegue *)segue in ViewControllerA.
In the storyboard connect the button in ViewControllerC to the Exit icon and select the previously defined method.
Then implement the method with the push call I wrote earlier.
ps: for documentation there is plenty on Apple's website.
Implement this using delegates.Decalre protocol in which class you want and define those methods and call the methods in the view controller you want.There is no many ways of calling some view and showing back button to go different view.modal view is just a concept.and you can use delegate methods to call whatever class you want.
Here I got a way to do so:-
You need to set no animation for segue from viewC to viewA as shown in below image. Then set a segue identifier for segue from viewA to viewB namely, "viewB" and in your viewA .m file add following code,
- (void)viewDidLoad {
[super viewDidLoad];
// Place your conditional check here.
[self performSegueWithIdentifier:#"viewB" sender:self]; //Will directly lead to viewB and viewA won't be shown as no animation is there from viewC to viewA.
}
And your rest flow be like-wise.
I found the solution myself.
First, I discovered that, my original proposal of
In the viewDidLoad method of View Controller A, I check the variable
'directlyProceedToViewB' and if it is YES, I call
'performSegueWithIdentifier' to segue to View B
works as I desired on iOS 7 but does not work on iOS 8.
So the solution is, in the viewDidLoad method of View Controller A, if 'directlyProceedToViewB' is YES, rather than calling performSegueWithIdentifier, use the following code:
ViewControllerB *destVC = [self.storyboard instantiateViewControllerWithIdentifier:#"ViewControllerBStoryboardID"];
[self.navigationController pushViewController:destVC animated:NO];

Unwind Segue Programmatically - New twist

Context: I have two VC, A and B. VC A contains several buttons and several labels. When pressing the button in VC A a segue will display VC B/C/ and so forth. Now, when finished with VC A/B/C so forth, the segue is being unwind so that VC A appears. For most of the VC B/C/D so forth, I am using the unwind method which I trigger through a button in that VC (ctrl + drag to "exit" icon). This works perfect, because upon returning to VC A, the following action is being called automatically:
- (IBAction)returned:(UIStoryboardSegue *)segue {
// Here I do some stuff
}
Problem: Now, in one of the secondary VCs (e.g. D), things are a bit special. In this VC I generate some hundred buttons through a loop programmatically, then detect which button is being pressed and finally unwind back to VC A (without a specific button; any of the buttons will trigger the unwind). I know I can do this eg by using this
[self dismissViewControllerAnimated: YES completion: nil]
but this does not trigger the above action when returning to VC A, or by using this
[self performSegueWithIdentifier:#"UnwindSegueIdentifier" sender:self]
but this will generate a new instance of VC A, which I do not want (because labels in the instance of VC A already contains some information).
So, what I want is to be able to return to the same instance of VC A which generated the VC D, and also trigger the "returned" action listed above. Thus, I want to achieve the same effect as when using a button connected to the "exit" icon, but I want to do this programmatically "inside the code" when one the many buttons in VC D is pressed.
Any thoughts?
[self performSegueWithIdentifier:#"UnwindSegueIdentifier" sender:self] should work just fine. Unwind segues do not create new instances of their destinations, no matter how they're performed. They are unique in that it's the only segue that does not create a new instance of the destination.
You can create a unwindsegue by to use programmatically by control+dragging from the scene's view controller icon to its exit icon. After assigning an identifier to this unwindseque, you can call it programmatically.
Reference:
https://developer.apple.com/library/ios/technotes/tn2298/_index.html
Ulas has properly understood and answered the question. The original poster wanted to ensure that the unwind '(IBAction)myCustomName:(UIStoryboardSegue*)segue' selector was called upon unwind without having to 'manually' link a button to the unwind segue. To emphasize this point, assume that you programmatically create a button at runtime based on some condition. You do not have the ability to manually wire that button to the unwind segue. You need to do something like this within VC B...
UIBarButtonItem *dynamicButton = [[UIBarButtonItem alloc]
initWithTitle:#"Cool"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(killView)];
self.navigationItem.leftBarButtonItem = dynamicButton;
- (void) killView
{
[self performSegueWithIdentifier:#"TEST" sender:self];
}
The 'TEST' segue was established from the VC B to Exit manually (i.e the view controller itself not a button). When the button is clicked, it calls the killView method and the segue is triggered via the identifier.

Pop the current view using Segues/Storyboard on iOS 5

I am creating an app using iOS 5 SDK. I managed to push views using the Storyboard's Segues, but I cannot find the proper way to pop the current view and go back to the previous one.
I am not using any navigationController (the app doesn't have any top or bottom bars).
I don't think using modal or push segue the other way would be the solution as it instantiates a new controller.
Do I have to use a custom Segue with the opposite animation and deletion of the view at the end ? Or is there a better way ?
Storyboards in iOS 5 don't provide a "no-code" way to return from a segue -- that's something you'll need to implement yourself.
If you use "push" segues (which require a navigation controller), use the navigation controller's popViewControllerAnimated: method to undo the last push segue. (Or other methods to undo more; see the UINavigationController documentation.)
If you use "modal" segues, call dismissViewControllerAnimated:completion: on the view controller which presented the current view controller (which you can get from its presentingViewController property).
Update: In iOS 6 and later there's unwind segues for going "back" in a storyboard. It's still not a no-code solution -- and it shouldn't be, because you need to be able to do things like differentiating between "Done" and "Cancel" exits from a modal view controller. But it does let you put more of the semantic flow of your app into the storyboard. Apple has a tech note that describes them in detail, and they're also covered in the video from WWDC 2012 Session 407.
You could try calling [self dismissViewControllerAnimated:YES completion:nil]; from the controller you want to dismiss (whether the controller has been pushed, or shown modally).
Here is the related documentation : UIViewController Class Reference
The presenting view controller is responsible for dismissing the view controller it presented. If you call this method on the presented view controller itself, it automatically forwards the message to the presenting view controller.
Just to clarify.
In the class that was pushed. Simply wire up the following and the controller and view will be popped off.
[self.navigationController popViewControllerAnimated:YES];
Create Segue type "Custom" on your stroyboard. This can be from a button.
Create a new UIStoryboardSegue class named "popSegue"
In the popSegue.m file add the following;
-(void)perform{
UIViewController *sourceViewContreoller = [self sourceViewController];
[sourceViewContreoller.navigationController popViewControllerAnimated:YES];
}
-In the storyboard editor.
-Select the segue and change the Segue Class to "popSegue"
-Set the Identifier to "popSegue"
Done!
You can use the same "popSegue" class throughout your project.
Hope this helps
I'm using Xcode 5 also and here's how it's done. First, in the view code file that pushed the other, create an IBAction method in the .h file such as this:
- (IBAction)exitToHere:(UIStoryboardPopoverSegue *)segue sender:(id)sender;
Then in the .m file add this:
- (IBAction)exitToHere:(UIStoryboardPopoverSegue *)segue sender:(id)sender {
}
You can add any cleanup code you want executed in this method. Next go to your storyboard and select the pushed view. I assume you've got some kind of button on the view that the user taps to signal he's finished. Click on that button, hold down the key and drag to the the green box below the view which is the Exit. Release the mouse button but continue to hold the key. A popup will appear and your method will show in the list. Select that method. Now when the user clicks on the button, the view will pop and you'll be returned to the starting method.

Resources