UIScrollView not working properly with layout constraints - ios

Im trying to place 20Textfields vertically and adding constraints to them programmatically and here is my code for that.
//For creating 20 textfields
-(void)createTextFields
{
//create textfields using for loop
for (int i=0; i<20; i++) {
[self createNew:i+1];
}
}
-(void)createNew:(int )count
{
UITextField *textField=[[UITextField alloc]init];
textField.translatesAutoresizingMaskIntoConstraints=NO;
textField.placeholder=[NSString stringWithFormat:#"Enter text:%u",count];
textField.font=[textField.font fontWithSize:10];
textField.backgroundColor = [UIColor colorWithHue:0.8 saturation:0.1 brightness:0.9 alpha:1];
[containerView addSubview:textField];
[inputFieldsArr addObject:textField];
//Left and width
[containerView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"|-[textField]-|"
options:0 metrics:nil
views:NSDictionaryOfVariableBindings(textField)]];
//Top and height
[containerView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|-count-[textField(30)]"
options:0
metrics:#{#"count":[NSNumber numberWithInt:count*50]}
views:NSDictionaryOfVariableBindings(textField)]];
// return textField;
}
//Adding a containerview to scrollview
-(void)createScroll
{
scrollview = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
scrollview.showsVerticalScrollIndicator=YES;
scrollview.scrollEnabled=YES;
scrollview.userInteractionEnabled=YES;
scrollview.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:scrollview];
scrollview.backgroundColor=[UIColor blackColor];
[scrollview setContentSize:CGSizeMake(self.view.bounds.size.width, self.view.bounds.size.height)];
UIView *view=self.view;
//Left and width
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"|-[scrollview]-|"
options:0 metrics:nil
views:NSDictionaryOfVariableBindings(scrollview)]];
//Top and height
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|[scrollview]|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(scrollview,view)]];
containerView = [[UIView alloc] init];
containerView.backgroundColor = [UIColor yellowColor]; // just so I can see it
containerView.translatesAutoresizingMaskIntoConstraints = NO;
[scrollview addSubview:containerView];
[self addCOnstriantsToContainer:0];
float height=self.view.frame.size.height;
// CGSize fittingSize = [scrollview systemLayoutSizeFittingSize: UILayoutFittingCompressedSize];
//
// NSLog( #"container fitting size: %#", NSStringFromCGSize( fittingSize ));
//Left and width
}
//Adding Constriants to containerview
-(void)addCOnstriantsToContainer:(float)height
{
float width=self.view.frame.size.width;
// NSLog(#"height:%f",containerView.bounds.size.height);
[scrollview addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"|-[containerView(width)]-|"
options:0
metrics:#{#"width":[NSNumber numberWithFloat:width]}
views:NSDictionaryOfVariableBindings(containerView,scrollview)]];
//Top and height
[scrollview addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|-[containerView(==1000)]-|"
options:0
metrics:#{#"height":[NSNumber numberWithFloat:height]}
views:NSDictionaryOfVariableBindings(containerView,scrollview)]];
//constraintsWithVisualFormat:#"V:|-[containerView(1000)]-|"
}
If I use constraintsWithVisualFormat:#"V:|-[containerView(==1000)]-|", it works fine. But I don't want to assign values statically. How can I set them dynamically? I also have tried constraintsWithVisualFormat:#"V:|[containerView(==scrollView)]|". But none of them worked for me.They can't be scrolled beyond the height of the device.
Any ideas to fix this?

Try to add top and height constraints separately for container view, set height same as scrollView's height, and set priority of height constraint to Low Priority.

Related

Vertical scrolling not working UIScrollView programatically

