Delegation between ViewControllers - ios

i know how to set up a "normal" delegation between to ViewController, which are directly dependent. So if i say, i have to send a message from one view to its upper one, i know how to do this.
But how do i set up a delegate, if there are more ViewControllers between the two?
So m let's say i have this setup according to the scheme:
ViewController1 -> ViewController2 -> ViewController3
(via ButtonPressed) (via ButtonPressed)
If i want to set a (for example) NSString in VC1 from VC2, i just write into the prepareForSegue, where i call the VC2:
VC2.delegate = self;
But what do i do when i want to transfer Data between VC3 and VC1 without having to change VC2?
What do i set the delegate for, if it isn't "self"?
I know my description is very bad, but i cannot describe it better.
I just want this: VC1 has one button and a label; the button opens VC2, which has one button too, but no label; this VC2Button shows VC3; And with a touch to the button in the VC3 i want to change the label in VC1. But without having to set a delegte to VC2 and then another to VC1, i know how this works.
Hope you get what i want.
Thanks in advance!

NSNotificationCenter may help if you don't want to maintain too many pointers just to link two unrelated views together.

You can either pass on the delegate from VC2 to VC3 like this:
Inside VC1:
VC2 *vc2 = [VC2 alloc] init];
vc2.delegate = self;
Inside VC2:
VC3 *vc3 = [VC3 alloc] init];
vc3.delegate = self.delegate;
Or you can use target-selector design pattern to achieve this.
Target-Selector way:
Inside VC1:
VC2 *vc2 = [VC2 alloc] initWithTarget:(id)iTarget andSelector:(SEL)iSelector];
Inside VC2: Pass on the target/selector received from VC1
VC3 *vc3 = [VC3 alloc] initWithTarget:(id)iTarget andSelector:(SEL)iSelector];

Related

Q:How to force destroy a viewcontroller in iOS?

