Multiple view Controllers and dismiss the one at same time - ios

I have 3 viewControllers.(Let VC1,VC2,VC3)I have overloaded VC2 on VC1 from a button click event of VC1.Now I want to overload the VC3 on VC1 from the button click event of VC2 and at the same time dismiss the VC2.
If I do these task separately it works fine..but when I do the same task concurrently It is not working..
Please Help

So what I would recommend in this case is setting up a delegate protocol inside of VC2 that VC1 implements.
Something like:
#protocol SomeDelegate <NSObject>
- (void)requestsDismissalOfViewController;
#end
#interface ViewController : UIViewController
#property (weak, nonatomic) id<SomeDelegate> delegate;
#end
So in VC2 you would call [self.delegate requestsDismissalOfViewController]; and then in VC1 you would implement the requestsDismissalOfViewController method where you could dismiss VC2 and present VC3. Your best bet would be to present in the completion block of the dismissal.

Related

Delegate between ViewControllers on TabBar not being called

I have a tabbar, the first tab has HomeViewController and the second tab has navigationcontroller has two ViewControllers - CheckOutViewController and PaymentViewController.
I am trying to add a delegate on PaymentViewController which allows me to update HomeViewController.
However, the delegate method on the HomeViewController is not getting called.
PaymentViewController
#protocol PaymentViewControllerDelegate <NSObject>
#required
-(void)paymentSuccessfull :(BOOL)isSuccessfull;
#end
#interface PaymentViewController
#property (nonatomic, weak) id <PaymentViewControllerDelegate> paymentDelegate;
-(void)orderProcessed
{
[paymentDelegate paymentSuccessfull : YES];
[self.navigationController popToRootViewControllerAnimated :YES];
}
HomeViewController.m
#interface HomeViewController : UIViewController<PaymentViewControllerDelegate>
// I do not know how to assign delegate here in the tabbar
-(void)paymentSuccessfull:(BOOL)isSuccessfull
{
NSLog(#"success");
}
From what I understand is that you have some structure like this:
Tabbar Controller:
HomeViewController
NavigationController
PaymentViewController
CheckOutViewController
Now since you've added protocol to PaymentViewController, according to your structure, the delegate method should be called on the Controller from where you are instantiating the PaymentViewController(I guess in your case that might be NavigationController/Tabbar Controller).
So,in one of these controller where you would have instantiated PaymentViewController from Storyboard, you would also have to specify that the class conforms to the delegate as well.
It will be something like:
paymentViewControllerObj.delegate = self;
I think this should help you out or give you the direction.

Can an action be called from a separate view controller?

I am coding an app in Xcode and would like to know if it is possible to have an action called from a button in separate view controller.
For example, if there is a button on ViewController1 and the user presses the button, I would like an image to appear on ViewController2 and stay there even if the user navigates back to ViewController1 and then back to ViewController2 again. Can anyone help me please? Thank you.
UPDATED: I found a the answer to the question at the link below...
if my "button" is selected on view controller 1, then image should then display and stay on view controller 2
Yes, it is possible. If you declare the instance of ViewController2 within ViewController 1, then you can have full control over what's going on in ViewController2 at all times from ViewController1. For instance, in the .m, have
#implementation ViewController1
#synthesize viewController2
- (void) viewDidLoad
{
viewController2 = [[UIViewControllerClass alloc] init];
}
and in the .h, have
#interface ViewController1
#property (nonatomic, strong) UIViewControllerClass *viewController2;
This will make sure that ARC does not scrap your second ViewController before you're done with it, which in this case is when you terminate the program.
Then, you can simply call methods that belong to ViewController2 from ViewController1 like you would with any other class.
[viewController2 doSomethingWithThisData: stringData];
A few considerations not yet mentioned...
Depending on how you transition between viewControllers & where the shared data (images etc) are set, one option is for VC2 to have a public #property to set the image.
VC2.h
#IBOutlet (nonatomic, weak) UIImageView *imageFromVC1;
VC1 should import the VC2 header so it can create & hold an instance of VC2 and set the image.
You'd need to keep track of VC2's state in VC1, as a #property of VC1.
VC1.m
#import VC2.h
#interface VC1()
#property (nonatomic, strong) VC2 *viewController2;
#end
#implementation
...
Note that this does not need to go in the .h for VC1 - unless other classes need to know about it, this should be a private property in a class extension of .m & not exposed in the header. You don't need to use #synthesize anymore either - that's automatic if you're using XCode 4.4 or later.
The issue at this point is that VC2 shouldn't exist until it's needed (it's a waste of limited memory). You could create it right away in -viewDidLoad in VC1, but then it's just sitting there sucking up resources.
Better option is 'lazy instantiation' where you only create a VC2 instance when you transition to showing VC2 for the first time. Also set the #property in VC1 at this point.
-(void)someMethodToShowVC2OrStoryboardPrepareForSegue {
if (self.viewController2 == nil)
{
self.viewController2 = [[VC2 alloc] init];
}
//handle transition & set up VC2.
}
You could also ask VC2 to be sure it has the public method that allows you can set the image. If it doesn't, it will crash. By writing & importing the VC2 header obviously you should know that the method exists - this is just a defensive practice and alternative to sending the message to VC2 directly:
if ([self.viewController2 respondsToSelector:#selector(setImageFromVC1)])
{
UIImage *myImage = [UIImage imageNamed:#"myImageName"];
[self.viewController2 setImageFromVC1:myImage];
}
Here "setImageFromVC1" is the name of the setter method that is automatically generated when you use an #property. Change this if you use another name.
Alternative is to use dot notation: self.viewController2.imageFromVC1 = myImage;
Note that this "Key-Value Observing" approach to setting properties on other objects isn't the only, or necessarily the best, approach to doing this - but it works.

Changing label in one ViewController from a button in another ViewController

I'm new to Obj-c, and I've been trying to figure this out and I've found a few posts, but I couldn't get the solutions to work. I don't know what I am doing wrong.
So here's the setup. I have a viewcontroller with a button in it. That button, when touched, is supposed to update the label in another viewcontroller.
This is what I've done so far.
VC1:
I've set the property in header of VC1:
MainScene *msc;
I have this method in implementation file being called upon clicking of the button:
-(void) button {
[msc updateLabel];
}
VC2:
Here is the method updateLabel.
-(void)updateLabel {
label.string = [NSString stringWithFormat:#"%.2LF", points];
}
I also have the method in the header:
-(void)updateLabel;
Not sure what I'm doing wrong here.
That's not how it works.
From VC1 you first create an instance of the ViewController VC2.
And before you present it set the string value on VC2 property.
you have just define a property in the VC1 named msc, but you have not assign it to any instance of VC2 so when you try call the method updateLabel nothing happened.
in your case I assume VC2 is the parent viewcontroller and VC1 is present by VC2. so you need setup a protocol and a delegate in VC1 to udate the label of vc2.
add these in VC1 head file:
#protocol VC1Delegate <NSObject>
-(void)updateLabel;
#end
#property (weak, nonatomic) id<VC1Delegate> delegate;
and set the VC1's delegate = VC2 while presenting the VC1
if VC1 is parent viewcontroller things will be easier, just use [msc updateLabel]; after you asign the instance value to msc.

Understanding the mechanism when passing data back from a second view controller to main view controller

I'm currently trying to have a better understanding on how the mechanisms of passing data between controllers work and I'm a little confused especially when passing data back from a second view controller to the main view controller.
This is what I have that works but don't fully understand. I have two view controllers, in the first one I have a button that when clicked it basically goes to the second view controller and a label which shows a message sent from the second view controller. In the second view controller I have a button and a textField, the button basically sends whatever is in the textfield to the label in main view controller.
Here is the code...
// FirstVC.h
#import <UIKit/UIKit.h>
#import "SecondVC.h"
#interface FirstVC : UIViewController <passNames>
#property (nonatomic, strong) NSString* firstNameString;
#property (weak, nonatomic) IBOutlet UILabel *firstNameLabel;
#end
//FirstVC.m
#import "FirstVC.h"
#implementation FirstVC
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier]isEqualToString:#"secondController"])
{
UINavigationController *navController = segue.destinationViewController;
SecondVC *vc2 = (SecondVC*)navController.topViewController;
[vc2 setDelegate:self];
}
}
-(void)viewWillAppear:(BOOL)animated
{
self.firstNameLabel.text = _firstNameString;
}
-(void)setFirstName:(NSString *)firstName
{
_firstNameString = firstName;
}
#end
//SecondVC.h
#import <UIKit/UIKit.h>
#protocol passNames <NSObject>
-(void)setFirstName:(NSString*)firstName;
#end
#interface SecondVC : UIViewController
#property (retain)id <passNames> delegate;
- (IBAction)send:(UIBarButtonItem *)sender;
#property (nonatomic, strong) NSString *firstNameString;
#property (weak, nonatomic) IBOutlet UITextField *firstNameText;
#end
//SecondVC.m
#import "SecondVC.h"
#import "FirstVC.h"
#interface SecondVC ()
#end
#implementation SecondVC
- (IBAction)send:(UIBarButtonItem *)sender
{
_firstNameString = _firstNameText.text;
[[self delegate]setFirstName:_firstNameString];
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
Can someone explain how the prepareForSegue method works in the above code? The reason for this question is because I added an NSLog and it looks like this method is only called in the transition from main view controller to the second controller. Why is this method needed if it is not called when transitioning from second view controller to main view controller which in my case is what I'm doing? It makes sense to use it when passing data from main view controller to a second controller not on the case shown above.
Can some explain the whole mechanism when passing data back to the main view controller?
FYI, I do understand about protocols and delegation.
Thanks a lot.
In your case, you are setting your delegate method of the second view controller to self in mainViewController in you prepareForSegue. This means that apart from navigating to the SecondViewController, you are implementing the callback mechanism in your main view controller, so that your delegate method gets called when the value is passed from the second view controller and this delegate method collects the value as a parameter to handle it in the main View Controller. You might have set the delegate of VC2 as self inn your prepareForSegue because you are creating the instance of VC2 in this method to navigate to the second controller.
Your goal is to hand back the data, like this:
[[self delegate] setFirstName:_firstNameString];
But you can't do that unless you know who to send setFirstName: to, and the compiler won't let you do it unless you guarantee that whoever you are sending setFirstName: to can accept that message.
That is what prepareForSegue prepares. FirstVC has declared that it adopts the passNames protocol, which means that it implements setFirstName:. And now you are saying:
[vc2 setDelegate:self];
...where self is the FirstVC instance. This solves both problems at once. The SecondVC instance (vc2) now has a delegate (the FirstVC instance), it is the right object to send the info back to, and because its delegate is declared as adopting passNames, we know that SecondVC can actually send setFirstName: to that delegate.
Now to the heart of your actual question: The reason for doing this in prepareForSegue is merely that this is the only moment when the FirstVC instance and the SecondVC instance "meet" one another! There is no other moment when the FirstVC instance has a reference to the SecondVC instance so as to be able to call setDelegate on it in the first place. If you weren't using segues and storyboards, the FirstVC would simply create the SecondVC instance directly - and would set itself as its delegate, just as you do:
SecondVC *vc2 = [SecondVC new];
UINavigationController *nav = [
[UINavigationController alloc] initWithRootViewController: vc2];
[vc2 setDelegate:self];
[self presentViewController: nav animated: YES completion: nil];
This is one reason I don't like storyboards: they muddy the story. It's all so simple and obvious when you don't use them and just do everything directly like this.

get sender of segue in destination view controller

I have a VC named Dashboard (D) which can open a VC named Login (L) and a VC named Register (R). Login can open VC Register too.
I try to use storyboard as often as possible, so I have created with it three Segues, D to L, D to R, L to R
So, in case of D -> L -> R and in case of D -> R, when I close R, I have to close L if it necessary and inform D which he can begin to load the user infos (launch function in nutshell).
So, I would like get the sender of Segue in destination vc, knowing that I put it in sender entrie of performSegueWithIdentifier like that :
[self performSegueWithIdentifier:#"SegueToFbRegister" sender:self];
I'd do this by having R send a notification when the registration/login is done, and having D listen to it then pop everything and load your data.
If however you insist on getting a reference to the sender, you can add this property on your destination VC and set it in the source VC's prepareForSegue:sender:
This sounds like a great place to use Delegates. In your RegisterViewController.h define a protocol like this
#protocol RegisterViewDelegate <NSObject>
- (void)tellRegisterDelegateSomething:(NSObject*)something;
#end
Then on your class keep a pointer to your delegate
#interface RegisterViewController : UIViewController
#property (weak, nonatomic) id <RegisterViewDelegate> delegate;
#end
Now tell the presenting view controllers that they implement the new protocol you just created. This is done in the .h files of the other viewcontrollers that present this view.
In LoginViewController.h
#interface LoginViewController : UIViewController <RegisterViewDelegate>
#end
In DashboardViewController.h
#interface DashboardViewController : UIViewController <RegisterViewDelegate>
#end
In the .m files of the above classes, implement the protocol's method
- (void)tellRegisterDelegateSomething:(NSObject*)something
{
}
Now you need to assign the delegate when you perform your segue from either presenting view controller like this.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"SegueToFbRegister"])
{
RegisterViewController* destination = [segue destinationViewController];
destination.delegate = self;
}
}
Now you can call the presenting view controller (delegate) and have it do something with any information you need to send back like this (this would be called in your RegisterViewController.m).
if ([self.delegate respondsToSelector:#selector(tellRegisterDelegateSomething:)])
{
// Tell the delegate something.
[self.delegate tellRegisterDelegateSomething:something];
}
The instance where you need to pass back through two controller you follow the same basic pattern.
#protocol LoginViewDelegate <NSObject>
- (void)tellLoginDelegateSomething:(NSObject*)something;
#end
Then on your class keep a pointer to your delegate
#interface LoginViewController : UIViewController
#property (weak, nonatomic) id <LoginViewDelegate> delegate;
#end
Now tell the Dashboard view controller that it implements the protocol. This is done in the .h files of the Dashboard viewcontrollers that present this view.
In DashboardViewController.h
#interface DashboardViewController : UIViewController <RegisterViewDelegate, LoginViewDelegate>
#end
In the .m files of the DashboardViewController implement the protocol's method
Follow the above pattern of setting the delegate on the viewcontroller when you perform the segue. Now when the delegate method is called in the LoginViewController, you call the delegate in the DashboardViewController as well.
in LoginViewController.m
- (void)tellRegisterDelegateSomething:(NSObject*)something
{
if ([self.delegate respondsToSelector:#selector(tellLoginDelegateSomething:)])
{
// Tell the delegate something.
[self.delegate tellLoginDelegateSomething:something];
}
}
Now you are all connected so you can pass data back through both controllers (or just one) and do something with it. You will know which scenario you are in because different delegate methods will be called in the DashboardViewController based on which viewcontroller was visible.
Hope this helps.
Create a delegate for R and make D and L to implement the delegate methods.Use prepareForSegue:sender to assign the delegate of R.When you finish task in R use your delegate to perform the rquired action.
Another way would be to use an unwind segue.
Place the following code in you Dashboard (D) view controller.
#IBAction func loadUserInfoAfterRegistration(segue: UIStoryboardSegue) {
}
In Interface Builder, do the following steps for the Register (R) view controller:
Select the button that will be pressed on the completion of registration.
Ctrl + drag to the exit symbol on top of the view.
Select loadUserInfoAfterRegistrationWithSegue: from the list displayed.
Using this approach, the Register (R) view controller will always navigate to the Dashboard (D) view controller, regardless of what is between them. The view controllers between them will not have to be touched. The loading of the user data in the Dashboard (D) view controller can also be customized in the method declared above.
You can get it easily by using the type of the parent controller such as
let temp = self.navigationController?.viewControllers
if (temp != nil){
if let _parent = temp![temp!.count-2] as? UIControllerClass {
//do what you want here with the _parent
}
}
You have to subtract 2 because last one is the current view that you want to get its parent.

Resources