iOS cell with dynamic height has bad width - ios

So first what I want. I want to have cell with dynamic height which is changing by lenght of text. Something like in image:
As you can see second cell is over the edge.
How I did it? First I have table with layout to edges:
I created custom SwitchCell with own xib file:
Label has these autolayout settings:
And switch these:
In viewDidLoad I set tableView properties:
tableView.registerNib(UINib(nibName: SwitchCellIdentifier, bundle: NSBundle.mainBundle()), forCellReuseIdentifier: SwitchCellIdentifier)
tableView.tableFooterView = UIView(frame: CGRectZero)
tableView.rowHeight = UITableViewAutomaticDimension;
tableView.estimatedRowHeight = 44.0;
And in cellForRowAtIndexPath I have this:
...
var cell:SwitchCell = tableView.dequeueReusableCellWithIdentifier(SwitchCellIdentifier) as SwitchCell
cell.titleLabel.text = "Receive payments via multipay and lorem ipsum bla bla"
return cell
So what I've done wrong? What should I change or add?

Perhaps set a max-width constraint on the label or a >= constraint from its trailing edge to the superview. Right now, everything horizontal is "Equal" to fixed values. You'd need to limit the label width, use an inequality constraint between it and the Switch, and/or play w/the constraint priority levels to keep the label from expanding. The estimatedRowHeight and automatic dimensions should still let it expand vertically. –

So for your label you can arrange auto shrink to minimum font size then you can avoid to disappear when the text is too long. And the problem is that you just give constraints in your xib not from xib to cell therefore it is appears like that and you should make it in code just examine below code it may give you an example how to do that.
NSDictionary *metrics = #{#"viewWidth":[NSNumber numberWithFloat:size.width],
#"viewHeight":[NSNumber numberWithFloat:size.height]};
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(yourXibView);
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[yourXibView]-0-|"
options:0
metrics:metrics
views:viewsDictionary];
[self.view addConstraints:constraints];
and do not forget to add yourXibView.translatesAutoresizingMaskIntoConstraints = NO; before above code. This code just in objective c but don't worry it has same logic in swift.

Try increasing Content Hugging Priority of the label

Related

Increase Scroll view height in middle

I have a scroll view and one Plus button is there to add the extra text fields under the add button.
Please check the screenshot below for reference.
- (IBAction)Textfield:(id)sender
{
i++;//global declaration int
UITextField *textfield=[[UITextField alloc]init];
textfield.tag=i;
NSDictionary *viewsDictionary = {#"give-textfield-name here":self.textfield};
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-10-[give-textfield-name]-10-|" options:NSLayoutFormatAlignAllBaseline metrics:nil views:viewsDictionary];
[self.textfield addConstraints:constraints];
}
Visual constraints example:
V:|-10-[give-textfield-name]-10-|
V means vertical constraints
| viewcontroller left margin &right margin
10 giving space from left margin .
And this is very basic thing of VisualConstraints ,you need to develop it more.
And one more thing you need to update the scrollviewcontent size after adding each textfield.
set the viewcontroller as freedom height and width.[update this also]
Hope it will help to start you program.
this is the link for visual constraints.
this is the link for autolayout.
Create textfields on clicking the plus button and add tags to textfields for fetching data from it.
let objForTextfield = UITextField()
objForTextfield.frame = CGRectMake(0, (scrollView.frame.origin.y + scrollView.frame.size.height ), 100, 100)
self.view.addSubview(objForTextfield)
scrollView.contentSize = CGSizeMake(scrollView.subviews.last?.frame.origin.y)!+(scrollView.subviews.last?.frame.size.height)!
firstly you need to group this view into three uiviews.
first one contain the two text field parts you want to show on buttonclick
2.Secondone contain upper part of that text fields
3.Thirdone contain lower part of that text fields
Then setheight constraintfor first view and vertical spacing bwn
First -Second and second - third.
At first time the height constraint must be zero for the first view
And priority of vertical spacing of second - third greater than priority of First -Second.
When the buttonclicks change theheight offirst view and also change the priority of that vertical spaces.
You can override viewDidLayoutSubviews and set content size of scrollview then call [self.view layoutIfNeeded]

Superview not increasing in height based on the subviews constraint

I have a scrollview and a separate UIView where I placed a series of textFields and labels with constraints which fully occupies the top and bottom. I'm trying to adjust the UIView's height based on its subview constraints but it won't. What is happening is that the view keeps its height and force other textfields to collapse or shrink thus breaking the constraints.
Details
Each subview priority values :
compression = 750
hugging = 250
UIView priority values:
compression = 249
hugging = 749 Set to be lower than the rest.
Most of the textfields has aspect ratio constraint. This causes the field to adjust.
Each subview has vertical/top/bottom spacing between each other. The top and bottom elements has top and bottom constraints to the view as well.
What's on my code:
-(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
/* I had to adjust the UIView's width to fill the entire self.view.*/
if(![contentView isDescendantOfView:detailsScrollView]){
CGRect r = contentView.frame;
r.size.width = self.view.frame.size.width;
contentView.frame = r;
[detailsScrollView addSubview:contentView];
}
}
Screenshots
The view
This is what currently happens. In this instance it forces the email field to shrink. If I place a height value on it, it does not shrink but the layout engine finds another element to break
Edit:
Solved
Maybe I just needed some break to freshen up a bit. I did tried using constraints before but got no luck. However thanks to the suggestion I went back setting the constraints instead of setting the frame on this one and got it finally working.
Solution:
-(void)viewDidLoad{
[detailsScrollView addSubview:contentView];
[contentView setTranslatesAutoresizingMaskIntoConstraints:NO];
[detailsScrollView setTranslatesAutoresizingMaskIntoConstraints:NO];
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(contentView,detailsScrollView);
NSArray *horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[contentView]-0-|"
options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil
views:viewsDictionary];
NSArray *verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[contentView]-0-|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
views:viewsDictionary];
NSArray *widthConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[contentView(==detailsScrollView)]-0-|" options:0 metrics:nil views:viewsDictionary];
}
When you use interface builder to deal with the UIScrollView and its child UIView. usually a top, bottom, left and equal width constraints are set between the UIScrollView and its child which is the contentView in your case.
Without those constraints the other option is to set the content size of the UIScrollView. which was the way of using the UIScrollView before introducing constraints.
So, 1. you should add those constraints programmatically.
By using the constraints, the views frame is no longer needed to resize the views.
So, 2. remove frame setting for your content view.
I am not so happy with the way you set the frame in the viewDidLayoutMethod. if I am going to do that here I would take the frame setting out of the if statement.
The code would be as follow with no if statement:
[detailsScrollView addSubview:contentView];
// then set the constraints here after adding the subview.
Put this code anywhere but not inside your viewDidLayoutSubviews method. it will be a bigger problem than setting the frame in there inside if statement.
Note: Originally, if you are going to set frame in the viewDidLayoutSubviews
method. you should do it for all cases. for example for the if case
and the else case. because, next time this method is going to be
called the views will respond to the constraint. and lose its frame.
Another observation: if you want the view to response to its subviews constraint why you need to set the frame for it? right?
After adding the constraint you may need to call the method constraintNeedsUpdate or another related method.

