uiscrollview doesn't scroll because of autolayout - ios

I know that there are already many answers to my questions, but I've tested each one of these and I still have the problem.
I have a UIScrollView which contains a UIView, I want active autolayout for an animation. But because of this, my scrollview doesn't scroll.
This is my code:
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_scrollView, _containerScrollView);
[_scrollView setScrollEnabled:YES];
[_scrollView setContentSize:CGSizeMake(_scrollView.frame.size.width, CGRectGetHeight(_containerScrollView.frame))];
_scrollView.userInteractionEnabled = YES;
_scrollView.delaysContentTouches = YES;
[self.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_scrollView]|" options:0 metrics:0 views:viewsDictionary]];
[self.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_scrollView]|" options:0 metrics:0 views:viewsDictionary]];
[_scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_containerScrollView]|" options:0 metrics:0 views:viewsDictionary]];
[_scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_containerScrollView]|" options:0 metrics:0 views:viewsDictionary]];
Somebody can explain me why doesn't it work?

Try to set your scrollView's Content size int "viewDidLayoutSubviews" method with keeping the autolayouts set.
-(void)viewDidLayoutSubviews
{
[self.itemList setContentSize:CGSizeMake(required_width, required_height)];
}

Related

Change constraints based on text length

I have two labels, that are ordered like this:
H:[label1]-10-[label2]
I want to change their order, based on the first label's length. So, if its text won't fit on one line, I want to change the constraints to look like this:
V:[label1]-2.5-[label2]
Here are my constraints right now:
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_carLabel]-2.5-[_statusLabel]|" options:0 metrics:nil views:views]];
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_carLabel]|" options:0 metrics:nil views:views]];
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_statusLabel]|" options:0 metrics:nil views:views]];
And this is how I want them to look, when _carLabel won't fit in one line:
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_carLabel]-10-[_statusLabel]|" options:0 metrics:nil views:views]];
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_carLabel]|" options:0 metrics:nil views:views]];
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_statusLabel]|" options:0 metrics:nil views:views]];
How would I do this?
I think your best bet would be to conditionally apply constraints based on the size of the label. I don't believe there's a way to add both constraints and have this work how you described. (Though if it is possible it will likely have to do with setting the priority of each constraint #"[label1]-10#99-[label2]" and #"V:[label1]-2.5#98-[label2] but I was unable to get that to work. The code below however seems to do what you're looking for.
UILabel *label1 = [[UILabel alloc] init];
label1.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:label1];
[label1 setText:#"Small text"];
// [label1 setText:#"Some really long text to test the other scenario also works"];
UILabel *label2 = [[UILabel alloc] init];
label2.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:label2];
[label2 setText:#"Small text 2"];
[label1 sizeToFit];
if (label1.frame.size.width + kPadding + label2.frame.size.width < self.view.frame.size.width) {
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[label1]-padding#100-[label2]"
options:0
metrics:#{#"padding": #(kPadding)}
views:NSDictionaryOfVariableBindings(label1, label2)]];
} else {
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[label1]-2.5#100-[label2]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(label1, label2)]];
}

What is setTranslatesAutoresizingMaskIntoConstraints:NO for?

