UITabBarController/UINavigationController rotation issues - ios

My problem is the following: I want to only allow Portrait orientation on all my ViewControllers except 1 ViewController which is supposed to allow both Portrait and landscapeLeft/Right. I have now spent almost 2 days into how to set orientation in IOS for different slides/ViewControllers. After some searching I found this thread here at stack: UITabBarController Rotation Issues in ios 6
I followed Kunani's example in that thread which I will post here to save all readers some time:
Zack, I ran into this same issue. It's because you have your viewController embedded inside of a TabBar Controller or UINavigationController and the calls to these methods are happening inside those instead of your normal View (Changed in iOS6). I ran into this issue because I was presenting a viewController embedded inside a UINavigationController on all my modal views that had Navigation to different views (Signup Process, Login, etc). My simple fix was to create a CATEGORY for UINavigationController that includes these two methods. I have shouldAutorotate returning NO anyway because I don't want my modal views rotating. Your fix may be this simple, give it a try. Hope it helps. I created a category and named it autoRotate and selected theUINavigationController option. The M+H file are below.
#import "UINavigationController+autoRotate.h"
#implementation UINavigationController (autoRotate)
-(BOOL)shouldAutorotate {
return NO;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
#end
... and the category .h:
#import <UIKit/UIKit.h>
#interface UINavigationController (autoRotate)
-(BOOL)shouldAutorotate;
- (NSUInteger)supportedInterfaceOrientations;
#end
I did what he said and tried to set category for my UITabBarController which worked, all classes connected to the tabBar now only allows orientationPortrait. But if you look at the following Picture
(screenshot from my project) there is a class in the middle of the StoryBoard called ShowTaskView. This class is connected to most classes (which are directly connected to the UITabBarController) via a UINavigationController. Even if I set UITabBarController to only allow Portrait also ShowTaskView seems to get affected by that rule and I can not make it to rotate. The scheme in my project can also be described as this:
TabBarController ----> UINavigationController -------> class X ----------> class ShowTaskView
What can I do from here if I want my classes connected to tabBarController only to allow orientationPortrait and the rest of the classes allow both portrait and landscape based on how my project is built? I am very frustrated at this issue since it is so damn hard to solve :/
Regards

please refer to my answer in similar thread: Navigation controller stack in landscape mode, but modally presented view controller view always in portrait frame size
iOS6 controls rotation by the navigation stacks, so wrap your rotatable view into separate navigation controller to be able to control it there.

Related

iOS8 - prevent rotation on presenting viewController

We have a MainViewController with a tableView, and it presents a new modalViewController.
The MainViewController is restricted to portrait only, and the modalViewController can rotate.
The problem is in iOS8, that when the modalViewController rotates, the callback method of rotation in iOS8 in MainViewcontroller is called - - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
Thus, the UITableView is getting its data reloaded, which is a behaviour we don't want.
Can we prevent this feature of iOS 8, and not rotate the presenting UIViewController?
So after long days of searching and investigating, I finally came up with a possible solution.
First of all, I can use navigation controller and push the viewController instead of presenting it, but it breaks my code and just isn't so true.
The second thing I can do is not setting constraints. I still can use autolayout, but if I don't set constraints, and let the default constraints to be set, the tableView doesn't get reloaded. of course this is also isn't very smart thing to do, as I have many elements in my viewController.
Finally, I figured out that I can show this "modal" viewController in another UIWindow. I create UIWindow and set the modalViewController as its rootViewController.
I put some example project in git:
https://github.com/OrenRosen/ModalInWindow
Hope it will be helpful.
I did something similar with a navigation controller, that wouldn't rotate unless the top pushed controller does rotate.
In your case check if the main controller is presenting another controller. If it isn't then just reject rotation, otherwise return whatever the presented controller returns for the rotation method.
As for your table view, it shouldn't get reloaded because of rotations.
In iOS 8 the view that rotates when you change the device orientation is the first view added to the UIWindow. So, if you save a reference to it in your presentedController, you can overwrite the shouldAutorotate and supportedInterfaceOrientations values.

How to configure SWRevealViewController/PKRevealController/etc to handle orientation and presentation changes

I am using SWRevealViewController (https://github.com/John-Lluch/SWRevealViewController) to handle switching between two controllers, a "front" and a "rear".
The front controller is a UINavigationController and the rear controller is just a plain UIViewController that displays a list of menu items. The front UINavigationController pushes an instance of a view controller named FrontViewController. The rear UIViewController is an instance of RearViewController. The instance of SWRevealViewController is set as the root view controller once it is configured with the front and rear controllers, the delegate property of the reveal controller is set to the app delegate itself.
In both FrontViewController and RearViewController I am overriding shouldAutorotate and returning NO as well as overriding supportedInterfaceOrientations and returning UIInterfaceOrientationMaskPortrait.
However the app auto rotates and goes into Landscape while these two views are displaying when I rotate the device.
supportedInterfaceOrientations seems to only be invoked in FrontViewController but the value is not honored and the device rotates into landscape orientation.
I can't simply set the entire app to Portrait either because I have other detail views that I do want to support Landscape (movie player, etc).
How can I get SWRevealViewController working so I can restrict the app to Portrait in certain child views of the controller?
I have also noticed that the presentation changes are not being honored as well. In RearViewController I am overriding prefersStatusBarHidden to return YES but this method is never invoked. Similarly in FrontViewController I am overriding preferredStatusBarStyle to return UIStatusBarStyleLightContent but this method is never called either.
I have UIViewControllerBasedStatusBarAppearance set to YES in my plist.
UPDATE:
I have tried to use PKRevealController as suggested in the comments but the behavior is exactly the same. Supported orientations and status bar styles are completely ignore. supportedInterfaceOrientations on FrontViewController is the only override invoked but the return value of UIInterfaceOrientationMaskPortrait is not honored.
UPDATE 2:
I'm thinking that this is simply a limitation of these controls and they pretty much expect the support orientations to be the same throughout the application. I did however try MFSideMenu (https://github.com/mikefrederick/MFSideMenu) and it seems to handle supported orientations in different child views exactly as you would expect it to. I still don't have the status bar visibility and styles working, unfortunately.
To achieve this while not avoiding SWRevealViewController: Inside of it there is implemented method supportedInterfaceOrientations, which needs to be edited. I have done this:
- (NSUInteger)supportedInterfaceOrientations
{
UINavigationController* frontNavigationController = (UINavigationController*)self.frontViewController;
if ([frontNavigationController.visibleViewController isKindOfClass:[VCgallery class]]) {
return UIInterfaceOrientationMaskAll;
}
return UIInterfaceOrientationPortrait | UIInterfaceOrientationPortraitUpsideDown;
}
All my viewControllers are portrait only except those that are of class VCgallery, which can be both portrait and lanscape.
The solution ended up being to subclass PKRevealController and override supportedInterfaceOrientations, shouldAutorotate, preferredInterfaceOrientationForPresentation, prefersStatusBarHidden, and preferredStatusBarStyle.
While MFSideMenu did this for orientation it did not support status bar configurations. I also ran into a major bug with MFSideMenu that prevented me from using it in my project.
I made each method I override return a value from the appropriate controller depending on the circumstance. In the case orientation I return the value from self.frontViewController.topViewController (since I am using a UINavigation controller). Status bar style and visibility came from either self.frontViewController or self.leftViewController depending on the current state.
The same solution probably would have worked for SWRevealViewController as well but I preferred the API design of PKRevealController.
I figured subclassing would work from the beginning but I assumed that such a common scenario would be handled in the configuration of these controls.

How to set two of the screens in Portrait mode and another two screen in Landscape on iPhone?

I have a homeViewController (should always be in portrait mode) , which has four buttons to be redirected to a tar bar controller, which has four different viewController (first, second, third, fourth).
We want to have firstViewController and fourthViewController in portrait mode always. And secondViewController and thirdViewController in landscape mode always, also when we will go back to homeViewController form any of these viewControllers the homeViewController should always be in portrait mode.
You can use the -shouldAutrotate - Method of the ViewControllers to make sure they dont rotate when the device does. Just return NO, and the VC will always be displayed like your xib-file or storyboard specifies.
Heres the docs:
https://developer.apple.com/library/ios/documentation/uikit/reference/UIViewController_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006926-CH3-SW124
and heres an example:
-(BOOL)shouldAutorotate{
return NO;
}
EDIT: In Response to your coment:
UITabBarControllers wont forward the shouldAutorotate - Value to the ChildViewControllers. (As a sidenote, neither will a NavigationController). Your best bet is to subclass The TabBarController. Heres a link to a Stackoverflow-Post describing this. Have fun.
IO6 doesn't call -(BOOL)shouldAutorotate

UITabBarController and rotation iOS6

Happy Memorial Day for those in America!
I am new to iOS and Objective-C programming; a few weeks ago I inherited an iPad app-in-development that was being designed for iOS 5. I now have everything working except the rotation in iOS 6. I know that iPad apps should rotate to every orientation be default (which is what I want), yet mine does not. Everything rotates perfectly in iOS 5, and I can get my splash screen to rotate perfectly in iOS 6, but that is all. I cannot get the activities (once you click through the splash screen) to rotate properly.
I have searched stackoverflow and other websites to figure out what I must do, so I know to implement -(BOOL)shouldAutorotate and -(NSUInteger)supportedInterfaceOrientations in any specific ViewController to control that view's orientation behavior. I've read that having that different rotation behavior in one VC can affect the entire app. So I made sure that every VC that I could find** would now implement those two iOS 6 methods to return YES and UIInterfaceOrientationMaskAll, respectively. That didn't work. I read about returning self.tabBarController.shouldAutorotate and self.tabBarController.supportedInterfaceOrientations in those methods to ensure that the tabbar rotation behavior is consistent, but that didn't work. I have read about implementing a category (UITabBarController+autoRotate.m and .h) that implements these two methods, and that didn't work. I have read about subclassing the tabBarController, and I think my code does that: in my appDelegate, I call
[myWindow setRootViewController:activityViewController],
where activityViewController is an instance of class BicycleFamilyAcitivityViewController, which is from
#interface BicycleFamilyActivityViewController : UIViewController <UITabBarControllerDelegate>
When I investigate what is being called during the successful splash screen rotation using the iOS 6 simulator, I notice that those two implemented methods in BicycleFamilyAcitivityViewController are being called (twice each, actually) and that -(void)willAnimateRotationToInterfaceOrientation:duration is as well. When I try to rotate while viewing an activity (after clicking through the splash screen), those two methods are only called once, and -(void)willAnimateRotationToInterfaceOrientation:duration is not called. In both instances, the appDelegate's -(NSUInteger)application:supportedInterfaceOrientationsForWindow method is called.
Any advice on how to get rotation to work throughout the entire app? Even if it's just pointing to an answer on StackOverflow that I haven't yet seen (or fully understood), I would be most grateful.
Many thanks in advance!
Bernie
** In looking for VC classes in the project, I made sure to consider any class that implemented the rotation method of iOS 5: -(BOOL)shouldAutorotateToInterfaceOrientation:interfaceOrientation
In iOS 6 the Orientation functions have changed. Your set up should look like this in your viewControllers:
-(NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAll;
}
-(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationLandscapeRight;
}
-(BOOL)shouldAutorotate {
return TRUE;
}
UIInterfaceOrientationMaskAll is all orientations, more information here.
Someone in-house figured out the problem. I thought I would post the answer to help anyone who has a similar predicament.
What I had tried (among other things) was: setting the UIWindow in my appDelegate class to be my instance of a subclass (BicycleFamilyAcitivityViewController) of UIViewController. In my appDelegate, I had:
[myWindow setRootViewController:activityViewController];
where activityViewController is an instance of a subclass of UIViewController.
But I should have created an additional UIWindow via delegate, then assign the TabBarController (tbc) as it's root VC when I build my TabBarController. In my primary view controller class, I had a buildTabBarController function. So these two lines in that function allowed my rotation to work:
UIWindow *keyWindow = [[[UIApplication sharedApplication] delegate] window];
[keyWindow setRootViewController:tbc];
Hope this helps!

viewWillAppear, viewDidAppear not called with pushViewController [duplicate]

I've read numerous posts about people having problems with viewWillAppear when you do not create your view hierarchy just right. My problem is I can't figure out what that means.
If I create a RootViewController and call addSubView on that controller, I would expect the added view(s) to be wired up for viewWillAppear events.
Does anyone have an example of a complex programmatic view hierarchy that successfully receives viewWillAppear events at every level?
Apple's Docs state:
Warning: If the view belonging to a view controller is added to a view hierarchy directly, the view controller will not receive this message. If you insert or add a view to the view hierarchy, and it has a view controller, you should send the associated view controller this message directly. Failing to send the view controller this message will prevent any associated animation from being displayed.
The problem is that they don't describe how to do this. What does "directly" mean? How do you "indirectly" add a view?
I am fairly new to Cocoa and iPhone so it would be nice if there were useful examples from Apple besides the basic Hello World crap.
If you use a navigation controller and set its delegate, then the view{Will,Did}{Appear,Disappear} methods are not invoked.
You need to use the navigation controller delegate methods instead:
navigationController:willShowViewController:animated:
navigationController:didShowViewController:animated:
I've run into this same problem. Just send a viewWillAppear message to your view controller before you add it as a subview. (There is one BOOL parameter which tells the view controller if it's being animated to appear or not.)
[myViewController viewWillAppear:NO];
Look at RootViewController.m in the Metronome example.
(I actually found Apple's example projects great. There's a LOT more than HelloWorld ;)
I finally found a solution for this THAT WORKS!
UINavigationControllerDelegate
I think the gist of it is to set your nav control's delegate to the viewcontroller it is in, and implement UINavigationControllerDelegate and it's two methods. Brilliant! I'm so excited i finally found a solution!
Thanks iOS 13.
ViewWillDisappear, ViewDidDisappear, ViewWillAppear and
ViewDidAppear won't get called on a presenting view controller on
iOS 13 which uses a new modal presentation that doesn't cover the
whole screen.
Credits are going to Arek Holko. He really saved my day.
I just had the same issue. In my application I have 2 navigation controllers and pushing the same view controller in each of them worked in one case and not in the other. I mean that when pushing the exact same view controller in the first UINavigationController, viewWillAppear was called but not when pushed in the second navigation controller.
Then I came across this post UINavigationController should call viewWillAppear/viewWillDisappear methods
And realized that my second navigation controller did redefine viewWillAppear. Screening the code showed that I was not calling
[super viewWillAppear:animated];
I added it and it worked !
The documentation says:
If you override this method, you must call super at some point in your implementation.
I've been using a navigation controller. When I want to either descend to another level of data or show my custom view I use the following:
[self.navigationController pushViewController:<view> animated:<BOOL>];
When I do this, I do get the viewWillAppear function to fire. I suppose this qualifies as "indirect" because I'm not calling the actual addSubView method myself. I don't know if this is 100% applicable to your application since I can't tell if you're using a navigation controller, but maybe it will provide a clue.
Firstly, the tab bar should be at the root level, ie, added to the window, as stated in the Apple documentation. This is key for correct behavior.
Secondly, you can use UITabBarDelegate / UINavigationBarDelegate to forward the notifications on manually, but I found that to get the whole hierarchy of view calls to work correctly, all I had to do was manually call
[tabBarController viewWillAppear:NO];
[tabBarController viewDidAppear:NO];
and
[navBarController viewWillAppear:NO];
[navBarController viewDidAppear:NO];
.. just ONCE before setting up the view controllers on the respective controller (right after allocation). From then on, it correctly called these methods on its child view controllers.
My hierarchy is like this:
window
UITabBarController (subclass of)
UIViewController (subclass of) // <-- manually calls [navController viewWill/DidAppear
UINavigationController (subclass of)
UIViewController (subclass of) // <-- still receives viewWill/Did..etc all the way down from a tab switch at the top of the chain without needing to use ANY delegate methods
Just calling the mentioned methods on the tab/nav controller the first time ensured that ALL the events were forwarded correctly. It stopped me needing to call them manually from the UINavigationBarDelegate / UITabBarControllerDelegate methods.
Sidenote:
Curiously, when it didn't work, the private method
- (void)transitionFromViewController:(UIViewController*)aFromViewController toViewController:(UIViewController*)aToViewController
.. which you can see from the callstack on a working implementation, usually calls the viewWill/Did.. methods but didn't until I performed the above (even though it was called).
I think it is VERY important that the UITabBarController is at window level though and the documents seem to back this up.
Hope that was clear(ish), happy to answer further questions.
As no answer is accepted and people (like I did) land here I give my variation. Though I am not sure that was the original problem. When the navigation controller is added as a subview to a another view you must call the viewWillAppear/Dissappear etc. methods yourself like this:
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[subNavCntlr viewWillAppear:animated];
}
- (void) viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[subNavCntlr viewWillDisappear:animated];
}
Just to make the example complete. This code appears in my ViewController where I created and added the the navigation controller into a view that I placed on the view.
- (void)viewDidLoad {
// This is the root View Controller
rootTable *rootTableController = [[rootTable alloc]
initWithStyle:UITableViewStyleGrouped];
subNavCntlr = [[UINavigationController alloc]
initWithRootViewController:rootTableController];
[rootTableController release];
subNavCntlr.view.frame = subNavContainer.bounds;
[subNavContainer addSubview:subNavCntlr.view];
[super viewDidLoad];
}
the .h looks like this
#interface navTestViewController : UIViewController <UINavigationControllerDelegate> {
IBOutlet UIView *subNavContainer;
UINavigationController *subNavCntlr;
}
#end
In the nib file I have the view and below this view I have a label a image and the container (another view) where i put the controller in. Here is how it looks. I had to scramble some things as this was work for a client.
Views are added "directly" by calling [view addSubview:subview].
Views are added "indirectly" by methods such as tab bars or nav bars that swap subviews.
Any time you call [view addSubview:subviewController.view], you should then call [subviewController viewWillAppear:NO] (or YES as your case may be).
I had this problem when I implemented my own custom root-view management system for a subscreen in a game. Manually adding the call to viewWillAppear cured my problem.
Correct way to do this is using UIViewController containment api.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIViewController *viewController = ...;
[self addChildViewController:viewController];
[self.view addSubview:viewController.view];
[viewController didMoveToParentViewController:self];
}
I use this code for push and pop view controllers:
push:
[self.navigationController pushViewController:detaiViewController animated:YES];
[detailNewsViewController viewWillAppear:YES];
pop:
[[self.navigationController popViewControllerAnimated:YES] viewWillAppear:YES];
.. and it works fine for me.
A very common mistake is as follows.
You have one view, UIView* a, and another one, UIView* b.
You add b to a as a subview.
If you try to call viewWillAppear in b, it will never be fired, because it is a subview of a
iOS 13 bit my app in the butt here. If you've noticed behavior change as of iOS 13 just set the following before you push it:
yourVC.modalPresentationStyle = UIModalPresentationFullScreen;
You may also need to set it in your .storyboard in the Attributes inspector (set Presentation to Full Screen).
This will make your app behave as it did in prior versions of iOS.
I'm not 100% sure on this, but I think that adding a view to the view hierarchy directly means calling -addSubview: on the view controller's view (e.g., [viewController.view addSubview:anotherViewController.view]) instead of pushing a new view controller onto the navigation controller's stack.
I think that adding a subview doesn't necessarily mean that the view will appear, so there is not an automatic call to the class's method that it will
I think what they mean "directly" is by hooking things up just the same way as the xcode "Navigation Application" template does, which sets the UINavigationController as the sole subview of the application's UIWindow.
Using that template is the only way I've been able to get the Will/Did/Appear/Disappear methods called on the object ViewControllers upon push/pops of those controllers in the UINavigationController. None of the other solutions in the answers here worked for me, including implementing them in the RootController and passing them through to the (child) NavigationController. Those functions (will/did/appear/disappear) were only called in my RootController upon showing/hiding the top-level VCs, my "login" and navigationVCs, not the sub-VCs in the navigation controller, so I had no opportunity to "pass them through" to the Nav VC.
I ended up using the UINavigationController's delegate functionality to look for the particular transitions that required follow-up functionality in my app, and that works, but it requires a bit more work in order to get both the disappear and appear functionality "simulated".
Also it's a matter of principle to get it to work after banging my head against this problem for hours today. Any working code snippets using a custom RootController and a child navigation VC would be much appreciated.
In case this helps anyone. I had a similar problem where my ViewWillAppear is not firing on a UITableViewController. After a lot of playing around, I realized that the problem was that the UINavigationController that is controlling my UITableView is not on the root view. Once I fix that, it is now working like a champ.
I just had this problem myself and it took me 3 full hours (2 of which googling) to fix it.
What turned out to help was to simply delete the app from the device/simulator, clean and then run again.
Hope that helps
[self.navigationController setDelegate:self];
Set the delegate to the root view controller.
In my case problem was with custom transition animation.
When set modalPresentationStyle = .custom viewWillAppear not called
in custom transition animation class need call methods:
beginAppearanceTransition and endAppearanceTransition
For Swift. First create the protocol to call what you wanted to call in viewWillAppear
protocol MyViewWillAppearProtocol{func myViewWillAppear()}
Second, create the class
class ForceUpdateOnViewAppear: NSObject, UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool){
if let updatedCntllr: MyViewWillAppearProtocol = viewController as? MyViewWillAppearProtocol{
updatedCntllr.myViewWillAppear()
}
}
}
Third, make the instance of ForceUpdateOnViewAppear to be the member of the appropriate class that have the access to the Navigation Controller and exists as long as Navigation controller exists. It may be for example the root view controller of the navigation controller or the class that creates or present it. Then assign the instance of ForceUpdateOnViewAppear to the Navigation Controller delegate property as early as possible.
In my case that was just a weird bug on the ios 12.1 emulator. Disappeared after launching on real device.
I have created a class that solves this problem.
Just set it as a delegate of your navigation controller, and implement simple one or two methods in your view controller - that will get called when the view is about to be shown or has been shown via NavigationController
Here's the GIST showing the code
ViewWillAppear is an override method of UIViewController class so adding a subView will not call viewWillAppear, but when you present, push , pop, show , setFront Or popToRootViewController from a viewController then viewWillAppear for presented viewController will get called.
My issue was that viewWillAppear was not called when unwinding from a segue. The answer was to put a call to viewWillAppear(true) in the unwind segue in the View Controller that you segueing back to
#IBAction func unwind(for unwindSegue: UIStoryboardSegue, ViewController subsequentVC: Any) {
viewWillAppear(true)
}
I'm not sure this is the same problem that I solved.
In some occasions, method doesn't executed with normal way such as "[self methodOne]".
Try
- (void)viewWillAppear:(BOOL)animated
{
[self performSelector:#selector(methodOne)
withObject:nil afterDelay:0];
}
You should only have 1 UIViewController active at any time. Any subviews you want to manipulate should be exactly that - subVIEWS - i.e. UIView.
I use a simlple technique for managing my view hierarchy and have yet to run into a problem since I started doing things this way. There are 2 key points:
a single UIViewController should be used to manage "a screen's worth"
of your app
use UINavigationController for changing views
What do I mean by "a screen's worth"? It's a bit vague on purpose, but generally it's a feature or section of your app. If you've got a few screens with the same background image but different overlays/popups etc., that should be 1 view controller and several child views. You should never find yourself working with 2 view controllers. Note you can still instantiate a UIView in one view controller and add it as a subview of another view controller if you want certain areas of the screen to be shown in multiple view controllers.
As for UINavigationController - this is your best friend! Turn off the navigation bar and specify NO for animated, and you have an excellent way of switching screens on demand. You can push and pop view controllers if they're in a hierarchy, or you can prepare an array of view controllers (including an array containing a single VC) and set it to be the view stack using setViewControllers. This gives you total freedom to change VC's, while gaining all the advantages of working within Apple's expected model and getting all events etc. fired properly.
Here's what I do every time when I start an app:
start from a window-based app
add a UINavigationController as the window's rootViewController
add whatever I want my first UIViewController to be as the rootViewController of the nav
controller
(note starting from window-based is just a personal preference - I like to construct things myself so I know exactly how they are built. It should work fine with view-based template)
All events fire correctly and basically life is good. You can then spend all your time writing the important bits of your app and not messing about trying to manually hack view hierarchies into shape.

Resources