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).
Related
I want to custom the separator line view like that, but I'm still stuck here. Facebook app make the separator line view is a good looking. I want to archive something like that. Can any one help me?
I try with set frame and update constraint, but it does not work on all devices (iPhone 4s, iPhone 5, iPhone 6, iPhone 6 Plus).
I use both constraints and custom frame for UIView to make it works like a separator line view, the height is 0.5 for constraint's constant or frame's height. But it does not like the UITableView separator. The height is not really thin like Facebook app does and sometimes it does not show.
Thanks
UIView * separtor = [[UIView alloc] initWithFrame:CGRectMake(0, 0, cell.contentView.frame.size.width, 1)];
separtor.backgroundColor = [UIColor whiteColor];
[cell.contentView addSubview:separtor];
Hi there,
You can still add a really thin and empty UIView with a background color you desire.
Create a custom cell / Prototype cell and design it according to the requirement.
At the bottom of the designed custom cell, add a view(with minimum constant height) and set its size colour and other parameters according to the UI need.
Set constraint to it (Prefer giving predefined height to the separator view) so that visual will be same and adopt the width for different devices.
Finally, remove the default separator which will be set by the table view.
i.e: Set 'separator' property to 'None' for table view.
Try this
UIView* separatorLineView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 3)];/// change size as you need.
separatorLineView.backgroundColor = [UIColor whiteColor];// you can also put image here
[cell.contentView addSubview:separatorLineView];
Hope it helps
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
UIView* separatorLineView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, screenWidth, 3)];/// change height as you need.
separatorLineView.backgroundColor = [UIColor whiteColor];// you can also put image here
[cell.contentView addSubview:separatorLineView];
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 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.