I am using Xcode 4.3 and need to know the parent view controller of the current view.
I am also using storyboard.
self.parentViewController always returns nil.
I saw different answers to save the parent view controller in AppDelegate class as a property. E.g., To create a variable: UIViewController parentViewController in AppDelegate and in the code write:
appDelegate.parentViewController = self;
I am afraid that it will consume memory - as this is a heavy object.
What is the best approach to know aretnViewController when using story board?
Whether or not an object is "heavy" does not matter as long as you store only a reference to it (in your case in the application delegate). Creating a second object would make a difference, but the line
appDelegate.parentViewController = self;
does not do that, it merely stores a reference to the object.
I know that this does not answer your direct question, but I think you should go ahead with the "store a reference in the app delegate" approach.
Related
I'm very comfortable with writing iOS apps, but OS X unexpectedly seems somewhat alien.
Here's the question upfront (read on for context):
When my application launches using the .xib set in the Main Interface field under the deployment info of my apps target, why does the AppDelegate get instantiated after the ViewControllers?
Context (no pun intended):
The reason I ask is because I'm using Core Data (spare me any heckling for this decision), and typically you keep a pointer to the MOC (Managed Object Context) in AppDelegate. One of my controllers is trying to get this MOC instance variable but the AppDelegate instance isn't around yet and therefore my app doesn't present data just after launch.
The AppDelegate and the two ViewControllers are in the .xib. The VCs are hooked to views inside a split view. They're trying to use the MOC in viewDidLoad to make queries. They are accessing the AppDelegate like this:
let delegate = NSApplication.sharedApplication().delegate as AppDelegate
let moc = delegate.managedObjectContext
This will crash as the .delegate property of the sharedApplication() returns nil.
I tried making an NSWindowController from the .xib in applicationDidFinishLaunching and removing the .xib from the Main Interface field, but then applicationDidFinishLaunching doesn't get called at all.
I've ensured that all the connections in IB for from the Application and the Files Owner (NSApplcation) delegate IBOutlets to the AppDelegate have been made.
UPDATE - 31/03/15
Stephen Darlington's answer below offers a good solution for my/this case. And as I understand it's actually better to setup the MOC in the way he's suggested.
If a correct answer arrives that explains why the AppDelegate is being instantiated at some later time in the launch process, I'll mark it correct instead of Stephen's. Thanks Stephen!
The "easy" solution would be to have managedObjectContext create a MOC if one doesn't exist (i.e., change it from a property to a method). That way which ever code gets there first the stack will be available.
(I'll spare the lectures about both creating the Core Data stack in the app delegate and accessing the app delegate like that!)
Here's another option without having to subclass NSApplication:
Don't put your view controllers in the .xib that you set as the Main Interface, just have the Main Menu (menu bar), AppDelegate and Font Manager in there.
Then make your view controllers in other .xibs.
Then in the applicationDidFinishLaunching method init your view controllers from their .xib files.
I also faced this issue with setting up Parse. To get around it, I simply subclassed NSApplication and set it as the Principle class in the Info.plist. In your NSApplication subclass, override the init methods and initialise Parse or anything else you need to, there.
I am new with Objective-C so apologies for a dumb question.
I am opening an "options" view controller from my main view controller. Both are built in the storyboard. Before and after presenting the options controller I need to stop and start a timer on my main view controller. After the options controller is closed (a button calls dismiss) I need to send some info back to my main controller or at least let my main controller know that it needs to refresh some values.
MAIN QUESTION
What's the best way of presenting a view controller and executing some presenter's methods before and after opening?
WHAT I'VE TRIED
I found a few ways to do it, but they are all cumbersome and I assume that there must be some plausible way of doing it.
Ideally I'd like to use the segue I set up in the storyboard between the two controllers.
I managed to call the options controller programmatically by accessing the storyboard and calling instantiateViewControllerWithIdentifier. It worked but looks a bit complex.
I was not able to find a delegate method on the UIViewController to handle the dismiss event
When I was trying to access the main controller in the options controller via presentingViewController and downcasting it, I got a linkage error by including my .h file twice (not sure what are the Obj-C standards of using #define).
Appreciate your help...
For communication between ViewControllers that are weakly linked, you could use the NSNotificationCenter:
https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/Reference/Reference.html
Here you can send a message to all ViewControllers listening, which need to process some changes (for example an option to change the font size).
It's really easy to implement and it keeps certain ViewControllers less dependent on each other.
All of this can be done quite easily with storyboard and NSNotificationCenter, and NSCoding. In the viewDidLoad method of your main controller, put this code:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveNotification:)
name:#"Update"
object:nil];
Then create this method in the same controller:
(void)receiveNotification:(NSNotification*)notification
{
//...
}
When you want to make the main controller update from the options controller:
[[NSNotificationCenter defaultCenter] postNotificationName:#"Update" object:self];
Also, I would suggest using NSArchiving for Basic Data Persistence. Just found this tutorial, looks pretty good.
http://samsoff.es/posts/archiving-objective-c-objects-with-nscoding
Basically, create an object that can store information, code it using nscoding, and then uncode it whenever you need it. It has worked great for me.
Hope that helps!
MAIN QUESTION What's the best way of presenting a view controller and executing some presenter's methods before and after opening?
Just in case the answers above are a bit more involved than you'd like, I'll suggest that the easiest way to execute a presenter's methods before opening is to do so in the presenter's prepareForSegue method. If you need to send data to the destination view controller, you can access its properties this way:
ViewController *destinationVC = [segue destinationViewController];
An easy way to execute the presenter's methods after opening would be:
ViewControllerSubclass *previousVC = [self presentingViewController];
And then use the class or instance to execute your class or instance methods. You could do this in the destination's viewWillAppear.
Sorry if you already knew all this; it's often difficult to surmise what level of complexity is needed.
I have run into this with almost every app I have on the market. Difference is I have never decided to go down the storyboard path.
The way I have always been able to accomplish this is to provide accessor functions between the controllers. You get past the linker issue by defining the cross defined controller as simply a UIViewController type within your options view header, then including the main view controller' header only in the .m file. Now when you call a main view controller routine from your options view, you will have to cast it to the type of your main view controller!
You will also have to provide a routine in your options view that will allow you to set the variable that will hold a pointer to your main view controller to self.
Example for your optionsView
#interface optionsViewController : UIViewController{
UIViewController * myReactiveMainViewController;
}
-(void)setMyReactiveMainViewController:(UIViewController *)controller;
No in the .m file for the optionsView
#import "myMainViewController.h"
-(void)setMyReactiveMainViewController:(UIViewController *)controller{
myReactiveMainViewController = controller;
}
In any other call back to the main view controller you will have to do this:
-(void)returnToMain{
[(myMainViewController *)myReactiveMainViewController someCall:variable];
}
This example would of course assume that your myMainViewController implements a method called "someCall" that take on input parameter.
Thanks for replies.
I ended up with
Calling prepareForSegue to execute pre-transition code
Calling performSelector on presentingViewController when releasing presented view controller.
I am sure other suggestions would work too.
I'm working with a Navigation Controller based iOS app. There are multiple tableView screens that pull and save data from a Core Data persistent store. Most of the data for the different table views comes from NSFetchedResultsController instances or NSFetchRequests.
The app works as intended but I have been getting a few random crashes and glitches that seem to be related to Core Data. For example sometimes when I save the context the app will crash but not always. Another thing I've been seeing is the very first tableView doesn't always update the reflect the data that was modified in it's detail view.
Currently I'm passing around a single Managed Object Context that was created in the app delegate to each of the different view controllers by setting the context property of the view controller just before I push it onto the navigation stack.
This seems like a clunky, hacky way of getting the job done. Is there a better design pattern to use?
I noticed in one of the WWDC sessions using delegation but I've never used creating my own delegates before and haven't been able to puzzle it out of the WWDC session.
Thanks.
=)
Use singleton NSManagedObjectContext for all Controllers isn't a best practice.
Each Controller should have your own Context to manage specific, sometimes atomic, operations at document store.
Think if you can edit a NSManagedObject attached to Controller that pass the same Context to other Controller that will select another instance to delete or edit.. you can lost the controll about modified states.
When you create a view controller, you pass it a context. You pass an
existing context, or (in a situation where you want the new controller
to manage a discrete set of edits) a new context that you create for
it. It’s typically the responsibility of the application delegate to
create a context to pass to the first view controller that’s
displayed.
http://developer.apple.com/library/ios/#documentation/DataManagement/Conceptual/CoreDataSnippets/Articles/stack.html
1)
Use a singleton for your CoreData setup (NSPesistentStoreCoordinator, NSManagedObjectModel & NSManagedObjectContext). You can use this singleton to execute the fetch requests you created in your Models and to add or delete Entities to your Context.
2)
Delegates are not that hard. Following is a sample:
#class SomeClass
#protocol SomeClassDelegate <NSObject> //Implements the NSObject protocol
- (void) someClassInstance:(SomeClass *)obj givesAStringObject:(NSString *)theString;
- (BOOL) someClassInstanceWantsToKnowABooleanValue:(SomeClass *)obj //Call to delegate to get a boolean value
#optional
- (NSString *) thisMethodIsOptional;
#end
#interface SomeClass : NSObject {
id<SomeClassDelegate> delegate;
//Other instance variables omitted.
}
#property (assign) id<SomeClassDelegate> delegate;
#end
#implementation SomeClass
#synthesize delegate;
- (void) someMethodThatShouldNotifyTheDelegate {
NSString *hello = #"Hello";
if (self.delegate != nil && [self.delegate respondsToSelector:#selector(someClassInstance:givesAStringObject:)]) {
[self.delegate someClassInstance:self givesAStringObject:hello];
}
}
#end
Option 1 could be something like this, you will have to setup the variables in the init of the object (and implement the singleton ofcourse):
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface CoreDataUtility : NSObject {
#private
NSManagedObjectModel *managedObjectModel;
NSManagedObjectContext *managedObjectContext;
NSPersistentStoreCoordinator *persistentStoreCoordinator;
}
+ (CoreDataUtility *)sharedCoreDataUtility;
- (NSEntityDescription *) entityDesctiptionForName:(NSString *)name;
- (NSMutableArray *) executeRequest:(NSFetchRequest *)request;
- (id) getInsertedObjectForEntity:(NSString *)entity;
- (void) deleteAllObjects:(NSString *) entityName;
- (void) deleteManagedObject:(NSManagedObject *)object;
- (void) saveContext;
#end
Currently I'm passing around a single Managed Object Context that was
created in the app delegate to each of the different view
controllers...This seems like a clunky, hacky way of getting the job
done. Is there a better design pattern to use?
There's nothing particularly special about a managed object context in this respect, it's just another object that your view controller may need to do its job. Whenever you're setting up an object to perform a task, there are at least three strategies that you can use:
Give the object everything it needs to get the job done.
Give the object a helper that it can use to make decisions or get additional information.
Build enough knowledge about other parts of the application into the object that it can go get the information it needs.
What you're doing right now sounds like the first strategy, and I'd argue that it's often the best because it makes your view controllers more flexible, less dependant on other parts of the app. By providing the MOC to your view controllers, you leave open the possibility that you might someday use that same view controller with a different context.
Jayallengator makes the helpful observation that every managed object has a reference to its context, and if you're passing around specific managed objects you don't also need to pass along the context. I'd take that a step further: if you're passing specific managed objects to your view controller, the view controller often won't need to know about the context at all. For example, you might keep Game objects in your data store, but a GameBoardViewController will probably only care about the one Game that's being played, and can use that object's interface to get any related objects (Player, Level, etc.). Perhaps these observations can help you streamline your code.
The second strategy is delegation. You'll usually use a protocol when you use delegation, so that your object knows what messages it can send its helper without knowing anything else about the helper. Delegation is a way to introduce a necessary dependency into your code in a limited, well-defined way. For example, UITableView knows that it can send any of the messages defined in the UITableViewDelegate protocol to its delegate, but it doesn't need to know anything else about the delegate. The delegate could be a view controller, or it could be some other kind of object; the table doesn't care. The table's delegate and data source are often the same object, but they don't have to be; again, the table doesn't care.
The third strategy is to use global variables or shared objects (which is what people usually mean when they talk about singletons). Having a shared object that you can access from anywhere in your code is certainly easy, and you don't have that "klunky" extra line of code that configures your object, but it generally means that you're locking your view controllers in to using that shared object and no other. It's a lot like gluing a hammer to your hand because you know for certain that that hammer is the tool you need. Works great for pounding nails, but it can be painful if you later discover that you'd like to use the same hand for driving screws or eating dinner.
The singleton approach seems to be best-practice, but another trick I found useful was that in cases where you're passing a NSManagedObject from one view controller to the next anyway (usually as an instance variable), you don't need to also pass the NSManagedObjectContext since you can get the context from the object you passed in by invoking [myManagedObject managedObjectContext]. This can be a handy shortcut when there's maybe only one or two methods where you need the context and you don't want the overhead of creating yet another NSManagedObjectContext ivar/property.
I'm having trouble referencing one view controller from another. The code works but I get warnings which makes me think I'm going about it wrong. I'm trying to reload the data in a tableView whose controller is in a NavigationController.
What's wrong with a message like this:
From the AppDelegate:
[self.tabBarController.selectedViewController.topViewController.tableView reloadData];
Although this works, I get the warning request for member 'topViewController' in something not a structure or union because Xcode doesn't know that the selectedViewController will return a navigationController. So I could do the following:
UINavigationController *myNavigationController = self.tabBarController.selectedViewController;
[myNavigationController.topViewController.tableView reloadData];
But then I get this warning: incompatible Objective-C types initializing 'struct UIViewController *', expected 'struct UINavigationController *'
How far do I have to go with this? The first line works. To get to the "right way" is it gonna take 8 lines of code?
A major code smell here, IMO. You're trying to do action at a (great) distance. It's not exactly clear what you're trying to accomplish, nor why you need to do this action from the app delegate. I have seen some developers treat the app delegate like a giant catch-all global lump of mud, and I think this is an anti-pattern that should be eliminated from iOS development.
Back to your question: you're trying to force a table view controller, inside a tab view controller, to reload its data. I'm assuming this is in response to something happening. Why not have the view controller in charge of that table watching for that event instead of the app delegate? That way, the thing that owns the table view is directly controlling it -- which is the entire point of the MVC pattern. This is a much better approach than having the app delegate drill down through a hierarchy to find a table view... in terms of complexity, readability, and brittleness.
If, for some reason, you can't or won't have that view controller observing for the event directly (hard to fathom why offhand), you could always have the app delegate post an NSNotification and let the view controller in charge of the table register as an observer for it. Not as good as direct observation, but definitely better than your current approach.
You can't use dot-notation unless the compiler knows what type of object you are using it on, and that that object type can receive a message with that name.
You can use dot-notation with a bunch of type-casts (which in this case, is hideously ugly):
[((UITableViewController *) ((UINavigationController *) self.tabBarController.selectedViewController).topViewController).tableView reloadData];
Or you can break it up into discrete steps:
UINavigationController *navController = (UINavigationController *) self.tabBarController.selectedViewController;
UITableViewController *tableViewController = (UITableViewController *) navController.topViewController;
[tableViewController.tableView reloadData];
Note that I'm assuming that your top VC is a sub-class of UITableViewController.
You really shouldn't be accessing the .tableView property externally - you should encapsulate that behaviour with a reloadData method on the View Controller itself. Even if all it does is call reloadData on its .tableView, you should encapsulate it. This will make your code more modular (which makes it easier to understand for you and others), and make it easier to expand on and add complexity to your View Controller down the track.
Without knowing exactly how this app is structured, I would guess that you're probably better off using notifications or observers to get your VC to reload its data. If you have some global event that requires a UI refresh, an NSNotification is a good way to make the UI layer get the message while keeping your code nice and modular.
I'm creating a custom ViewController. This VC needs to load some data that is known in the event that creates it and pushes it to the top of the NavigationController that it is going to be part of.
My question is, how should I pass data from the view that handles the custom ViewController's creation into that custom ViewController.
I've thought of four possible options, and I was hoping to get feedback on why each one is good or not for this functionality.
1) Expose public properties in the custom ViewController and set the UI elements in the view based on those properties in - (void) ViewDidLoad.
2) Expose the actual UI elements themselves and set their .text/.image/.whatever attributes as the ViewController is being created.
3) Create a custom constructor for the custom view and pass in the values I need to set up the UI elements
4) Create a custom model that both views have access to, set the data before the CustomView is created/pushed, and access that data in the ViewDidLoad event.
I'm still new to all of this, and I want to make sure that I understand the proper handling of these handoffs of data. It seems like something like this is probably a simple answer, but I'm still a little confused and its probably really important to do this right to avoid memory loss/leaks.
Also, in case anyone cares, I'm using Stanford's CS193p class on iTunes U and Mark/Lamarche's "Beginning iPhone Development" to teach myself cocoa for the iPhone. I'm working on an application with a NavigationController and a couple ViewControllers (Presence 1 if you're familiar with 193p).
Well, I believe there are advantages & disadvantages to each of those methods depending on your requirements...often it will require some combination of approaches. I believe the most common, for me anyway, is to do something like this where you give it enough to get started.
MyViewController *vc = [[MyViewController alloc] init]; // (or initWithNibName:bundle:)
// transfer vc values here
vc.value1 = aValue;
vc.value2 = anotherValue;
[self.navigationController pushViewController:vc animated:YES];
[vc release];
After your view controller is instantiated you have an opportunity to pass objects to it. Say MyViewController is a detail view then you'd give it the object it will be displaying the details for. Or, if it's a table view you can give it the NSArray it will need for display. Then in viewDidLoad or awakeFromNib or awakeFromCoder, or... you can fill out the view...so to speak.
#1 is fine, with or without #3 (these two are not mutually exclusive)
#4 is my preferred solution. For instance, if I had a UserViewController, I would probably also like to have a User object and create it this way:
User *user = [self.users objectAtIndex:someIndex];
UserViewController *uvc = [[[UserViewController alloc] initWithUser:user] autorelease];
#2 is not a good idea. Objects should not access the UI elements of other objects. Much trouble comes from this when you decide to change your UI around (and you will).