Is it possible to recycle/force destroy a UIViewController in iOS ?
I am using this github project to get a custom UIViewControllerTransition:
Here is the flow of the program:
vc1 presents to a nav to which the rootVc is vc2
In vc2 there is a UIButton. When clicked, vc2's NavigatioCcontroller will dismiss.
But the issue is that vc2 is not recycled by the OS, so when I progress to vc1, then present to nav (which rootVc is vc2), vc2 does not call the viewDidLoad method.
Not sure where the problem lies. Is there a way so that when I click vc2's UIButton, force destroy the nav and vc2? This way, when I re-present to nav, vc2's viewDidload will be called again.
Code:
in vc1:
LMLQQSearchSelectViewController *search_vc = [[LMLQQSearchSelectViewController alloc] initWithNibName:#"LMLQQSearchSelectViewController" bundle:nil];
search_vc.fromController = #"KnowledgeViewController";
search_vc.pre_type = #"ENCYCLOPEDIACOL";
LMLQQSearchNavController *nav = [[LMLQQSearchNavController alloc] initWithRootViewController:search_vc];
nav.navigationBarHidden = YES;
__weak typeof(self) weakSelf = self;
_search_header.block = ^(){
weakSelf.transition = [[HYBEaseInOutTransition alloc] initWithPresented:^(UIViewController *presented, UIViewController *presenting, UIViewController *source, HYBBaseTransition *transition) {
HYBEaseInOutTransition *modal = (HYBEaseInOutTransition *)transition;
// If you don't specify, it will use default value
// Default is NO, if set to YES, it will use spring animation.
modal.animatedWithSpring = NO;
} dismissed:^(UIViewController *dismissed, HYBBaseTransition *transition) {
// do nothing
}];
nav.transitioningDelegate = weakSelf.transition;
[weakSelf presentViewController:nav animated:YES completion:NULL];
};
When nav is dismissed, you must be hanging onto a reference to it so that you can redisplay it later. And because nav is holding a reference to vc2, you get the same instance back again when you redisplay nav.
You have two options. One, you could release your reference to nav after it is dismissed, which will then release vc2 as well. Two, you could move the code you want to run every time vc2 appears from viewDidLoad into viewDidAppear.

iOS passing data between two Views

I'm using two views (view controllers VC1 and VC2) and passing data from VC1 to VC2
I stored the values for two variables in VC2
//VC1 store obj to VC2
self.VC2.twtid=ide;
self.VC2.urlString=[[NSURL alloc] initWithString:vurl];
I received the values after VC2 was shown.
My problem is when I click in any cell in VC1 for the second time.
When I navigate back to VC1(tableview) again and tried to select cell, the values aren't stored again.
//VC.h I declare the two var
#property(strong, nonatomic) NSNumber *twtid;
#property(strong, nonatomic) NSURL *urlString;
note: I didn't use prepareForSegue.without Segue!!
Use global variables othereise use Segue methods.
ViewController2 *VC2 = (ViewController2 *)[self.storyboard instantiateViewControllerWithIdentifier:#"ViewController2"];
VC2.url = urlString; // use your variable over here, this is just and example
[self presentViewController:VC2 animated:YES completion:^{ }];
[self.navigationController pushViewController:VC2 animated:YES];
user presentViewController or pushViewController as per your requirement
You don't need to store the VC2 as VC1's property, just init it every time.

Setting delegates (for protocols) only works in prepareForSegue?

Most of the information I found involving implementing protocols and delegates involves a step where you do this;
DestinationViewController *destinationVC = [[destinationViewController alloc] init];
destinationVC.delegate = self;
But after hours of frustration because I couldn't get it to work I finally stumbled across another way to allocate the destinationVC in prepareForSegue
DestinationViewController *destinationVC = segue.destinationViewController;
destinationVC.delegate = self;
Which actually works. What was I doing wrong? It seemed using the first method my delegate was never set to self.
When instantiated from a storyboard, the initWithCoder: methid is called, not the init method.
DestinationViewController *destinationVC = [[destinationViewController alloc] init];
destinationVC.delegate = self;
is how you do when your controller is not from a storyboard: you init it from the code. After that you have to manually handle the transition from your source VC to your destination VC.
DestinationViewController *destinationVC = segue.destinationViewController;
destinationVC.delegate = self;
is how you do when your controller is defined in a storyboard and is the destination of a segue.
When you perform a segue, the prepareForSegue: method of the source view controller is called, in which you should configure your destination like you want: setting properties, delegates, passing data,...
There is two way you can pushController while using UIStoryboard.
Option 1 : taking reference of actually UIViewController from storyboard.
UIViewController *displayTable = [self.storyboard instantiateViewControllerWithIdentifier:#"nextViewcontroller"];
[self.navigationController pushViewController:displayTable animated:YES];
Option 2 : Using Segue
[self performSegueWithIdentifier:#"MySegue" sender:sender];
In your first case you are allocating object and assign delegate. that does't means while performing pushViewController operation same reference is passing. so in that case two different reference is created. so you delegate is point out some other reference that doesn't exist.
may this help you.
Here is the basic tutorial about segues, updated for Xcode 6 and above.
https://developer.apple.com/library/ios/recipes/xcode_help-IB_storyboard/chapters/StoryboardSegue.html
When you use storyboards, all necessary context provided by UIStoryboardSegue class. it holds destination view controller for you. So, you must access destination controller throw destinationViewController property.
if you want to manually add controller to your navigation stack:
{
// binds your viewController from storyboard with local instance
UIViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"YOUR_STORYBOARD_IDENTIFIER"];
// set your delegate
vc.delegate = self;
// push controller into navigation stack
[self.navigationController pushViewController:vc animated:YES];
}

Is it possible to insert a subview while being on the same tab bar index?

To give some context, I have logic that programmatically decides what view controller to insert into the navigation controller. For example:
If(true){
MyViewController * MyObject = [[MyViewController alloc]init];
myNavigationController = [[UINavigationController alloc]initWithViewController:MyObject];
else {
MyOtherViewController * MyOtherObject = [[MyViewController alloc]init];
myNavigationController = [[UINavigationController alloc]initWithViewController:MyOtherObject];
}
self.tabBarController.viewControllers=[NSArray arrayWithObjects:myNavigationController,nil];
Hopefully that illustrates my point of how I insert views inside of navigation controller. Now onto my problem:
I have an action listener with a button inside of "MyViewController" that essentially replaces the navigation/tab bar index when the user clicks the button. Is it possible to update a navigation/tab bar index with just a button?
MyViewController.m
- (IBAction)MyActionListener:(id)sender {
MyOtherViewController *MyOtherObject = [[MyOtherViewController alloc] initWithNibName:#"MyOtherViewController" bundle:nil];
[self.view insertSubview:MyOtherViewController.view atIndex:2];
}
When I do this, I get a crash EXEC_BAD_ACCESS I'm just wondering if my implementation/approach is wrong. I noticed this question: Update UITabBar Views?
However, doesn't seem to fit the results I am looking for. Hopefully I am clear. Thanks!
Yes it is possible to switch your views on button click with navigation.
You currently in VC1 , you have other two vc VC2 & VC3 and on button click you choose VC2 or VC3 but you did not change the VC1 place.

General way to pass data to UIViewControllers when using a UITabController

I still haven't grasped this transfer with the structure below. I have read many posts, and have seen the same unanswered post by others, but no resolution.
I will try to simplify the question to make it easier for all.
The structure of the project is:
UITabbar with tab1 and tab2
Tab1 has a Nav controller-->ViewController1
Tab2 has a Nav controller -->ViewController2
In viewcontroller1 (tab1) I have object X.
In ViewCOntroller2 (tab2) I want to display object X.
Don't worry about displaying, that's the easy part.
Question: How do you pass object X from tab1 to tab2. (what is the general pattern).
If you want to do it using prepareForSegue, is this ok, or is there a better way.
If using prepareForSegue, where do you drag the segue to?
The tabbarcontroller
OR*****
2. to the second VC
Hopefully this is clear enough. With this in mind how would you perform the transfer?
Using the segue 1:
I tried doing this:
//(From View controller 1)
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"CreateObjectXToDisplayObjectX"])
ViewController2* vc2 = [[ViewController2 alloc] init];
UITabBarController* tbc = [segue destinationViewController];
vc2 = (ViewController2 *)[[tbc customizableViewControllers] objectAtIndex:1];
//Crash here with with [MainNavigationControllerDesign setViewController1Delegate:]: unrecognized selector sent to instance 0x1064ef70'
vc2.viewController1Delegate=self;
vc2.objectXAtViewController2 = _objectXFromViewController1;
}
}
So, how is this Object X transfer accomplished?
Thank you in advance
You don't want to use segues in this way. Segues always instantiate new controllers when you go to them, but you already have these controllers embedded in the tab bar controller. If you were setting this up in code, I would say use a delegate, but if you set this up in IB, it's hard to do that. From VC2, you can get a reference to VC1's navigation controller with self.tabBarController.viewControllers[0]. VC1 will be that navigation controller's topViewController, so, putting that together, and adding a cast, you can access VC1 like this:
ViewController1 *vc1 = (ViewController1 *)[self.tabBarController.viewControllers[0] topViewController];
Once you have that reference, you can access any of vc1's properties. Don't forget to import ViewController1.h into ViewController2's .m file.

Resources