this question is extremely basic and I apologize if the answer should be staring me in the face.
So: I have a UITableView and another viewController. I'm updating some information for CoreData in the UITableView's .m file. In order to do this I need the text in a text field in the other viewController.
So being rather inexperienced, I tried:
[entity setName:[NSString stringWithFormat:#"%#", viewController.entityNameInputField.text]];
I've renamed a few things for clarity. Entity is my entity (no kidding), the attribute I'm setting is called Name, viewController is where the textField is and the text field is called entityNameInputField.
I'm a lot more interested in why this sort of thing doesn't work than what the solution to my problem might be. Why can't I just get the text from the text field in this way?
I suppose to word everything differently:
If I'm in Class A and I want to access the value of say, an NSString in class B, why can't I just use ClassA.TheString or [ClassA TheString] .... from what I've managed to understand, I'm sending these objects getter methods and I can't seem to see why this doesn't work...
Any help would be appreciated :)
Some more code would be useful to see what might be causing your confusion, but if you have declared properties in your classes the associated instance variables (or have manually declared accessors) then you will be able to access the associated ivars.
So, something like this (below) will work fine.
MyClassA * instanceOfA = [[myClassA alloc] init];
MyClassB * instanceOfB = [[myClassB alloc] init];
instanceOfA.myProperty = instanceOfB.myProperty;
As far as I understand, this is not a problem of accessing values from another class. Assuming that both view controllers are retained in memory, you can always call each other's getters/setters.
I assume that you've got a UITableViewController named A and then you're presenting a custom UIViewController named B with a text field. The user taps something in the text field and you want this text to go back to your table view controller. If inside B you have a valid reference to A, you can then call it's setter for the appropriate variable, and then dismiss the custom controller.
For example, in B's header you could have:
#interface MyViewController : UIViewController {
id tableViewController;
}
#property (nonatomic, retain) id tableViewController;
then in A, when you're creating B:
MyViewController *B = [[MyViewController alloc] init];
B.tableViewController = self; // this passes a reference to A
so when you're in B and the user has entered input in the text field, you could do something like
[self.tableViewController setVarname:textField.text];
assuming there's a varname variable in A, and a textField in B.
You can also gain access to A without having to create a separate variable, by calling
[self parentViewController]
if that suits you better.
Related
I've got a custom class BoardMatchData, containing information about a chess match.
I've also got a custom UIViewController named BoardViewController, which is alloc'ed when a user selects a match from a list.
Here's a snippet from where I create the BoardViewController, set its delegate, and then set the new BoardMatchData:
Games *selectedGame = [[self fetchedResultsController] objectAtIndexPath:indexPath];
if (!self.bvc) {
NSLog(#"Alloc-ing a BVC");
self.bvc = [[BoardViewController alloc] init];
self.bvc.delegate = self;
}
[self.bvc setNewBoardMatchData:[MasterViewController boardMatchDataFromGame:selectedGame]];
When debugging, I can see this method setNewBoardMatchData being called, and it has valid data coming into it. However, later on within the BoardViewController, this boardMatchData always seems to be nil. Here's the setNewBoardMatchData method:
- (void)setNewBoardMatchData:(BoardMatchData *)newBoardMatchData {
NSLog(#"BMD is being set");
if (self.boardMatchData != newBoardMatchData) {
self.boardMatchData = newBoardMatchData;
[self configureView];
}
if (self.masterPopoverController != nil) {
[self.masterPopoverController dismissPopoverAnimated:YES];
}
}
And within BoardViewController.h, I just have an instance variable:
BoardMatchData *boardMatchData;
A method declaration:
- (void)setNewBoardMatchData:(BoardMatchData *)newBoardMatchData;
And then at the top of BoardMatchData.m, I have:
#interface BoardViewController ()
#property (strong, nonatomic) UIPopoverController *masterPopoverController;
#property (nonatomic,retain) BoardMatchData *boardMatchData;
- (void)configureView;
#end
#synthesize boardMatchData = _boardMatchData;
...my intent here was to make sure that the setter was only ever being called by itself, and not by some other object.
Is there something wrong with how I'm trying to set self.boardMatchData?
I'm not doing any retains/releases because I'm using ARC.
Edit:
Caleb - I did that #synthesize statement as part of my flailing around trying to find this bug. Originally I had:
#synthesize boardMatchData;
... which I just switched back to. The behaviour is the same; self.boardMatchData always ends up nil, even after I've set it.
I think now I have only one ivar, boardMatchData, and I'm always accessing it through self.boardMatchData.
How do I prevent this from becoming nil?
#synthesize boardMatchData = _boardMatchData;
This says that the ivar the accessors should use is _boardMatchData, but you've also got an ivar named boardMatchData. If you're using the accessors, only _boardMatchData will be set. Since Objective-C automatically clears any ivars when your object is created, theboardMatchData ivar will always be nil.
Your comment (that you posted as an answer which you shouldn't do) suggests that you work on two different instances.
Here are a couple of possible reasons for this:
self.bvc is just assign property instead of retain.
You load one from within a nib and one is constructed in your code
(as shown) - this is probably the most like one. Maybe you just
forgot to wire up the outlet.
Your set self.bvc to nil somewhere so that you keep creating new
instances.
Aha; I found this question with an almost identical problem:
Objective-C – Retained property after being set is nil?
There was no conclusion to that one, but the last suggestion was to "try logging the address of self in -viewDidLoad and -viewWillAppear and -queueFinished. Something like NSLog(#"self is %p", self); and making sure they are the same."
I did that myself and now see that in initWithNibName/configureView/setNewBoardMatchData, I'm seeing one pointer for self, and then when viewDidLoad runs, I'm getting a different one!
I'm not sure how or why yet, but this clearly appears to be the problem (a new instance of my class is being instantiated AFTER I've set boardMatchData).
Edit:
While the above led me to the path of finding this bug (I was getting multiple versions of my BoardViewController), it's not the complete answer. I wanted to add here should anyone find this and be in the same position.
I was actually using storyboarding in Xcode4, and the Apple provided master-detail template with Core Data.
When I was instantiating my detail controller (BoardViewController), I was doing an alloc/init. I should have simply been referencing [segue destinationViewController], as the segue already instantiated a version for me.
I did not provide enough context to actually get to the root
Are there alternatives to "delegates" to pass back data from one controller to another?
Just seems like a lot of work implementing a delegate just to pass back the result from a child controller, back to the parent controller. Is there not another method? Are "blocks" one answer, and if so some example code would be great.
Delegates aren't a lot of work, aren't a lot of code, and are commonly the most appropriate solution. In my opinion they're neither difficult nor messy.
Five lines of code in the child's interface. Before #interface:
#protocol MyUsefulDelegate <NSObject>
- (void)infoReturned:(id)objectReturned;
#end
Inside #interface:
id <MyUsefulDelegate> muDelegate;
After #inteface's #end:
#property (assign) id <MyUsefulDelegate> muDelegate;
One line of code in the child's implementation:
[[self muDelegate] infoReturned:yourReturnObject];
One addition to an existing line of code in the parent's interface:
#interface YourParentViewController : UIViewController <MyUsefulDelegate>
Three lines of code in the parent's implementation. Somewhere before you call the child:
[childVC setMuDelegate:self];
Anywhere in the implementation:
- (void)infoReturned:(id)objectReturned {
// Do something with the returned value here
}
A total of nine lines of code, one of which is merely an addition to an existing line of code and one of which is a closing curly brace.
It's not as simple as a returning a value from a local method, say, but once you're used to the pattern it's super straightforward, and it has the power of allowing you do do all kinds of more complex stuff.
You could use many ways:
Calling a method of the super controller, needs casting maybe
Notifications
Simple Key-Value-Observing
Core Data
Example for for 1.
interface of your MainViewController: add a public method for the data to be passed
- (void)newDataArrivedWithString:(NSString *)aString;
MainViewController showing ChildController
- (void)showChildController
{
ChildController *childController = [[ChildController alloc] init];
childController.mainViewController = self;
[self presentModalViewController:childController animated:YES];
[childController release];
}
Child Controller header / interface: add a property for the mainViewController
#class MainViewController;
#interface ChildController : UIViewController {
MainViewController *mainViewController;
}
#property (nonatomic, retain) MainViewController *mainViewController;
Child Controller passing data to the MainViewController
- (void)passDataToMainViewController
{
NSString * someDataToPass = #"foo!";
[self.mainViewController newDataArrivedWithString:someDataToPass];
}
KVO or notifications are the way to go in many cases, but delegation gives a very good foundation to build upon. If you plan on extending the relationship between the view controllers in the future, consider using delegation.
Blocks are not really relevant to the above, but in short - it is a technique introduced with iOS 4, where you pass around blocks of code as variables/ parameters. It is very powerful and has many uses. For example, here is how you enumerate objects in an array using a block:
[someArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
NSLog(#"obj descriptions is - %#", [obj description]);
}];
The part from the ^ until the } is a block. Note that I've passed it in as parameter. Now, this block of code will be executed for every object in the array (i.e. output will be the description of each object).
Blocks are also very efficient performance-wise, and are used heavily in many new frameworks.
Apple's blocks beginners guide is quite good.
Check out NSNotificationCenter — NSNotificationCenter Class Reference
Folks pay a lot of attention the the V and the C in MVC, but often forget the M. If you've got a data model, you can pass it from one controller to the next. When one controller makes changes to the data stored in the model, all the other controllers that share the same model will automatically get the changes.
You might find using a singleton is practical. Just use it as a central storage for all your shared data.
Then throw in saving the state of your application too;)
This is a noob question. If I have a ViewController and inside that class I have an object called UserInfo and other ViewController, lets just call it X,Y,Z, etc..etc.
What do I need to do so that those X,Y,Z can use the information of UserInfo?
Well I can have another info called UserInfo inside X,Y,Z and pass UserInfo inside, but I don't think this is good OOP technique. I think inheritance is needed here... but I don't think that it is right too, as the only commonality they have is only the UserInfo
View controllers are just objects, you have to remember that.
If you're in your first view controller and it has a property NSString *aString to pass it to the next view controller you just make the second view controller's NSString *aString property equal to the first one.
Basically do this:
MyNextViewController *viewControllerTwo = [[MyNextViewController alloc] initWithNibName:nil bundle:nil];
[viewControllerTwo setAString:self.aString];
if you subclass all you inherit are the properties, and not their values.
The Situation
I populate my TTTableViewController with a TTListDatasource(with TTModel).
My didSelectObject methods opens a TTViewController.
The Question
I d like to show more informationen in the TTViewController.
1.Question: Should i hand over the datasource (and run a new query) or should i hand over a specific VO (with all information)?
2.Question: How do I acces the datasour in the new TTViewController?
thanks,
In your TTTableViewController's didSelectRowAtIndexPath: definition, use your TTListDatasource to grab a reference to the corresponding object. Then pass it to your TTViewController. This is best accomplished by using a property of the TTViewController so that you don't have to worry about retain/releasing'ing.
#property (nonatomic, retain) TTModel *model;
and then in the didSelectRowAtIndexPath:
TTModel *model = //get the correct model
TTViewController *vc = [[TTViewController alloc] initHoweverYouWant];
vc.model = mode;
A few tips: TTViewController doesn't sound terribly descriptive, since you're (correctly) prefixing your classes. Maybe TTPhotoViewController or something may be more descriptive. Always err on the side of verbosity :)
Additionally, I find it pretty useful to just have my UITableViewController subclass implement the UITableViewDataSource protocol instead of creating a separate object, unless the exact same datasource is going to be used elsewhere for the exact same reason. This works more nicely with NSFetchedResultsController if you're using core data, as well.
In one classes .h i have
NSMutableArray *rountines;
and in another classes .m i want to do something like this
[routines addOject:#"Hello];
the other class is an ModalViewController type set up.
So, in essence i'd like the .m file of the MVC to be able to read, and edit and do other things to the ivars i declare in the header.
Cheers,
Sam
edit
Another example, which is similar to what im trying to achieve is like an edit screen.
You can't share ivars between classes really. ivar stands for instance variable, and that is a variable that belongs to some particular instance of an object. The way to solve this problem is to allow other objects to access this object's state. This is most commonly done through setter and getter methods. Objective-C 2.0 makes this easier by providing the #property and #synthesize keywords.
If you had access to the object that had the routines array, you could access it through its property (getter method) like this:
[[someObject routines] addObject:#"hello"];
you can either do this by making the ivars you want to share globals (in which case they would be ivars of the singleton class or app delegate class) or by passing a reference to the class you want to modify the ivars of as an argument to a method of the ModalViewController class:
#implementation ModalViewController
......
-(void)addObjectToRoutinesFromClass: (MyClass *)myclass {
[myclass.routines addObject:#"Hello"];
}
#implementation MyClass
......
ModalViewController *myModalViewController = [[ModalViewController alloc] init];
[myModalViewController addObjectToRoutinesFromClass:self];
#end