Autoresizing not working for UIView added programatically - ios

I have followed this SO link for autoresizing, but Autoresizing not working.
How to set frame programmatically with autoresizing?
I have set frame for iPhone 4
UIView *box = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 120)];
[box setBackgroundColor:[UIColor redColor]];
[box setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
[box setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
[box setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin];
[box setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
[box setAutoresizingMask:UIViewAutoresizingFlexibleRightMargin];
[self.view addSubview:box];
But it is not working in iPad or iPhone 6

First thing first : As per open suggestion, you should use constraints.
Add autoresizing mask after adding it to view.
UIView *box = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 120)];
[box setBackgroundColor:[UIColor redColor]];
[self.view addSubview:box];
[box setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
[box setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
[box setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin];
[box setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
[box setAutoresizingMask:UIViewAutoresizingFlexibleRightMargin];

If I understand you question correctly you're trying to set red view on top with height = 120
ADD EXPLANATION
You could achieve it with using constraints:
UIView *box = [[UIView alloc] init];
// Prevent creating constraints from masks automatically
box.translatesAutoresizingMaskIntoConstraints = NO;
box.backgroundColor = [UIColor redColor];
[self.view addSubview:box];
// Define metrics (constants) which will be used to create constraints.
// Key #"boxSize" - name which will be used in constraints, Value - constant
NSDictionary *metrics = #{#"boxSize" : #(120)};
// Define views that will participate in auto layout constraints.
// Key #"readBox" - name which will be used in constraints, Value - real UIView object
NSDictionary *views = #{ #"redBox" : box };
// Here we create constraints. For Vertical, and for Horizontal
// I'm using Visual language format (you can find it in Apple Documentation
// In a few words:
// H:|-0-[redBox]-0-|
// "H" - means horizontal
// "|" - short cut for parent view (in our case it is UIViewController.view)
// "[redBox]" - view name from view's dictionary
// "-0-" - gap between views (you could set number), in our case it is "|" and "[redBox]"
// "[redBox(boxSize)]" - means that view (redBox) size should be qual to "boxSize" from metrics' dictionary
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[redBox]-0-|" options:NSLayoutFormatAlignAllCenterY metrics:metrics views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[redBox(boxSize)]" options:NSLayoutFormatAlignAllCenterY metrics:metrics views:views]];
Apple Documentation

You are setting a UIView with a frame (0, 0, 320, 120). This frame will fit the iPhone 4 screen, as the phone screen width is 320 pixels. But you cant expect same when you run the code in iPhone 6/6s. Setting Autoresizing will not handle this. You need to use constraints/autolayout for that.
Autoresizing masks describe how a subview will resize or move when its superview is resized.
So after adding this view, if you change the phone orientation, this will resize the view in position accordingly. But you need to set the frame according to the superview first. You can set the width dynamically, like: (0, 0, self.view.frame.size.width, 120).

Related

How to create three views using autolayout, with one fix width height and other two growing height

I am trying to create a view setup vertically where i have one UIView (fixed width, height) at the top and two UILabels (fixed width, dynamic height) at the bottom. Padding all around the view (aView) is 5. Padding all around of _mylabel is 5. Padding on left and right of _yourLable is 5. _yourLable will grow as based on text, but when text content is too large, it will just stop to grow for maintain padding from superview of 5.
This is what i have tried:
- (void)viewDidLoad {
[super viewDidLoad];
UIView *superview = self.view;
UIView *aView = [UIView new];
[aView setBackgroundColor:[UIColor blackColor]];
[aView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:aView];
_mylabel = [[UILabel alloc]init];
[_mylabel setNumberOfLines:0];
[_mylabel setBackgroundColor:[UIColor redColor]];
[_mylabel setTranslatesAutoresizingMaskIntoConstraints:NO];
_mylabel.text = #"i am trying to create a view setup vertically where i have one UIView(fix width, height) at top and other two UILables(fix width, dynamic height) at bottom respectively. Padding on allaround of view/lables is 5. this is what i have tried:";
[self.view addSubview:_mylabel];
_yourLable = [[UILabel alloc]init];
[_yourLable setNumberOfLines:0];
[_yourLable setBackgroundColor:[UIColor redColor]];
[_yourLable setTranslatesAutoresizingMaskIntoConstraints:NO];
_yourLable.text = #"i am trying to create a view setup vertically where i have one UIView(fix width, height) at top and other two UILables(fix width, dynamic height) at bottom respectively. Padding on allaround of view/lables is 5. this is what i have tried:";
[self.view addSubview:_yourLable];
NSDictionary * views = NSDictionaryOfVariableBindings(aView,_mylabel, _yourLable, superview);
NSArray * heightConstraintforLabel = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-5-[aView(==200)]-5-[_mylabel]-5-[_yourLable]-5-|" options:0 metrics:nil views:views];
NSArray * widthConstraintforView = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-5-[aView]-5-|" options:0 metrics:nil views:views];
NSArray * widthConstraintformylabel = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-5-[_mylabel]-5-|" options:0 metrics:nil views:views];
NSArray * widthConstraintforyourLable = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-5-[_yourLable]-5-|" options:0 metrics:nil views:views];
[superview addConstraints:heightConstraintforLabel];
[superview addConstraints:widthConstraintforView];
[superview addConstraints:widthConstraintformylabel];
[superview addConstraints:widthConstraintforyourLable];
}
and
-(void)viewWillLayoutSubviews{
[super viewWillLayoutSubviews];
// Your layout logic here
CGFloat availableLabelWidth = _mylabel.frame.size.width;
_mylabel.preferredMaxLayoutWidth = availableLabelWidth;
availableLabelWidth = _yourLable.frame.size.width;
_yourLable.preferredMaxLayoutWidth = availableLabelWidth;
}
This is what i am getting, without warnings:
I want both labels to resize based on exact text height.
I want last red label to grow as per text written in it, but never go beyond bottom space of 5. That is it should grow but maintain bottom padding of 5.
I have tried various combination with vertical content compression for labels..., but not got exact solution.
Help :)
Just in case you have not set the priorities, use
[_mylabel setContentCompressionResistancePriority:751 forAxis:UILayoutConstraintAxisVertical];
[_mylabel setContentHuggingPriority:252 forAxis:UILayoutConstraintAxisVertical];
You don't need to set any priorities for _yourLable.
And you don't need to set the preferredMaxLayoutWidth for any of the labels in viewWillLayoutSubviews, hence you don't need to override viewWillLayoutSubviews. You can comment the whole method.
Verified on iOS 7 and iOS 9 devices.
Simulator screenshot looks like this,

Get frame of a view after autolayout

I have a method:
- (void)underlineTextField:(UITextField *)tf {
CGFloat x = tf.frame.origin.x-8;
CGFloat y = tf.origin.y+tf.frame.size.height+1;
CGFloat width = self.inputView.frame.size.width-16;
UIView *line = [[UIView alloc] initWithFrame:(CGRect){x,y,width,1}];
line.backgroundColor = [UIColor whiteColor];
[self.inputView addSubview:line];
}
That underlines an input UITextField; The textfield has a width that changes depending on the screen width (nib autolayout).
I have tried using
[self.view setNeedsLayout];
[self.view layoutIfNeeded];
and
[self.inputView setNeedsLayout];
[self.inputView layoutIfNeeded];
before I call this method with no change in result. the resulting line is much wider than the UITextField (it matches the original size in the Nib).
I just want the resulting frame of the UITextField in question after being processed by the autolayout
SOLUTION: (using 'Masonry' Autolayout)
- (UIView *)underlineTextField:(UITextField *)tf {
UIView *line = [[UIView alloc] initWithFrame:CGRectZero];
line.backgroundColor = [UIColor whiteColor];
[self.inputView addSubview:line];
[line mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.equalTo(tf.mas_centerX);
make.width.equalTo(tf.mas_width).with.offset(16);
make.height.equalTo(#1);
make.top.equalTo(tf.mas_bottom);
}];
return line;
}
Your underline view has a static frame, it is not connected to the textField through constraints. Instead of setting the frame, add constraints to self.inputView
- (void)underlineTextField:(UITextField *)tf {
UIView *line = [[UIView alloc] init];
line.backgroundColor = [UIColor whiteColor];
[line setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.inputView addSubview:line];
// Vertical constraints
[self.inputView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[line(==1)]-(-1)-|" options:0 metrics:nil views:#{ #"line": line}]];
// Horizontal constraints
[self.inputView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(-8)-[line]-8-|" options:0 metrics:nil views:#{ #"line": line}]];
[self.inputView layoutIfNeeded];
}
After the layoutIfNeeded call, the frame for your view should be right. I hoped I got the constants right. Because the line appears one unit under the textView make sure to unset Clip Subviews in the storyboard for the textField
I hope this works for you. Let me know if you have questions!
You could adjust the frame of the underline in viewDidLayoutSubviews which is called after auto layout has set all the frames.
Note that you're creating a new view and calling addSubview: each time. You should do that once when you create the text view, outside of this method. Otherwise you'll create a new UIView every time your user rotates the device.

UIScrollView with autolayout mixed approach?

I am trying to work an example code thats using the 'mixed approach' mention on apple dev link:
I am trying to stack 3 views vertically in UIScrollView. In below example the UIScrollView shows the red view on load but does not scroll as expected. I can scroll a little bit to see the green view below the red view - but the scroll view springs back up and does not scroll to the green view or view below it(blue view). I understand I need a constraint I tried to add one between view 1 & 2 so that view2.top = view1.bottom ,but seems I am missing something.
Also I noticed the content size of the scrollview is zero ( in viewDidAppear method).
Any tips on what I am missing or help on how to get this mixed approach working would be of great!!
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIScrollView* scrollView = ((UIScrollView*)self.view);
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
CGFloat w = self.view.frame.size.width;
CGFloat h = self.view.frame.size.height;
contentView = [[UIView alloc] initWithFrame:CGRectMake(0,0, w, h*3)];
[scrollView addSubview:contentView];
v1 =[[UIView alloc] initWithFrame:CGRectMake(0,0, w, h)];
v2 =[[UIView alloc] initWithFrame:CGRectMake(0,h, w, h)];
v3 =[[UIView alloc] initWithFrame:CGRectMake(0,h*2, w, h)];
v1.backgroundColor = [UIColor redColor];
v2.backgroundColor = [UIColor greenColor];
v3.backgroundColor = [UIColor blueColor];
[contentView addSubview:v1];
[contentView addSubview:v2];
[contentView addSubview:v3];
NSLayoutConstraint *myConstraint =[NSLayoutConstraint
constraintWithItem:v1
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:v2
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
[contentView addConstraint:myConstraint];
scrollView.contentSize = contentView.bounds.size;
}
I dont understand why you would need a constraint if you are just trying to stack views in a scroll view. I tried your code and it does not scroll well like you said, but I think it is being caused because you are using a UIScrollView as the main view for the view controller, is there a specific reason you want to do it like this?
I changed the code to instead add a UIScrollView to the normal UIView and it works perfectly as expected without using any complicated constraints. Just remember to define the scroll view at the top by using UIScrollView *scrollView;
- (void)viewDidLoad {
[super viewDidLoad];
scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:scrollView];
CGFloat w = self.view.frame.size.width;
CGFloat h = self.view.frame.size.height;
contentView = [[UIView alloc] initWithFrame:CGRectMake(0,0, w, h*3)];
[scrollView addSubview:contentView];
v1 =[[UIView alloc] initWithFrame:CGRectMake(0,0, w, h)];
v2 =[[UIView alloc] initWithFrame:CGRectMake(0,h, w, h)];
v3 =[[UIView alloc] initWithFrame:CGRectMake(0,h*2, w, h)];
v1.backgroundColor = [UIColor redColor];
v2.backgroundColor = [UIColor greenColor];
v3.backgroundColor = [UIColor blueColor];
[contentView addSubview:v1];
[contentView addSubview:v2];
[contentView addSubview:v3];
scrollView.contentSize = contentView.bounds.size;
}
I had to struggle alot with stupid pure based auto-layout approach for UIScrollView. I wanted a scrollview that is based on subviews that are auto-layout constraint based and works with rotation. In the end the following "Mixed Approach" code worked for me for a vertical scrolling and kept things simple:
- (void)viewDidLoad {
UIView *contentView;
contentView = [[UIView alloc] initWithFrame:self.scrollView.bounds];
//don't add UIViewAutoresizingFlexibleHeight as that messes things up but next line helps with rotation
contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[scrollView addSubview:contentView];
// DON'T change contentView's translatesAutoresizingMaskIntoConstraints,
// which defaults to YES;
// Set the content size of the scroll view to match the size of the content view
[scrollView setContentSize:CGSizeMake(contentWidth, contentHeight)];
/* the rest of your code here... pre auto-layout style and you can change the origins of subviews to position them within contentView */
[contentView addSubview:subView1];
[contentView addSubview:subView2];
}
The key point to realize is that DO NOT change size of contentView any point later after initialization and adding it to scrollview as that would cause its internal constraints to break but you can still continue to add subviews without worrying about its size.
You'll calculate the scroll view content size independently from size of contentView unless you use the systemLayoutSizeFittingSize: method which I haven't tested still

how to horizontally align 3 subviews of a UIView with NSLayoutConstraint visual format

I have the following setup:
A UIView with size (40,40)
3 subviews of this view, each with frames (0,0,40,40)
Now I'd like for this setup to have the following constraint hold at all times:
H:|-10-[v1]-10-[v2]-10-[v3]-10-|
Which implies the following desired behavior throughout the lifecycle of the superview:
Immediately after setting the constraints, the views should autolayout as shown above.
Since the superview itself is only (40,40), it should automatically resize to make the layout possible.
From there onwards, changing the width of any of the subviews should layout the views again so that they still stick to the layout format.
My approach so far is like so:
- (id)init
{
self = [super initWithFrame:CGRectMake(0, 0, 40, 40)];
if (self) {
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.v1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
self.v2 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
self.v3 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
[self addSubview:self.v1];
[self addSubview:self.v2];
[self addSubview:self.v3];
NSDictionary *views = #{#"v1" : self.v1, #"v2" : self.v2, #"v3" : self.v3};
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-10-[v1]-10-[v2]-10-[v3]-10-|" options:0 metrics:#{#"10":#10} views:views]];
[self layoutIfNeeded];
}
}
but I get the conflicting constraints message:
(
"<NSLayoutConstraint:0xed9f040 H:|-(10)-[UIView:0xed9ad30] (Names: '|':MyView:0xed9a040 )>",
"<NSLayoutConstraint:0xed9f270 H:[UIView:0xed9ad30]-(10)-[UIView:0xed9c370]>",
"<NSAutoresizingMaskLayoutConstraint:0xeda3770 h=&&& v=-&- UIView:0xed9c370.midX == 0.5*MyView:0xed9a040.width>",
"<NSAutoresizingMaskLayoutConstraint:0xeda37a0 h=&&& v=-&- UIView:0xed9c370.width == MyView:0xed9a040.width>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0xed9f270 H:[UIView:0xed9ad30]-(10)-[UIView:0xed9c370]>
My guess is that it has to do with the superview not being able to resize. But I'm overall somewhat confused about it. Does anyone know what am I doing wrong?
You want to make sure that for your programmatically created views that you set translatesAutoresizingMaskIntoConstraints to NO, e.g.:
self.v1.translatesAutoresizingMaskIntoConstraints = NO;
self.v2.translatesAutoresizingMaskIntoConstraints = NO;
self.v3.translatesAutoresizingMaskIntoConstraints = NO;
If you want these three subviews to be the same width as each other, spaced 10 apart, you might use a horizontal VFL of:
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-10-[v1]-10-[v2(==v1)]-10-[v3(==v1)]-10-|" options:0 metrics:nil views:views]];
Clearly, with spacing of 10 points between each of the subviews, the superview would have to be wider than 40 points wide. For example, if the superview is 100 points wide, once you factor out the 10 point spaces between the subviews (leaving 60 points of width for the subviews, themselves), the constraints system would make the subviews 20 points wide each.
And, if you want those to be 40 points tall, you might define vertical constraints (as well as define how far from the top of their superview you want them, too):
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[v1(40)]" options:0 metrics:nil views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[v2(40)]" options:0 metrics:nil views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[v3(40)]" options:0 metrics:nil views:views]];
This defines them to have zero offset from their superview, but a height of 40 points.
And, if you've defined the views based upon constraints, you don't need to use initWithFrame, but rather just use init. When using auto layout, you don't generally specify frame for anything, but rather let the constraints dictate the size.

Implementing auto layout for views generated programmatically

I have an app whose views are generated programmatically. Example:
-(void)loadView
{
[super loadView];
// SET TOP LEFT BTN FOR NEXT VIEW
UIBarButtonItem *topLeftBtn = [[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStyleBordered target:nil action:nil];
self.navigationItem.backBarButtonItem = topLeftBtn;
[topLeftBtn release];
// programmatically set up the view for cart tableView
CGRect iouTableViewFrame = CGRectMake(0, 0, 320, 348);
iouTableView = [[UITableView alloc]initWithFrame:iouTableViewFrame style:UITableViewStylePlain];
[[self iouTableView] setDelegate:self];
[[self iouTableView] setDataSource:self];
[[self view] addSubview:iouTableView];
// set up the summary label
CGRect summaryTableFrame = CGRectMake(0, 348, 320, 18);
UILabel *summaryTableLabel = [[UILabel alloc] initWithFrame:summaryTableFrame];
[summaryTableLabel setFont:[UIFont fontWithName:#"Helvetica" size:14]];
[summaryTableLabel setText:#" Summary"];
UIColor *labelColor = UIColorFromRGB(MiddleBlueColor);
[summaryTableLabel setBackgroundColor:labelColor];
[summaryTableLabel setTextColor:[UIColor whiteColor]];
[[self view] addSubview:summaryTableLabel];
// set up the summary table
CGRect summaryTableViewFrame = CGRectMake(0, 366, 320, 44);
summaryTableView = [[UITableView alloc]initWithFrame:summaryTableViewFrame style:UITableViewStylePlain];
[summaryTableView setScrollEnabled:NO];
[[self summaryTableView] setDelegate:self];
[[self summaryTableView] setDataSource:self];
[[self view] addSubview:summaryTableView];
}
Yes. I will update to NIBs and use interface builder and storyboard in the future but I have not done ios programming in a year.
With the new iPhone 5 having a different screen size, the app just does not look good and I need to implement auto layout of some sort. Is there a way to do it programmatically for now instead of using IB?
Thanks much!
Yes there is, by using two methods in NSLayoutConstraint
-(NSArray*)constraintsWithVisualFormat:options:metrics:views:
-(NSLayoutConstraint*)constraintWithItem:attribute:relatedBy:toItem:attribute:
multiplier:constant:
The visual format language is all packaged up into an NSString
So I'll take your iouTableView for example.
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"|[iouTableView]|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(iouTableView)]];
The pipe symbol "|" represents the superview's edge.
The [] represent a view.
So what we did there was we hooked the iouTableView's left and right edge to the left and right edge of its superview.
Another example of the visual format:
Let's hook your table view, summary label and summary table vertically.
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:
#"V:|[iouTableView(348)][summaryTableLabel(18)][summaryTableView(44)]"
options:NSLayoutFormatAlignAllLeft
metrics:nil
views:NSDictionaryOfVariableBindings(iouTableView, summaryTableLabel, summaryTableView)]];
Now this links up all three views vertically on each of their edges, NSLayoutFormatAlignAllLeft tells all the views to align left and they'll do so based on other constraints, in this case, the previous constraint.
The ()s are used to specify the size of the views.
There's a bit more like inequalities and priorities as well as the "-" spacer symbol but check out the apple docs for that
Edit: Corrected the examples to use constraintsWithVisualFormat as shown in the method signature.
In addition to Aplle provided methods you can use Parus lib for operating with AutoLayout from code.
For example you will be able to specify:
PVVFL(#"[view1]-20-[view2]").fromRightToLeft.withViews(views).asArray
Instead of
[NSLayoutConstraint constraintsWithVisualFormat:#"[view1]-20-[view2]"
options:NSLayoutFormatDirectionRightToLeft
metrics:nil
views:views]
Also you will be able to group layouts settings, mix VFL and not VFL constraints.
Parus able to prevent common mistakes, differentiate location and parameters constriaints, and provide great auto-completion support.

Resources