Fit UIView height to its content with AutoLayout - ios

I'm using iOS 6 AutoLayout feature with Masonry DSL to arrange views within UITableViewCell. This is the layout arrangements that I want to achieve:
The containerView is a virtual container that should dynamically adapt its size to fit its content. With my current implementation, this is what I got instead:
It seems that containerView did get vertically centered correctly, but it has zero width and height, hence not showing up properly. How can I instruct containerView to fit its size to its content? Code snippets are attached below.
Thanks!
UITableViewCell initializer
- (id)initWithReuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier]) {
self.titleLabel = [[UILabel alloc] init];
self.titleLabel.font = [UIFont boldSystemFontOfSize:[UIFont smallSystemFontSize]];
self.titleLabel.text = #"ノートタイトル";
self.coverImage = [[UIView alloc] init];
self.coverImage.backgroundColor = [UIColor carrotColor];
self.avatarImage = [[UIView alloc] init];
self.avatarImage.backgroundColor = [UIColor emerlandColor];
self.authorLabel = [[UILabel alloc] init];
self.authorLabel.font = [UIFont systemFontOfSize:[UIFont smallSystemFontSize]];
self.authorLabel.text = #"著者の名前";
self.containerView = [[UIView alloc] init];
self.containerView.backgroundColor = [UIColor lightGrayColor];
[self.containerView addSubview:self.titleLabel];
[self.containerView addSubview:self.avatarImage];
[self.containerView addSubview:self.authorLabel];
[self.contentView addSubview:self.containerView];
[self.contentView addSubview:self.coverImage];
self.selectionStyle = UITableViewCellSelectionStyleNone;
[self updateConstraintsIfNeeded];
}
return self;
}
UITableViewCell updateConstraints
- (void)updateConstraints
{
[super updateConstraints];
[self.coverImage mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.contentView).with.offset(20);
make.top.equalTo(self.contentView).with.offset(5);
make.bottom.equalTo(self.contentView).with.offset(-5);
make.width.equalTo(self.coverImage.mas_height).multipliedBy(0.75);
}];
[self.containerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.coverImage.mas_right).with.offset(10);
make.centerY.equalTo(self.contentView);
}];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.containerView);
make.top.equalTo(self.containerView);
}];
[self.avatarImage mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.titleLabel.mas_bottom).with.offset(5);
make.left.equalTo(self.titleLabel);
make.width.equalTo(#20);
make.height.equalTo(#20);
}];
[self.authorLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.avatarImage.mas_right).with.offset(5);
make.centerY.equalTo(self.avatarImage);
}];
}

I found out the solution to my own question. Apparently, in order for containerView to resize itself dynamically, we have to make sure that each elements within are connected rigidly to each other and the edges of the superview (i.e. containerView). For example, in VFL it should be something like this: "V:|-10-[child1]-5-[child2]-5-|"
So in my case, I add a new constraint make.bottom.equalTo(self.containerView) to avatarImage and now the superview knows how to height itself!

Related

Auto Layout - UILabel inside UITableViewCell is not wrapping on initial load for iOS 6

I am seeing a weird issue with UILabel wrapping in my custom UITableViewCell. I am using auto-layout and while this works fine on iOS > 6, the UILabel inside UITableViewCell is not wrapping on first load for iOS 6. When I tap on the cell and show details view and then come back, then UILabel wraps up as expected.
Here is how I am adding label to the cell:
self.productNameLabel = [[UILabel alloc] init];
self.productNameLabel.translatesAutoresizingMaskIntoConstraints = NO;
self.productNameLabel.backgroundColor = [UIColor clearColor];
self.productNameLabel.textAlignment = NSTextAlignmentLeft;
self.productNameLabel.font = kFontDynamicSubHead;
self.productNameLabel.lineBreakMode = NSLineBreakByWordWrapping;
self.productNameLabel.numberOfLines = 2;
[self.contentView addSubview:self.productNameLabel];
Then I add some constraints to position the label. I do not set any Height/Width constraint.
Finally, in my cell' layoutSubviews I set the preferredMaxLayoutWidth for my label.
- (void)layoutSubviews {
[super layoutSubviews];
self.productNameLabel.preferredMaxLayoutWidth = self.productNameLabel.bounds.size.width;
}
If anyone faced this before or know the workaround then kindly suggest.
Try adding constraints for your UILabel in your code, try adding this implementation for your cell initWithCoder
-(instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if(self)
{
self.productNameLabel = [[UILabel alloc] init];
self.productNameLabel.translatesAutoresizingMaskIntoConstraints = NO;
self.productNameLabel.backgroundColor = [UIColor clearColor];
self.productNameLabel.textAlignment = NSTextAlignmentLeft;
self.productNameLabel.lineBreakMode = NSLineBreakByWordWrapping;
self.productNameLabel.numberOfLines = 2;
[self setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.contentView addSubview:self.productNameLabel];
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[view]-0-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:#{#"view":self.productNameLabel}]];
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[view]-0-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:#{#"view":self.productNameLabel}]];
[self setNeedsLayout];
}
return self;
}
Hope this helps

