View Controller inside View Controller won't receive rotation messages - ios - objective-c

I have a customized View Controller (we'll call it the wrapper). Its view contains only a UIScrollView. The scroll view contains another customized view controller (we'll call it the inside view), initialized from an xib file (the scroll view itself is initialized from a xib file as well, but I don't believe it matters).
The wrapper view is displayed using a UITabBarController, which contains several more similar view controllers.
I have this weird problem: the wrapper's rotation functions - shouldAutoRotate, willAnimateRotation - get called every time that I rotate the device. For some reason, the inside view's rotation functions don't get called, but it still rotates. The inside view's shouldAutoRotate does get called when initializing it (when the app starts).
I've looked at google and couldn't find anything that is relevant to my case. I'm not sure if it is related, but the Autoresize subviews is checked on all xib files.
I'd be glad if you could help me solve this problem. I need the inside view's rotation function to get called on rotation in order to arrange it manually, but I'd like to avoid calling them from the wrapping view (rather it to work as it should).
Thank you in advance!

Well the innerViewController's rotation function will not be called because they are added as subview's to your scrollView what you can do is generate a NSNotification when orientation changes in your parent controller then you can receieve notification in subview and manage them accordingly. Or you can iterate through subviews of UIScrollView when your shouldAutoRoatate called in you parent controller and then manually call should autorotate method of child views. Hope you understand.

The simplest way is to hold some UIInnerViewController* innerController in your .h file and in .m to call inner's
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation inside of the wrapper like so:
wrapper.m
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
BOOL innerResult = [innerController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
//may be more computations here
return innerResult;// or any other value, based on your needs
}
Other approach that you may use is to register inner controller to UIDeviceOrientationDidChangeNotification like so:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(orientationChanged:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
and layout subviews in -(void)orientationChanged:(NSNotification*)notification;in the same inner controller. The only thing you should be awere of, is that UIDeviceOrientation is a little-bit different than UIInterfaceOrientation and may hold value such UIDeviceOrientationFaceUp that is not applicable to UI changes in most cases.

Nesting viewcontrollers inside custom viewcontrollers is not supported in iOS 4. You can usually forward all the necessary messages to your child VC's manually, though, with acceptable results.

Related

Should we call ads in viewDidLoad or viewDidAppear?

We have a tab-heavy app, which has 5 tabs to use back and forth. We have iAds and admobs(as backup for countries without iAd), and we 'call' the ads in viewDidLoad. Would it make a difference to call them in viewDidAppear instead? And then remove them in viewDidDisappear or shomething not to screw up the frames etc? Would this give more impressions etc?
viewDidLoad:
viewDidLoad
Called after the controller’s view is loaded into memory.
- (void)viewDidLoad
Discussion
This method is called after the view controller has loaded its view hierarchy into memory. This method is called regardless of whether the view hierarchy was loaded from a nib file or created programmatically in the loadView method. You usually override this method to perform additional initialization on views that were loaded from nib files.
viewDidAppear:
viewDidAppear:
Notifies the view controller that its view was added to a view hierarchy.
- (void)viewDidAppear:(BOOL)animated
Parameters
animated
If YES, the view was added to the window using an animation.
Discussion
You can override this method to perform additional tasks associated with presenting the view. If you override this method, you must call super at some point in your implementation.
Answering
So viewDidLoad is called slightly earlier than viewDidAppear: , the only difference is that when viewDidAppear: the view have been already drawn, instead in viewDidLoad the view has still to be drawn.
So answering to your questions:
Would it make a difference to call them in viewDidAppear instead?
If calling the ads is a slow operation, then you would see first the view appearing in it's color, and the ads after a few interval of time.However this has to be too slow to make a real difference.
And then remove them in viewDidDisappear or shomething not to screw up the frames etc?
It doesn't "screw up frames", that for sure.
you just need call it in viewDidLoad
Putting your ad code in viewDidAppear: (and removing it in viewDidDisappear:) will certainly give you more impressions, but unless you're a whitelisted pub, you're probably getting paid on a cost per click basis anyway (AdMob Help Center article).
In this case, instead of having the overhead of creating and destroying GADBannerView objects on tab changes, you might as well create a singleton GADBannerView that you use throughout your TabbedController (look at an example here).

When will viewWill/DidAppear/Disappear is called anyway and how exactly does it work?

I understand that viewWillAppear will be called when duh.... when the the view is about to appear.
But how does IOS know that a controller's view is about to appear?
When exactly that and how it is implemented?
For example, does the childController.view check first that window is one of it's super ancestors? Does the view has a pointer to it's controller? How exactly that works? Does everytime a view is added it check whether it's window is it's super ancestor and whether it is the view outlet of a UIViewController?
For example, if I add childcontroller.view but not to a subview of any view that's being called. Will viewWillAppear called?
Does the childController need to be the a child of a parentController so that viewWillAppear of the childController will be called when the parentController's viewWillAppear is called automatically?
The view is loaded by your controller using the - (void)loadView method. This method is implemented to load a blank view or a view from a nib/storyboard. You only need to override it if you really need to create a view hierarchy from scratch.
All of the magic happens when the value of the view property is first requested and the controller detects the value is nil. All of the life cycle method calls are handled by the UIViewController. There is nothing you need to do other than implement the methods if you need them. Remember one thing: There is no guarantee the view has been loaded until the - (void)viewDidLoad method has been called.
Everything I've learned about controllers how they work has come from the View Controller Programming Guide.

The child view does not rotate from landscape to portrait and vice versa

I'm making an application for the iPad.
I have a view controll that has to stay in landscape, this works just fine.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return UIInterfaceOrientationIsLandscape(toInterfaceOrientation);
}
From this view i push towards an other view, this child view should rotate to the appropriate rotation (landscape or portrait) whenever the user tilts his ipad.
in this child view i use this code to make this happen, but it doesn't work. It sticks to the landscape.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
In order for a view controller to support rotation, all view controllers in its hierarchy must support rotation.
From the Responding to Orientation Changes documentation:
The window looks for an appropriate view controller and calls itsshouldAutorotateToInterfaceOrientation:method to determine if it supports the new orientation.
Container view controllers may intercept this method and use their own heuristics to determine whether the orientation change should occur. For example, the tab bar controller allows orientation changes only if all of its managed view controllers support the new orientation.
Further, you should not be using multiple view controllers to manage a single screen.
From the View Controller Programming Guide documentation (emphasis mine):
The one-to-one correspondence between a view controller and the views in its view hierarchy is the key design consideration. You should not use multiple content view controllers to manage different portions of the same view hierarchy. Similarly, you should not use a single content view controller object to manage multiple screens’ worth of content.
In this case, I'd suggest disabling rotation handling in your parent view controller, changing the child view controller to be simply a view (to meet the above criteria), and manually monitoring orientation changes to update your child view's layout.
You can monitor for orientation changes by listening for the UIDeviceOrientationDidChangeNotification notification. Example code:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(yourSelector:)
name:UIDeviceOrientationDidChangeNotification object:nil];
Update
If by "push" you mean push a view controller onto a navigation view controller then please disregard the second part of my response.
If this is the case, you must ensure that you have overridden the shouldAutorotateToInterfaceOrientation: method of your navigation controller to return YES as well as your view controller in order to support rotation handling.

