So, I am looking at the code from Parse Anypic tutorial here
and my question is this :
There are these 2 view controllers :
#interface PAPHomeViewController : PAPPhotoTimelineViewController
#end
and this one :
#interface PAPPhotoTimelineViewController : PFQueryTableViewController <PAPPhotoHeaderViewDelegate>
- (PAPPhotoHeaderView *)dequeueReusableSectionHeaderView;
#end
I have a segue let's say that opens the homeViewController.
What is the relationship between these two? Both of the viewcontrollers have implemented the viewDidLoad function - and they are both called. In what order are they called? Is any of them having priority over the other? I do not understand the idea of a viewcontroller that extends another viewcontroller.
Can I have my HomeViewController extends the UIViewController and the PhotoTimeline to be initiated inside the HomeViewController, given a specific frame?
Presumably PAPHomeViewController is the one you are actually instantiating. If that's the case, then its viewDidLoad gets called, if it has one. If PAPHomeViewController calls [super viewDidLoad] within its viewDidLoad method then PAPPhotoTimelineViewController will be called at that point. In turn, if PAPPhotoTimelineViewController calls [super viewDidLoad] then PFQueryTableViewControllers viewDidLoad will be called.
This process will continue all the way up to UIViewControllers viewDidLoad method.
You asked
What is the relationship between these two?
I strongly suggest you stop programming for a bit and read up on basic programming principles. Especially read anything you can find on "implementation inheritance".
Related
Is there a way to accomplish this? Like let's say a framework perhaps?
All I need is something like:
aViewController appeared
bTableViewController appeared
cViewController appeared
Like I added a code in each viewDidLoad, but without adding that code. I know that there is a visibleViewController if I used navigation embed but I don't and I can't.
The framework I'm working on is based on no baseline, so I can not assume anything, like I'll past a code in a project and when the user compiles the code, I will get the views.
The purpose is I'm creating an analytic tool without being have to add individual code in each view controllers's viewDidLoad method.
First of all, there's no such thing as a "visible view controller" technically speaking, despite the annoyingly named method of navigation controllers. Instead, we should be talking about visible views. There's nothing visible about the controller.
But what you really want to know is what view controller is active and controlling visible views. With a navigation controller, it's the top of the nav-stack, with a tab bar controller, it's the selected view controller, etc.
By implementing ONLY code in app-delegate, I don't know of any way to accomplish what you're trying to accomplish.
Instead, what would be your best option would be to provide a framework that included a subclass of UIViewController and UIAppDelegate.
Set the code in these to do all the analytical work necessary. Provide this framework as a whole and inform your users that if they want to make use of the analytics you've provide, they'll need to subclass your view controller instead of UIViewController, etc.
In your custom classes, just override viewDidAppear: to send a notification and viewDidDisappear: to send a notification. And the appdelegate just manages an array that keeps track of which is on top, adding when the viewDidAppear: notification fires, and removing when the viewDidDisappear: notification fires.
And to be sure your viewDidAppear:/viewDidDisappear: isn't overridden, be sure to make use of this: When a subclass overrides a method, how can we ensure at compile time that the superclass's method implementation is called?
So in your view controller base class, it'd be as simple as posting a notification:
// AnalyticViewController.h
#interface AnalyticViewController : UIViewController
- (void)viewDidAppear NS_REQUIRES_SUPER;
- (void)viewDidDisappear NS_REQUIRES_SUPER;
#end
// AnalyticViewController.m
#implementation AnalyticViewController
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[NSNotificationCenter defaultCenter] postNotificationName:#"AnalyticVCAppeared"
object:self
userInfo:nil];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidAppear:animated];
[[NSNotificationCenter defaultCenter] postNotificationName:#"AnalyticVCDisappeared"
object:self
userInfo:nil];
}
#end
And register for these in the App delegate in application:didFinishLaunchingWithOptions:. You'll want an mutable array to keep track of the stack of view controllers. And you'll want separate methods for each notification.
Given a property #property (nonatomic,strong) NSMutableArray *analyzedViewControllers;, the methods that respond to the notifications should look like this:
- (void)handleViewAppear:(NSNotification *)aNotification {
if (!self.analyzedViewControllers) {
self.analyzedViewControllers = [NSMutableArray array];
}
[self.analyzedViewControllers addObject:[aNotification object]];
}
- (void)handleViewDisappeared:(NSNotification *)aNotification {
[self.analyzedViewControllers removeObject:[aNotification object]];
}
And the top-most view controller will always be at [self.analyzedViewControllers lastObject].
If you need to send more information about the view controller, you can using the userInfo argument when posting the notification. You could even put the entire notification into the array, so you have the view controller reference and the user info reference.
What can also be important to note however is that just because one view controller has appeared doesn't inherently mean another has disappeared. View controllers don't necessarily take up the entire screen, and although there's eventually a practical limit to how many view controllers have visible views on screen, it's a quite high limit.
ADDENDUM: To add some clarity... in the AnalyticViewController class I'm recommending, we have this line:
#interface AnalyticViewController : UIViewController
A line similar to this exists in every one of your view controller's header files. What I'm recommending is that all your other view controllers subclass this one, so instead of:
#interface FooBarViewController : UIViewController
You'd instead have this:
#import AnalyticViewController.h
#interface FooBarViewController : AnalyticViewController
And the code in the viewDidAppear: and viewDidDisappear: of AnalyticViewController is now automatically in FooBarViewController unless you ignore the NS_REQUIRES_SUPER warning and override the viewDidAppear: or viewDidDisappear: methods.
It will be a little bit of effort to change over existing view controllers, but for any future view controllers that are not yet created, it's as simple as choosing to subclass AnalyticViewController rather than UIViewController when you create the file.
For creating a analytical framework i would say you should write a kinda protocol of a rootviewController and viewController class and that delegate methods can be implemented in app delegate for knowing and handling your purpose.
I have problem with calling methods from one UIViewController by another UIViewController.
Currently I have UIScroll view with two UIViewControllers.
I want to change something in second one and see results in first one.
I try to do this in this way:
Inside function of second UIViewController:
-(void)doSomething:(){
FirsOneViewController *firstVC = [FirsOneViewController alloc] init];
[firstVC changeUnits:0];
}
Function is called but I don't se any changes in first controller.
BR,
Paul
From your code I see you create a new instance of FirstViewController and so there is no reason why the current instance inside the scrollview would receive this message.
You need to send the changeUnits: message to the current FirstViewController, so you need a reference to it. To do this you may want to think about creating a protocol, so that you parent container (the scrollview) is notified by the SecondViewController and then notifies the FirstViewController. A simpler(and lazier) solution is make the SecondViewController have a strong reference to the FirstViewController (though this solution may bite you in the future).
As said in other answers you are creating a new instance of FirsOneViewController instead of referencing to the one you already have.
Here are three ways of doing what you are asking:
Delegation:
The FirstViewController should be the delegate of the SecondViewController (as the secondViewController is calling methods on the FirstViewController). You should tell the SecondViewController that the FirstViewController is its delegate in what ever class initialises the two viewControllers.
From what you have said so far this seems like your best option.
NSNotification:
This could be good option if you think more than one object will want to listen to the change in the SecondViewController. Just post an NSNotification in the SecondViewController and add an NSNotification listener in the FirstViewController
Singleton:
if there should only ever be one instance of the FirstViewController in existence then make it a singleton. By making a class initialiser method. so that you can create/get the current instance of the object from anywhere in your appellation.
Hope this helped.
I am using Split View Controller. For master I have Navigation Controller with Table View Controller as a root view. For details I have Navigation Controller with Custom View Controller as a root view. From master I am selecting tableView row and displaying row details in details view. From details view i can edit details of this row using another View Controller with modal segue. The question is: How can I refresh tableView (master) after saving changes in details editing view (modal). When I tap save button the -(IBAction) unwindEditRow:(UIStoryboardSegue *)segue (of details) is triggered.
You have a lot of ways to send messages to ViewController when particular event has happened.
The first method: Use delegation pattern. What is delegate?
Delegation is a clean and simple way of connecting objects and helping them to communicate with other. In other words, delegation is a dating service for Objective-C objects. :) There are some useful links: Basic concepts in Objective-C, Writing your own custom delegate
Here is the way of declaring your new protocol:
#protocol DetailViewControllerDelegate <NSObject>
-(void)itemHasBeenChanged:(id)edittedObject;
#end
In your DetailViewController declare your future delegate:
#property (weak,nonatomic) MasterViewController <DetailViewControllerDelegate> *delegate;
Implement itemHasBeenChanged: method in MasterViewController.m:
-(void)itemHasBeenChanged:(id)edittedObject{
//editting logic goes here
}
Tell our class to implement the DetailViewControllerDelegate protocol so that it knows which functions are available to it by line:
#interface MasterViewController : UITableViewController <DetailViewControllerDelegate>
After all these steps, you can call method in your DetailViewController whenever you want by:
[self.delegate itemHasBeenChanged:yourObject];
Here is my example code on github
Second way
We can use NSNotificationCenter as an alternative to custom protocols. Making custom protocols and registering methods is hard to do in a big project and NSNotificationCenter can free us from that burden. The main approach used with NSNotificationCenter is that any object can send a notification to the notification center and at the same time any other object can listen for notifications on that center.
So I'm on my first iPhone app. I'm actually rather far into it. I have already learned from many mistakes, but I feel I've made an ultimate mistake. I'm using segues to navigate to different views. I get into about 5 segue views deep, which I'm realizing is causing a LOT of allocated memory. In other words, View A calls View B, B segues into C, C into D, etc..From what I understand, by the time I get to D I now have instances of A B C and D open, which does not sound good. I am using delegates for example like below:
Just an example of what I'm doing throughout my app:
First View:
#interface FirstViewController : UIViewController<SecondViewControllerDelegate>
#end
Second View:
#class SecondViewController;
#protocol SecondReviewOrderViewControllerDelegate <NSObject>
- (void)secondViewControllerDidCancel:(SecondViewController *)controller;
#end
#interface SecondViewController : UIViewController<ThirdViewControllerDelegate>
#property (strong, nonatomic) id <SecondViewControllerDelegate> delegate;
#end
Third View:
#class ThirdViewController;
#protocol ThirdReviewOrderViewControllerDelegate <NSObject>
- (void)thirdViewControllerDidCancel:(ThirdViewController *)controller;
#end
#interface ThirdViewController : UIViewController<>
#property (strong, nonatomic) id <ThirdViewControllerDelegate> delegate;
#end
And so on and on onto view 4 and 5.
My question is, if this is wrong, which it seems to be, what is the right way to about navigating views and passing data from one viewcontroller to another? Thanks for any tips.
From what I understand, by the time I get to D I now have instances of A B C and D open, which does not sound good
A view controller, of itself, is a fairly lightweight object, and there is no problem whatever with going many levels deep (e.g. pushing five view controllers onto a navigation controller stack). However, memory and images you may be holding on to are not lightweight, so be sure to implement didReceiveMemoryWarning and take it seriously if it arrives.
A strategy for letting go of large retained memory-hogging stuff in response to didReceiveMemoryWarning is to save it off to disk (if it can't be recreated on demand) and then use lazy initialization to read it back in the next time you are asked for it.
Got a question, I'm trying to return to a previous view and share some data over to the frame I'm returning to. The data will be date and time and I would like to send this to a textField.
For example I'm calling the date *returneddate and the textField I'm calling *dateTime. The views are call *PickDateTime and SubmitEventsP2.
If you need more information just ask me and I'll add it if I can to make it easier for you to help me.
I'm using Xcode 4.2.
Ok.. its pretty simple.. you should use a delegate... if i understood correctly, you are on a secondary view and when you return to the main view you wish to send the data from the second view back to the main view, right?
so, in your second view, in the .h file, on top of interface, you will declare the delegate with:
#class nameOfTheViewController;
#protocol nameOfTheViewControllerDelegate <NSObject>
-(void)methodNameOfDelegateReturning:(NSString *)string otherString:(NSString *)string2;
#end
And in your interface, still in the .h, you will create a reference of this delegate like:
#property(nonatomic, weak) id <nameOfTheViewControllerDelegate> delegate;
after that, in your .m of nameOfTheViewController you will do:
#synthesize delegate = _delegate;
After you created you delegate in the nameOfTheViewController file, you will call the delegate method you just created exactly where and when you want to return to the previous view, filling it with the parameters you want to pass back... and of course, in your mainViewController, right in your didPrepareForSegue method, you will create a instance of the nameOfTheViewController class and set its delegate proeprty to self... for this to be possible, in your mainViewController .h you must conform to the nameOfTheViewControllerDelegate protocol.