In some project that I try to understand there is some code:
[[[self foregroundContentView] contentView] addSubview:[self hueLabel]];
[[[self foregroundContentView] contentView] addSubview:[self hueSlider]];
[[[self foregroundContentView] contentView] addSubview:[self saturationLabel]];
[[[self foregroundContentView] contentView] addSubview:[self saturationSlider]];
[[[self foregroundContentView] contentView] addSubview:[self brightnessLabel]];
[[[self foregroundContentView] contentView] addSubview:[self brightnessSlider]];
[[[self foregroundContentView] contentView] addSubview:[self saveButton]];
[[self foregroundContentScrollView] addSubview:[self foregroundContentView]];
[[self foregroundContentScrollView] addSubview:[self imageView]];
[[self view] addSubview:[self backgroundView]];
[[self view] addSubview:[self foregroundContentScrollView]];
Every view has a property setTranslatesAutoresizingMaskIntoConstraints set to NO, for instance:
[[self saveButton] setTranslatesAutoresizingMaskIntoConstraints:NO];
Then it adds some constraints to these views:
NSDictionary* views = #{
#"backgroundView" : [self backgroundView],
#"foregroundContentScrollView" : [self foregroundContentScrollView],
#"foregroundContentView" : [self foregroundContentView],
#"hueLabel" : [self hueLabel],
#"hueSlider" : [self hueSlider],
#"saturationLabel" : [self saturationLabel],
#"saturationSlider" : [self saturationSlider],
#"brightnessLabel" : [self brightnessLabel],
#"brightnessSlider" : [self brightnessSlider],
#"saveButton" : [self saveButton],
#"imageView" : [self imageView]
};
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[backgroundView]|" options:0 metrics:nil views:views]];
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[backgroundView]|" options:0 metrics:nil views:views]];
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[foregroundContentScrollView]|" options:0 metrics:nil views:views]];
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[foregroundContentScrollView]|" options:0 metrics:nil views:views]];
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[foregroundContentView]|" options:0 metrics:nil views:views]];
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[foregroundContentView]|" options:0 metrics:nil views:views]];
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[hueLabel]-|" options:0 metrics:nil views:views]];
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[hueSlider]-|" options:0 metrics:nil views:views]];
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[saturationLabel]-|" options:0 metrics:nil views:views]];
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[saturationSlider]-|" options:0 metrics:nil views:views]];
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[brightnessLabel]-|" options:0 metrics:nil views:views]];
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[brightnessSlider]-|" options:0 metrics:nil views:views]];
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[saveButton]-|" options:0 metrics:nil views:views]];
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[imageView(==foregroundContentScrollView)]|" options:0 metrics:nil views:views]];
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(>=30)-[hueLabel]-[hueSlider]-[saturationLabel]-[saturationSlider]-[brightnessLabel]-[brightnessSlider]-[saveButton]-(>=10)-[imageView(==200)]|" options:0 metrics:nil views:views]];
The questions are:
what is setTranslatesAutoresizingMaskIntoConstraints:NO for? Why when I remove one of line with that method, then everything is broken?
what that line do?
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[foregroundContentScrollView]|" options:0 metrics:nil views:views]];
Auto layout is a relatively recent feature. That means there are three types of code out there:
Code which is unaware of auto layout. It uses the springs-and-struts approach because it doesn't know about anything else.
Code which is aware of auto layout but chooses to use springs-and-struts, at least in some circumstances.
Code which uses auto layout.
This applies to your code, the code of the frameworks (UIKit), and the code of third-party libraries.
Because of the existence of the first type of code, all views must have the old springs-and-struts behavior by default. Code which is aware of auto layout can turn that off if it wants, but code which is unaware obviously couldn't turn it on, because it doesn't know it has to or what property to set to do so.
Because the old springs-and-struts behavior would conflict with most ways that auto layout is used, code which uses auto layout usually has to turn it off. It does so by setting the translatesAutoresizingMaskIntoConstraints property to NO.
That decision has to be left to the controller code which places the view in question into the view hierarchy, because only that code knows how it wants to arrange the view in the layout (or, in the case of the first type of code, doesn't "know" but uses springs-and-struts out of ignorance).
If you fail to clear translatesAutoresizingMaskIntoConstraints in your controller code that uses auto layout, you will usually get exceptions due to unsatisfiable constraints. The exceptions will be caught and mostly ignored, but the auto layout system will have to ignore some of your constraints to complete layout. As a consequence, your views may be laid out incorrectly.
This line:
[[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[foregroundContentScrollView]|" options:0 metrics:nil views:views]];
adds constraints which keep the foregroundContentScrollView filling its superview horizontally. That view's leading edge will be made to equal its superview's leading edge, and its trailing edge will be made to equal its superview's trailing edge.
Its a way for views that are using auto layout to ignore the old springs and struts layout. See
https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AdoptingAutoLayout/AdoptingAutoLayout.html
this link for a great explanation. If you want to use auto layout like you are, you will want to listen to auto layout and not the springs and struts. That is why it is set to NO.

UIScrollView does not scroll with AutoLayout

I can't make UIScrollView scroll vertically when using Autolayout, I've tried many suggestions. I've read Apple's technical note but even that doesn't seem to work with my pure auto layout approach.
Here is my simple code where I'm adding two UIView blocks1 & 2 in a container view. The container view itself is the only child of the UIScrollView as suggested by many references online. I set the heights of block 1 and 2 as 800 points each, but the scroll view won't scroll.
- (void)viewDidLoad {
[super viewDidLoad];
// Test UIScrollView with Autolayout, scrolling should word
UIView *mainView = self.view;
UIScrollView* scrollView = [UIScrollView new];
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
scrollView.backgroundColor = [UIColor redColor];
[self.view addSubview:scrollView];
UIView* contentView = [UIView new];
contentView.translatesAutoresizingMaskIntoConstraints = NO;
contentView.backgroundColor = [UIColor greenColor];
[scrollView addSubview:contentView];
UIView* block1 = [[UIView alloc] init];
block1.translatesAutoresizingMaskIntoConstraints = NO;
[block1 setBackgroundColor:[UIColor blackColor]];
[contentView addSubview:block1];
UIView* block2 = [[UIView alloc] init];
block2.translatesAutoresizingMaskIntoConstraints = NO;
[block2 setBackgroundColor:[UIColor blackColor]];
[contentView addSubview:block2];
NSDictionary* viewDict = NSDictionaryOfVariableBindings(mainView,scrollView, contentView, block1, block2);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[scrollView]|" options:0 metrics:0 views:viewDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[scrollView]|" options:0 metrics:0 views:viewDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[contentView(==mainView)]|" options:0 metrics:0 views:viewDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView(==mainView)]|" options:0 metrics:0 views:viewDict]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[block1(==300)]" options:0 metrics:0 views:viewDict]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[block1(==800)]" options:0 metrics:0 views:viewDict]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[block2(==300)]" options:0 metrics:0 views:viewDict]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-810-[block2(==800)]" options:0 metrics:0 views:viewDict]];
}
Looking to your code, It seems that the size of you content view will be of same size as UIView.
e.g. : IF Device screen height is 568 then your content view size will be 568, and you are set your block2 at y position = 180.
Your Code:
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView(==mainView)]|" options:0 metrics:0 views:viewDict]];
change:
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView(2000)]|” options:0 metrics:0 views:viewDict]];
Hope it solves your problem if any further query please let me know.
If all you want to do is make a scroll view work with auto layouts, you can try this.
http://natashatherobot.com/ios-autolayout-scrollview/
Do the following changes in your code
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView]|" options:0 metrics:0 views:viewDict]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-810-[block2(==800)]|" options:0 metrics:0 views:viewDict]];
Hope this helps

