How to dismiss 2 view controller - ios

I have an issue need to support:
Assume, I have the controller A and View Controller B. The view controller A present view controller B. After that view controller B load content completed. At View controller B I have a button.
How to dismiss both view controller when I touch event button touch up inside?
Here my code: At view controller A
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"mainstoryboard" bundle:nil];
ViewControllerB *viewcontrollerB = [storyBoard instantiateViewControllerWithIdentifier:#"viewcontrollerB"];
[self presentViewController:viewcontrollerB animated:YES completion:nil];
And code in view controller B
- (IBAction)buttonTouchUpInside:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
Thanks for helping me. You will save my time. :)

Use delegation. When your button pressed call delegate, and in delegate method implementation write following:
[self dismissViewControllerAnimated:NO completion:nil];

Use this delegation on your ViewControllerB
On ViewControllerB.h do this
#protocol ViewControllerDismissDelegate;
#interface ViewControllerB : UIViewController
#property(nonatomic) id<ViewControllerDismissDelegate> delegate;
#end
#protocol ViewControllerDismissDelegate <NSObject>
#optional
- (void)didViewControllerBDismiss;
#end
On ViewControllerB.m do this
// Button action
- (void)buttonDidClick {
if ([self.delegate respondsToSelector:#selector(didViewControllerBDismiss)]) {
[self dismissViewControllerAnimated:NO completion:nil];
[self.delegate didViewControllerBDismiss];
}
}
Learn more about delegates and data sources here
https://developer.apple.com/library/ios/documentation/General/Conceptual/CocoaEncyclopedia/DelegatesandDataSources/DelegatesandDataSources.html

Related

Hide LoginViewController or dismiss to parentView

This is a normal LoginView calling on a specific action in my app
sourceViewVontroller
if ([password length] == 0) {
loginViewController *seatView = [mainStory instantiateViewControllerWithIdentifier:#"loggingView"];
[self presentViewController:login animated:YES completion:nil];
}
it checks if user not signed in yet when he's calling this action, so it redirects him to the loginViewController, then
loginViewController
UIStoryboard *mainStory = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
DestinationViewController *Dest = [mainStory instantiateViewControllerWithIdentifier:#"destView"];
[self presentViewController:Dest animated:YES completion:nil];
it goes to the destinationView that needs a login, now my problem is how to go back (dismiss not presentmodalView) to the sourceViewController, or simply, how to remove the loginViewController from the queue on success and dismiss directly to the source?
destinationViewController
// Tried to use presentView .. but i dont need to reload sourceView, just dismiss to it !
//[self presentViewController:srcView animated:YES completion:NULL];
//This is what am doing ..
[self dismissViewControllerAnimated:NO completion:nil];
Or just let me know if there is other professional way to do this login flow
I suggest for you another way:
First in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
You will check user login or not. If login you will set rootView to destinationViewController. If not set rootView to loginViewController
SampleCode:
UIStoryboard *mainStory = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
if (logged) {
DestinationViewController *Dest = [mainStory instantiateViewControllerWithIdentifier:#"destView"];
self.window.rootViewController = Dest;
} else {
LoginViewController *seatView = [mainStory instantiateViewControllerWithIdentifier:#"loggingView"];
self.window.rootViewController = seatView;
}
When logout you just call notification or delegate set rootViewController of window to loginViewController.
Do not use presentViewController for bringing login controller.
Complete Process :
In Singleton class. set a global property.
#property (assign)BOOL isUserLoggedIn;
When user logs in or logs out, set this variable to true or false.
Set an enum in LoginViewController.
typedef enum {
DestViewControllerOne =1,
DestViewControllerTwo
} SignInType;
In this enum, put all your view controller where you wanna put that login check.
Set a property in login view controller to hold source controller value -
#property (nonatomic, assign) NSInteger signInType;
Set up a delegate in login controller to redirect after successfull login -
#protocol SignInProtocolDelegate <NSObject>
#optional
-(void) signInSuccess:(NSInteger) signInType;
#end
Create a property with that delegate in login controller -
#property (nonatomic, assign) NSObject<SignInProtocolDelegate>* delegate;
Now before proceeding to required view controller, perform below check -
if(![[TESingleton shareData] isUserLoggedIn]){
[self funcNavigateToSignInWithAlert:YES withSignInType:proceedToDestViewControllerOne];
}
else
{
[self proceedToDestViewControllerOne];
}
-(void)funcNavigateToSignInWithAlert:(BOOL)showAlert withSignInType:(NSInteger) signIntype
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:STORYBOARDNAME bundle:nil];
LoginViewController *viewController = (LoginViewController *)[storyboard instantiateViewControllerWithIdentifier:#"LoginViewController"];
[viewController setSignInType:signIntype];
[viewController setDelegate:self];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController];
navController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[[SlideNavigationController sharedInstance] presentViewController:navController animated:YES completion:nil];
}
By this, it will bring login page.
Next -
After successfull login, Do this in login controller :
[self dismissViewControllerAnimated:YES completion:nil];
[[self delegate] signInSuccess:self.signInType];
Import login delegate method in source controller that we have written above -
-(void) signInSuccess:(NSInteger) signInType
{
switch (signInType)
{
case DestViewControllerOne:
[self performSelector:#selector(proceedToDestViewControllerOne) withObject:nil afterDelay:0.5];
break;
case DestViewControllerTwo:
[self performSelector:#selector(proceedToDestViewControllerTwo) withObject:nil afterDelay:0.5];
break;
default:
break;
}
}
Implement these methods in source view controller -
-(void) proceedToDestViewControllerOne
{
//Restricting navigation to signin
//Addded by vikas Jul 8,2015
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:STORYBOARDNAME bundle:nil];
DestViewcontrollerOne *objDestViewcontrollerOne = (DestViewcontrollerOne *)[storyboard instantiateViewControllerWithIdentifier:#"DestViewcontrollerOne"];
[self.navigationController objDestViewcontrollerOne animated:YES];
}
This is complete process.
Use Unwind Segues
Add a method in SourceViewController
For Swift
#IBAction func unwindToThisViewController(segue: UIStoryboardSegue) {
}
For Objective-C
- (IBAction)unwindToThisViewController:(UIStoryboardSegue *)unwindSegue {
}
You can come back to the SourceViewController in two ways.
Using Storyboard e.g when an UIButton action fires.
Using performSegueWithIdentifier in code.
Storyboard:
Control drag form an UIButton in DestinationViewController to Exit segue inSourceViewController
In Code:
Control drag form an UIButton in DestinationViewController to Exit segue inSourceViewController
Call performSegueWithIdentifier:#"ExitToSourceViewController" when need to come back to SourceViewController

nil delegate in modal view after presentViewController.

My first view in the viewDidLoad has the following code
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Dialpad" bundle:nil];
self.vc = [sb instantiateViewControllerWithIdentifier:#"DialpadBoard"];
self.vc.delegate = self;
The header file contains the definition of the view controller
#property (nonatomic, retain) DialpadTableViewController *vc;
after catching an event the view loads a new modal view
- (void)handleEvent:(UIGestureRecognizer*)recognizer {
[self presentViewController:self.vc animated:YES completion:NULL];
}
The view also contains the method to dismiss the modal view:
- (void) dialpadControllerDidCancel:(SearchDialpadTableViewController *)controller {
[self dismissViewControllerAnimated:YES completion:nil];
}
The last method never gets called.
The problem is that the modal view when it is loaded has nil self.delegate. The new modal view is loaded from the storyboard as seen below. Why the delegate is nil? I cannot how a segue since the view is in another storyboard.
you can try this
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Dialpad" bundle:nil];
UINavigationController *nav= [sb instantiateViewControllerWithIdentifier:#"DialpadBoard"];
((SearchDialpadTableViewController *)[nav viewControllers][0]).delegate = self;
[self presentViewController:nav animated:YES completion:NULL];
Since we have the nav let's take the first controller and assign the delegate
the trick ;)
((SearchDialpadTableViewController *)[nav viewControllers][0]).delegate = self;
Does SearchDialpadTableViewController define a delegate protocol and does it have a property that is specific to that protocol?
The header for that file should look something like:
#protocol ViewControllerDelegateProtocol <NSObject>
- (void)dialpadControllerDidCancel:(SearchDialpadTableViewController *)controller;
#end
#interface ViewController : UIViewController
#property (weak, nonatomic) id<ViewControllerDelegateProtocol> searchPadDelegate;
#end
Then when the user taps Done which is I'm assuming what you're trying to do (send a delegate message when that happens). You would write something like
- (IBAction)donePressed:(id)sender
{
if ([self.searchPadDelegate respondsToSelector:#selector(dialpadControllerDidCancel)]) {
[self.searchPadDelegate dialpadControllerDidCancel:self];
}
}
you must be certain, that self.vc is a class, that you expected. It can be also NavigationController

Delegate not called after dismissing controller

I have a Controller A and there is a UIButton, which on click I am presenting a new Controller B. But the problem is that the controller B is first embedded with a NAV. So ultimately I am presenting the UINavigationController.
Now there is a UIButton in Controller B on which the controller will dismiss and a delegate should be passed on controller A with some message
Controller A UIButtonCode
- (IBAction)summaryButtonClick:(id)sender {
UIStoryboard* storyBoard=[UIStoryboard storyboardWithName:#"Main" bundle:nil];
UINavigationController* summaryVC=[storyBoard instantiateViewControllerWithIdentifier:#"SummaryNavVC"];
[self presentViewController:summaryVC animated:YES completion:nil];
summaryVC=nil;
}
Now Controller B
.h file
#import <UIKit/UIKit.h>
#protocol SummaryViewWhatsNewDelegate <NSObject>
#required
- (void) SummaryViewWhatsNew:(NSString*)title;
#end
#interface SummaryViewController :
UIViewController<UITableViewDataSource,UITableViewDelegate>
{
id <SummaryViewWhatsNewDelegate> _delegate;
}
#property (nonatomic,strong) id delegate;
#property (weak, nonatomic) IBOutlet UITableView *summaryTableView;
- (IBAction)closeSummaryView:(id)sender;
#end
.m
//Button Click
[self dismissViewControllerAnimated:YES completion:^{
[_delegate SummaryViewWhatsNew:#"Sales Triggers Filter"];
}];
I have already made the implementation of delegate in my Controller A
.h
#interface ControllerA : UIViewController<SummaryViewWhatsNewDelegate>
.m of Controller A
#pragma mark -ControllerB Delegate
-(void)SummaryViewWhatsNew:(NSString *)title{
NSLog(#"Delegate Called");
}
In this case I know I haven't provided the delegate.self part as I am presenting the NAV controller and not the Controller B
So I made a Object in viewDidLoad and Controller *B and set the delegate to self. But it doesn't work and delegate is never called
On the other hand if I only present the Cntroller B without Navigation and just before presenting I keep the b.delegate=self, it works.
Another alternate can be firing Notifications. But I want to work with delegates.
So is there any way to call the delegate of the presented view controller which is embedded by Nav. Any help will be highly appreciated.
Well I got the answer
We have to fetch the Controller object from NAV
Where I a presenting I just need to add these lines and it worked
UIStoryboard* storyBoard=[UIStoryboard storyboardWithName:#"Main" bundle:nil];
UINavigationController* summaryVC=[storyBoard instantiateViewControllerWithIdentifier:#"SummaryNavVC"];
//Add These lines in order to get the object of Controller B.
// SummaryViewController is my Controller B
SummaryViewController* summary=[[summaryVC viewControllers] objectAtIndex:0];
summary.delegate=self;
[self presentViewController:summaryVC animated:YES completion:nil];
And it worked.

How to push or present UIViewcontroller or storyboard from UIView subclass?

What I did:
I am using scrollview to view some UIView(s), with paging enabled.
In last page in subview of scrollView I added a button.
I also wrote a function which is in subclass of UIView ,which is invoked when we press that button.
I want to view storyboard when I press that button.
You need to implement protocol method for custom UIView class.
For example;
#import <UIKit/UIKit.h>
#protocol YourCustomViewDelegate <NSObject>
- (void)buttonTapped;
#end
#interface YourCustomView : UIView
#property (nonatomic, strong) id< YourCustomViewDelegate > delegate;
#end
And the .m file you can call buttonTapped delegate method.
- (void)myCustomClassButtonTapped:(id)sender
{
[self.delegate buttonTapped];
}
For proper work;
set your custom view delegate to self in your custom view controller.
add buttonTapped method to that view controller
And do what you want in that method. Present/Push view controllers like the other answers explained.
Edit
I will try to illustrate very simple usage of protocols, hope it will help you.
#import "MyViewController.h"
#import "YourCustomView.h"
#interface MyViewController ()<YourCustomViewDelegate> // this part is important
#end
#implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
YourCustomView *customView = // initialize your custom view, I suppose that you already did it
customView.delegate = self;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)buttonTapped
{
YOUR_VIEW_CONTROLLER_CLASS *viewCon =[storyboard instantiateViewControllerWithIdentifier:#"mainVC"]; //mainVC is just a example and u will have to replace it with your viewController storyboard id
//Pushing VC
[self.navigationController pushViewController:viewCon animated:YES];
}
Take a look at this
YourViewController *obj=[[YourViewController alloc]init];
UINavigationController *nav=[[UINavigationController alloc]initWithRootViewController:obj];
[[[UIApplication sharedApplication]keyWindow].rootViewController presentViewController:nav animated:YES completion:NULL];
try like this
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
UIViewController* initialView = [storyboard instantiateInitialViewController];
Firstly you need to set a storyboard Id to your ViewController, namely "mainVC". Then you could pus/present it using below code :-
YOUR_VIEW_CONTROLLER_CLASS *viewCon =[storyboard instantiateViewControllerWithIdentifier:#"mainVC"]; //mainVC is just a example and u will have to replace it with your viewController storyboard id
//Pushing VC
[self.navigationController pushViewController:viewCon animated:YES];
//OR presenting VC
[self presentViewController:viewCon animated:YES completion:nil];
Also in case if you want your another storyboard to be initiated then below,
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil]; //Change MainStoryboard with your storyboard id
hope this will help
the way i perceive your question is
->Rightnow your inside you are in UIView class and inside it there is a button by clicking it you want to push or present a ViewController and offcourse from inside your UIView class you cant push or present your view Controller. So
you can do one simple thing first inside action method make a ref(forward declaration) or you can make a whole new object of your ViewController class in which your UIView is present right now , then call a function(which exist in your ViewController) like
//function inside your uiview calss
- (void)btnaction
{
[_objCSInformationViewController pushViewController];
}
// function inside your view controller
-(void)pushViewController
{
yourViewController *objViewController = [[yourViewController alloc]initWithNibName:#"yourViewController" bundle:nil];
[strong.navigationController pushViewController:objViewController animated:YES];
}

Property nil when using delegate to pass data back to VC

I've got a view in a xib file that I load to my viewController - its a temp view and thus isn't on screen all the time. However the temp view and the main view, use the same view controller class as they both are doing the same job.
Now, in the xib view I load up a second viewControoler - lets call it viewControllerB - which is a UITableView with 197 cells. Once a cell is selected, the view is dismissed and the value of the cell is returned to viewControllerA.
viewControllerB is on the storyboard.
Here is the code I have used:
viewControllerB (The VC I need data back from)
Delegate Protocol:
#protocol CountrySelectedDelegate <NSObject>
-(void)selectedCountry: (id)countryReturned;
#end
#property (strong, nonatomic) id <CountrySelectedDelegate> myDelegate;
Then in the .m file:
Set the delegate:
ViewControllerA *viewA = [[ViewControllerA alloc]init];
self.myDelegate = viewA;
Then I call the delegate in the same file;
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[[self myDelegate] selectedCountry:indexPath.row];
[self dismissViewControllerAnimated:YES completion:nil];
}
Now in viewControllerA:
pragma mark - CountrySelected Delegate
-(void)selectedCountry:(id)countryReturned
{
self.testInt = countryReturned;
}
Now this method does get called by the delegate. Looking in the console: countryReturned is the correct value. Once this method has finished, self.testInt is also the correct value.
Then, in my viewWillAppear method, I simply check the value again to see if its still the same with a NSLog print.
the value is always nil in the viewWillAppear method (Or anywhere else I check it) - I have tried with NSMutableString and NSString - always nil.
Why are my properties / instance variables getting set to nil?
Update:
This is how I show viewControllerB
- (IBAction)countrySelect:(UIButton *)sender
{
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil];
UITableViewController *vc = [sb instantiateViewControllerWithIdentifier:#"CountrySelection"];
vc.modalTransitionStyle = UINavigationControllerOperationPop;
[self presentViewController:vc animated:YES completion:NULL];
}
On ViewB do the below I guess you have already done
#protocol CountrySelectedDelegate <NSObject>
-(void)selectedCountry: (NSInteger)countryReturned;
#end
#property (week, nonatomic) id <CountrySelectedDelegate> myDelegate;
and in didSelectRowAtIndexPath
if ([[self myDelegate] respondsToSelector:#selector(selectedCountry:)])
{
[[self myDelegate] selectedCountry:indexPath.row];
}
On ViewA
Conform the protocol
#interface ViewA : UIViewController < CountrySelectedDelegate >
create the call ViewB
ViewB *viewBObjc=[[ViewB alloc] init];
[viewBObjc setMyDelegate:self];
[[self navigationController] presentViewController:ViewB animated:YES completion:Nil];
implement the delegate on ViewA
-(void)selectedCountry: (NSInteger)countryReturned
{
NSLog(#"Country Id is :%d", countryReturned);
}
Try this,
Set delegate by
//in ViewControllerB
self.myDelegate = [self parentViewController];
or
- (IBAction)countrySelect:(UIButton *)sender
{
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil];
ViewControllerB *vc = (ViewControllerB *)[sb instantiateViewControllerWithIdentifier:#"CountrySelection"];
vc.modalTransitionStyle = UINavigationControllerOperationPop;
vc. myDelegate = self;
[self presentViewController:vc animated:YES completion:NULL];
}

Resources