Best practices for framing a hierarchy of views - iphone

From other stack overflow questions, I understand that in your view controller, you can use the viewDidLoad method to initialize subviews and the viewWillLayoutSubviews method to set the frames. This implies that we need to keep a reference of all the views within the view controller instance (as below). This is all fine and great and handles device rotations, etc.
UILabel* myLabel;
- (void)viewDidLoad
{
myLabel = [[UILabel alloc] init];
[myLabel setText:#"Hello"];
}
- (void)viewWillLayoutSubviews
{
[myLabel setFrame:CGRectMake(0, 0, self.view.frame.size.width, 21)];
}
My question is what if you have a complex hierarchy of subviews. For example, what if you have a subview which is a UIView and you add subviews to that UIView (and potentially it even goes deeper). Do you have to keep a reference to every view in this whole view hierarchy so that on viewWillLayoutSubviews, you can modify the frame of each view? This seems like the only way. Are constraints the best way so that you don't have to modify frames as often? Is the best answer to build tons of custom UIViews to make managing this hierarchy more manageable and self-contained within each custom view?
Hopefully this is a clear enough question..thanks in advance.

Anytime you manipulate a view or subview, in a way that could change the layout constraints, you have to call
[view layoutSubviews];
this will remake the constraints with the new property settings. When the viewController is loaded, you would set all properties not dealing with the frame or bounds and then with in the viewWillLayoutSubviews you set the frames of the views this is so all the constraints are made and ensured that they are met.
Any object you put in the storyboard that you want to use, should have a property this will hold a reference to that object. This is a good coding practice, especially if you follow the rules of test driven development.
Now to answer your questions, if you create the complex hierarchy in the storyboard then everything should be fine as long as your constraints are fine, if you make it in code just be sure to call layoutSubviews when ever you call addSubview
Example:
UIView* view1 = [[UIView alloc] initWithFrame:CGRectZero];
UIView* view2 = [[UIView alloc] initWithFrame:CGRectZero];
[view1 addSubview:view2];
[view1 layoutSubviews];
More views does not make the structure better, but if you have 100 subviews... lets be real.. something needs to be categorized in a separate view..
Constraints let you define the frames of views perfectly. You might not notice the differences at first, but if you compare it to Autoresizing Mask then you will see the differences. Also, here is a guide that blatantly shows you the differences.
LayoutConstraints and Autolayout

Also worth considering is using child view controllers if you wish to compartmentalize some of the logic and reuse components.

Related

On iOS, a custom view won't animate?

In a new Single View App, the following code:
- (void)viewDidAppear:(BOOL)animated {
self.fooView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
self.fooView.backgroundColor = [UIColor yellowColor];
[self.view addSubview:self.fooView];
[UIView animateWithDuration:2 animations:^{
self.fooView.backgroundColor = [UIColor blueColor];
}];
}
will animate the yellow background to blue. The fooView is just:
#property (strong, nonatomic) UIView *fooView;
But if a custom view class FooView is defined, and I change the above code above by replacing UIView with FooView, and also define an empty -drawRect for FooView, then there will be no animation -- the background is just changed to blue immediately.
It made no difference if using [UIView animateWithDuration: ...] or [FooView animateWithDuration: ...] -- it is no animation.
If inside -drawRect, I call [super drawRect:rect];, then it is the same thing: no animation.
If I change drawRect to drawRect2, or if I remove the whole method, then the animation will work.
(and the -drawRect might be needed, for example, what if we need to draw a circle in the custom view FooView?)
Why is that? Besides giving a solution of how to make it work even with a -drawRect, please explain what was going on that made it not animate?
The presence of drawRect: changes the way UIView operates. You should only use drawRect: when absolutely necessary and you should never have an empty drawRect: implementation.
If the -drawRect: method is present in your subclass then UIView must do a number of things before it can call this method. Namely, it must allocate a potentially expensive backing store into which your custom drawing will occur, and it must fill this backing store with the background color.
UIView will then call your drawRect: implementation to do any custom drawing on to the backing store, before handing the backing store to the GPU. Only after all that does your animation block begin to run.
So at the start of your animation the backing store that represents your custom UIView has already been painted a new background color.
If your UIView subclass has no drawRect: method then the view has no backing store. The background color is handled directly by the GPU, which can very efficiently animate transitions between colors.
Empty UIView objects (without a drawRect: implementation) are rather lightweight. Your custom view should be composed of multiple subviews depending on what animation effects you want to achieve.
For example, the UITableViewCell class is composed of many subviews. UITableViewCell itself is a UIView. It contains a background view and a selected background view. On top of that is the content view, and inside the content view are various text labels and image views. All of these views combine to produce the selection and resize animations you see in a regular UITableView.
Using 'CATransition' class .You will have to be do animation.
Using this method
+(CAAnimation *)setAnimation{}
Use following line in animation Block
[self.view addSubview:self.fooView];
Instead of this we have to create a new class and using the CATransition and using this class methods '+(CAAnimation *)setAnimation'.We have to create an object for this and call when will transition happen

iOS: xcode instance of a subview

I have a view with a button and 8 labels, i want to repeat this view with input from the user to customize these labels, so there will be this base view displayed multiple times on the screen and i don't want them to cover up each other, the button and placement of the labels are the same of each view.
How would i programmatically make a new view appear after user input of the instance view and make sure it doesn't cover up any other view, i hope this isn't to broad, i just want to have one set view with a button and 8 labels, copy it multiple times and display the user input, thanks.
If I'm understanding what you are looking for, you want the display to scroll up showing all the different views that have been created by the user, one after the other.
To accomplish this, you can use a UIScrollView and programmatically add the views to the scroll view as needed. Make sure to increase the contentSize of the scroll view to account for the added views.
Here is some code:
UIScrollView *scrollview = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
//create and add as many of yourView as necessary for your project - y would be the the offset so it gets displayed under the other views
YourCustomView *yourCustomView = [[YourCustomView alloc] initWithFrame:CGRectMake(0, y,self.view.frame.size.width, self .view.frame.size.height)];
//populate yourCustomView with the appropriate information...
[scrollview addSubview:yourCustomView];
//when you are done adding your views - w and h are whatever your content dictates they are
scrollview.contentSize = CGSizeMake(w, h);
[self.view addSubview:scrollview];
Alternatively, depending on your setup and design, you could use a UITableView with a custom UITableViewCell that displays the pertinent info.
Hope that helps!

iPhone. How to get the rect of parent view?

My application creates two views:
topView (CGRect = 0,0, 320,60)
bottomView (CGRect = 0,60, 320,480)
Bottom view creates UITabBarController with UIViewControllers:
ListViewController
etc...
ListViewController has own views that are created in viewDidLoad method:
background = [[UIImageView alloc] initWithFrame: rect ];
So my question is how to get the bottomView rect inside ListViewController?
I want to layout all controller views without intersection with topView.
Thank you.
Might I suggest you read the HIG. Spend a lot of time there as it will answer many design questions that your question shows you don't yet grasp. If you're not able to lay things out with standard controls and positions, you're probably going about it the wrong way.
That being said, your question in the content of your post seems to diverge from the title. If you simply want the parent view's rectangle, use:
CGRect parentRect = [[[self view] superview] frame];
in your view controller.
And when you say ListViewController, is this a view controller you created yourself, one you got from a library somewhere, or do you mean UITableViewController?
I suggest you don't layout your controls with code if you can use Interface Builder instead. That's not always possible, but it's a good practice to use IB when it is.

how to add uiview as a subview to existing view

I want to add a UIView of smaller frame as subview to parental view but I am not getting the needed.
UIView *view = [[UIView alloc] initWithFrame:something];
[self.view addSubview:view];
Can anyone suggest me the answer?
Here you are adding a blank view to another view. Maybe you don't see it because it is blank?
What is "something"?
Have you tried - (void)bringSubviewToFront:(UIView *)view? It might be that you're adding this subview somewhere in the view hierarchy where it's obscured. In your code fragment there, you would add:
[self.view bringSubviewToFront:view];
Of course, without supplying more code (preferably directly from your project, and which actually compiles), I'm just guessing. What were you expecting to happen? What actually happened?

Sending view to back

When I try to send a view to the back, it hides some of the buttons and labels in my view controller. The view I am sending to the back is a UIImageView. Does anyone have an opinion of what might be the problem?
Here is the code I am using:
UIImage *image = [UIImage imageNamed: #"background.jpg"];
UIImageView *backImage = [[UIImageView alloc] initWithImage: image];
[self.view addSubview: backImage];
[self.view sendSubviewToBack: backImage];
Then, when I am adding controls to self.view, they does not always show
I managed to get it roght by moving my code from init to loadView. I don't understand why that should make a difference, but hey.. it works!
If you are using UIView's sendSubviewToBack: or a similar message, you probably have your buttons inserted in the hierarchy under the UIImageView. When a view moves in the hierarchy, all of its subviews move with it.
To fix this, you need to add your controls as subviews of the same view (possibly the UIWindow) you added the UIImageView to initially.
Without seeing your code, it's very difficult to be more precise.
By not having to add it programmatically and adjust the views, much easier to layer each one in the interface builder. Make sure the image view, if this is set as the background, make sure it is the first to be listed.
Agreed. Not quite sure by what you mean by "send to the back" but here's a guess...
If you are adjusting the layering within your main view, be sure you are not sending a view "to the back" (changing it's layer) that has number of subviews... or else they would all go to the back (their layering would change) at the same time.
If this is totally not what you meant, just let me know, and I'll delete this answer.