I am building a utility-based application, the data is stored in the MainViewController, and now I know how to pass data to the FlipsideViewController (many regards to this thread BTW, Sending data from Mainview to Flipside?). But I am getting the data onto an subview (subclass of UIView) that I have added to the flipside view. How can I pass data to this subview? I saw there is already a delegate and protocol set up in the FlipsideViewController.h, I am really new to the delegate sort of things. Any help would be greatly appreciated!
Updates:
On the main view, I have a couple of text fields for users to input to create an object. All the objects are stored in an array. Namely, my data is created and stored in the MainViewController. Now on the flip side, I have a custom UIView subclass which allows me to do my own drawing based on the data in that array. What I need to do here is pass the data that stored in MainViewController to this subview. Here is my relevant code:
In the MainViewController.m
- (IBAction)showInfo:(id)sender {
FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:#"FlipsideView" bundle:nil];
controller.delegate = self;
controller.receiver = data;//this is what I've done.
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:controller animated:YES];
[controller release];
}
In the FlipsideViewController.h
#protocol FlipsideViewControllerDelegate;
#interface FlipsideViewController : UIViewController {
id <FlipsideViewControllerDelegate> delegate;
DataModel *receiver; //create a property to receive the data transferred from main view
}
#property (nonatomic, assign) id <FlipsideViewControllerDelegate> delegate;
#property (nonatomic, retain) DataModel *receiver;
- (IBAction)done:(id)sender;
#end
#protocol FlipsideViewControllerDelegate
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller;
#end
In the above code, "data" is an DataModel object declared in the MainViewController.h file.
And I want to do my custom drawing in drawing class (subclass of UIView), how can I pass data from the FlipsideViewControllerto this subview? Do I need to make use of delegate declared in the FlipsideViewController.h file? Thanks in advance!
I have had a quick look at the template and think you are getting confused with what the delegate is being used for.
The delegate in this template is not transferring data. When you have clicked the done button it calls back to MainViewController and asks it to call the dismissModalViewControllerAnimated method so that it can remove the view controller. This seems a bit superflous as the documentation states
If you call this method on the modal view controller itself, however, the modal view controller automatically forwards the message to its parent view controller.
Therefore you don't really need to call the parent to do this.
In Interface builder you can see that the FlipsideView.xib has it's File's Owner set to FlipsideViewController.xib.
Now if you right click the File's Owner you will see that view is connected to View this basically means that view is the name of the property in FlipsideViewController and View is the element in Interface Builder.
Therefore we can access elements in the xib file from FlipsideViewController using outlets.
To say draw a label you will need to do a couple of things
First add a property in the .h and synthesize it in the .m like
// FlipsideViewController.h
#interface FlipsideViewController : UIViewController
#property (nonatomic, retain) IBOutlet UILabel *testLabel; // <----- Added this
#property (nonatomic, assign) id <FlipsideViewControllerDelegate> delegate;
- (IBAction)done:(id)sender;
#end
// FlipsideViewController.m
#implementation FlipsideViewController
#synthesize delegate = _delegate;
#synthesize testLabel = _testLabel; // <----- Added this
// More methods
- (void)dealloc
{
[_testLabel release]; // Always do you memory management
[super dealloc];
}
Then back in Interface Builder
Add a UILabel element to your view
ctrl + drag from File's Owner to the UILabel you added
Select the label in my example it is testLabel
Now these are hooked up correctly. The place where you want to be setting the value of the label is in viewDidLoad: which you can now do like this
- (void)viewDidLoad
{
[super viewDidLoad];
self.testLabel.text = #"It Works"; // You would use the data passed in from `MainViewController`
}
I find the easiest way to pass data from one view to another is by directly setting the data in the next view from the original view.
For example;
In your FlipsideViewController.h, declare a 'container' for the data you want to pass. It must be the same class on both sides to work properly, ie. NSArray to NSArray, NSMutableDictionary to NSMutableDictionary.
NSMutableArray *newData;
...
#property (nonatomic, retain) NSMutableArray *newData; // This allows you to access this object from outside this class.
and in FlipsideViewController.m
#synthesize newData;
...
[newData release];
Now we need to pass the data, so to speak. Let's say the data we want to 'send' is stored in a NSMutableArray called 'results'.
In our MainViewController.m, when we are instantiating our next view controller (in this case FlipsideViewController) we can directly reference the newData mutable array after we initalize the nib.
FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:#"FlipsideView" bundle:nil];
controller.newData = results;
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:controller animated:YES];
[controller release];
Make sure you are importing your FlipsideViewController in your MainViewController.h file.
If the property is declared in your .h file, you can pretty much reference the contents of the object from anywhere within the view stack!
Hope that helps :D
Related
How can I access the value from an inputField located in a second viewController?
The class name of the second view controller is SettingsViewController and the outlet name for the inputField is setRateInput.
I tried this but it didn't work…
double taxRateFromInput = [[self.settings.setRateInput text]doubleValue];
when I NSLog it comes out as The value is: (null)
Any idea what I'm doing wrong?
Here is the implementation file for the main viewController:
#import "SettingsViewController.h"
#interface ViewController ()
#property (strong, nonatomic) SettingsViewController * settings;
#end
#implementation ViewController
// lazy instantiation
-( SettingsViewController *) settings
{
if (_settings == nil) {
_settings = [[SettingsViewController alloc]init];
}
return _settings;
}
- (IBAction)calculatePrice:(id)sender {
double taxRateFromInput = [[self.settings.setRateInput text]doubleValue];
#end
In theory, you could create a global. Create a new class, call it something like taxRate (.h and .m)
In taxRate.h, add the following code:
#import <Foundation/Foundation.h>
#class MyTaxRate;
#interface TaxRate : NSObject {
}
#property (nonatomic, retain) double * taxRateFromInput;
+(TaxRate*)getInstance;
#end
Then, in your controller, put a "#import taxRate.h" in there. In your .m file, add the following:
#import "TaxRate.h"
#implementation TaxRate
#synthesize taxRateFromInput;
static TaxRate *instance =nil;
+(TaxRate *)getInstance
{
#synchronized(self)
{
if(instance==nil)
{
instance= [TaxRate new];
}
}
return instance;
}
#end
Note: This is extremely similar in structure to what I'm purposing.
if you have the reference from the object view controller you can just access by the property from your attribute.
You instantiated a new SettingsViewController, but you didn't do anything to instantiate its textfield setRateInput. You can do it when you instantiate it:
_settings = [[SettingsViewController alloc]init];
_settings.setRateInput = [UITextField alloc] initWithFrame:someFrame]];
or, as a beter solution, instantiate the text field in -init of SettingsViewController
- init {
if (self = [super init] {
self.setRateInput = [UITextField alloc] initWithFrame:someFrame]];
}
return self;
}
If you use nib files, this would be a lot easier.
Note: setRateInput is a bad name for a property. Consider rateTextField instead.
Edit I forgot to add that you have to add the text field as a subview to its parent view.
So it will be like,
_settings = [[SettingsViewController alloc]init];
_settings.setRateInput = [[UITextField alloc] initWithFrame:someFrame] autorelease];
[_settings.view addSubView:_settings.setRateInput];
In this case, the setRateInput is retained by its super view. You're not using ARC, so you can call autorelease on your text field.
The better solution: Use - (void) loadView; inside SettingsViewController. Loading the view is the responsibility of the correspondent view controller.
- (void) loadView {
self.setRateInput = [[UITextField alloc] initWithFrame:someFrame] autorelease];
[self.view addSubView:_settings.setRateInput];
}
Edit: xib files and storyboards can help you out. Give these tutorials a try.
You are on the right track, also well done with your lazy instantiation (as
a demonstration that you grasped the concept, I mean).
But note, that outlets don't get connected until viewDidLoad is called. So if you
just alloc/init your viewController (lazily), the outlet to your textfield is pointing to nil.
The outlet doesnt get connected until your controller's view property is accessed, ie the view is displayed.
What you could do is give the settings viewController a handle to your calculating viewController and let it set a public property on the calculating viewController that represents the rate.
This is a common pattern - delegation - where one viewController (settingsViewcontroller) calls a method on its delegate (calculating viewController).
You wouldn't need the settingsViewcontroller property in your calculating viewController then, but just instantiate a new settings viewController every time you want it to be brought up, giving it a reference to your calculating viewController.
Another possibility - maybe even better - is to define a model object that does calculation and takes care of the rate it needs to calculate. Then you could give your settingsViewcontroller a reference to that model object (probably instantiated in your
other viewController), so that it can change the rate on it.
PS: also re think how you instantiate viewControllers generally. The designated initialiser is -initWithNibName:bundle: - so usually, you wouldn't just alloc/ -init them.
If you use storyboards (you probably should!), use storyboard's -instantiateViewControllerWithIdentifier: or use the above mentioned designated initialiser.
Thanks in advance for your help!
In the main ViewController.m of my project I am adding a customized tableView like so:
messageController = [[MyMessagesController alloc] initWithStyle:UITableViewStylePlain];
[self addChildViewController:messageController];
[self.view addSubview:messageController.view];
Then, in the MyMessagesController.m section tableView didSelectRowAtIndexPath: I'd like to write code that would take effect in the ViewController.m where it was created and added as a childViewController.
How can I access the functions of the ViewController.m from MyMessagesController.m?
Can I make it a delegate somehow so I could call [delegate functionName];?
Could I pass information back to the ViewController.m? About which of the rows in table was selected by sending through an NSString or NSArray or anything?
Yes, use a delegate, if you are unsure how best to accomplish this, here is a good reference from Apple about delegate programming
Got it figured out, here's how you turn one viewController into a delegate for another:
In the .h of the parent controller -
#interface ViewController : UIViewController <NameOfDelegate> {
In the .m of the parent controller, once you create the new view -
newViewController.delegate = self;
and also:
- (void)functionToCall:(id)sender {
NSLog(#"Function Called!!!");
}
In the .h of the newViewController you're adding -
#protocol NameOfDelegate;
#interface newViewController : UIViewController/OR/TableViewController {
id <NameOfDelegate> delegate;
}
#property (nonatomic, strong) id <NameOfDelegate> delegate;
#end
#protocol NameOfDelegate
- (void)functionToCall:(id)sender;
#end
in the .m of the newViewController -
#implementation newViewController
#synthesize delegate;
and when you're ready to call the function in your delegate -
[delegate functionToCall:self];
My problem is that in ProductNote class UIButton Action I did initWithNibName Notes Class to show the popOver with a UITableView in Notes Class. I am fetching data from sqlite and then load it to UITableView in tableViewDidSelectRowAtindexPath:. I got the selectedNote and creates object of ProductNote Class to call selectedNote instance method that only set IBOutlet's textview.text but its nil. Thats my problem, Below is the code please help me to knowing why i face this type of issue. I am using Xcode 4.3.3 and not using ARC. Manually I defined dealloc method on every ViewController
//**ProductNote.h Class****************************
#import <UIKit/UIKit.h>
#class Notes;
#interface ProductNote : UIViewController<UIPopoverControllerDelegate>
{
UIPopoverController *popOverController;
UITextView *txtmesssagenotes;
Notes *objNotes;
}
#property (retain, nonatomic) IBOutlet UITextView *txtmesssagenotes; //It is Connected to ProductNote.xib
#property (retain,nonatomic) UIPopoverController *popOverController;
#property (retain,nonatomic) Notes *objNotes;
-(IBAction)presentPopOver:(UIButton*)sender;
-(void)selectedNote:(NSString*)note;
#end
//ProductNote.m Class
#implementation ProductNote
#synthesize txtmesssagenotes,popOverController,objNotes;
-(IBAction)presentPopOver:(UIButton*)sender
{
self.objNotes=[[Notes alloc]initWithNibName:#"Notes"bundle:nil];
UIPopoverController *popOver=[[[UIPopoverController alloc]initWithContentViewController:objNotes]autorelease];
self.popOverController=popOver;
self.popOverController.delegate=self;
self.popOverController.popoverContentSize=objNotes.view.frame.size;
CGRect rect=[self.view convertRect:objNotes.view.frame fromView:self.view];
rect.origin.y+=110;
rect.origin.x+=23;
[self.popOverController presentPopoverFromRect:rect inView:self.view permittedArrowDirections:0 animated:YES];
//Then I get popOver with tableview
}
-(void)selectedNote:(NSString*)note
{
self.txtmesssagenotes.text=note;
//Here I am getting txtmesssagenotes=0X0 or nil. Using self or without self Please tell me the reason.
}
- (void)dealloc {
[txtmesssagenotes release];
[popOverController release];
[objNotes release];
[super dealloc];
}
#end
#interface Notes : UITableViewController
{
NSMutableArray *arrNotes;
NSString *selectedNote;
UITableView *tblNotes;
}
#property(nonatomic,retain)NSMutableArray *arrNotes;
#property(nonatomic,retain)NSString *selectedNote;
#property(nonatomic,retain)IBOutlet UITableView *tblNotes;
#end
//Actually I skip the other methods that makes larger program to read . other methods only used to fetch data from sqlite and fill up the arrNotes that is used to fill all rows in the tableview.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.selectedNote=[self.arrNotes objectAtIndex:indexPath.row];
//Here self.arrNotes valid object and contains the data, and assigns NSString object to self.selectedNote
ProductNote *productNote=[[ProductNote alloc]init];
[productNote selectedNote:self.selectedNote]; //after calling selectedNote i goto to selectedNote implementation or definition above.
[productNote release];
}
-(void)dealloc
{
[arrNotes release];
[selectedNote release];
[tblNotes release];
[super dealloc];
}
It is because just as you stated that txtmesssagenotes was created using IB. And you never presented your ProductNote UIViewController instant productNote. So the txtmesssagenotes was never allocated and initialized properly.
Are you calling selectedNote: before your view has loaded?
Just because you created a view controller with a xib doesn't mean that it loads all your subviews immediately.
The xib is parsed and all your views are created the first time that the view controller's view property is asked for - this is usually just before it appears for the first time.
Until then, all the IBOutlet connections will be nil.
You need to store the text for your note as an NSString property of you view controller. Then, inside either viewDidLoad you need to call self.txtmesssagenotes.text=self.note; - viewDidLoad is automatically called just after your view is loaded so you are guaranteed to have all your IBOutlets set by then.
You shouldn't rely on your view objects to store the state of your app (in this case, the text of the note). If a low memory warning is received while your view controller isn't visible then all your view objects will be deleted - they will only be recreated when your view controller becomes visible again - if you are storing your note string inside a UITextView then it might no be there the next time you ask for it :) You should always store data in a property of your view controller (or somewhere else you control) and create your views using it.
im new to IOS and Objective-C and the whole MVC paradigm and i'm stuck with the following.
I am working on (replica) Contact app, also available in iphone as build in app. i want to pass data through another view controller and the data is pass (null) :(.
My Question is, How do I transfer the data from one view to another?
As most the answers you got, passing data between one controller and another just means to assign a variable from one controller to the other one.
If you have one controller to list your contacts and another one to show a contact details and the flow is starting from the list and going to detail after selecting a contact, you may assign the contact variable (may be an object from the array that is displayed in your list) and assign it to the detail view controller just before showing this one.
- (void)goToDetailViewControllerForContact:(Contact *)c
{
ContactDetailViewController *detailVC = [[[ContactDetailViewController alloc] init] autorelease];
detailVC.contact = c;
[self.navigationController pushViewController:c animated:YES];
//[self presentModalViewController:detailVC animated:YES]; //in case you don't have a navigation controller
}
On the other hand, if you want to insert a new contact from the detail controller to the list controller, I guess the best approach would be to assign the list controller as a delegate to the detail one, so when a contact is added the delegate is notified and act as expected (insert the contact to the array and reload the table view?).
#protocol ContactDelegate <NSObject>
- (void)contactWasCreated:(Contact *)c;
// - (void)contactWasDeleted:(Contact *)c; //may be useful too...
#end
#interface ContactListViewController : UIViewController <ContactDelegate>
#property (nonatomic, retain) NSArray *contacts;
...
#end
#implementation ContactListViewController
#synthesize contacts;
...
- (void)goToDetailViewControllerForContact:(Contact *)c
{
ContactDetailViewController *detailVC = [[[ContactDetailViewController alloc] init] autorelease];
detailVC.contact = c;
detailVC.delegate = self;
[self.navigationController pushViewController:c animated:YES];
//[self presentModalViewController:detailVC animated:YES]; //in case you don't have a navigation controller
}
- (void)contactWasCreated:(Contact *)c
{
self.contacts = [self.contacts arrayByAddingObject:c]; //I'm not sure this is the correct method signature...
[self reloadContacts]; //may be [self.tableView reloadData];
}
...
#end
#interface ContactDetailViewController : UIViewController
#property (nonatomic, assign) id<ContactDelegate> delegate;
...
#end
#implementation ContactDetailViewController
#synthesize delegate; //remember to don't release it on dealloc as it is an assigned property
...
- (void)createContactAction
{
Contact *c = [[[Contact alloc] init] autorelease];
[c configure];
[self.delegate contactWasCreated:c];
}
...
#end
Technically, you shouldn't!
The whole idea is not for "views" to control what happens to the data.
What you want to do is to pass data between controllers (which I imagine is exactly what you are planning to do anyway).
You can have shared model (an instance of an object that both view controllers would access) keeping the data you want to share,
You can use notifications to pass data (it is best suited for certain cases).
You can write something to disk and read it again later.
You can use NSUserDefaults.
You can use KeyChain.
...
The best way is:
declare the appropriate #property in the second view controller
when you create it, simply set the property with
viewController.property = valueYouWantToPass;
I'm a big fan of delegates and protocols.
And in some occasions use a Singleton pattern.
two ways to pass/share data between view controller
create an object and sent the data like this
QGraduteYr *tableverify=[[QGraduteYr alloc]initWithStyle:UITableViewStyleGrouped];
tableverify.mystring=myString
[self.navigationController pushViewController:tableverify animated:YES];
another method is stor it in the delegates and use it via shared delegates
MedicalAppDelegate *appdelegate=(MedicalAppDelegate *)[[UIApplication sharedApplication]delegate];
appdelegate.collnameStr=collStr;
and ust this appdelegates value whereever you need
I am trying to call up a modal table view controller using presentModalViewController but I am not sure what to do about the delegate. The following code gives me an error:
MyRidesListView *controller = [[MyRidesListView alloc] init];
controller.delegate = self;
[self presentModalViewController:controller animated:YES];
[controller release];
Error:
Request for member 'delegate' is something not a structure or union
Now, I realized there is no delegate property in my MyRidesListView class. So, how would I add a reference to my delegate there? What am I missing here?
Generally delegates are properties defined as such:
id<NameOfDelegateProtocol> delegate;
And:
#property (nonatomic, assign) id<NameOfDelegateProtocol> delegate;
EDIT: You said your parent class is UITableViewController. You may have wanted to do this:
controller.tableView.delegate = self;
Why do you think you need a delegate? Why not just remove the "controller.delegate = self" line. Otherwise you need to implement a delegate system the way I outline below or else make MyRidesListView a subclass of a viewcontroller that implements delegates.
It looks like you cut and pasted some sample code that uses a delegate, then substituted your own viewcontroller that doesn't provide a delegate. If you don't know what the delegate is for, then just delete that line.
I'll cut and paste some actual code from one of my test programs to show you how it's done:
from the Interface file:
Add a delegate instance variable to your class and make it a property so you can use the "blah.delegate = foo" syntax to set it.
#interface BAPClient : NSObject {
CGSize imageSize;
id <BAPClientDelegate> delegate;
}
#property (nonatomic, readonly) CGSize imageSize;
#property (nonatomic, assign) id <BAPClientDelegate> delegate;
#end
// define the protocol spoken. (what the delegate must implement)
#protocol BAPClientDelegate
- (void)addTile:(BAPTile *)tile;
#end
in the implementation, you must call the delegate at the appropriate time:
- (void)deliverTile:(BAPTile *) tile {
NSLog(#"%s tile=%p",__FUNCTION__,tile);
if ([self delegate])
[[self delegate] addTile:tile];
[tile release];
}
Try to set the delegate object by the setter
[controller setDelegate:self];
This often works wonders.