Cannot select/edit text in UITextView within UIScrollView

I have setup my UIScrollView with subviews like so using Masonry:
- (void)setupViews {
self.view.backgroundColor = [UIColor Background];
self.scrollView = [UIScrollView new];
self.scrollView.alwaysBounceVertical = YES;
[self.view addSubview:self.scrollView];
[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
self.contentView = [UIView new];
[self.scrollView addSubview:self.contentView];
self.scrollView.backgroundColor = [UIColor clearColor];
[self.contentView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.scrollView);
make.left.and.right.equalTo(self.view);
}];
self.imageButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.imageButton sd_setImageWithURL:[NSURL URLWithString:self.card.cardImageURL] forState:UIControlStateNormal];
[self.imageButton.imageView setContentMode:UIViewContentModeScaleAspectFill];
[self.contentView addSubview:self.imageButton];
[self.imageButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.and.right.equalTo(self.contentView);
make.height.equalTo(self.imageButton.mas_width).multipliedBy(3/4.0);
}];
self.textField = [UITextField new];
self.textField.backgroundColor = [UIColor whiteColor];
self.textField.font = kFontRegular(14);
self.textField.text = self.card.cardTitle;
[self.contentView addSubview:self.textField];
[self.textField mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.contentView).with.offset(8);
make.top.equalTo(self.imageButton.mas_bottom).with.offset(8);
make.right.equalTo(self.contentView).with.offset(-8);
make.height.equalTo(#(45));
}];
}
but it will not let me interact with the UITextField. what element here is preventing user interaction?
Your contentView's height will be zero. So it will never get some touch events. You should not use this constraint:
make.edges.equalTo(self.scrollView);
It will not get a correct value. Try to set top and height constraints for your contentView.
make.top.equalTo(#0);
make.height.equalTo(#700);

Width of contentView in UITableViewCell is different for each cell

In an UITableViewController defined in a Storyboard with Auto Layout enabled, a UITableViewCell subclass is created programatically (no IB prototype cell) and sets constraints through updateConstraints using PureLayout.
For some reason, the table view has each contentView with a different width, instead of using the const width of UITableView.
Here's the init and updateConstraints method of UITableViewCell subclass -
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
_nameLabel = [GTPaddedLabel newAutoLayoutView];
_nameLabel.font = [BQFontUtil standardFontSize:StandardFontSize1a bold:YES italic:NO];
_nameLabel.textColor = [UIColor blackColor];
_drugType = [UILabel newAutoLayoutView];
_drugType.font = [BQFontUtil standardFontSize:StandardFontSize0 bold:NO italic:NO];
_drugType.textColor = [UIColor darkGrayColor];
_drugType.textAlignment = NSTextAlignmentRight;
_genericsLabel = [UILabel newAutoLayoutView];
_genericsLabel.font = [BQFontUtil standardFontSize:StandardFontSize0 bold:NO italic:NO];
_genericsLabel.textColor = [UIColor darkGrayColor];
_regimen = [UILabel newAutoLayoutView];
_regimen.font = [BQFontUtil standardFontSize:StandardFontSize0 bold:NO italic:NO];
_regimen.textColor = [UIColor darkGrayColor];
[self.contentView addSubview:_nameLabel];
[self.contentView addSubview:_drugType];
[self.contentView addSubview:_genericsLabel];
[self.contentView addSubview:_regimen];
self.contentView.backgroundColor = [UIColor redColor];
}
return self;
}
- (void)updateConstraints {
if (!constraintsCreated) {
// [self.contentView autoPinEdgesToSuperviewEdgesWithInsets:UIEdgeInsetsMake(0, 0, 0, 0)];
[UIView autoSetPriority:UILayoutPriorityRequired forConstraints:^{
[_nameLabel autoSetContentCompressionResistancePriorityForAxis:ALAxisVertical];
}];
[_nameLabel autoPinEdgesToSuperviewEdgesWithInsets:UIEdgeInsetsMake(8, 8, 0, 8) excludingEdge:ALEdgeBottom];
[_genericsLabel autoPinEdgeToSuperviewEdge:ALEdgeLeading withInset:16];
[_genericsLabel autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:_nameLabel withOffset:4];
[_genericsLabel autoSetDimension:ALDimensionHeight toSize:21];
[_drugType autoPinEdgeToSuperviewEdge:ALEdgeTrailing withInset:16];
[_drugType autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:_nameLabel withOffset:4];
[_drugType autoPinEdge:ALEdgeLeading toEdge:ALEdgeTrailing ofView:_genericsLabel withOffset:8];
[_drugType autoSetDimension:ALDimensionHeight toSize:21];
[_regimen autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:_genericsLabel withOffset:2];
[_regimen autoSetDimension:ALDimensionHeight toSize:21];
[_regimen autoPinEdgeToSuperviewEdge:ALEdgeTrailing withInset:16];
[_regimen autoPinEdgeToSuperviewEdge:ALEdgeLeading withInset:16];
[_regimen autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:8];
constraintsCreated = YES;
}
[super updateConstraints];
}
Why does contentView (brown color) doesn't have the same width as the table view?
Maybe that's a workaround, it works well when I'm constraining contentView's width to cell's width -
- (void)updateConstraints {
if (!constraintsCreated) {
[self.contentView autoSetDimension:ALDimensionWidth toSize:self.frame.size.width];
...
}
}

