Auto Layout with UITextField causes exception - ios

I'd like to position a UITextField inside a UIViewController view using auto layout (dynamic resizing and a 5 pt margin between the textfield and its superview). I create the view programmatically using the loadView method.
Header of UIViewController
#property (weak, nonatomic) UITextField *textField;
Implementation
-(void)loadView{
//Frame
UIView *frame = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 100, 40)];
[self setView:frame];
[self.view setBackgroundColor:[UIColor redColor]];
//TextField
[self.textField setDelegate:self];
[self.textField setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.textField setBackgroundColor:[UIColor blueColor]];
[frame addSubview:self.textField];
[frame addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(5#50)-[textField(==10#10)]-(5#50)-|"
options:0
metrics:nil
views:#{#"textField" : self.textField}]];
[frame addConstraints:[NSLayoutConstraint constraintsWithVisualFormat: #"V:|-(5#50)-[textField(==10#10)]-(5#50)-|"
options:0
metrics:nil
views:#{#"textField" : self.textField}]];
// //Test with UIView instead
// UIView *sub = [[UIView alloc]init];
// [sub setTranslatesAutoresizingMaskIntoConstraints:NO];
// [sub setBackgroundColor:[UIColor greenColor]];
// [frame addSubview:sub];
//
// [frame addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(5#50)-[sub(==10#10)]-(5#50)-|"
// options:0
// metrics:nil
// views:#{#"sub" : sub}]];
//
// [frame addConstraints:[NSLayoutConstraint constraintsWithVisualFormat: #"V:|-(5#50)-[sub(==10#10)]-(5#50)-|"
// options:0
// metrics:nil
// views:#{#"sub" : sub}]];
Running this throws an exception. Curiously using a UIView ("sub") as subview instead (the part commented out) does work as expected.
Ouput:
Shouldn't the textfield behave just like this UIView? How come the textField causes an exception?
New finding: The app crashes only when I use addConstraints on the self.textField. If I remove those two calls I spare the crash, however the textfield doesn't show up either.

NSLayoutConstraint is only part of IOS 6. if you try to work this code other than IOS6
( such as IOS 5, IOS 4.3) you are highly likely get this runtime error.

Related

Subview width not fitting superview with autolayout

I am adding a subview to a UICollectionViewCell. I am programmatically adding constraints to make it fill the cell, however the width does not fill the cell.
When I view it in the view debugger, it says the position is ambiguous. How can this be since I am specifying all 4 sides are pinned to the superview?
This is what the views look like in the debugger. The inner, white view should fit the parents width (blue border):
Inspecting the constraints on the parent view shows this with the "position ambiguous" warning:
The code I am using is as follows:
[self.contentView addSubview:calloutView];
calloutView.translatesAutoresizingMaskIntoConstraints = NO;
self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[calloutView]-0-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(calloutView) ]];
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[calloutView]-0-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(calloutView) ]];
The problem seems to be related to the use of contentView, you must use the cell itself to achieve what you want, this is the code and works, check the picture below
#import "CollectionViewCell.h"
#interface CollectionViewCell()
#property UIView * testView;
#end
#implementation CollectionViewCell
#synthesize testView;
-(void)awakeFromNib
{
[super awakeFromNib];
testView = [[UIView alloc]initWithFrame:CGRectZero];
testView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:testView];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-2-[testView]-2-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(testView) ]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-2-[testView]-2-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(testView) ]];
self.testView.layer.borderWidth = 2;
self.testView.layer.borderColor = [UIColor blueColor].CGColor;
}
#end
I hope this helps you, best regards

Align UIButtons horizontally using Auto layout (programmatically)