UIView subviews's layout update

In the viewWillAppear delegate method, I'm setting subview's frames with rects ( location and size).
Now, There is one subview whose content will grow with user actions.
So, I need a callback in that superview controller ( The One I am talking on first sentence) , to update the layout. In this callback, I can find out how much the size increased, and then set other subviews frame rects too.
It can be done through calling setNeedsLayout method on the view, but it requires layoutSubviews to be overridden on the UIView. Please note that it is not a delegate method, I need to have a custom view, Which I don't want to do,
Is there any delegate method for me to update the layout in the view's controller ?
UPDATE:
This need to be support on iOS 4.2
There are two UIViewController methods of possible relevance:
-viewWillLayoutSubviews
-viewDidLayoutSubviews
You can probably guess when they're called.

UIWindow and UIView addSubview Question

Does the addSubview method actually load the view into the application or not? The reason I am asking is because I have two two views in my application. The application delegate adds the two views as subviews and then brings one of the views up front. Now, I have a print statement in each of the viewDidLoad methods for each view. When I run the application, the application delegate loads the views as subViews and as each view is loaded, I actually see the console print out the statements that I placed in each of the viewDidLoad methods. Is this supposed to be doing this?
viewDidLoad is actually a method of UIViewController, not UIView. It gets called after the view gets loaded into memory (after your init method, but before the awakeFromNib). You'll notice that addSubview: takes a UIView as a parameter, so the view must have been loaded in order for the view to be added to another view. Otherwise you'd be trying to add an imaginary view.
In answer to your question, yes it is supposed to be doing this. viewDidLoad is called long before you addSubview. In fact, if you take out the addSubview: lines, you'll notice that it's still getting called (because you're creating the view's controller).
My understanding is that views are lazily loaded. If your viewcontroller has 10 view, they are not all loaded until you actually try to access them.