I have learned that the proper way to set a UITableView that covers up the whole main view (full width and full height), in a Single View app is, in viewDidLoad:
table = [[UITableView alloc] initWithFrame:self.view.bounds];
table.autoresizingMask = UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
[self.view addSubview:table];
note that if a physical iPad 2 is held at Landscape mode, and if the self.view.bounds above is printed inside viewDidLoad, it will show: {{0, 0}, {768, 1004}}. So I thought the idea is: don't worry the width and height not being correct, because the UIViewAutoresizingFlexibleWidth and UIViewAutoresizingFlexibleHeight will take care of setting the correct width and size automatically.
So I actually tried replacing the above first line by:
table = [[UITableView alloc] init];
or
table = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
or even
table = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 1004, 768)];
or the ultimately "correct value":
table = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 1024, 748)];
when the iPad 2 is held at Landscape position. But now the table won't fully expand to the whole main view. So what is the governing principle here? The width and height can be set incorrectly, but it must be incorrectly at {768, 1004}? It can't be "incorrect" with other values? If no CGRect was given, or some dummy values {200, 200} was given, what should the code in viewDidLoad do to make the table have the full main view's width and height whether it is Landscape or Portrait mode?
dont use bounds, use frame instead:
table = [[UITableView alloc] initWithFrame:self.view.frame];
table.autoresizingMask = UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
UIAutoresizingFlexibleWidth means that the view's width will expand and shrink when its superview's width expands and shrinks. UIAutoresizingFlexibleHeight means the same thing for height. Adding these is equivalent to turning on "springs" in Interface Builder. I think that if there are no "struts" set, when the superview doubles in width, the view will double in width as well.
The strut settings are:
UIViewAutoresizingFlexibleRightMargin
UIViewAutoresizingFlexibleLeftMargin
UIViewAutoresizingFlexibleTopMargin
UIViewAutoresizingFlexibleBottomMargin
If UIViewAutoresizingFlexibleRightMargin is NOT set, the right strut is turned on. This means that the space between the right side of your view and the right side of its superview will not change when the view expands. The same concept holds for all the other margins.
There is as good discussion here: https://stackoverflow.com/a/10470469/472344.
I think you are confused by the self.view.bounds that you are getting. This is the bounds of the view before rotation, as jrturton pointed out the height is 1004 because 20 pixels are subtracted for the status bar. I don't know why after rotation the width and height haven't updated (I'm assuming your rotation happens before loading the view, otherwise the printed out bounds are exactly what you should expect), but I have noticed this as well after rotation. I have seen some discussion on Stackoverflow about this, but I can't find it at the moment.
My guess is that as jrturton said, when the view is loaded, it uses the bounds set in the xib (or the storyboard), and the resizing happens after viewDidLoad. Maybe try printing out the bounds of self.frame.view in viewDidAppear, as the bounds should have changed at that point.
To address the behaviour of the auto resizing mask settings again, understand that these do NOTHING unless the view's superview changes size. The auto resizing mask settings will tell your view how to automatically resize when its superview changes size. If you want your table view to have the same size as self.view always (such as after rotation, or zooming), you need to set it exactly as you have done:
table = [[UITableView alloc] initWithFrame:self.view.bounds];
table.autoresizingMask = UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
[self.view addSubview:table];
1004 is correct - the status bar is 20px. Your first code snippet is fine.
Note that in viewDidLoad your view's orientation will still be in portrait - hence the numbers you are seeing. The resize happens afterwards. Using the bounds rectangle of the main view as the frame of a subview will always let the subview fill the whole view.
Related
I'm trying to add a UIToolbar to the bottom of a screen within my app. But I'm having some difficulties.
This is the code:
self.toolBar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0f, self.view.frame.size.height-44.0f, self.view.frame.size.width, 44.0f)];
self.toolBar.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.toolBar];
However, when the device is rotated the UIToolbar is offscreen. How do I use NSAutoLayout solve this?
You could add
self.toolBar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
That will generate the correct constraints, since by default for code-created UIViews, translatesAutoresizingMaskIntoConstraints is turned on. A resizing mask of 0 will almost never do what you want, so if you are setting frames yourself, you always need to set the mask correctly as well (since that determines what will happen to the frame when the superview size changes). The pattern would be 1) set frame; 2) set mask; 3) call addSubview.
Alternatively, you could set translatesAutoresizingMaskIntoConstraints to NO, don't bother with calculating a frame, and create equivalent layout constraints yourself (height constraint of 44, left/right/bottom constraints to touch the superview).
Im practicing objective-C, and I try to do everything programmatically.
I'm making a simple view that I add on my view of the ViewController, but this subview is going out of the screen.
When I set my frame, the position for the X and Y are respected, but the rest, no...
Here is the screenshot of my result :
As you can see... The red subview is going out of the screen.
Here is my loadView where I add that subview :
HomeViewController.m - loadView
-(void)loadView
{
self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIView *subview = [[UIView alloc] initWithFrame:CGRectMake(15, 15, self.view.frame.size.width - 30, self.view.frame.size.height - 30)];
[subview setBackgroundColor:[UIColor redColor]];
[self.view addSubview:subview];
}
For the padding, I did put 15 for the position x and y... And for the frame, I did calculate with the size of the self.view by removing paddings... As you see, it works well for the width, but for the height, it is a big fail. It goes outside the screen.
In my AppDelegate.h, I set the navigationController.navigationBar.translucent = NO;, in order to that when I set position for x, and y, it starts well after the navigationBar .
I don't understand this weird behavior for the height... If someone has a good explanation for this please.
Thanks
First, you shouldn't rely on the value of self.view in viewDidLoad. It is set to a correct value later, in viewWillAppear:. You can keep your code, if you make your subview resize automatically when self.view is displayed. For that, set autoresizingMask on the subview:
subview.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
(or add an equivalent set of constraints if you use Auto Layout.)
Also, I recommend to use bounds instead of frame:
UIView *subview = [[UIView alloc] initWithFrame:CGRectMake(15, 15, self.view.bounds.size.width - 30, self.view.bounds.size.height - 30)];
It doesn't make a difference here, but it often does, e.g. if you calculate the frame's x and y based on the parent frame.
loadView method just creates the view. At the point when loadView gets called there is no information about final view frame hence its children views cannot be placed properly.
The right place to update your children views' frames is viewDidLayoutSubviews:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
// update child view frame here
}
Remarks: you can define auto-layout constraints of your child view in code and they will be automatically applied to child views when view controller's view gets resized.
How would I get the actual dimensions of a view or subview that I'm controlling? For example:
UIView *firstView = [[UIView alloc] initWithFrame:CGRectMake(0,0,200,100)];
[self addSubview:firstView];
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 20, 230, 120)];
[firstView addSubview:button];
As you can see, my button object exceeds the dimensions of the view it is being added to.
With this is mind (assuming there are many objects being added as subviews), how can I determine the actual width and height of firstView?
If you would like to have the button have the same width and height as firstView you should use the bounds property of UIView like this:
UIView *firstView = [[UIView alloc] initWithFrame:CGRectMake(0,0,200,100)];
[self addSubview:firstView];
UIButton *button = [[UIButton alloc] initWithFrame:firstView.bounds];
[firstView addSubview:button];
To just get the width/height of firstView you could do something like this:
CGSize size = firstView.bounds.size;
NSInteger width = size.width;
NSInteger height = size.height;
The first view really is 200 x 100. However, the views inside are not clipped unless you use clipsToBounds.
A preferable solution to your problem is to stop adding views that exceed the dimensions or to stop resizing views so that they overflow the available space, as appropriate.
(To actually calculate the full bounds including all subviews in the hierarchy is not hard, just a lot of work that won't actually get you anywhere. UIView happens to allow views to be larger than their containers, but it doesn't necessarily give any guarantees about what will work correctly and is likely to be a source of bugs if taken advantage of. The reason for clipsToBounds is probably that clipping is expensive but necessary during animation where subviews may be temporarily moved out of bounds for the purposes of an effect before they are removed from the view.)
firstview.frame.size.width
firstview.frame.size.height
I am trying to better understand how to control the size of Views in terms of older iphones and the new iphones difference in size.
If I have a design like the below example, how should this be coded programatically in terms of the subviews.
Excuse the badly drawn diagram but it should help to explain.
In this example, the fieldView and buttonView would always need to remain a fixed size as they have objects which would not look great when made smaller. However the logoview has another sub view for the logo itself, so could be shrinked depending on device/screen size.
How would this be accomplished? Setting up the example subviews programatically. The part I do not understand is that in viewDidLoad where the subviews are created would you not have to create in order like this:
-(void)ViewDidLoad {
CGRect screen = [[UIScreen mainScreen] applicationFrame];
wholeView = [[UIView alloc] initWithFrame:CGRectMake(self.view.bounds.origin.x, self.view.bounds.origin.y, self.view.bounds.size.width, self.view.bounds.size.height)];
logoView = [[UIView alloc] initWithFrame:CGRectMake(0,0,320,150);
fieldView = [[UITableView alloc] initWithFrame:CGRectMake(0, logoView.bounds.size.height, 320, 100);
I understand about using
autoresizingMask
but how would it come into use in terms of working out a height depending on the actual view size available?
Not sure I understand your doubts, but adding the following should do the trick:
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
logoView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
The remaining view would keep both their margins and height/width fixed (i.e., the default value of UIViewAutoresizingNone for their autoresizingMask is fine.)
how would it come into use in terms of working out a height depending on the actual view size available?
basically, what you see on your iPhone display is a hierarchy of views; the topmost view in this hierarchy is a UIWindow. This has the same size as the device screen (it is initialized that way).
Now, in the code above, our logoView has fixed margins: this means that it will remain at the same distance from the container view frame; if we specify that is size is flexible, then the logoView will simply occupy the whole space to keep the margins fixed.
I faced a strange problem, the scrollview does not scroll down, only scroll up. I have scrollview in my app, please look at my coding
.....
self.scrollView = [[UIScrollView alloc] initWithFrame: CGRectMake(0, 0, 320,427)];
[self.view addSubViews: self.scrollView];
UIView *blueView = [[UIView alloc] initWithFrame: CGRectMake(0, 47, 320, 320)];
blueView.backgroundColor = [UIColor blueColor];
[self.scrollView addSubViews: blueView];
self.scrollView.contentSize = CGSize(320, 640);
....
My problem is no matter what value I changed contentSize, my ScrollView only scroll up, not scroll down. I want user can move blueView to the top or bottom of iPhone screen from the original position.
do you have this problem?
The Problem
It looks like your issue is with how you're orienting blueView within scrollView. You're setting the frame of blueView to the CGRect (0, 47, 320, 320). When you set the frame like this, one of the things you're implicitly saying is:
The top edge of blueView is 47 points below the top edge of scrollView.
That's a perfectly valid thing to say, but it's what's causing the problem you describe. scrollView won't scroll down because it is designed to start, by default, with the rect (0, 0, 320, 480) in view. The contentSize property only indicates the size of the content within the UIScrollView, not its positioning. When you set it, you're basically telling scrollView:
Starting from your content origin, the content is 320 points wide and 640 points tall.
Thus, scrollView won't scroll up because, as far as it knows, there's no content above the coordinate (0, 0).
The Solution
There are three steps you'll need to take to get the functionality you want.
Set the contentSize to be just big enough to allow blueView to scroll all the way up and down.
Put blueView in the vertical center of scrollView.
Scroll the scrollView so that it is initially centered on blueView.
Set the contentSize to be just big enough to allow blueView to scroll all the way up and down.
We'll want to calculate the correct value of the contentSize property. It is of the type CGSize, so we need two parts: width and height. width is easy – since you don't seem to want horizontal scrolling, just make it the width of the screen, 320. Height is a little more tricky. If you want blueView to just touch the top and bottom of the screen when scrolled up or down, you need to do some math. The correct total height will be double the height of the screen, minus the height of blueView. So:
scrollView.contentSize = CGSizeMake(320, 480 * 2.0 - blueView.frame.size.height);
Put blueView in the vertical center of scrollView.
That's easy; just set the center property of blueView:
blueView.center = CGPointMake(160, scrollView.contentSize.height / 2.0);
Scroll the scrollView so that it is initially centered on blueView.
If you check the Apple UIScrollView documentation, you'll see an instance method - (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated. This is exactly what you need to scroll scrollView programmatically. The rect you want is the one centered on blueView, with the size of the iPhone screen. So:
CGRect targetRect = CGRectMake(0, scrollView.contentSize.height / 2.0 - 240,
320, 480);
[scrollView scrollRectToVisible:targetRect animated:NO];
Make sure you do this scrolling in viewWillAppear, so it's ready right when the user sees the view.
That should be it. Let me know if you have any questions!
The content size of the scrollView should be the size of the view it is holding. This is how the code should be, try something like this.
self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(X, Y, W, H1)];
UIView * blueView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, W, H2)];
self.scrollView .contentSize = blueView.frame.size;
[self.scrollView addSubview:blueView];
[self.view addSubView: self.scrollView];
Thanks to Riley. Here, the H1 is the height of the UIScrollVIew and H2 is the height of the blueView and (H1 < H2).