I'm trying to place 2 UIButtons horizontally but no luck
I'm using auto layout constraints programmatically.
I don't want to put them in a container and align center to that container.
NSDictionary *metrics = #{#"kLeftPadding":#(kLeftPadding),
#"kRightPadding":#(kRightPadding),
#"kMiddlePadding":#(kMiddlePadding),
#"kBottomPadding":#(kBottomPadding)};
NSDictionary *constrainedViews = #{#"titleLabel": self.titleLabel,
#"btnToday" : self.btnToday,
#"btnPickaDay" : self.btnPickaDay,
#"dividerView" : dividerView};
NSArray *contraints = #[#"V:|-[titleLabel]-kBottomPadding-[btnToday(==40)]-[btnPickaDay(==btnToday)]-kBottomPadding-[dividerView]-0-|",
#"H:|-kLeftPadding-[titleLabel]-|",
#"H:|-kLeftPadding-[btnToday]-kMiddlePadding-[btnPickaDay(==btnToday)]-kRightPadding-|",
#"H:|-0-[dividerView]-0-|"];
second button starts from where first ends but in next line, not on same.
I've tried your code but it's getting crashed so as per your question you simply need to write below code.
UILabel *lblText = [UILabel new];
[lblText setText:#"Some Title Text"];
[lblText setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:lblText];
UIButton *btnRed = [UIButton new];
[btnRed setBackgroundColor:[UIColor redColor]];
[btnRed setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:btnRed];
UIButton *btnGreen = [UIButton new];
[btnGreen setBackgroundColor:[UIColor greenColor]];
[btnGreen setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:btnGreen];
UIView *vwLine = [UIView new];
[vwLine setBackgroundColor:[UIColor blueColor]];
[vwLine setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:vwLine];
NSDictionary *dictViews = #{#"red":btnRed,#"green":btnGreen,
#"line":vwLine,#"lbl":lblText};
NSDictionary *dictMetrics = #{#"height":#(40),#"offset":#(-40)};
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-20-[red]-0-[green(==red)]-20-|" options:0 metrics:nil views:dictViews]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-20-[line]-20-|" options:0 metrics:nil views:dictViews]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-20-[lbl]-20-|" options:0 metrics:nil views:dictViews]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(height)-[lbl]-(height)-[red(height)]-(offset)-[green(==red)]-50-[line(2)]" options:0 metrics:dictMetrics views:dictViews]];
result:
Hope it will help you solve your problem. If any more query do let me know.

Not able to set the width of button with autolayout