I am not sure what is wrong with my code. I have embedded UIView inside the UIScrollView. There are some controls which have been embedded inside this UIView. I am expecting the scrolling to happen for the UIView but it does not. Eventually I am unable to view all the controls. I am just pasting my code below. Appreciate if somebody points out what I am missing here.
UIView *parentView = [[UIView alloc]init];
[parentView setTranslatesAutoresizingMaskIntoConstraints:NO];
parentView.backgroundColor = [UIColor whiteColor];
[self.view addSubview: parentView];
NSArray *parentViewHConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[parentView(==mainView)]" options:0 metrics:0 views:#{#"parentView": parentView, #"mainView":self.view}];
NSArray *parentViewVConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-10-[parentView]-10-|" options:0 metrics:0 views:#{#"parentView": parentView}];
[self.view addConstraints:parentViewHConstraints];
[self.view addConstraints:parentViewVConstraints];
self.myScrollView = [[UIScrollView alloc] init];
//CGSize scrollableSize = CGSizeMake(100, 200);
//[self.myScrollView setContentSize:scrollableSize];
[parentView addSubview:self.myScrollView];
self.myScrollView.backgroundColor = [UIColor lightGrayColor];
self.myScrollView.translatesAutoresizingMaskIntoConstraints = NO;
NSArray *scrollViewHContraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[scrollView]|" options:0 metrics:0 views:#{#"scrollView": self.myScrollView}];
[parentView addConstraints:scrollViewHContraints];
UIView *contentView = [[UIView alloc]init];
contentView.translatesAutoresizingMaskIntoConstraints = NO;
[self.myScrollView addSubview:contentView];
self.myScrollView.showsVerticalScrollIndicator = YES;
self.myScrollView.pagingEnabled = YES;
self.myScrollView.contentSize = CGSizeMake(510,221);
NSArray *contentViewHConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[contentview(==scrollView)]|" options:0 metrics:nil views:#{#"contentview": contentView,#"scrollView": self.myScrollView}];
NSArray *contentViewVConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentview(==scrollView)]|" options:0 metrics:nil views:#{#"contentview": contentView,#"scrollView": self.myScrollView}];
[self.myScrollView addConstraints:contentViewHConstraints];
[self.myScrollView addConstraints:contentViewVConstraints];
UIFont *headingFont = [UIFont fontWithName:#"Helvetica Neue" size:40];
self.m_ObjTopHeadingLbl = [[UILabel alloc]init];
[self.m_ObjTopHeadingLbl setText:#"Registration"];
self.m_ObjTopHeadingLbl.font = headingFont;
[self.m_ObjTopHeadingLbl setTextColor:[UIColor redColor]];
self.m_ObjTopHeadingLbl.translatesAutoresizingMaskIntoConstraints = NO;
[contentView addSubview:self.m_ObjTopHeadingLbl];
NSArray *topHeadingHorzConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[registView]-|" options:0 metrics:nil views:#{#"registView": self.m_ObjTopHeadingLbl}];
[contentView addConstraints:topHeadingHorzConstraint];
// Name field
self.m_ObjNameField = [[JVFloatLabeledTextField alloc]init];
self.m_ObjNameField.borderStyle = UITextBorderStyleRoundedRect;
self.m_ObjNameField.translatesAutoresizingMaskIntoConstraints = NO;
self.m_ObjNameField.placeholder = [NSString stringWithFormat:#"Enter your short name"];
[contentView addSubview:self.m_ObjNameField];
NSArray *nameFieldHorzConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[nameFieldView]-|" options:0 metrics:nil views:#{#"nameFieldView": self.m_ObjNameField}];
[contentView addConstraints:nameFieldHorzConstraint];
// Info on the name field
UILabel* nameFieldLbl = [[UILabel alloc]init];
[nameFieldLbl setText:#"Will be visible to all on ipomo"];
UIFont *nameFieldLblFont = [UIFont fontWithName:#"Arial-BoldMT" size:13];
[nameFieldLbl setFont:nameFieldLblFont];
[nameFieldLbl setTextColor:[UIColor grayColor]];
nameFieldLbl.translatesAutoresizingMaskIntoConstraints = NO;
[contentView addSubview:nameFieldLbl];
NSArray *nameFieldlblHorzConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[nameFieldLbl]-|" options:0 metrics:nil views:#{#"nameFieldLbl": nameFieldLbl}];
[contentView addConstraints:nameFieldlblHorzConstraint];
// Mobilenumber field
self.m_ObjMobNo = [[JVFloatLabeledTextField alloc]init];
self.m_ObjMobNo.borderStyle = UITextBorderStyleRoundedRect;
self.m_ObjMobNo.translatesAutoresizingMaskIntoConstraints = NO;
self.m_ObjMobNo.placeholder = [NSString stringWithFormat:#"Enter your mobilenumber"];
[contentView addSubview:self.m_ObjMobNo];
NSArray *mobnoFieldHorzConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[mobnoField]-|" options:0 metrics:nil views:#{#"mobnoField": self.m_ObjMobNo}];
[contentView addConstraints:mobnoFieldHorzConstraint];
// Info on the mobile number field
UILabel* mobnoFieldLbl = [[UILabel alloc]init];
[mobnoFieldLbl setText:#"To send you the activation code. Hidden and secure"];
dispatch_async(dispatch_get_main_queue(), ^{
mobnoFieldLbl.preferredMaxLayoutWidth = self.view.bounds.size.width;
});
UIFont *mobnoFieldLblFont = [UIFont fontWithName:#"Arial-BoldMT" size:13];
[mobnoFieldLbl setFont:mobnoFieldLblFont];
mobnoFieldLbl.numberOfLines = 0;
[mobnoFieldLbl setTextColor:[UIColor grayColor]];
mobnoFieldLbl.translatesAutoresizingMaskIntoConstraints = NO;
[contentView addSubview:mobnoFieldLbl];
NSArray *mobnoFieldlblHorzConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[mobnoFieldlbl]-|" options:0 metrics:nil views:#{#"mobnoFieldlbl": mobnoFieldLbl}];
[contentView addConstraints:mobnoFieldlblHorzConstraint];
// Email id field
self.m_ObjEmailId = [[JVFloatLabeledTextField alloc]init];
self.m_ObjEmailId.borderStyle = UITextBorderStyleRoundedRect;
self.m_ObjEmailId.translatesAutoresizingMaskIntoConstraints = NO;
self.m_ObjEmailId.placeholder = [NSString stringWithFormat:#"Enter your email id"];
[contentView addSubview:self.m_ObjEmailId];
NSArray *emailFieldHorzConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[emailField]-|" options:0 metrics:nil views:#{#"emailField": self.m_ObjEmailId}];
[contentView addConstraints:emailFieldHorzConstraint];
// Info on the email field
UILabel* emailFieldLbl = [[UILabel alloc]init];
[emailFieldLbl setText:#"To send analytics and reports on happenings in you room(s). Hidden and secure"];
emailFieldLbl.numberOfLines = 0;
UIFont *emailFieldLblFont = [UIFont fontWithName:#"Arial-BoldMT" size:13];
dispatch_async(dispatch_get_main_queue(), ^{
emailFieldLbl.preferredMaxLayoutWidth = self.view.bounds.size.width;
});
[emailFieldLbl setFont:emailFieldLblFont];
[emailFieldLbl setTextColor:[UIColor grayColor]];
emailFieldLbl.translatesAutoresizingMaskIntoConstraints = NO;
[contentView addSubview:emailFieldLbl];
NSArray *emailFieldlblHorzConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[emailFieldlbl]-|" options:0 metrics:nil views:#{#"emailFieldlbl": emailFieldLbl}];
[contentView addConstraints:emailFieldlblHorzConstraint];
// Promo code field
self.m_ObjPromoCode = [[JVFloatLabeledTextField alloc]init];
self.m_ObjPromoCode.borderStyle = UITextBorderStyleRoundedRect;
self.m_ObjPromoCode.translatesAutoresizingMaskIntoConstraints = NO;
self.m_ObjPromoCode.placeholder = [NSString stringWithFormat:#"Enter promocode (if applicable)"];
[contentView addSubview:self.m_ObjPromoCode];
NSArray *promocodeFieldHorzConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[promocodeField]-|" options:0 metrics:nil views:#{#"promocodeField": self.m_ObjPromoCode}];
[contentView addConstraints:promocodeFieldHorzConstraint];
// Submit button
self.m_ObjSubmitBut = [[UIButton alloc]init];
[self.m_ObjSubmitBut setTitle: [NSString stringWithFormat:#"SUBMIT"] forState:UIControlStateNormal];
self.m_ObjSubmitBut.backgroundColor = [UIColor redColor];
[self.m_ObjSubmitBut setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
self.m_ObjSubmitBut.translatesAutoresizingMaskIntoConstraints = NO;
self.m_ObjSubmitBut.layer.cornerRadius = 10;
self.m_ObjSubmitBut.clipsToBounds = YES;
[parentView addSubview:self.m_ObjSubmitBut];
NSDictionary *myTopViews = #{
#"scrollView": self.myScrollView,
#"submitButton": self.m_ObjSubmitBut,
};
NSArray *myTopVConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[scrollView]-[submitButton(40)]|" options:0 metrics:nil views:myTopViews];
NSArray *myTopHConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[submitButton]-|" options:0 metrics:nil views:#{#"submitButton": self.m_ObjSubmitBut}];
[parentView addConstraints:myTopVConstraints];
[parentView addConstraints:myTopHConstraints];
NSDictionary* myViews = #{
#"registView": self.m_ObjTopHeadingLbl,
#"nameView": self.m_ObjNameField,
#"nameFieldLbl": nameFieldLbl,
#"mobnoView":self.m_ObjMobNo,
#"mobnoFieldLbl":mobnoFieldLbl,
#"emailView":self.m_ObjEmailId,
#"emailFieldLbl":emailFieldLbl,
#"promocodeView":self.m_ObjPromoCode
};
NSDictionary* myMetrics = #{
#"sepHeight" : #30.0f,
#"sepHeight1" : #5.0f
};
NSArray *otherConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[registView]-sepHeight-[nameView]-sepHeight1-[nameFieldLbl]-sepHeight-[mobnoView]-sepHeight1-[mobnoFieldLbl]-sepHeight-[emailView]-sepHeight1-[emailFieldLbl]-sepHeight-[promocodeView]" options:0 metrics:myMetrics views:myViews];
[contentView addConstraints:otherConstraints];
You're setting the size of your contentview to be equal to the size of the containing scrollview. Logically this means the scrollview has no area to scroll to and therefore will be fixed. You need to make the contentview longer (for vertical scrolling) or wider (for horizontal scrolling).
I think you are missing to mention scrollEnabled of UIScrollView, try following line and hope it works for you.
scrollView.scrollEnabled = YES;
Also set contentSize and its size should be greater than size of containing window to allow it to be scrollable.
[self.scrollforImage setContentSize:CGSizeMake(3000, 3000)];// replace 3000, 3000 according to your need
The constraints for the scrollview in your code are not sufficient. When using auto-layout, ensure that a proper chain of constraints are added in the vertical and horizontal directions.
For a scroll view to scroll, these are some general guidelines,
Ensure all the different sections of the UI have sufficient number of vertical and horizontal constraints. Take a simple UILabel in the screen. It needs a x,y position - can be a leading/top, centre attribute and it needs a size definition - either fixed width and height or a constraint with the nearby elements. It needs the position - size combination clearly defined for it to be managed by auto-layout.
If you set the translatesAutoresizingMaskIntoConstraints to NO, what you are saying is that the component shouldn't take the frame,size already set to be constraint requirements. If you want to explicitly mention frame or size for some elements, leave this as YES.
If the contentview is pinned in all four sides to the scrollview and the scrollview has its size,position defined as constraints or frame, it will be enough to make the content scrollable.
If the content size of the contentview changes dynamically during different events, you can set the scrollview.contentsize to the contentview.width and contentview.height explicitly.
Also, check this, a technical note from Apple on AutoLayout and Scrollviews.

Single Auto Layout constraint change breaks entire layout

I have a UIViewController which creates this layout:
The part that gives me trouble is the darker area on the bottom, containing the text.
The darker part is a UIScrollView subclass, which creates this layout using Auto Layout:
I have a couple of UILabels ("Omschrijving", "Informatie", and the small labels at the bottom of the view) and a UITextView (the view containing the text starting with "Robert Langdon").
I set the UITextViews height explicitly to 60 points, and when the "Meer" button is tapped, I calculate its full height using boundingRectWithSize:options:attributes:context. I then change its height constraint constant from its prior hardcoded value to the value I've calculated.
That's where it goes wrong. The layout is absolutely fine until the UITextView's height changes. All the content in the UIScrollView subclass seems to move inexplicably. I looked at the view hierarchy with Reveal: .
I've been messing with the constraints for hours now and I can't find a solution.
All the views have translatesAutoresizingMaskIntoConstraints set to NO.
This is iOS 8 on the simulator, with Xcode 6 b4.
My init methods looks like this:
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.translatesAutoresizingMaskIntoConstraints = NO;
[self layoutIfNeeded];
[self addExpandButton];
[self setupUI];
[self setupConstraints];
[self layoutIfNeeded];
self.backgroundColor = [UIColor clearColor];
self.textView.backgroundColor = [UIColor clearColor];
self.textView.userInteractionEnabled = NO;
}
return self;
}
setupUI creates all the views and adds them to the scrollView. I've removed some repetitive lines of code for the sake of brevity
- (void)setupUI
{
// Initialization
UILabel *descriptionLabel = [[UILabel alloc] initWithFrame:CGRectZero];
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 60.0) textContainer:nil];
textView.translatesAutoresizingMaskIntoConstraints = NO;
textView.editable = NO;
textView.textContainerInset = UIEdgeInsetsMake(0.0, -5.0, 0.0, -5.0);
textView.textColor = [UIColor whiteColor];
// init all the UILabels with CGRectZero frames
// ...
// ...
UIView *separator = [[UIView alloc] initWithFrame:CGRectZero];
separator.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.5];
// Set the text
descriptionLabel.text = #"Omschrijving";
textView.text = #"Robert Langdon, hoogleraar kunstgeschiedenis en symboliek, wordt op een nacht wakker in een ziekenhuis in Florence zonder te weten hoe hij daar is beland. Geholpen door een stoïcijnse jonge vrouw, Sienna Brooks, vlucht Langdon en raakt hij verzeild in een duizelingwekkend avontuur. Langdon ontdekt dat hij in het bezit is van een reeks verontrustende codes, gecreëerd door een briljante wetenschapper; een genie dat geobsedeerd is door het einde van de wereld en het duistere meesterwerk Inferno van Dante Alighieri.";
// Set the text of the labels
// ...
// ...
descriptionLabel.font = [UIFont fontWithName:#"ProximaNova-Semibold" size:14.0];
textView.font = [UIFont fontWithName:#"ProximaNova-Regular" size:12.0];
informationLabel.font = [UIFont fontWithName:#"ProximaNova-Semibold" size:14.0];
UIFont *font = [UIFont fontWithName:#"ProximaNova-Regular" size:10.0];
// Set the font of the labels
// ...
// ...
// Add the views to the view hierarchy
[self addSubview:descriptionLabel];
[self addSubview:textView];
[self addSubview:separator];
[self addSubview:informationLabel];
// Add the other labels as subviews
// ...
// ...
[self.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[UILabel class]]) {
UILabel *label = (UILabel *)obj;
[label sizeToFit];
label.textColor = [UIColor whiteColor];
}
}];
// Assign the local properties to properties
// ...
// ...
for (UIView *view in self.subviews) {
view.translatesAutoresizingMaskIntoConstraints = NO;
}
}
Now on to the constraints. I have a big method that adds all the constraints called -addConstraints.
- (void)setupConstraints
{
// Omschrijving label top
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-3-[descriptionLabel]-3-[textView]"
options:NSLayoutFormatAlignAllLeading
metrics:nil
views:#{#"descriptionLabel": self.descriptionLabel,
#"textView": self.textView}]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.descriptionLabel
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:3.0]];
// Omschrijving label leading
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-10-[descriptionLabel]"
options:0
metrics:nil
views:#{#"descriptionLabel": self.descriptionLabel}]];
// Text view width
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"[textView(==width)]"
options:0
metrics:#{#"width": #220.0}
views:#{#"textView": self.textView}]];
// Text view height
NSArray *textViewHeightConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[textView(==60.0)]"
options:0
metrics:nil
views:#{#"textView": self.textView}];
[self addConstraints:textViewHeightConstraints];
self.textViewHeightConstraints = textViewHeightConstraints;
NSLog(#"%#", self.textViewHeightConstraints);
// Text view expand button
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[textView][expandButton(==12.0)]"
options:NSLayoutFormatAlignAllTrailing
metrics:nil
views:#{#"textView": self.textView, #"expandButton": self.expandButton}]];
// Separator
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[expandButton]-6-[separator]"
options:NSLayoutFormatAlignAllTrailing
metrics:nil
views:#{#"expandButton": self.expandButton, #"separator": self.separator}]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"[separator(==textView)]"
options:0
metrics:nil
views:#{#"separator": self.separator, #"textView": self.textView}]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[separator(==0.5)]"
options:0
metrics:nil
views:#{#"separator": self.separator}]];
NSString *leftVisualFormatString = #"V:[separator]-6-[informationLabel]-spacing-[languageDescriptionLabel]-spacing-[categoryDescriptionLabel]-spacing-[publisherDescriptionLabel]-spacing-[publishedDateDescriptionLabel]-spacing-[pageCountDescriptionLabel]-|";
NSDictionary *descriptionViews = #{#"separator": self.separator,
#"informationLabel": self.informationLabel,
#"languageDescriptionLabel": self.languageDescriptionLabel,
#"categoryDescriptionLabel": self.categoryDescriptionLabel,
#"publisherDescriptionLabel": self.publisherDescriptionLabel,
#"publishedDateDescriptionLabel": self.publishedDateDescriptionLabel,
#"pageCountDescriptionLabel": self.pageCountDescriptionLabel};
NSDictionary *metrics = #{#"spacing": #1.0};
// All at once: vertical spacing and leading alignment for the labels on the left
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:leftVisualFormatString
options:NSLayoutFormatAlignAllLeading
metrics:metrics
views:descriptionViews]];
// Same, for the righthand labels
NSDictionary *views = #{#"languageLabel": self.languageLabel,
#"categoryLabel": self.categoryLabel,
#"publisherLabel": self.publisherLabel,
#"publishedDateLabel": self.publishedDateLabel,
#"pageCountLabel": self.pageCountLabel};
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"[pageCountDescriptionLabel]-20-[pageCountLabel]"
options:0
metrics:nil
views:#{#"pageCountDescriptionLabel": self.pageCountDescriptionLabel,
#"pageCountLabel": self.pageCountLabel}]];
NSString *rightVisualFormatString = #"V:[languageLabel]-spacing-[categoryLabel]-spacing-[publisherLabel]-spacing-[publishedDateLabel]-spacing-[pageCountLabel]-|";
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:rightVisualFormatString
options:NSLayoutFormatAlignAllLeading
metrics:metrics
views:views]];
}
As I said, this works fine until I call this method:
- (void)tappedExpandButton:(id)sender
{
if (self.textViewHeightConstraints.count == 1) {
NSLayoutConstraint *constraint = self.textViewHeightConstraints.firstObject;
CGFloat newHeight = [self.textView.text boundingRectWithSize:CGSizeMake(self.textView.textContainer.size.width, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName: self.textView.font}
context:nil].size.height;
constraint.constant = ceilf(newHeight);
[self layoutIfNeeded];
}
}
Thanks already!
I think I've figured it out. For some reason I don't yet understand, the containing UIScrollView's frame changed when adding the constraint. That still seems really weird to me, since I think only its contentSize should change, but adding constraints from the dark area (which is a UIView) to the UIScrollView fixed it. This is the code:
[self.darkEffectView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[descriptionView]|"
options:0
metrics:nil
views:#{#"descriptionView": self.descriptionView}]];
[self.darkEffectView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[descriptionView]|"
options:0
metrics:nil
views:#{#"descriptionView": self.descriptionView}]];

scrollView doesn't work even though contentSize is larger than frame and subview is added before setting content size

- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationController.navigationBar.translucent = YES;
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:#"sprites_0001_Rectangle-1.png"] forBarMetrics:UIBarMetricsDefault];
self.navigationController.navigationBar.translucent = YES;
UIImageView *imageView = [[UIImageView alloc]init];
UIImage *image = [UIImage imageNamed:#"sprites_0000s_0008_1st-Page.png"];
imageView.image = image;
//imageView.translatesAutoresizingMaskIntoConstraints = NO;
imageView.backgroundColor = [UIColor clearColor];
self.view = imageView;
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
//Write the code to set up view for iPad
}else{
UIScrollView *scrollView = [[UIScrollView alloc] init];
scrollView.backgroundColor = [UIColor clearColor];
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:scrollView];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-40-[scrollView(==240)]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(scrollView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-50-[scrollView(==468)]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(scrollView)]];
UIImageView *imageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"ParentsMock.png"]];
imageView.translatesAutoresizingMaskIntoConstraints = NO;
[scrollView addSubview:imageView];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[imageView(==1000)]|"
options:0
metrics:0
views:NSDictionaryOfVariableBindings(imageView)]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[imageView(==2000)]|"
options:0
metrics:0
views:NSDictionaryOfVariableBindings(imageView)]];
}
This is my attempt to create UIScrollView programmatically however I couldn't make the scrollView work even though I set the contentSize after adding the subview into the scrollView.
As you can see in this picture, I use UINavigationController to wrap a UIViewControllar and set UIImageView as its view. the I created a scrollView and add it on top of the view. then I create another imageView1 and insert it into the scrollView.
please note that the view of the entire view controller is an imageView which is different from the imageView i insert into the scrollView.
You should set your content size using constraints, ton by setting it directly. It does not scroll because you don't have constraints to the left, top, right or bottom of your scroll view content. See my answer here
Edit:
Try adding this:
imageView.translatesAutoresizingMaskIntoConstraints = NO;
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:
#"|[imageView(==1000)]|"
options:0
metrics:0
views:NSDictionaryOfVariableBindings(imageView)]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:
#"V:|[imageView(==2000)]|"
options:0
metrics:0
views:NSDictionaryOfVariableBindings(imageView)]];
and remove the code where you are setting the content size.
Edit:
Replace your entire code with this:
UIScrollView *scrollView = [[UIScrollView alloc] init];
scrollView.backgroundColor = [UIColor clearColor];
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:scrollView];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-40-[scrollView(==240)]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(scrollView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-50-[scrollView(==468)]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(scrollView)]];
UIImageView *imageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"ParentsMock.png"]];
imageView.translatesAutoresizingMaskIntoConstraints = NO;
[scrollView addSubview:imageView];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[imageView(==1000)]|"
options:0
metrics:0
views:NSDictionaryOfVariableBindings(imageView)]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[imageView(==2000)]|"
options:0
metrics:0
views:NSDictionaryOfVariableBindings(imageView)]];
The only way I have been successful in creating UIScrollViews is to use Autolayout. This is the simplest and most effective way I know. For this you will need to remove the
initWithFrame:CGRectMake(40, 100, 240, 168)];
Replace:
NSDictionary *layoutDic = ....
With:
NSDictionary *layoutDic = NSDictionaryOfVariableBindings(scrollView);
Edit: I use your code, and it works, I can scroll, I do in viewDidLoad
UIScrollView *scrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(40, 100, 240, 168)];
UIImageView *imageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"purple_button.png"]];
[scrollView addSubview:imageView];
scrollView.contentSize = CGSizeMake(1000, 2000);
scrollView.backgroundColor = [UIColor clearColor];
scrollView.scrollEnabled = YES;
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
NSDictionary *layoutDic = NSDictionaryOfVariableBindings(scrollView);
[self.view addSubview:scrollView];
NSArray *scrollViewConstraintWidth = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-40-[scrollView(==240)]" options:0 metrics:nil views:layoutDic];
NSArray *scrollViewConstraintHeight = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-50-[scrollView(==468)]" options:0 metrics:nil views:layoutDic];
[self.view addConstraints:scrollViewConstraintHeight];
[self.view addConstraints:scrollViewConstraintWidth];
in properties under Show the file inspector uncheck Use Auto Layout
Add the delegate to self like this:
UIScrollView *scrollView = [[UIScrollView alloc]init];
scrollView.delegate=self;
UIImageView *imageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"ParentsMock.png"]];
[scrollView addSubview:imageView];
I found out the answer. It seems that I need to enable userInteraction on UIImageView. Remember when I use UIImageView as the view of the view controller. Therefore default value for userInteractionEnabled Property of UIImageView is NO. It ACCEPTS NO touches at all. We need we enable it like this
imageView.userInteractionEnabled = YES;
PROBLEM Solved.

