I create a subview with a UIview (acts as header), a UIImage, and 10 UILabels. I'm putting these into a UICollectionView as cells.
When designed completely, it does not scroll smoothly. If i remove all the UILabels, it scrolls smoothly.
I'm assuming it's sluggish cause the UICollectionView loads on demand, so when it needs each new cell, it has to draw it which locks up the main thread.
Is it just a matter that its too much for iOS to handle to create them? If so, is there another way I can put text into it?
what my cell looks like:
Here is DatasetFilterListPanelView, this creates the UIView that I put into the UICollectionViewCell. I did it this way cause I created this before I decided to use UICollectionView.
#implementation DatasetFilterListPanelView
-(id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.translatesAutoresizingMaskIntoConstraints = FALSE;
UIView *contentView = [self createContentView];
[self addSubview:contentView];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[contentView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(contentView)]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(contentView)]];
}
return self;
}
-(UIView *) createContentView {
UIView *contentView = [[UIView alloc] initWithFrame:self.frame];
// contentView.translatesAutoresizingMaskIntoConstraints = FALSE;
contentView.backgroundColor = [UIColor myDarkGrayColor];
UIView *headerView = [self createHeaderView];
[contentView addSubview:headerView];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[headerView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(headerView)]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[headerView]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(headerView)]];
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"gear12.png"]];
imageView.translatesAutoresizingMaskIntoConstraints = FALSE;
imageView.backgroundColor = [UIColor blueColor];
self.imageView = imageView;
[imageView addConstraint:[NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:imageView attribute:NSLayoutAttributeWidth multiplier:1 constant:0]];
[contentView addSubview:imageView];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[imageView]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(imageView)]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[headerView]-[imageView]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(headerView, imageView)]];
UILabel *acresLabel = [self createLabelWithTitle:#"Label01:" andFont:[UIFont fontWithName:HELVETICA_FONT_STYLE_BOLD size:12]];
[contentView addSubview:acresLabel];
UILabel *addedLabel = [self createLabelWithTitle:#"Label02:" andFont:[UIFont fontWithName:HELVETICA_FONT_STYLE_BOLD size:12]];
[contentView addSubview:addedLabel];
UILabel *typeLabel = [self createLabelWithTitle:#"Label03:" andFont:[UIFont fontWithName:HELVETICA_FONT_STYLE_BOLD size:12]];
[contentView addSubview:typeLabel];
UILabel *zonesLabel = [self createLabelWithTitle:#"Label04:" andFont:[UIFont fontWithName:HELVETICA_FONT_STYLE_BOLD size:12]];
[contentView addSubview:zonesLabel];
UILabel *sceneLabel = [self createLabelWithTitle:#"Label05:" andFont:[UIFont fontWithName:HELVETICA_FONT_STYLE_BOLD size:12]];
[contentView addSubview:sceneLabel];
UILabel *acresValueLabel = [self createLabelWithTitle:#"Data" andFont:[UIFont fontWithName:HELVETICA_FONT_STYLE size:12]];
acresValueLabel.textAlignment = NSTextAlignmentLeft;
[contentView addSubview:acresValueLabel];
UILabel *addedValueLabel = [self createLabelWithTitle:#"Data" andFont:[UIFont fontWithName:HELVETICA_FONT_STYLE size:12]];
addedValueLabel.textAlignment = NSTextAlignmentLeft;
[contentView addSubview:addedValueLabel];
UILabel *typeValueLabel = [self createLabelWithTitle:#"Name" andFont:[UIFont fontWithName:HELVETICA_FONT_STYLE size:12]];
typeValueLabel.textAlignment = NSTextAlignmentLeft;
[contentView addSubview:typeValueLabel];
UILabel *zonesValueLabel = [self createLabelWithTitle:#"Data" andFont:[UIFont fontWithName:HELVETICA_FONT_STYLE size:12]];
zonesValueLabel.textAlignment = NSTextAlignmentLeft;
[contentView addSubview:zonesValueLabel];
UILabel *sceneValueLabel = [self createLabelWithTitle:#"Name" andFont:[UIFont fontWithName:HELVETICA_FONT_STYLE size:12]];
sceneValueLabel.textAlignment = NSTextAlignmentLeft;
[contentView addSubview:sceneValueLabel];
NSDictionary *views = NSDictionaryOfVariableBindings(headerView, imageView, acresLabel, acresValueLabel, addedLabel, addedValueLabel, typeLabel, typeValueLabel, zonesLabel, zonesValueLabel, sceneLabel, sceneValueLabel);
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[headerView]-[acresLabel]"
options:0
metrics:nil
views:views]] ;
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[acresLabel]-[addedLabel(==acresLabel)]-[typeLabel(==acresLabel)]-[zonesLabel(==acresLabel)]-[sceneLabel(==acresLabel)]-|"
options:NSLayoutFormatAlignAllRight
metrics:0
views:views]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[acresValueLabel]-[addedValueLabel(==acresLabel)]-[typeValueLabel(==acresLabel)]-[zonesValueLabel(==acresLabel)]-[sceneValueLabel(==acresLabel)]-|"
options:NSLayoutFormatAlignAllLeft
metrics:nil
views:views]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[imageView]-20-[acresLabel]-[acresValueLabel]" options:0 metrics:nil views:views]];
return contentView;
}
-(UIView *)createHeaderView {
UIView *view = [UIView new];
view.translatesAutoresizingMaskIntoConstraints = FALSE;
view.backgroundColor = [UIColor blueColor];
view.clipsToBounds = YES;
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[view(30)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)]];
UILabel *title = [UILabel new];
title.translatesAutoresizingMaskIntoConstraints = FALSE;
title.text = #"Default text";
title.font = [UIFont fontWithName:HELVETICA_FONT_STYLE_BOLD size:14];
title.textColor = [UIColor whiteColor];
title.backgroundColor = [UIColor clearColor];
self.headerLabel = title;
[view addSubview:title];
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[title]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(title)]];
[view addConstraint:[NSLayoutConstraint constraintWithItem:title attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];
self.headerGradient = [UIColor grayGradient];
self.headerGradient.frame = CGRectMake(0, 0, 360, 30);
[view.layer insertSublayer:self.headerGradient atIndex:0];
return view;
}
-(UILabel *)createLabelWithTitle:(NSString *)title andFont:(UIFont *)font; {
UILabel *label = [UILabel new];
label.translatesAutoresizingMaskIntoConstraints = FALSE;
label.text = title;
label.font = font;
label.textAlignment = NSTextAlignmentRight;
label.textColor = [UIColor whiteColor];
label.backgroundColor = [UIColor clearColor];
return label;
}
Here is my UICollectionViewCell file, i just addSubview a DatasetFilterListPanelView to it.
#implementation DatasetViewCell
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self addSubview:[[DatasetFilterListPanelView alloc] initWithFrame:CGRectMake(0, 0, 360, 160)]];
}
return self;
}
When I use the same panels in a UIScrollview, once they are all loaded and positioned, it will scroll smoothly. So it has to be the loading a cell on demand aspect of the UICollectionView.
I followed this UICollectionView Tutorial
EDIT: creating the cell:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
DatasetViewCell *datasetCell = [collectionView dequeueReusableCellWithReuseIdentifier:DatasetCellIdentifier forIndexPath:indexPath];
return datasetCell;
}
EDIT 2: Instrument tracing:
Ok, after much playing around I figured out the culprit: constraints! CodaFI was right. I didn't have that many constraints in the panel so i didn't think it could be the issue.
I created a nib file and removed autolayout and it now scrolls smoothly.
Lesson of the day: Constraints are slow to compute!
Generally the problem is that you don't reuse the cells. Make sure you use dequeueReusableCellWithReuseIdentifier:forIndexPath: to reuse existing cells.
Related
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.
Im using AutoLayout in my cells but, when the cell has large text inside the cell and the cell expands the cell seems to overlap the cell below. Image shows how this is overlapping the cells:
UITableViewCell:
#import "PostTableViewCell.h"
#implementation PostTableViewCell
#synthesize main;
#synthesize top;
#synthesize center;
#synthesize centerLeft;
#synthesize centerRight;
#synthesize bottom;
#synthesize labelMessage;
#synthesize labelCat;
#synthesize labelSubCat;
#synthesize labelCountry;
#synthesize labelUser;
#synthesize labelDate;
#synthesize imageviewThreadImage;
#synthesize split;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self.contentView setTranslatesAutoresizingMaskIntoConstraints:NO];
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
CGRect screenBound = [[UIScreen mainScreen] bounds];
CGSize screenSize = screenBound.size;
CGFloat screenWidth = screenSize.width;
//CGFloat screenHeight = screenSize.height;
main = [UIView new];
[self.contentView addSubview:main];
main.translatesAutoresizingMaskIntoConstraints = NO;
[main sizeToFit];
main.backgroundColor = [UIColor whiteColor];
top = [UIView new];
[main addSubview:top];
top.translatesAutoresizingMaskIntoConstraints = NO;
[top sizeToFit];
top.backgroundColor = [UIColor whiteColor];
labelUser = [UILabel new];
[top addSubview:labelUser];
labelUser.translatesAutoresizingMaskIntoConstraints = NO;
[labelUser sizeToFit];
[labelUser setFont:[UIFont systemFontOfSize:10]];
labelUser.textColor = [UIColor colorWithRed:(114.0/255.0) green:(114.0/255.0) blue:(114.0/255.0) alpha:1.0];
labelDate = [UILabel new];
[top addSubview:labelDate];
labelDate.translatesAutoresizingMaskIntoConstraints = NO;
[labelDate sizeToFit];
[labelDate setFont:[UIFont systemFontOfSize:10]];
labelDate.textColor = [UIColor colorWithRed:(114.0/255.0) green:(114.0/255.0) blue:(114.0/255.0) alpha:1.0];
center = [UIView new];
[main addSubview:center];
center.translatesAutoresizingMaskIntoConstraints = NO;
[center sizeToFit];
center.backgroundColor = [UIColor whiteColor];
imageviewThreadImage = [UIImageView new];
[center addSubview:imageviewThreadImage];
imageviewThreadImage.translatesAutoresizingMaskIntoConstraints = NO;
imageviewThreadImage.backgroundColor = [UIColor colorWithRed:(207.0/255.0) green:(215.0/255.0) blue:(248.0/255.0) alpha:1.0];
[imageviewThreadImage sizeToFit];
labelMessage = [UILabel new];
[center addSubview:labelMessage];
labelMessage.translatesAutoresizingMaskIntoConstraints = NO;
[labelMessage sizeToFit];
[labelMessage setFont:[UIFont systemFontOfSize:12]];
labelMessage.preferredMaxLayoutWidth = screenWidth - 10 - 36;
labelMessage.lineBreakMode = NSLineBreakByWordWrapping;
labelMessage.numberOfLines = 0;
labelMessage.textColor = [UIColor lightGrayColor];
//labelMessage.backgroundColor = [UIColor colorWithRed:(207.0/255.0) green:(215.0/255.0) blue:(248.0/255.0) alpha:1.0];
//labelMessage.lineBreakMode = UILineBreakModeWordWrap;
bottom = [UIView new];
[main addSubview:bottom];
bottom.translatesAutoresizingMaskIntoConstraints = NO;
[bottom sizeToFit];
labelCat = [UILabel new];
[bottom addSubview:labelCat];
labelCat.translatesAutoresizingMaskIntoConstraints = NO;
[labelCat sizeToFit];
[labelCat setFont:[UIFont systemFontOfSize:10]];
labelCat.textColor = [UIColor colorWithRed:(58.0/255.0) green:(82.0/255.0) blue:(207.0/255.0) alpha:1.0];
labelCat.lineBreakMode = NSLineBreakByWordWrapping;
labelCat.numberOfLines = 0;
labelSubCat = [UILabel new];
[bottom addSubview:labelSubCat];
labelSubCat.translatesAutoresizingMaskIntoConstraints = NO;
[labelSubCat sizeToFit];
[labelSubCat setFont:[UIFont systemFontOfSize:10]];
labelSubCat.textColor = [UIColor colorWithRed:(58.0/255.0) green:(82.0/255.0) blue:(207.0/255.0) alpha:1.0];
labelCountry = [UILabel new];
[bottom addSubview:labelCountry];
labelCountry.translatesAutoresizingMaskIntoConstraints = NO;
[labelCountry sizeToFit];
[labelCountry setFont:[UIFont systemFontOfSize:10]];
labelCountry.textColor = [UIColor colorWithRed:(58.0/255.0) green:(82.0/255.0) blue:(207.0/255.0) alpha:1.0];
split = [UIView new];
[main addSubview:split];
split.translatesAutoresizingMaskIntoConstraints = NO;
[split sizeToFit];
split.backgroundColor = [UIColor grayColor];
}
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
// Make sure the contentView does a layout pass here so that its subviews have their frames set, which we
// need to use to set the preferredMaxLayoutWidth below.
[self.contentView setNeedsLayout];
[self.contentView layoutIfNeeded];
// Set the preferredMaxLayoutWidth of the mutli-line bodyLabel based on the evaluated width of the label's frame,
// as this will allow the text to wrap correctly, and as a result allow the label to take on the correct height.
self.labelMessage.preferredMaxLayoutWidth = CGRectGetWidth(self.labelMessage.frame);
}
- (void)updateConstraints {
[super updateConstraints];
if (self.didSetupConstraints) return;
NSDictionary *viewsDictionary7 = #{#"main":main};
NSArray *constraint_H37 = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[main]|"
options:0
metrics:nil
views:viewsDictionary7];
NSArray *constraint_V37 = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[main]|"
options:0
metrics:nil
views:viewsDictionary7];
[self.contentView addConstraints:constraint_H37];
[self.contentView addConstraints:constraint_V37];
//NSString *text = labelMessage.text;
NSDictionary *viewsDictionary3 = #{#"top":top,#"center":center,#"bottom":bottom,#"split":split};
NSArray *constraint_H3 = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-10-[top]-5-[center(>=40)]-5-[bottom]-10-[split(1)]|"
options:0
metrics:nil
views:viewsDictionary3];
NSArray *constraint_H33cvcv4545fdd = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[split]|"
options:0
metrics:nil
views:viewsDictionary3];
NSArray *constraint_H33 = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[top]|"
options:0
metrics:nil
views:viewsDictionary3];
NSArray *constraint_H333 = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[center]|"
options:0
metrics:nil
views:viewsDictionary3];
NSArray *constraint_H3335657 = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[bottom]|"
options:0
metrics:nil
views:viewsDictionary3];
NSDictionary *viewsDictionary4 = #{#"labelUser":labelUser,#"labelDate":labelDate};
NSArray *constraint_H4 = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[labelUser]|"
options:0
metrics:nil
views:viewsDictionary4];
NSDictionary *viewsDictionary45 = #{#"labelDate":labelDate};
NSArray *constraint_H4555 = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[labelDate]|"
options:0
metrics:nil
views:viewsDictionary45];
NSArray *constraint_H44 = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-5-[labelUser]-20-[labelDate]-5-|"
options:0
metrics:nil
views:viewsDictionary4];
NSDictionary *viewsDictionary48 = #{#"labelMessage":labelMessage,#"imageviewThreadImage":imageviewThreadImage};
NSArray *constraint_H48 = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-5-[labelMessage]-5-|"
options:0
metrics:nil
views:viewsDictionary48];
NSArray *constraint_H48342 = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-8-[imageviewThreadImage(36)]"
options:0
metrics:nil
views:viewsDictionary48];
NSArray *constraint_H448345fgdfg = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-5-[imageviewThreadImage(36)]"
options:0
metrics:nil
views:viewsDictionary48];
CGRect screenBound = [[UIScreen mainScreen] bounds];
CGSize screenSize = screenBound.size;
CGFloat screenWidth = screenSize.width;
//CGFloat screenHeight = screenSize.height;
NSArray *constraint_H448345fgdfgdfdf454 = [NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"H:|-46-[labelMessage(%f)]-10-|",screenWidth - 51]
options:0
metrics:nil
views:viewsDictionary48];
/**NSArray *constraint_H448 = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-5-[labelMessage]-5-|"
options:0
metrics:nil
views:viewsDictionary48];**/
NSDictionary *viewsDictionary488 = #{#"labelCat":labelCat,#"labelCountry":labelCountry,#"labelSubCat":labelSubCat};
NSArray *constraint_H488 = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-5-[labelCat]|"
options:0
metrics:nil
views:viewsDictionary488];
NSArray *constraint_H48898 = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-5-[labelCountry]|"
options:0
metrics:nil
views:viewsDictionary488];
NSArray *constraint_H48898fgf54 = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-5-[labelSubCat]|"
options:0
metrics:nil
views:viewsDictionary488];
NSArray *constraint_H4488 = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-5-[labelCat]-10-[labelSubCat]-10-[labelCountry]"
options:0
metrics:nil
views:viewsDictionary488];
[self.contentView addConstraints:constraint_H3];
[self.contentView addConstraints:constraint_H33];
[self.contentView addConstraints:constraint_H333];
[self.contentView addConstraints:constraint_H4];
[self.contentView addConstraints:constraint_H44];
[self.contentView addConstraints:constraint_H48];
//[self.contentView addConstraints:constraint_H448];
[self.contentView addConstraints:constraint_H4555];
[self.contentView addConstraints:constraint_H3335657];
[self.contentView addConstraints:constraint_H488];
[self.contentView addConstraints:constraint_H48342];
[self.contentView addConstraints:constraint_H4488];
[self.contentView addConstraints:constraint_H448345fgdfg];
[self.contentView addConstraints:constraint_H48898];
[self.contentView addConstraints:constraint_H48898fgf54];
[self.contentView addConstraints:constraint_H33cvcv4545fdd];
[self.contentView addConstraints:constraint_H448345fgdfgdfdf454];
self.didSetupConstraints = YES;
}
- (void)awakeFromNib {
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
How my cells are reused:
CellForRowIndexPath -
if(tableView == tableViewPost){
Post *t = (Post*)[tmpArray2 objectAtIndex:indexPath.row];
static NSString *simpleTableIdentifier = #"SimpleTableItem46";
PostTableViewCell *cell = (PostTableViewCell*)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
//cell.layer.shouldRasterize = YES;
//cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
if (cell == nil) {
cell = [[PostTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell.labelMessage.text = t.message;
cell.labelCat.text = t.cat;
cell.labelSubCat.text = t.subcat;
cell.labelCountry.text = t.country;
cell.labelUser.text = [NSString stringWithFormat:#"%#", t.user];
cell.labelDate.text = [NSString stringWithFormat:#"%#", t.date];
[cell.contentView setNeedsUpdateConstraints];
[cell.contentView updateConstraintsIfNeeded];
[cell.contentView setNeedsLayout];
[cell.contentView layoutIfNeeded];
return cell;
}
HeightForRowIndexPath -
if(tableView == tableViewPost){
Post *t = (Post*)[tmpArray2 objectAtIndex:indexPath.row];
PostTableViewCell *cell = (PostTableViewCell*)[self tableView:tableView cellForRowAtIndexPath:indexPath];
[cell.contentView setNeedsUpdateConstraints];
[cell.contentView updateConstraintsIfNeeded];
[cell.contentView setNeedsLayout];
[cell.contentView layoutIfNeeded];
CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
//CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return height;
}
if you are using iOS 8 or greater you can try
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 70.0
I'm basically trying to achieve this effect: http://youtu.be/VBW2i0P11iI
The tableview is a basic one that's pinned to it's superview with autolayout. The view underneath is added with the classic insertSubview:belowSubview: / addChildViewController combo.
I've tried a couple of approaches. What I have now is:
if (scrollOffset >= -scrollView.contentInset.top) {
self.resultsTableViewContainerTopConstraint.constant = 0;
} else {
self.resultsTableViewContainerTopConstraint.constant = MAX(self.resultsTableViewContainerTopConstraint.constant, self.resultsTableViewContainerTopConstraint.constant - scrollDiff);
}
}
So I'm basically changing the top constraint based on the delta of contentOffset. The problem with this is that the uitableview bounces back so it always gets into the first branch of the if. But even if I solve this problem I feel like I'll just patch it. I'm sure there's a way more elegant way of achieving the effect in the video having the same responsiveness.
Any suggestion will be much appreciated.
Thanks
I'm not sure if this is what you want but I hacked up a quick demo:
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate>
#property (nonatomic, strong) UIView *headerView;
#property (nonatomic, strong) UILabel *lblTitle;
#property (nonatomic, strong) UIButton *btnReset;
#property (nonatomic, strong) UIImageView *imageView;
#property (nonatomic, strong) UIScrollView *scrollView;
#property (nonatomic, strong) UITableView *tableView;
#property (nonatomic, strong) NSLayoutConstraint *scrollViewTopConstraint;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//self.view.backgroundColor = [UIColor whiteColor];
[self initViews];
[self initConstraints];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)initViews
{
self.view.backgroundColor = [UIColor colorWithRed:43.0/255.0 green:39.0/255.0 blue:55.0/255.0 alpha:1.0];
self.scrollView = [[UIScrollView alloc] init];
self.scrollView.backgroundColor = [UIColor clearColor];
self.scrollView.alwaysBounceVertical = YES;
self.scrollView.delegate = self;
self.headerView = [[UIView alloc] init];
self.headerView.backgroundColor = [UIColor colorWithRed:43.0/255.0 green:39.0/255.0 blue:55.0/255.0 alpha:1.0];
self.headerView.layer.shadowColor = [UIColor blackColor].CGColor;
self.headerView.layer.shadowOffset = CGSizeMake(0,0);
self.headerView.layer.shadowOpacity = 0.25;
self.headerView.layer.shadowRadius = 4;
self.lblTitle = [[UILabel alloc] init];
self.lblTitle.text = #"HEADER VIEW";
self.lblTitle.textColor = [UIColor whiteColor];
self.lblTitle.textAlignment = NSTextAlignmentCenter;
self.btnReset = [[UIButton alloc] init];
[self.btnReset setTitle:#"Reset" forState:UIControlStateNormal];
self.btnReset.backgroundColor = [UIColor colorWithRed:0.75 green:0.0 blue:0.0 alpha:1.0];
self.btnReset.layer.cornerRadius = 5.0;
[self.btnReset addTarget:self action:#selector(resetView) forControlEvents:UIControlEventTouchUpInside];
self.imageView = [[UIImageView alloc] init];
self.imageView.backgroundColor = [UIColor colorWithRed:43.0/255.0 green:39.0/255.0 blue:55.0/255.0 alpha:1.0];
self.tableView = [[UITableView alloc] init];
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.rowHeight = 150.0;
self.tableView.layer.shadowColor = [UIColor blackColor].CGColor;
self.tableView.layer.shadowOffset = CGSizeMake(0,-5);
self.tableView.layer.shadowOpacity = 0.5;
self.tableView.layer.shadowRadius = 20;
self.tableView.backgroundColor = [UIColor clearColor];
self.tableView.scrollEnabled = NO;
self.tableView.clipsToBounds = NO;
[self.headerView addSubview:self.lblTitle];
[self.headerView addSubview:self.btnReset];
[self.scrollView addSubview:self.tableView];
[self.view addSubview:self.imageView];
[self.view addSubview:self.scrollView];
[self.view addSubview:self.headerView];
}
-(void)initConstraints
{
self.scrollView.translatesAutoresizingMaskIntoConstraints = NO;
self.headerView.translatesAutoresizingMaskIntoConstraints = NO;
self.lblTitle.translatesAutoresizingMaskIntoConstraints = NO;
self.btnReset.translatesAutoresizingMaskIntoConstraints = NO;
self.imageView.translatesAutoresizingMaskIntoConstraints = NO;
self.tableView.translatesAutoresizingMaskIntoConstraints = NO;
id views = #{
#"scrollView": self.scrollView,
#"headerView": self.headerView,
#"lblTitle": self.lblTitle,
#"btnReset": self.btnReset,
#"imageView": self.imageView,
#"tableView": self.tableView
};
[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]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.scrollView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0]];
self.scrollViewTopConstraint = [NSLayoutConstraint constraintWithItem:self.scrollView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0];
[self.view addConstraint:self.scrollViewTopConstraint];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.scrollView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[headerView(320)]|" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[headerView(50)]" options:0 metrics:nil views:views]];
[self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[tableView(320)]|" options:0 metrics:nil views:views]];
[self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-150-[tableView(300)]|" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[imageView(320)]|" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[imageView(320)]" options:0 metrics:nil views:views]];
[self.headerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[lblTitle]|" options:0 metrics:nil views:views]];
[self.headerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[lblTitle]|" options:0 metrics:nil views:views]];
[self.headerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[btnReset(80)]-5-|" options:0 metrics:nil views:views]];
[self.headerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-8-[btnReset]-8-|" options:0 metrics:nil views:views]];
}
-(BOOL)prefersStatusBarHidden
{
return YES;
}
#pragma mark - TableView Methods -
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 2;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellID = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
cell.backgroundColor = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1.0];
return cell;
}
#pragma mark - ScrollView Delegate -
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
//NSLog(#"scrollView offset = %lf", scrollView.contentOffset.y);
if(scrollView.contentOffset.y <= -145)
{
//self.scrollView.scrollEnabled = NO;
self.scrollViewTopConstraint.constant = self.view.bounds.size.height;
[UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
[self.scrollView layoutIfNeeded];
} completion:^(BOOL finished) {
}];
}
}
-(void)resetView
{
self.scrollViewTopConstraint.constant = 0;
[UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
[self.scrollView layoutIfNeeded];
} completion:^(BOOL finished) {
}];
}
What you get when you open the app:
Then you drag down to the edge, the view snaps open:
Press the red reset button to bring it back up :D
I have a simple view (controller) with two subviews:
- (void)viewDidLoad {
[super viewDidLoad];
UIView *viewA = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
viewA.translatesAutoresizingMaskIntoConstraints = NO;
viewA.backgroundColor = [UIColor redColor];
[self.view addSubview:viewA];
UIView *viewB = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
viewB.translatesAutoresizingMaskIntoConstraints = NO;
viewB.backgroundColor = [UIColor grayColor];
[self.view addSubview:viewB];
}
Of course they are overlapping at this point (i can only see viewB). So I add a constraint to make viewB go below viewA, like this:
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:viewB
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:viewA
attribute:NSLayoutAttributeBottom
multiplier:1.f
constant:0]];
This makes both views disappear. What am I doing wrong?
Thanks!
When using constraints, you should do everything with constraints (no setting of frames), so adding one constraint isn't enough to define you positions and sizes. I'm not sure why adding the one constraint negated the sizes you set for the views, but it seems that it does. You should do something like this to fully describe the views,
- (void)viewDidLoad {
[super viewDidLoad];
UIView *viewA = [UIView new];
viewA.translatesAutoresizingMaskIntoConstraints = NO;
viewA.backgroundColor = [UIColor redColor];
[self.view addSubview:viewA];
UIView *viewB = [UIView new];
viewB.translatesAutoresizingMaskIntoConstraints = NO;
viewB.backgroundColor = [UIColor grayColor];
[self.view addSubview:viewB];
NSDictionary *dict = NSDictionaryOfVariableBindings(viewA,viewB);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[viewA(==50)]" options:0 metrics:nil views:dict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"[viewB(==50)]" options:0 metrics:nil views:dict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[viewA(==50)][viewB(==50)]" options:NSLayoutFormatAlignAllLeft metrics:nil views:dict]];
}
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}]];