I am new in auto layout and used auto layout in scrollview i have some simple setup in UIScrollView but i don't achieve it.
First think i need to discuss is that i used auto layout in coding not in storyboard.
I have 20 button of same width and height and want to setup in UIScrollView.
But surprise my button width set as per contain insert into it. I try lots R&D but not able to find the solution
My code is following
UIScrollView *sview = [[UIScrollView alloc] init];
[sview setBackgroundColor:[[UIColor blackColor] colorWithAlphaComponent:0.7]];
[sview setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:sview];
NSDictionary *dict = NSDictionaryOfVariableBindings(sview);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[sview]|" options:0 metrics:nil views:dict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-60-[sview]-|" options:0 metrics:nil views:dict]];
setup scrollview whose width is same as view
NSMutableDictionary *dictValues = [NSMutableDictionary dictionaryWithDictionary:#{#"xpos":#10,#"ypos":#10,#"height":#30}];
UIButton *previousBtn;
for (int i=0; i<20; i++)
{
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setTitle:[NSString stringWithFormat:#"%d",i+1] forState:UIControlStateNormal];
[btn setBackgroundColor:[UIColor redColor]];
[btn setTranslatesAutoresizingMaskIntoConstraints:false];
[sview addSubview:btn];
if (previousBtn == nil)
{
[sview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[btn]-|" options:0 metrics:dictValues views:#{#"btn":btn}]];
[sview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(ypos)-[btn(height)]" options:0 metrics:dictValues views:#{#"btn":btn}]];
}
else
{
[sview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[btn]-|" options:0 metrics:dictValues views:#{#"btn":btn}]];//Look here i set button width same as a scrollview width but not able to do that
[sview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[previousBtn]-10-[btn(height)]" options:0 metrics:dictValues views:#{#"btn":btn,#"previousBtn":previousBtn}]];
}
previousBtn = btn;
}
NSDictionary *dictBtn = NSDictionaryOfVariableBindings(previousBtn);
[sview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[previousBtn]-|" options:0 metrics:dictValues views:dictBtn]];
[sview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[previousBtn(height)]-|" options:0 metrics:dictValues views:dictBtn]];
Here are screenshot of this code
In portrait
In Landscape
Look in screen shot button is same of the text size not like scroll view and one more thing i am used pure autolayout

UIScrollView using pure auto layout not expanding right

I've read the official documentation from Apple on auto layout and UIScrollView:
RN-iOSSDK-6_0
I'm trying the "Pure Auto Layout approach".
I'm adding a UILabel and a UITextField. I want both to expand to the width of the interface:
RootViewController.h
#interface rootViewController : UIViewController
#property (nonatomic, strong) UIScrollView *scrollView;
#property (nonatomic, strong) UIView *contentView;
#property (nonatomic, strong) UILabel *bigLetters;
#property (nonatomic, strong) UITextField *aTextField;
#end
viewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
self.view.backgroundColor = [UIColor lightGrayColor];
_scrollView = [[UIScrollView alloc] init];
[_scrollView setTranslatesAutoresizingMaskIntoConstraints:NO];
_contentView = [[UIView alloc] init];
[_contentView setTranslatesAutoresizingMaskIntoConstraints:NO];
_bigLetters = [[UILabel alloc] init];
[_bigLetters setTranslatesAutoresizingMaskIntoConstraints:NO];
_bigLetters.font = [UIFont systemFontOfSize:30];
_bigLetters.textColor = [UIColor blackColor];
_bigLetters.backgroundColor = [UIColor clearColor];
_bigLetters.text = #"Why so small?";
_bigLetters.textAlignment = NSTextAlignmentCenter;
_aTextField = [[UITextField alloc] init];
_aTextField.backgroundColor = [UIColor whiteColor];
_aTextField.placeholder = #"A Text Box";
[_aTextField setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:_scrollView];
[self.scrollView addSubview:_contentView];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_scrollView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_scrollView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_scrollView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_scrollView)]];
[self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_contentView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_contentView)]];
[self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_contentView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_contentView)]];
[self.contentView addSubview:_bigLetters];
[self.contentView addSubview:_aTextField];
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[_bigLetters]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_bigLetters)]];
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[_aTextField]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_aTextField)]];
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[_bigLetters]-[_aTextField]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_bigLetters, _aTextField)]];
// Do any additional setup after loading the view.
}
I get both elements squished against the top left corner like this:
If I switch _scrollView to a UIView, things look fine. What am I doing wrong?
Thanks!
UIScrollViews are a little bit complicated.
Firs of all:
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
You should not be doing that. Your root view does not need to work with autolayout. Your subviews need it.
Also, the contentSize of the scrollview is defined by what you put inside of them. If you want to have a scrollView screen-sized fix one of it's subviews to screen width (or eight).
What you are seeing there, is your scrollview wrapping up it's content, and the width is probably the label intrinsic content size.
That's why it works with a View.
Hope it helps!!

UIScrollView with iOS Auto Layout Constraints: Wrong size for subviews

