I'm using a Split View Controller for an iPad app. I'm trying to send a label change to the detailReceiving Controller from the rootSending Controll when a button is pushed. I've read through tutorials on protocols and came up with the code below. When I click the button on rootSending, nothing happens to the label on detailReceiving. Do I have to do something else with a splitViewContoller so that the label will update? Shouldn't detailReceiving change the label when it receives the message?
rootSending.h
#import <UIKit/UIKit.h>
#protocol TestDelegate <NSObject>
-(void)tester:(NSString*)testString;
#end
#interface rootSending : UIViewController
#property (nonatomic, assign) id <TestDelegate> delegate;
#end
rootSending.m
#import "rootSending.h"
#implementation rootSending
#synthesize delegate;
-(void)viewDidLoad{
}
-(IBAction)buttonPressed:(id)sender{
[delegate tester:#"button pressed"];
}
#end
detailReceiving.m
#import "detailReceiving.h"
#import "rootSending.h"
#interface detailReceiving ()<TestDelegate>{
IBOutlet UILabel *label2;
}
#end
#implementation detailReceiving
-(void)viewDidLoad{
rootSending *obj = [rootSending alloc];
obj.delegate = self ;
}
-(void)tester:(NSString *)testString{
label2.text = testString;
}
#end
First of all, never ever have an alloc without an init! But in this case, even if you did use alloc/init, it still wouldn't work because that just creates a new instance of rootSending, not the one that you have in your split view. You need to get a reference to the one you have, which you can get from the split view controller,
-(void)viewDidLoad{
rootSending *obj = (rootSending *)self.splitViewController.viewControllers.firstObject;
obj.delegate = self;
}
After Edit:
If your mate controller is embedded in a navigation controller, then you need to get the navigation controller's topViewController to get your reference.
-(void)viewDidLoad{
UINavigationController *nav = (UINavigationController *)self.splitViewController.viewControllers.firstObject;
xmlListOfItems *obj = (xmlListOfItems *)nav.topViewController;
obj.delegate = self;
}
Related
I am trying to add custom elements including drop downs and view changing from my navigationBar, because of this I need a custom one. However when trying to set the delegate to my view controller it throws an error.
#interface MyViewController : UIViewController <MyCustomNavigationBarDelegate>
#implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
UINavigationController *nav = [[UINavigationController alloc] initWithNavigationBarClass:[MyCustomNavigationBar class] toolbarClass:[UIToolbar class]];
nav.navigationBar.assessmentDelegate = self;
}
The last line throws the error Property 'assessmentDelegate' not found on object of type 'UINavigationBar *' However it should be type of class MyCustomNavigationBar which clearly has the delegate:
#protocol MyCustomNavigationBarDelegate;
#interface MyCustomNavigationBar : UINavigationBar
#property (weak, nonatomic) id <MyCustomNavigationBarDelegate> assessmentDelegate;
#end
#protocol MyCustomNavigationBarDelegate <NSObject>
-(void)presentViewController:(UIAlertController *)alert;
#end
cast your navigaton Bar to MyCustomNavigationBar
MyCustomNavigationBar *switchNav = (MyCustomNavigationBar *)nav.navigationBar;
switchNav.assessmentDelegate = self
I have a table view controller called StackViewController, this is where I hold a list of todo's that has been created in CreateViewController...
I have an NSString property in StackViewController called currentTarget that represent the first to do in the stack:
import <UIKit/UIKit.h>
#interface StackTableViewController : UITableViewController
#property (nonatomic, strong) NSString *currentTarget;
#end
This property holds the first NSString object in the table view, I get it like this:
#import "StackTableViewController.h"
#import "Target.h"
#import "CoreDataStack.h"
#interface StackTableViewController () <NSFetchedResultsControllerDelegate>
#property (nonatomic, strong) NSFetchedResultsController *fetchedResultController;
#end
#implementation StackTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.fetchedResultController performFetch:nil];
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
Target *target = [self.fetchedResultController objectAtIndexPath:indexPath];
self.currentTarget = target.body;
}
Now, when I log into my home page which called HomeViewController I want to initiate the StackTableViewController and get its currentTatget property value...
I know that there are delegate to help you notify other views when a change has happened, but in my case I want to get this property value before even I have been in this page (StackTableViewController), because the HomeViewController is the first view controller that is loaded (my initial view controller) and I what to access this property when I was just logged in to the app and populate a label with it.
How should I do this?
I thought maybe something like this:
#import "HomeViewController.h"
#import "CreateViewController.h"
#import "StackTableViewController.h"
#interface HomeViewController ()
#property (strong, nonatomic) IBOutlet UILabel *targetLabel;
#end
#implementation HomeViewController
- (void)viewDidLoad {
[super viewDidLoad];
StackTableViewController *vc = [[StackTableViewController alloc] init];
NSString *current = vc.currentTarget;
self.targetLabel.text = current;
}
But i'm missing something here...my label is not populated...
I think there is something with the views lifecycle.
i'm a newbie please help me to figure this out...thanks
Don't do anything to do with graphics in viewDidLoad. The earliest you want to do it is in viewWillAppear (most of the time) and occasionally you will need to do it in viewDidAppear.
Try the same code in viewWillAppear and it should work.
Oh right, since your other viewController is setup in viewDidLoad, you need to call
[stackTableViewController view] on your stackTableViewController after you alloc init it. Seems weird, but this actually works. This is because the StackTableViewController doesn't have its calculation done when you initialize it, it runs through it in it's viewDidLoad delegate.
- (void)viewDidLoad {
[super viewDidLoad];
StackTableViewController *vc = [[StackTableViewController alloc] init];
[vc view];
NSString *current = vc.currentTarget;
self.targetLabel.text = current;
}
In UINavigationController this is child controller
.h
#protocol childProtocol <NSObject>
-(void)childMethod:(NSArray*)params;
#end
#property (strong, nonatomic) id<childProtocol>childDelegate;
#property (weak, nonatomic) parentVC *pVC;
.m
if([self.childDelegate respondsToSelector:#selector(childMethod:)]) {
[self.childDelegate performSelector:#selector(childMethod:) withObject:self.arry];
}
This is my parent controller
.m
-(void)childMethod:(NSArray *)params {
// some work
}
...
childVC *cVC = [[childVC alloc]init];
cVC.pVC = self;
But childMethod: is not getting called so I searched on internet and got this post
UINavigationControllers: How to pass value to higher (parent?) controller in stack?
I tried to create a weak reference but dont know how to use to make delegate pass data from child to parent?
Try this. Check the sample project attached
ParentViewController.h
#import <UIKit/UIKit.h>
#interface ParentViewController : UIViewController
- (void)passData:(NSString *)strText;
#end
ParentViewController.m
- (IBAction)btnGoToSecondView:(id)sender {
ChildViewController *secondVC = [[ChildViewController alloc] initWithNibName:#"ChildViewController" bundle:nil];
secondVC.delegate = self;
[self presentViewController:secondVC animated:YES completion:nil];
}
- (void)passData:(NSString *)strText {
NSLog(#"Data Passed = %#",strText);
}
ChildViewController.h
#import <UIKit/UIKit.h>
#import "ParentViewController.h"
#class ParentViewController;
#interface ChildViewController : UIViewController
#property(nonatomic, assign) ParentViewController *delegate;
#end
ChildViewController.m
- (IBAction)btnPassDataBack:(id)sender {
if([self.delegate respondsToSelector:#selector(passData:)]) {
[self.delegate passData:#"Hello"];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
Sample Project
This is child controller.h
#protocol childProtocol <NSObject>
-(void)childMethod:(NSArray*)params;
#end
#property (strong, nonatomic) id<childProtocol>childDelegate;
#property (weak, nonatomic) parentVC *pVC;
.m
if([self.childDelegate respondsToSelector:#selector(childMethod:)]) {
[self.childDelegate performSelector:#selector(childMethod:) withObject:self.arry];
}
This is my parent controller.h
#import <UIKit/UIKit.h>
#import "ChildController.h"
#interface perentController : UIViewController < childProtocol >
.m
- (void)childMethod:(NSArray *)params {
// some work
}
EDITED :
And Dont Forget to add childViewOBJ.childDelegate = self; at the time of create ChildViewController's object. such like,
childVC *cVC = [[childVC alloc]init];
cVC.childDelegate = self;
cVC.pVC = self;
[self presentModalViewController:cVC animated:YES];
For More information about How to create/use of Protocol.
First of all, you are not checking for the same selector as you declared in your protocol declaration so it won't respond to that. You declared the method childMethod: whereas you are checking if your childDelegate responds to myMethod: selector which does not so it won't go into the if condition.
Also the parent view controller is missing the implementation the method childMethod: in its .m. Implement that in your parent view controller or it will crash because of not finding the exact selector definition.
Since you are using a UINavigationController, the parent view controller won't be lost till the child view controller exist so the childDelegate property must not be strong unless you intend to hold onto your delegate in child view controller for some reason.
I'm trying to access some things from another viewcontroller (iOS).
I have my ViewController.h:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UITextFieldDelegate> {
...
}
#end
ViewController.m:
#import "ViewController.h"
#import "ViewController2.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
ViewController2.someVar = #"cakes"; // this is where I'm trying to set something in vc2
}
ViewController2.h:
#import <UIKit/UIKit.h>
#interface ViewController2 : UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchDisplayDelegate>
{
...
}
#property (nonatomic, copy) NSString *someVar;
#end
ViewController2.m:
#import "ViewController2.h"
#interface ViewController2 ()
#end
#implementation ViewController2
#synthesize someVar;
etc.
But at the line where I try to access this var, it gives me the following error:
Property 'someVar' not found on object of type 'ViewController2'.
In what way would I achieve accessing this other view controller?
You have to create a ViewControlle2 object in your Viewcontroller.m.
ViewController2 *myVC2 = [[ViewController2 alloc] initWithNibName:#"ViewController2" bundle:nil];
myVC2.someVar = #"cakes";
Another way to do this is by using delegates. If you created ViewController1 in AppDelegate, you should also create ViewController2 in AppDelegate, or where ever you create your ViewControllers. Then you ViewController1 would send a message to AppDelegate to get the data from ViewController2 and vice versa. This makes ViewController1 and ViewController2 no longer dependent on each other.
So in AppDelegate.h we'd have something like this
#interface AppDelegate : NSObject <MyViewControllerDelegate>
#property (nonatomic) ViewController1 *viewController1;
#property (nonatomic) ViewController2 *viewController2;
#end
Then in both view controllers you can add this line in the .h
#property (nonatomic) id<MyViewControllerDelegate> delegate;
This just gives us a variable to use to refer to the delegate from within the ViewController's .m
You also need to create the MyViewControllerDelegate protocol, so in a file called MyViewControllerDelegate.h
#protocol MyViewControllerDelegate
- (ViewController1 *) viewController1;
- (ViewControlelr2 *) viewController2;
#end
Then when you create the ViewControllers in AppDelegate.m, you should also set AppDelegate as the ViewController's delegate.
self.viewController1 = [[ViewController1 alloc] init];
self.viewController1.delegate = self;
self.viewController2 = [[ViewController2 alloc] init];
self.viewController2.delegate = self;
So with all of this delegate set up done, you should be able to access viewController2.someVar from viewController1 through the delegate by using:
self.delegate.viewController2.someVar = #"Cakes";
Hope this isn't too long winded.
Alright, so this is an extension to a question I asked last night. I have a little firmer grasp on how data can be passed between view controllers using various techniques. I wanted to go the MVC route, and creating a Singleton class seems the closest concept similar to MVC.
Basically I created a simple app with two View Controllers and a singleton class. I am trying to pass the value of a text field into a UILabel. For whatever reason it isn't working. This is what my code looks like.
ViewController.h
#import <UIKit/UIKit.h>
#import "Model.h"
#import "ViewController2.h"
#interface ViewController : UIViewController {
NSString *text2pass;
}
#property (weak, nonatomic) IBOutlet UITextField *tf;
#property (weak, nonatomic) IBOutlet UILabel *btn;
- (IBAction)go:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize tf = _tf;
#synthesize btn = _btn;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSString *tfstring = _tf.text;
NSLog(#"string = %#",tfstring);
}
- (void)viewDidUnload
{
[self setTf:nil];
[self setBtn:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (IBAction)go:(id)sender {
NSLog(#"btn pressed");
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewController2 *vc2 = (ViewController2 *) [storyboard instantiateViewControllerWithIdentifier:#"home"];
text2pass = _tf.text;
[self passValues];
[self presentModalViewController:vc2 animated:YES];
}
-(void) passValues {
Model *model = [Model sharedModel];
model.passedText = text2pass;
}
#end
ViewController2.h
#import <UIKit/UIKit.h>
#import "ViewController.h"
#interface ViewController2 : UIViewController {
NSString *passedText;
}
#property (nonatomic)NSString *passedValue;
#property (weak, nonatomic) IBOutlet UILabel *lbl;
- (IBAction)back:(id)sender;
#end
ViewController2.m
#import "ViewController2.h"
#interface ViewController2 () {
NSString *passedtext;
}
#end
#implementation ViewController2
#synthesize lbl = _lbl;
#synthesize passedValue = _passedValue;
- (void)viewDidLoad
{
// do code stuff here
NSLog(#"passedText = %#",passedText);
_lbl.text = passedText;
[super viewDidLoad];
}
- (void)viewDidUnload
{
[self setLbl:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (IBAction)back:(id)sender {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewController *vc = (ViewController *) [storyboard instantiateViewControllerWithIdentifier:#"welcome"];
[self presentModalViewController:vc animated:YES];
}
#end
Model.h
#import <Foundation/Foundation.h>
#interface Model : NSObject {
NSString *passedText;
}
#property (nonatomic, strong) NSString* passedText;
+ (Model *) sharedModel;
#end
Model.m
#import "Model.h"
#implementation Model
#synthesize passedText = _passedText;
static Model *sharedModel = nil;
+ (Model *) sharedModel {
#synchronized(self){
if (sharedModel == nil){
sharedModel = [[self alloc] init];
}
}
return sharedModel;
}
#end
The project can be downloaded in its entirety from here http://chrisrjones.com/files/KegCop-Test.zip
If you know why the UILabel is not displaying the text field text let me know. Oh I pretty much followed this -> http://www.youtube.com/watch?v=ZFGgMPcwYjg&feature=plcp
Your addressing, and memory management is just plain... off. Firstly, there's absolutely no reason to create a singleton for this, but that's beside the point here.
Secondly, when declaring properties, (atomic, assign) is defaulted to if not otherwise specified, which means your string:
#property (nonatomic)NSString *passedValue;
is weak sauce, ripe for deallocation and destruction at a moments notice. Declare it copy, strong, or retain.
Thirdly, there's absolutely no reference to your singleton in the pushed view controller, yet you seem to have the belief that objects that are named the same in different classes retain their value (especially when #import'ed). Not so. You need to reference your singleton and pull the value of [Model sharedModel].passedText into that text field.
In fact, I fixed your sample in two lines:
//ViewController2.m
#import "ViewController2.h"
//actually import the singleton for access later
#import "Model.h"
#interface ViewController2 () {
NSString *passedtext;
}
#end
#implementation ViewController2
#synthesize lbl = _lbl;
#synthesize passedValue = _passedValue;
- (void)viewDidLoad
{
// do code stuff here
NSLog(#"passedText = %#",passedText);
//actually reference the singleton this time
_lbl.text = [Model sharedModel].passedText;
[super viewDidLoad];
}
- (void)viewDidUnload
{
[self setLbl:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (IBAction)back:(id)sender {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewController *vc = (ViewController *) [storyboard instantiateViewControllerWithIdentifier:#"welcome"];
[self presentModalViewController:vc animated:YES];
}
#end
Which yields this:
I wouldn't recommend using a Singleton as a good way to pass data around your application. Most apps are simple enough that this kind of central access is not necessary, and it usually creates a maintenance nightmare... but I don't think the fact that you're using a Singleton is actually important to getting your code working.
Assuming you have access to the data in ViewController1, in your case through the a Singleton instance of Model (which needs a more descriptive name), then all you have to do is pass through the data to ViewController2 when it is created and presented, which eliminates the need for a Singleton at all.
Once you create the controller, set the data you need, and then present the view controller - which is basically what you're doing anyway.
As to why it's not working: Is the view controller being presented, just not with the correct data? Or is there actually an issue presenting the controller at all? I would set a breakpoint in the go: action of ViewController1, make sure the data you expect is in the textfield, correctly populates the Model and that the value is correctly pulled out of the Model in ViewController2.
Unless you've removed some of the code, it looks like you correctly populate the Model property in ViewController1, but in ViewController2 you refer to a local ivar passedTextrather than pulling it from the model.
On a separate note, the way to go back from a presented modal view controller is usually to dismiss that controller, not to re-create the initial controller and present that over the top.