Auto Layout: view height is always zero

I have code something like this in viewDidLoad:
UIView *parentView = [[UIView alloc] init];
parentView.translatesAutoresizingMaskIntoConstraints = NO;
UIView *topView = nil;
for (int i = 0; i < 3; i++) {
UILabel *nameLabel = [[UILabel alloc] init];
nameLabel.text = self.data[i];
nameLabel.translatesAutoresizingMaskIntoConstraints = NO;
[parentView addSubview:nameLabel];
UIImageView *clockIconView = [[UIImageView alloc] init];
clockIconView.image = [UIImage imageNamed:#"img"];
clockIconView.translatesAutoresizingMaskIntoConstraints = NO;
[parentView addSubview:clockIconView];
UILabel *timeLabel = [[UILabel alloc] init];
timeLabel.text = self.time;
timeLabel.translatesAutoresizingMaskIntoConstraints = NO;
[parentView addSubview:timeLabel];
[parentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[nameLabel(>=150)]-[clockIconView(20)]-1-[timeLabel(39)]-|"
options:NSLayoutFormatAlignAllBaseline
metrics:nil
views:NSDictionaryOfVariableBindings(nameLabel, clockIconView, timeLabel)]];
// First view - add constraint to superview
if (i == 0) {
[parentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[nameLabel(20)]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(nameLabel)]];
}
// Add constraint to previous view
if (topView) {
[parentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[topView]-[nameLabel(20)]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(nameLabel, topView)]];
}
// Last view - add constraint to superview's bottom
if (i == 2) {
[parentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[nameLabel(20)]-|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(nameLabel)]];
}
[parentView layoutIfNeeded];
topView = nameLabel;
}
NSLog(#"%#", parentView);
First, I add horizontal constraints to align labels and an imageView. Then, I create vertical constraints.
However, parentView's frame has negative origin x and height is always 0. Can't figure out why. Any ideas why height is zero?
<UIView: 0x17037b600; frame = (-129 0; 258 0); autoresize = W+H; layer = <CALayer: 0x170036da0>>

Align two uiviews below each other using autoloayout

I am trying desperately to align two views below each other with autolayout hoping to animate them in the future and with a relative position this makes it more rigid than just setting frames and updating etc...
So the code:
// create two views
UIView *one = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
UIView *two = [[UIView alloc] initWithFrame:CGRectMake(0, 150, 50, 50)];
// add some color
[one setBackgroundColor:[UIColor whiteColor]];
[two setBackgroundColor:[UIColor redColor]];
// add them to self ( self = uiview )
[self addSubview:one];
[self addSubview:two];
// make dict
NSDictionary *views = NSDictionaryOfVariableBindings(one, two);
// add constraints to make the views as wide as the container ( self )
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[one]|"
options:0
metrics:nil
views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[two]|"
options:0
metrics:nil
views:views]];
// add constraint to make the second ( two ) item 10px below the first ( one )
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[one]-10-[two]"
options:0
metrics:nil
views:views]];
```
<-- the result
<-- the goal
What am i doing wrong?
Thanks in advance!
These two lines explain it all:
UIView *one = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
UIView *two = [[UIView alloc] initWithFrame:CGRectMake(0, 150, 50, 50)];
This is exactly what you are seeing as a result: both views are 50x50 in size, and view two is placed at x=0 and y=150.
When working with autolayout, you need to forget everything about frames and rects. State what constraints you want, for each view and between views, and let autolayout create the frames behind the scenes.
Declare views for autolayout like this:
UIView *one = [[UIView alloc] init];
one.translatesAutoresizingMaskIntoConstraints = NO;
UIView *two = [[UIView alloc] init];
two.translatesAutoresizingMaskIntoConstraints = NO;
You should not set frames while using constraints. That's what constraints are for. Constraints are the cause, frame is the effect
// create two views
UIView *one = [[UIView alloc] init];
UIView *two = [[UIView alloc] init];
one.translatesAutoresizingMaskIntoConstraints = NO;
two.translatesAutoresizingMaskIntoConstraints = NO;
// add some color
[one setBackgroundColor:[UIColor whiteColor]];
[two setBackgroundColor:[UIColor redColor]];
// add them to self ( self = uiview )
[self addSubview:one];
[self addSubview:two];
// make dict
NSDictionary *views = NSDictionaryOfVariableBindings(one, two);
// add constraints to make the views as wide as the container ( self )
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[one]|"
options:0
metrics:nil
views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[two]|"
options:0
metrics:nil
views:views]];
// add constraint to make the second ( two ) item 10px below the first ( one )
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[one]-10-[two]|"
options:0
metrics:nil
views:views]];
Note: I have changed a constraint in the last line. Your constraints did not specify the height of view two. It would have given you an "AMBIGUOUS" layout.

Resources