constraintsWithVisualFormat set same Y position for all the views

I am just playing with constraintsWithVisualFormat but not able to figure out how to do.
I have two Views placed together with 10 pix margin in horizontal. and Y position will be the same for the both of the views.
I have tried the following things.
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-10-[view1(==100)]-10-[view2(==100)]" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-10-[view1(==100)]-[view2(==100)]" options:0 metrics:nil views:views]];
Horizontal Constraint : 10 pix left margin from superview - 100 width
of view1- 10 pix margin - 100 width of view2
Vertical Constraint : 10 pix top from superview - 100 height of view1-
100 height of view2
Problem
The second view Y position start after the view1 i want the same Y position for the both of the view.
Please if possible explain the answer
Mark '-' is margin 8px in VisualFormatLanguage.
Try this.
`[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-10-[view1(==100)]-10-[view2(==100)]" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-10-[view1(==100)]" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-10-[view2(==100)]" options:0 metrics:nil views:views]];`
Horizontal layout with all bottom alignment:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-10-[view1(==100)]-10-[view2(==100)]"
options:NSLayoutFormatAlignAllBottom
metrics:nil
views:views]
Vertical layout untouched:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-10-[view1]"
options:0
metrics:nil
views:views]
Please also note that two approaches -- constrain each view at top of superview vs. align all views around common top/bottom/baseline/centery differ in obvious way. And that other answer cannot address the case of center-alignment, btw.
UPD: Here is a complete working example of using alignment options in layout:
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
NSTextField *view1 = [[NSTextField alloc] initWithFrame:NSZeroRect];
NSTextField *view2 = [[NSTextField alloc] initWithFrame:NSZeroRect];
NSView *contentView = [self.window contentView];
NSDictionary *views = NSDictionaryOfVariableBindings(view1, view2);
[view1 setTranslatesAutoresizingMaskIntoConstraints:NO];
[view2 setTranslatesAutoresizingMaskIntoConstraints:NO];
[contentView addSubview:view1];
[contentView addSubview:view2];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[view1(==100)]-[view2(==100)]"
options:NSLayoutFormatAlignAllBottom
metrics:nil
views:views]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[view1]"
options:0
metrics:nil
views:views]];
}

