Autolayout issue in iOS - ios

I have a label,image and button in a container view of type UIView . The container view is subview of supercontainerview of type UIView again. All the layout is done in code using autlayout visual format language. What i want to achieve is remove the label when a button is pressed and expecting the super container to resize its contents. but what currently happens is the whole super container disappears from the screen. Can someone tell me why this is happening? Attached is my code sample.
- (void)viewDidLoad {
[super viewDidLoad];
superContainer = [[UIView alloc] initWithFrame:CGRectZero];
superContainer.backgroundColor = [UIColor orangeColor];
[self.view addSubview:superContainer];
Container = [[UIView alloc] initWithFrame:CGRectZero];
Container.backgroundColor = [UIColor redColor];
[superContainer addSubview:Container];
NSDictionary *sViews = NSDictionaryOfVariableBindings(Container);
[superContainer addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-10.0-[Container]-10.0-|" options:0 metrics:nil views:sViews]];
[superContainer addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-10.0-[Container]-10.0-|" options:0 metrics:nil views:sViews]];
CGSize temp1 = [superContainer systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
superContainer.frame = CGRectMake(superContainer.frame.origin.x, superContainer.frame.origin.y, temp1.width, temp1.height);
closeButton = [UIButton buttonWithType:UIButtonTypeCustom];
//[closeButton setImage: forState:UIControlStateNormal];
[closeButton setBackgroundImage:[UIImage imageNamed:#"closebox.png"] forState:UIControlStateNormal];
[closeButton addTarget:self action:#selector(hide:) forControlEvents:UIControlEventTouchUpInside];
NSLog(#"Close button frame is %#",NSStringFromCGRect(closeButton.frame));
//closeButton.frame = CGRectMake(0, 10, 32, 32);
[Container addSubview:closeButton];
helpLabel = [[UILabel alloc] init];
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:#"This is sample Text. This is sample Text.This is sample Text. This is sample Text.This is sample Text. This is sample Text.This is sample Text. This is sample Text.This is sample Text. This is sample Text. "];
helpLabel.attributedText = attrString;
helpLabel.numberOfLines = 0;
helpLabel.backgroundColor = [UIColor greenColor];
[Container addSubview:helpLabel];
helpImageView = [[UIImageView alloc] init];
helpImageView.image = [UIImage imageNamed:#"testimage.png"];
NSLog(#"frame of imageview is %#",NSStringFromCGRect(helpImageView.frame));
[Container addSubview:helpImageView];
dismissButton = [UIButton buttonWithType:UIButtonTypeCustom];
[dismissButton setTitle:#"Dismiss" forState:UIControlStateNormal];
[[dismissButton titleLabel] setLineBreakMode:NSLineBreakByWordWrapping];
dismissButton.backgroundColor = [UIColor blueColor];
[Container addSubview:dismissButton];
[Container setClipsToBounds:YES];
[self addAutoLayoutProperties];
NSDictionary *views = NSDictionaryOfVariableBindings(helpLabel,helpImageView,dismissButton,closeButton);
NSDictionary *metrics = #{#"buttonHeight":#32.0};
// Horizontal layout - for helplabel
[Container addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-5.0-[helpLabel(400)]-5.0-|" options:NSLayoutFormatAlignAllLeft|NSLayoutFormatAlignAllRight metrics:metrics views:views]];
// Horizontal layout - for helpImageView
[Container addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[helpImageView]|" options:NSLayoutFormatAlignAllLeft|NSLayoutFormatAlignAllRight metrics:metrics views:views]];
// Horizontal layout - for dismissButton
[Container addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-[dismissButton]-|" options:NSLayoutFormatAlignAllCenterX|NSLayoutFormatAlignAllCenterY metrics:metrics views:views]];
// Horizontal layout - for dismissButton
[Container addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[closeButton]-1.0-|" options:0 metrics:metrics views:views]];
// Vertical layout
[Container addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-1.0-[closeButton]" options:0 metrics:metrics views:views]];
[Container addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-buttonHeight-[helpLabel]-5.0-[helpImageView]-5.0-[dismissButton]-5.0-|" options:0 metrics:metrics views:views]];
CGSize temp = [Container systemLayoutSizeFittingSize:UILayoutFittingExpandedSize];
Container.frame = CGRectMake(Container.frame.origin.x, Container.frame.origin.y, temp.width, temp.height);
superContainer.center = self.view.center;
}
My method to add autolayout properties
-(void)addAutoLayoutProperties {
helpLabel.translatesAutoresizingMaskIntoConstraints = NO;
helpImageView.translatesAutoresizingMaskIntoConstraints = NO;
dismissButton.translatesAutoresizingMaskIntoConstraints = NO;
closeButton.translatesAutoresizingMaskIntoConstraints = NO;
superContainer.translatesAutoresizingMaskIntoConstraints = NO;
Container.translatesAutoresizingMaskIntoConstraints = NO;
}
my methods to remove label.
- (IBAction)removeASubview:(id)sender {
[helpLabel removeFromSuperview];
}
Also one more question. What happens to the constraint objects when the related view is removed from its superview. Do they exists there or get deleted??

You can add another constraint that should be applied in the absence of the helpLabel and its constraints, but give this new one a lower priority, so that when helpLabel is present, its constraints will govern the view, but when helpLabel is removed, the new constraint will come into play. e.g.:
[container addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-buttonHeight#500-[helpImageView]" options:0 metrics:metrics views:views]];
By the way, if you want to see what happened to your views after helpLabel was removed, run the app with the debugger, tap the pause button () to pause the app, and at the (lldb) debugger prompt, type the following command:
po [[UIWindow keyWindow] recursiveDescription]
Note, it can sometimes be hard to identify which window is which, so I will frequently give the various views unique numeric tag properties to make it easier.
Also, at the same command line, you can use the following command to identify ambiguous layouts in your constraints:
po [[UIWindow keyWindow] _autolayoutTrace]

Related

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

Why are items outside the view?

I had two buttons in a UIView, then I add the view to main view, but I eventually got something like this:
As you can see these two buttons went outside the red view.
I wanted a little bit of margin on top, so I use constraintsWithVisualFormat:#"V:|-100-[buttonGroup]-10-|", I'm not sure if that matters.
Here's the original code:
- (UIButton*) getButtonWithTitle: (NSString*) title
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
button.layer.borderColor = [UIColor blackColor].CGColor;
button.layer.borderWidth = 0.5f;
button.layer.cornerRadius = 2.0f;
[button setTranslatesAutoresizingMaskIntoConstraints:NO];
[button setTitle:title forState:UIControlStateNormal];
[button setTitleColor:[UIColor blackColor] forState: UIControlStateNormal];
return button;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
UIButton *loginBtn = [self getButtonWithTitle:#"Login"];
UIButton *registerBtn = [self getButtonWithTitle:#"Register"];
UIView *buttonGroup = [[UIView alloc] init];
[buttonGroup setTranslatesAutoresizingMaskIntoConstraints:NO];
[buttonGroup addSubview: loginBtn];
[buttonGroup addSubview: registerBtn];
[self.view addSubview: buttonGroup];
[buttonGroup setBackgroundColor:[UIColor redColor]];
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(loginBtn, registerBtn, buttonGroup);
[buttonGroup addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[loginBtn]-20-[registerBtn(==loginBtn)]-|"
options:0
metrics:nil
views:viewsDictionary
]];
NSArray *verticalConstraints = [NSLayoutConstraint
constraintsWithVisualFormat:#"V:|-100-[buttonGroup]-10-|"
options:0
metrics:nil
views:viewsDictionary];
NSArray *horizontalConstraints = [NSLayoutConstraint
constraintsWithVisualFormat:#"H:|-[buttonGroup]-|"
options:0
metrics:nil
views:viewsDictionary];
[self.view addConstraints: horizontalConstraints];
[self.view addConstraints: verticalConstraints];
}
EDIT
The constraints I added:
[buttonGroup addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[loginBtn]-|"
options:0
metrics:nil
views:viewsDictionary
]];
[buttonGroup addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[registerBtn]-|"
options:0
metrics:nil
views:viewsDictionary
]];
It looks like I did extra work ...
You never created any vertical constraints between the buttons and buttonGroup; add those, and you should be good. Also, you shouldn't set translatesAutoresizingMaskIntoConstraints to NO for the controller's self.view (only for subviews that you add to it).
In the VFL , the | means the superview, but your two buttons(loginBtn, registerBtn) are the subviews of the buttonGroup. So you should define the V relation about the loginBtn, registerBtn with the buttonGroup. As rdelmar said, the self.view should set the translatesAutoresizingMaskIntoConstraints to No. This can close the autosizingmask function. I think the best way to do this things in xib or storyboard.

How to add constraints programatically to place a label at the left bottom of the screen?

I want to place a label at the bottom left of the screen: So I added the following code:
UILabel *versionLabel = [UILabel new];
versionLabel.text = #"some text";
versionLabel.textColor = [UIColor whiteColor];
versionLabel.font = [UIFont systemFontOfSize:10.0];
[self.view addSubview:versionLabel];
[versionLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
NSDictionary *views = NSDictionaryOfVariableBindings(versionLabel);
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"H:|-3-[versionLabel]"
options:0
metrics:nil
views:views]];
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:[versionLabel]-3-|"
options:0
metrics:nil
views:views]];
My issue is that the label ends up at the top left always. No way to move it at the bottom when I add constraints.
If you wrap the UITableViewController in a UIViewController, you can layout the label and table view as you see fit.
#import "WrappingViewController.h"
#import "YourCustomTableViewController.h"
#implementation WrappingViewController {
}
- (void)viewDidLoad {
// I instantiate this manually, but you could use an outlet from interface builder
UITableViewController *tableViewController = [YourCustomTableViewController new];
[self addChildViewController:tableViewController];
UIView *tableView = tableViewController.view;
UILabel *label1 = [UILabel new];
label1.text = #"Some text";
label1.font = [UIFont systemFontOfSize:10.0];
label1.translatesAutoresizingMaskIntoConstraints = NO;
tableView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:tableView];
[self.view addSubview:label1];
NSDictionary *views = NSDictionaryOfVariableBindings(tableView, label1);
[self.view addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:#"H:|[tableView]|" // make the tableview take the whole width
options:0
metrics:nil
views:views]
];
[self.view addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:#"H:|-3-[label1]" // attach the label 3 pixels from the left
options:0
metrics:nil
views:views]
];
[self.view addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|[tableView]-[label1]-3-|" // attach tableview to top, then space, then label, 3px, then bottom
options:0
metrics:nil
views:views]
];
}
#end

iOS: Visual format constraints are broken

I'm trying to lay a button over a map and constrain it to 10 points in from the left and 10 points up from the bottom. However, when I run it in simulator it tells me my constraints are broken. Here's my code. What am I missing?
_map = [[MKMapView alloc] init];
[self setView:_map];
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(39.810166, -86.156708);
_loc = [[TCMLocationPoint alloc] initWithCoordinate:coord title:#"My Location" subtitle:#"My Subtitle"];
[_loc setParent:_map];
[_map addAnnotation:_loc];
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(coord, 2000, 2000);
[_map setRegion:region animated:NO];
_directionsButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[_directionsButton addTarget:self action:#selector(getLocation:) forControlEvents:UIControlEventTouchDown];
[_directionsButton setTitle:#"Get Directions" forState:UIControlStateNormal];
[_directionsButton setFrame:CGRectMake(0.0, 0.0, 150.0, 25.0)];
[[self view] addSubview:_directionsButton];
NSDictionary *nameMap = #{#"button" : _directionsButton};
NSArray *horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-10-[button]-(>=0)-|"
options:0
metrics:nil
views:nameMap];
[[self view] addConstraints:horizontalConstraints];
NSArray *verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(>=0)-[button]-10-|"
options:0
metrics:nil
views:nameMap];
[[self view] addConstraints:verticalConstraints];
A couple of thoughts:
I'd set translatesAutoresizingMaskIntoConstraints to NO.
You can probably simplify your VFL, too, and eliminate the >=0 stuff. You can also set the width and height right in your VFL.
You also don't need to set the frame. Let the constraints set the width, height.
Unless you're doing this in loadView, I'd suggest you add the map as a subview rather than trying to set self.view.
Thus:
_map = [[MKMapView alloc] init];
_map.delegate = self; // if you've implemented any `MKMapViewDelegate` methods, you should set your delegate
[_map setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:_map];
NSDictionary *views = #{#"map" : _map};
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[map]|"
options:0
metrics:nil
views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[map]|"
options:0
metrics:nil
views:views]];
// do your stuff where you're adding your annotation here
_directionsButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[_directionsButton addTarget:self action:#selector(getLocation:) forControlEvents:UIControlEventTouchDown];
[_directionsButton setTitle:#"Get Directions" forState:UIControlStateNormal];
[_directionsButton setTranslatesAutoresizingMaskIntoConstraints:NO];
[[self view] addSubview:_directionsButton];
NSDictionary *nameMap = #{#"button" : _directionsButton};
NSArray *horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-10-[button(150)]"
options:0
metrics:nil
views:nameMap];
[[self view] addConstraints:horizontalConstraints];
NSArray *verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[button(25)]-10-|"
options:0
metrics:nil
views:nameMap];
[[self view] addConstraints:verticalConstraints];
You might even want to set the priority for the width and height constraints to be less than 1000, that way if the font dictates that the button should be a little larger, you'll let it grow, e.g. #"H:|-10-[button(150#500)]" and #"V:[button(25#500)]-10-|".

Resources