UIScrollView paging disabled after rotation

Following is my code for App Tour using UIScrollView with paging enabled using AutoLayout (Masonry) It works fine in Portrait but when I rotate paging gets disabled so it doesn't work in Landscape. Then it doesn't work in Portrait mode either. Can someone help what's wrong here?
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor clearColor];
self.scrollView = [UIScrollView new];
self.scrollView.backgroundColor = [UIColor clearColor];
self.scrollView.scrollEnabled = YES;
self.scrollView.pagingEnabled = YES;
self.scrollView.delegate = self;
self.scrollView.showsHorizontalScrollIndicator = NO;
[self.view addSubview:self.scrollView];
[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
UIImageView *esImageview1 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"CommonResources.bundle/page1"]];
esImageview1.contentMode = UIViewContentModeScaleAspectFit;
esImageview1.backgroundColor = [UIColor clearColor];
esImageview1.userInteractionEnabled = YES;
[self.scrollView addSubview:esImageview1];
[esImageview1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.bottom.equalTo(self.scrollView);
make.width.height.equalTo(self.view);
}];
UIImageView *esImageview2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"CommonResources.bundle/page2"]];
esImageview2.contentMode = UIViewContentModeScaleAspectFit;
esImageview2.backgroundColor = [UIColor clearColor];
esImageview2.userInteractionEnabled = YES;
[self.scrollView addSubview:esImageview2];
[esImageview2 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(esImageview1.mas_right);
make.top.bottom.equalTo(self.scrollView);
make.width.height.equalTo(self.view);
}];
UIImageView *esImageview3 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"CommonResources.bundle/page3"]];
esImageview3.contentMode = UIViewContentModeScaleAspectFit;
esImageview3.backgroundColor = [UIColor clearColor];
esImageview3.userInteractionEnabled = YES;
[self.scrollView addSubview:esImageview3];
[esImageview3 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(esImageview2.mas_right);
make.top.bottom.equalTo(self.scrollView);
make.width.height.equalTo(self.view);
}];
self.pageControl = [UIPageControl new];
self.pageControl.numberOfPages = 3;
self.pageControl.currentPage = 0;
self.pageControl.backgroundColor = [UIColor clearColor];
[self.view addSubview:self.pageControl];
[self.pageControl mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.view);
make.height.equalTo(#40);
make.top.equalTo(self.view).with.offset(40);
}];
UILabel *titleLabel = [UILabel new];
titleLabel.text = #"App Tour";
titleLabel.backgroundColor = [UIColor clearColor];
titleLabel.textColor = [UIColor whiteColor];
titleLabel.font = [UIFont fontWithName:#"Helvetica-Light" size:16];
titleLabel.textAlignment = NSTextAlignmentCenter;
[self.view addSubview:titleLabel];
[titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.view);
make.height.equalTo(#40);
make.top.equalTo(self.view).with.offset(20);
}];
UIButton *doneButton = [UIButton new];
[doneButton.titleLabel setFont:[UIFont fontWithName:#"Helvetica-Light" size:16]];
[doneButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[doneButton setTitleColor:[UIColor grayColor] forState:UIControlStateHighlighted];
[doneButton setTitle:#"Done" forState:UIControlStateNormal];
[doneButton addTarget:self action:#selector(doneButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
doneButton.backgroundColor = [UIColor clearColor];
[self.view addSubview:doneButton];
[doneButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.view);
make.height.equalTo(#40);
make.width.equalTo(#70);
make.top.equalTo(self.view).with.offset(20);
}];
}
- (void)doneButtonPressed:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width*self.pageControl.numberOfPages, self.view.frame.size.height);
}
- (void)scrollViewDidScroll:(UIScrollView *)sender {
CGFloat pageWidth = self.scrollView.frame.size.width;
int page = floor((self.scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
self.pageControl.currentPage = page;
}
I'm guessing its getting covered up by another view or something similar to that. Try subclassing the UIScrollView and implementing the following method to check if its responding to your touches at all.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"OMG TOUCHES WORK, NOW WHAT?");
}
You can easily check if it was getting covered up by forcing it to be in front. Call this line of code in the callback for rotation in the UIViewController containing the UIScrollView. It should fix the problem. You may have to subclass the UIView for the controller and put this method in the didLayoutSubviews method instead, not quite sure, depends on your implementation.
[self.view bringSubViewToFront:self.scrollView];
Also check the frame and bounds before and after rotation and see if anything is out of place looking
I just wanted to reset Scrollview's contentsize in didRotateFromInterfaceOrientation delegate. Not sure why though because when I rotate, videDidLayoutSubview already gets called where I am already reseting content size.
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width*self.pageControl.numberOfPages, self.view.frame.size.height);
}

UIProgressView work not correctly when setFrame in UICollectionViewCell

I create a UIProgressView inside UICollectionViewCell, I try to setFrame for UIProgressView to change position in UICollectionViewCell but when do that, some cells not display progressView.
When I delete setFrame, It's OK but default width at the top of UICollectionViewCell
What's the problem?How to change UIProgressView size, origin? Please help!
//Cell:UICollectionViewCell
//Cell.m
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
//ProgressView
self.progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleBar];
[self.progressView setFrame:progressViewFrame];//Some cells not display progressView
[self.progressView addSubview:self.downloadBar];
}
return self;
}
I have modified you code. Now, progress view is working properly. All cells are showing the progress view. Also, width & position of the cell can be modified, if you change the frame of self.downloadBin the below code
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self.contentView setBackgroundColor:[UIColor underPageBackgroundColor]];
//cellImage
self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 0, 57, 57)];
[self.imageView setBackgroundColor:[UIColor clearColor]];
[self.contentView addSubview:self.imageView];
//ProgressView
self.downloadBar = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleBar];
[self.downloadBar setFrame:CGRectMake(0, 10, 300, 10)];
[self.contentView addSubview:self.downloadBar];
[self.downloadBar setHidden:YES];
self.receivedData = [[NSMutableData alloc] init];
}
return self;
}
I have modified the code of cell.m from the github url which you have provided.

Resources