How do I auto layout constraints when I add a new view such as a error message to the top of a main view

This is my controller as it loaded
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.mainTitleText];
[self.view addSubview:self.subTitle];
[self.view addSubview:self.tableView];
[self.view addSubview:self.acceptBtn];
[self.view addSubview:self.notAcceptBtn];
[self addConstraints];
}
I add my constraints here
- (void)addConstraints {
id metrics = #{ #"textHMargin": #10, #"topMargin": #110, #"margin": #8, #"titleHeight": #60, #"subtitleHeight": #40, #"tableHeight": #88 };
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-textHMargin-[mainTitleText]-textHMargin-|" options:0 metrics:metrics views:self.views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-textHMargin-[subTitle]-textHMargin-|" options:0 metrics:metrics views:self.views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[tableView]|" options:0 metrics:metrics views:self.views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-margin-[acceptBtn]-margin-|" options:0 metrics:metrics views:self.views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-margin-[notAcceptBtn]-margin-|" options:0 metrics:metrics views:self.views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-topMargin-[mainTitleText(titleHeight)]-margin-[subTitle(subtitleHeight)]-[tableView(tableHeight)]-margin-[acceptBtn]-margin-[notAcceptBtn]" options:0 metrics:metrics views:self.views]];
}
This is where I'm getting stuck. When I add my error message view it does not stack on top and push everything down.
- (void)acceptAction:(id)sender {
[self.activeTextfield resignFirstResponder];
if (self.airlineInfo.flightNumber && self.airlineInfo.airlineDescription) {
//TODO: success code
} else {
//show error
id metrics = #{ #"textHMargin": #10, #"topMargin": #110, #"margin": #8, #"titleHeight": #60, #"subtitleHeight": #40, #"tableHeight": #88 };
[self.view addSubview:self.infoMessage];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-textHMargin-[infoMessage]-textHMargin-|" options:0 metrics:metrics views:self.views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-topMargin-[infoMessage(20)]-margin-[mainTitleText(titleHeight)]-margin-[subTitle(subtitleHeight)]-[tableView(tableHeight)]-margin-[acceptBtn]-margin-[notAcceptBtn]" options:0 metrics:metrics views:self.views]];
}
}
view http://kgaddy.com/errorView.jpg.
What I would do is not use constraints for this extra view at all. Just stick it into the interface. But change the constant on your existing top view to a larger value, thus pushing it down and pushing down everything that is pinned to it below it.
So, right now, you are saying:
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-topMargin-[mainTitleText(titleHeight)]-margin-[subTitle(subtitleHeight)]-[tableView(tableHeight)]-margin-[acceptBtn]-margin-[notAcceptBtn]" options:0 metrics:metrics views:self.views]];
Instead of just adding those constraints directly, first keep a reference to them; then add them. That way you can sneakily remove those constraints later! Then you do the same thing again but with a push-down value at the top:
[self.view removeConstraints:self.verticalContraints]; // previously saved
self.verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(40)-topMargin-[mainTitleText(titleHeight)]-margin-[subTitle(subtitleHeight)]-[tableView(tableHeight)]-margin-[acceptBtn]-margin-[notAcceptBtn]" options:0 metrics:metrics views:self.views]];
[self.view addConstraints:self.verticalConstraints];
Notice the "40" that I snuck in there. You can fix that to a better value experimentally. You see from this how to swap out a bunch of constraints and put a different set in their place.

Resources