UICollectionView cell height/scrollable content behaving strange/incorrect

I'm trying to make UICollectionView work like UITableView (I want the extra flexibility of collection view instead of just going with table view, both for some current and possible future feature extensions); having a fixed width (screen width) and dynamic cell height (just like iOS 8's new table view feature). I've been struggling a lot to get it working. First, I've enabled automatic sizing by setting an estimated size on layout on my collection view subclass:
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout*)self.collectionViewLayout;
layout.estimatedItemSize = CGSizeMake(SCREEN_WIDTH, 100);
Then I've set up my constraints of my custom cell in Interface Builder to let it grow according to my text view's content.
I've also set up a width constraint equal to screen width programatically, otherwise my cells were having a default width of 50pt. Inside my custom cell's awakeFromNib:
[self addConstraint: [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:SCREEN_WIDTH]];
It seems to work for width. When I run my app, it breaks height constraints as my own constraints conflict with UIView-Encapsulated-Layout-Height which is set to 100 (this actually means that auto-height is not really working well).
I lower the priority from my vertical constraints, especially the one with textview (which grows depending on its content) from required (1000) to some lower number (900). I'm not getting broken constraints anymore, but that's because we don't need to break constraints anymore as UIView-Encapsulated-Layout-Height takes precedence. Result is exactly the same. Here is what I'm getting:
There is no problem with the GIF above. It really does jump at one point exactly as seen on the GIF.
How can I prevent this behavior and make my collection view cells grow in height dynamically (of course, without creating a dummy offscreen cell to calculate view sizes)?
I don't need to support iOS 7, so iOS 8-and-above-only solutions are welcome.
UPDATE: If I implement preferredLayoutAttributesFittingAttributes: method as seen on the answer to UICollectionView Self Sizing Cells with Auto Layout:
-(UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes{
UICollectionViewLayoutAttributes *attrs = [layoutAttributes copy];
CGRect frame = attrs.frame;
self.frame = frame;
[self setNeedsLayout];
[self layoutIfNeeded];
float desiredHeight = [self.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
frame.size.height = desiredHeight;
attrs.frame = frame;
return attrs;
}
Then it's always returning the NIB's original view size that was in the Interface Builder even though I've set:
self.translatesAutoresizingMaskIntoConstraints = NO;
self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
I have no idea why it's returning an incorrect value.
After spending hours and days, I've decided to go back to table view. While collection view offers great flexibility and is definitely the future, it's just not there yet.

UITableViewCell cell reuse and UILabel auto-layout dynamic sizing

I'm working with a subclassed UITableViewCell and I need to align a UILabel's text to the Top Left all while using Auto Layout. I realize reading up that sizeToFit really shouldn't be used with Auto Layout and I'd like to avoid it, and somehow use a constraint. Basically the label's text is reset every reuse of the cell so the sizing would need to be dynamic on reuse.
Here's the Lazy initializer label inside the subclassed cell:
- (UILabel *)commentsLabel {
if (_commentsLabel == nil) {
_commentsLabel = [[UILabel alloc] init];
[_commentsLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
_commentsLabel.numberOfLines = 0;
_commentsLabel.lineBreakMode = NSLineBreakByWordWrapping;
_commentsLabel.preferredMaxLayoutWidth = 100;
}
return _commentsLabel;
}
There are auto-layout constraints being set on the label (commentsLabel is a subView added to self.customView on the cell subclass):
[self.customView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-locationToTopPadding-[locationLabel(locationHeight)]-locationToCommentsPadding-[commentsLabel]-commentsToBottomPadding-|"
options:0
metrics:#{
#"locationToTopPadding":#(locationToTopPadding),
#"locationHeight":#(locationHeight),
#"locationToCommentsPadding":#(locationToCommentsPadding),
#"commentsToBottomPadding":#(commentsToBottomPadding)
}
views:viewsDictionary]];
[self.customView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[thumbnailImageView]-[commentsLabel]-|"
options:0
metrics:#{#"commentsWidth":#(commentsWidth)}
views:viewsDictionary]];
setting just:
self.commentsLabel.preferredMaxLayoutWidth = 100;
doesn't seem to work although that is mentioned in most answers.
currently have this implemented but not seeing any results.
UILabel sizeToFit doesn't work with autolayout ios6
another answer I tried.
UILabel sizeToFit only works with AutoLayout turned off
I feel that I'm just missing 1 constraint that can be added in addition to the constraint above but the programmatic constraints I try to add don't work or throw an exception. I'm working completely in code there are no xibs.
ETA: tried setting a height constraint inside the existing vertical constraint line:
like suggested here: Dynamically change UILabel width not work with autolayout
in the vertical constraint line above making it:
-[commentsLabel(>=30#900)]-
I've messed around with the height value and priority value and nothing changed.
ETA: making some progress, I think it has to do with the bottom padding of the label, tried this and some of the labels are aligned correctly some aren't:
->=commentsToBottomPadding-
solution in the event any one else runs into the same issue with a UITableViewCell and a custom Label that changes dynamically:
-[commentsLabel]->=yourBottomPadding-
put that in the vertical constraint
call this in the viewController after you set the text:
[cell updateConstraints];

Autoresize view to fit multiline label with dynamic text length

I have a UIView containing a UILabel made in an Interface Builder .xib file. The label text is set from code and I want the UIView and UILabel to resize to fit the text using auto layout.
Label configuration:
Pinned width 320
Number of lines 0
Leading space to superview 0
Bottom space to superview 0
Trailing space to superview 0
The view is loaded like this:
MyView *myView = [[[NSBundle mainBundle] loadNibNamed:#"MyView" owner:self options:nil] objectAtIndex:0];
myView.myLabel.text = #"My short or long text";
and I use it as a table header:
[self.tableView setTableHeaderView:myView];
The view does not resize, it stays one line truncated with ... at the end. How do I get it to resize?
I tried increasing vertical content hugging and content resistance for the label, I tried adding a low priority height constraint, I tried setPreferredMaxLayoutWidth, all without luck.
Note: This is a simplified example. I will need more elements in the view, and I would like to avoid calculating every coordinate manually based on the label's sizeWithFont. It's my understanding that auto layout should be the holy grail for this.
CGSize maximumSize = CGSizeMake(maxWidthOfYourLabel, 9999);
UIFont *myFont = label.font;
CGSize myStringSize = [yourString sizeWithFont:myFont constrainedToSize:maximumSize lineBreakMode:NSLineBreakByWordWrapping];
label.frame = CGRectMake (label.frame.origin.x, label.frame.origin.y, myStringSize.width, myStringSize.height);
yourView.height = label.height;
This is the only way i know and this involves sizeWithFont:
I doubt UITableView will play nicely with your view if you are resizing its header with autolayout. The way we work with dynamic-sized table headers (or footers) is like this:
Compute and resize the header view's frame.
Re-set the header via self.tableView.tableHeaderView = myView;
Step 2 is the key. You have to set tableHeaderView again even if the same view will be applied. I understand that having to play with frames in an autolayout paradigm is very ugly, but you may not have to if you find the proper method to put self.tableView.tableHeaderView = myView;.
I just come across the same problem like you.
What I do is like this:
//Init headerView from nib file
//Set the content to show
[headerView setupWith:somethingToShow];
headerView.height = [headerView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
self.tableView.tableHeaderView = headerView;

Resources