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.
Related
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;)
First off, I despise singletons with a passion. Though I should probably be trying to use one, I just don't want to. I want to create a data class (that is instantiated only once by a view controller on loading), and then using a different class, message the crap out of that data instance until it is brimming with so much data, it smiles.
So, how do I do that? I made a pointer to the instance of the data class when I instantiated it. I'm now over in a separate view controller, action occurs, and I want to update the initial data object. I think I need to reference that object by way of pointer, but I have no idea how to do that. Yes, I've set properties and getters and setters, which seem to work, but only in the initial view controller class.
There's no need to use a singleton if you don't like the pattern or if it doesn't fit. Assuming you are creating your second view controller in the first one, just declare an ivar and property for your model object in your second view controller and when you instantiate it, assign the model object to this property.
Make a global variable for your object and store it there on creation. You can wire that up in the init method (probably bad style), or from the caller or via interface builder. Just make your variable known in the files that use it.
Or - well - use some kind of singleton pattern and get the instance directly from that class. Looks much cleaner.
Seriously use a singleton. In case you don't like them cause you don't know the code:
#interface Order : NSObject {
NSMutableArray *order;
}
#property (nonatomic, retain) NSMutableArray *order;
+ (Order *)sharedInstance;
#end
#import "Order.h"
#implementation Order
#synthesize order;
+(Order *)sharedInstance {
static Order *myInstance = nil;
#synchronized(self) {
if(!myInstance) {
myInstance = [[Order alloc] init];
}
}
return myInstance;
}
-(void)dealloc {
[order release];
[super dealloc];
}
#end
Um. Hello. Isn't Core Data a good enough framework for you? It allows you to have a single persistent store and multiple contexts to manage updates and merging of changes in response to notifications. I may be out of line here, but seeing how you start the question with a strong opinion about a well accepted pattern in your first question indicates that you have not spent much time discovering the ways in which the objective c runtime and Foundation classes in iOS can collaborate to achieve a task. In any software, one object and only one object owns a specific resource. You should embrace singletons. I suggest you spend some time reading http://www.cocoadesignpatterns.com/. Oh yeah, check out the meaning of KVO.
Why not make it a property of your app delegate? That way you don't have to use the singleton pattern but you are taking advantage of Apple's already existing usage of the singleton pattern.
Don't forget that Objective-C is a superset of C.
Basically, a data class is a plain C struct.
If you want to access a variable of that class from another class, make it global.
mydata.h:
struct MyData {
// Whatever data fields that you need, e.g.:
NSInteger value;
};
extern struct MyData mydata;
mydata.c:
struct MyData mydata = {
// Whatever initial value that you find relevant, e.g.:
.value = 42;
};
So I have a couple properties that are common across several of my Core Data entities. Notes, URL, etc.
I have a single view controller for editing this information, and I pass the Entity to it as a NSManagedObject
#property (nonatomic, retain) NSManagedObject *editedObject;
#property (nonatomic, retain) Thing *thing;
#property (nonatomic, assign, getter=isEditingThing) BOOL editingThing;
And to actually get the object in a state I could make changes I would
if([editedObject isKindOfClass:[Thing class]]) {
thing = (Thing *)editedObject;
editingThing = YES;
}
That way I can set the values on thing and save the managedObjectContext like normal.
However, this is crashing my application, but only if I enter and exit the View Controller for editing the field over and over again.
Which brings me to my question: What's the best way to handle using a single view controller to edit a common field on more than one Core Data Entity? Should I just set the Boolean and populate the correct entity property when I call the View Controller?
I imagine your thing property is not getting retained because you're not using your accessor to set it. Change:
thing = (Thing *)editedObject;
to:
[self setThing:editedObject];
As to your bigger question, I think you need to provide more context. What do you mean by "edit a common field on more than one Core Data Entity"?
If both entities have a property called notes then change your property:
#property (nonatomic, retain) id managedObject;
Then when you go to set it, you can just set it without caring what the object is:
[[self managedObject] setNotes:...];
As long as whatever you set managedObject to responds to the property notes it will work just fine. If not, it will error out and you can test that easily.
You could go a more paranoid route and test if it responds first:
if ([[self managedObject] respondsToSelector:#selector(notes)]) {
but that is over protective since the only thing you would want to do that point is crash anyway.
This seems to be a common problem, but I can't figure out anything from the answers I've seen so far. I have an iPhone app that uses a subclass of NSMutableArray to store objects, plus some additional properties. The subclass is skhCustomArray. The subclass initializes fine, with no objects in the skhCustomArray, and I assign it to the the property of my view controller, which is a pointer to an skhCustomArray.
prescriptionListVC* newPrescList = [[prescriptionListVC alloc] initWithNibName:#"PrescriptionList" bundle:nil];
newPrescList.curPersonPrescriptions = [personDetails objectAtIndex:0];
That works fine. Yet when I push my view managed by my view controller onto the navigation controller stack, the count method in the numberOfRowsInSection method crashes the app, see below.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [curPersonPrescriptions count];
}
What could be causing this? How can a valid custom array, with no objects, not return a valid count? Where am I going wrong? Thanks.
Subclass of NSArray? You're aware that NSArray is a class cluster, and is therefore somewhat difficult to subclass, right? In fact, it's so fraught with danger that the NSArray documentation has a whole section dedicated to what you need to do in order to subclass it.
I'll bet that that's the source of your woes.
You almost certainly don't need to subclass NSMutableArray in this situation. Instead, make a new class which has an array as a property, along with the extra properties you desire.
When you subclass NSMutableArray, you need to implement some mandatory methods like count, addObject:, insertObjectAtIndex etc. This is what we call as class cluster.
If you want to add some more feature/behavior to already implemented object then you can write a "category" instead of "subclassing" it.
If you want to subclass it, then you have to implement all those methods which your are going to use so better write a category of NSMutableArray and extend the feature what you want and use the NSMutableArray object only. This will solve your problem and also this is the easy and almost right way to add new behavior to already existing class.
I want an array that gets data from one class to be avaliable to me in another class with the same data.
If we declare an array on the applicationDelegate class.
Then declare an object of applicationDelegate in both classes.
And assign the array into appDelegate.array from one class, will i be able get the array across the classes?
I'm with Mike. Leave the App Delegate out of it.
You're in control of when and how your objects are instantiated. If it's important for more than one class to have access to the same data, hand off the data, or a means of getting at your data, as you create instances of the dependent class.
An example, given classes WVParent and WVChild.
WVParent has a property, someArray, which is the array your other objects need:
#interface WVParent : NSObject {
NSArray *someArray
}
#property (nonatomic, retain) NSArray *someArray;
#end
Then you have WVChild, which itself has a property called parentObject:
#class WVParent;
#interface WVChild : NSObject {
WVParent *parentObject;
}
#property (nonatomic, assign) WVParent *parentObject;
#end
Assuming the parent is creating the child instance, you'd allocate it and assign the parent:
WVChild *child = [[WVChild alloc] init];
child.parentObject = self;
Then the child instance can access someArray by:
self.parentObject.someArray
This is just one option. You could also just pass the array itself to the child, assuming the data is static and unlikely to change over the course of the application session.
This way, instead of having a source of data living somewhere in the App Delegate, it lives within a class more appropriately responsible for its creation, maintenance and vending. If you've got a class that pops into existence and can only reach the rest of your app by getting the app delegate, you might want to put a little more thought into your architecture.
More on dependency injection here:
http://en.wikipedia.org/wiki/Dependency_injection
Yes, you could declare a member/property on your applicationDelegate class, although you should try to follow the Single Responsibility Principle and make sure you don't end up stuffing lots of miscellaneous shared stuff in your app delegate (which happens a lot with iPhone code).
Another alternative would be to inject the array into the objects' constructors when you create them.
It's hard to know the best solution in terms of design without knowing what this data is and where it really belongs.