I'm trying to generate a view in code. Here's the hierachy of my view object
UIScrollView
UIView
UIButton
The ScrollView should be the same size as the window.
The button should be as big as possible.
I'm using iOS auto layout, so the constraint strings for all of my objects look like this
H:|[object]|
V:|[object]|
I've also set translatesAutoresizingMaskIntoConstraints to NO for each object.
The problem is that the button only gets the default button-size. Its parent view object (UIView) only gets the size its subviews need.
red: UIScrollView / yellow: UIView
How can I force those views to be as big as the scrollView?
When I use a UIView instead of th UIScrollView everything works great...
Here's some code:
- (void) viewDidLoad {
[super viewDidLoad];
// SCROLL VIEW
UIScrollView* scrollView = [UIScrollView new];
scrollView.backgroundColor=[UIColor redColor];
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
//CONTAINER VIEW
UIView *containerView = [UIView new];
containerView.translatesAutoresizingMaskIntoConstraints = NO;
containerView.backgroundColor = [UIColor yellowColor];
[scrollView addSubview:containerView];
// CONSTRAINTS SCROLL VIEW - CONTAINER VIEW
[scrollView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[containerView]|"
options:0 metrics:nil
views:#{#"containerView":containerView}]];
[scrollView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[containerView]|"
options:0 metrics:nil
views:#{#"containerView":containerView}]];
// BUTTON
UIButton* button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.translatesAutoresizingMaskIntoConstraints = NO;
[button setTitle:#"I'm way to small" forState:UIControlStateNormal];
[containerView addSubview:button];
// CONSTRAINTS CONTAINER VIEW - BUTTON
[containerView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[button]|"
options:0 metrics:nil
views:#{#"button":button}]];
[containerView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[button]|"
options:0 metrics:nil
views:#{#"button":button}]];
self.view = scrollView;
}
UPDATE:
I really don't know, why this is happening. If you set up the view in IB, connect the outlets and instanciate the view in code, the scrollview behaves like a normal view (which bounces vertically). Its contentSize is not calculated correctly. More here. But how to do it correctly?
A couple of observations:
Constraints for subviews in scroll views don't work like constraints in other views. They're used to set the contentSize of the scroll view. (See TN2154.) That way, you throw a bunch of stuff on a scroll view, set the constraints for the stuff inside it, and the contentSize is calculated for you. It's very cool feature, but it's antithetical to what you're trying to do here.
Worse, buttons will, unless you set an explicit constraint for their width and height of a button, will resize according to their content.
The net effect of these two observations is that your existing constraints say "(a) set my container to be the size of my button; (b) let my button resize itself dynamically to the size of the text; and (c) set my scrollview's contentSize according to the size of my container (which is the size of the button)."
I'm unclear as to what the business problem is. But here are some constraints that achieve what I think your technical question was:
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *view = self.view;
UIScrollView *scrollView = [[UIScrollView alloc] init];
scrollView.backgroundColor = [UIColor redColor]; // just so I can see it
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:scrollView];
UIView *containerView = [[UIView alloc] init];
containerView.backgroundColor = [UIColor yellowColor]; // just so I can see it
containerView.translatesAutoresizingMaskIntoConstraints = NO;
[scrollView addSubview:containerView];
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.translatesAutoresizingMaskIntoConstraints = NO;
[button setTitle:#"I'm the right size" forState:UIControlStateNormal];
[containerView addSubview:button];
NSDictionary *views = NSDictionaryOfVariableBindings(scrollView, button, view, containerView);
// set the scrollview to be the size of the root view
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[scrollView]|"
options:0
metrics:nil
views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[scrollView]|"
options:0
metrics:nil
views:views]];
// set the container to the size of the main view, and simultaneously
// set the scrollview's contentSize to match the size of the container
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[containerView(==view)]|"
options:0
metrics:nil
views:views]];
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[containerView(==view)]|"
options:0
metrics:nil
views:views]];
// set the button size to be the size of the container view
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[button(==containerView)]"
options:0
metrics:nil
views:views]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[button(==containerView)]"
options:0
metrics:nil
views:views]];
}
Frankly, I don't understand the business intent of your UI, as this feels like a contortion of auto layout to achieve a very simply UI. I don't know why you have a scroll view if you have "screen sized" content in it (unless you were paging through buttons). I don't know why you'd have a content view with a single item in it. I don't understand why you're using a full-screen button (I'd just put a tap gesture on the root view at that point and call it a day).
I'll assume you have good reasons for all of this, but it might make sense to back up, ask what is your desired user experience is, and then approach the problem fresh to see if there's a more efficient way to achieve the desired effect.

Resources