I have a custom UITableViewCell with a UILabel that has multiple lines. I'm trying to implement a tap expand/contract feature so that when the label is only one line, no expansion happens when you tap the cell, but if the label is multiple lines, the cell is capable on expanding/contracting when tapped. I got the expansion and contracting working, but I can't figure out how to make it work based on the number of lines in the UILabel.
This is currently what I'm doing for the cell expansion, but its not based on the UILabel at all.
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if (indexPath.row == self.selectedRow?.row && self.expand == true)
{
self.previousRow = self.selectedRow?.row
return 200
}
else if(self.previousRow == self.selectedRow?.row && self.expand == false){
return 65
}
return 65
}
You can use table view dynamic cell sizing with autolayout in conjunction with the numberOfLines property of UILabel to expand and compress cell.
The heart of the solution is this:
tableView.beginUpdates()
label.numberOfLines = label.numberOfLines == 0 ? 1 : 0
tableView.endUpdates()
This is essentially triggering an animation on the table and toggling the number of lines from 0 (any number of lines) to 1 line. Autolayout is doing the rest.
I think this is better than updating height metrics manually. The full example project can be found on my github: https://github.com/rayfix/MultilineDemo
You probably need to measure the text yourself because the label won't have more lines until you expand.
Something like this (the 100000 just needs to be a big number)
CGSize textSize = [self.label.text sizeWithFont:self.label.font constrainedToSize:CGSizeMake(self.label.bounds.width, 100000) lineBreakMode: UILineBreakModeWordWrap];
Related
So currently, I have a tableviewcell that looks like this
What I want to happen, is that if an expense of the date already exists, the top label should disappear, the tableviewcell height should be reduced from 95 to 64 and everything should be centrally aligned. Sort of like this
I tried doing this many ways.
Use 2 different cells and switch, but that didn't work as only one expense was returned at a time and my tableviewcontroller didn't populate correctly.
Try using a stack view, but in that, I can't get the constraints to match as they are currently.
I have all the correct row height being returned in the heightForRowAtIndexPath method, but it centrally reduces the height and some of the data is cut.
How is it possible to achieve what I want to do (have the label not visible, the row height reduced and everything vertically center)?
Here is the code for switching of the cells.
func tableView(_ tableView: UITableView,
heightForRowAt indexPath: IndexPath) -> CGFloat
{
if newMode==true {
return 95
}
else if newMode==false {
return 64
}
else {
return 0
}
}
This works, however it reduces the height from the top and the bottom and I only want the height from the top to be reduced.
You should not use the delegate method heightForRowAt instead you should use:
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 80
then give the (date of expense) label a hight constraint and connect an outlet to it.
in cellForRowAtIndex delegate method should have :
if expense != nil
{
lblExpenseConstraintHeight.constant = 0
}
else
{
lblExpenseConstraintHeight.constant = 34
}
cell.layoutIfNeeded()
Edit:
More info about about dynamic tableViewHeight:
Using Auto Layout in UITableView for dynamic cell layouts & variable row heights
Another possible solution is remove the height constraint of the (date of expense) label and set it's text to empty string.
I have a collection view written programmatically with swift 3. It has 1 section and 6 vertical items in it. The default minimum line spacing for each item is 10 I assume, but I want to set different line spacing for each item. For example, the space between item 0 and 1 become 50 but space between item 1 and 2 become 0. How can I do that? I searched a lot but couldn't find solution. Thanks in advance.
It is directly not possible by setting some property of your collection view but you can do one trick I think,
set your minimum line spacing and minimum interitem spacing to 0 like,
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
so your collection view cell will not have any space at any side!
now keep your cell's background color as clear color!
and add UIView (your content) in your cell with size(less than cell size) that shows spacing between two cell. So, you can add different size of view in every cell and it will display like different space between every cell!
Or
If you don't want to resize your inner view of cell then you can return different size for your cell from delegate method sizeForItemAtIndexPath for different items!
For example,
I am writing objective c code for example,
your sizeforitem,
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.item == 1 || indexPath.item == 6 || indexPath.item == 7 || indexPath.item == 9 || indexPath.item == 15) {
return CGSizeMake(screenWidth/3 -20, screenWidth/2);
}
return CGSizeMake(screenWidth/3, screenWidth/3);
}
and result is
An idea could be to consider the lines spacing as "other kind of cell", like blank cell, you see? And in the method heightForRowAtIndexPath, when the indexPath correspond to those blank cell, you return their custom height, otherwise you return the row height of your "real" cells.
(This imply to return those blank cell in the cellForRow, and add their number to your current numberOfRowInSection count too)
I've an issue for few days and really I can't explain why it goes like that.
I'm doing a chat, set up with a tableView, printing message into cell. These cells are designed with prototypes, there are 3 different type (but anyway it doesn't matter). Text is typed, message is send, cell is inserted in table and then we scroll to the bottom of the tableView.
As you know, in a chat view the container of the message has to fit this text (which is a view), and then the cell has to fit to this container (Label in orange, container in purple).
This container has a variable height and grow along the text, changing cell height.
I've set many constraint for auto-layout display but I didn't have different cell height than the height I initially set for it in the project (no adaptative behaviour). Chat Message were cut.
So, I've tried to set each rowHeight by myself using the method heightForRowAtIndexPath, calculating constraint along text size but it create a real bad behaviour (changing cells when scrolling for example). The size was often wrong calculated/recalculated.
That's why I'm finally using estimatedRowHeight set to 44 combine with UITableViewAutomaticDimension. Here it does the trick ! Waouh ! But it's not as good as expected..
When the table view appears all is good. Containers fit to their label, rows' height fit to their container and it's beautiful. The problem appears during an insert.
After many tests, I notice that this bad behaviour only appears when multilines label remains in the table view.
During an insert, each cell seems to resize to 44 before adapt its height to content, and then create a gap compared to previous state which create a strange scroll of the table view :
If I change the estimatedRowHeight it made this worst.
I can show my code if you want, but I don't think it will be very useful here because I'm not using complicated function.. only insert, automatic height for cells and scroll down which are functions delegate for tableView.
Can you help me please ? Really I don't know how to change that.. every chat application does the trick but I can't found out the way.
Thank you for answer, excuse my english level I'm a poor french student..
If you need some code comment I'll give it.
I think you can cache height value for every cell in cellForRowAtIndex method and in HeightForRowAtIndex set the corresponding height for that cell.
You can just do this way :
var cellCached = Dictionary<Int,AnyObject>()
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
table.rowHeight = yourCell.yourLabel.frame.origin.y + yourCell.yourLabel.frame.height + 10
cellCached[indexPath.row] = yourTable.rowHeight
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if(tableView == yourTable)
{
if(cellCached[indexPath.row] != nil)
{
return cellCached[indexPath.row] as! CGFloat
}
else
{
return 200
}
}
else
{
return UITableViewAutomaticDimension
}
}
I am trying to make a tableview that is similar to apps like facebook, where it shows the post's texts and if the post is too large, it will cut the size down and have a "Read More" button. I have looked at many different solutions for resizing UITextViews to the height of their text, however none of them are solving my problem. My problem is only existing with large posts. When I have a large post, my function that returns the height of the textView should return the maxHeight of the posts before being expanded. This part works. However, when I click my "Read More" button, I have a massive amount of white space at the bottom of the full textView. Here is my code:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return heightForTextOfRow(indexPath.row) + PostCell.additionalVertSpaceNeeded
}
func heightForTextOfRow(row: Int) -> CGFloat {
var textView = UITextView(frame: CGRectMake(0, 0, prototypeTextViewWidth, CGFloat.max))
let post = data[row]
textView.text = (post.postText as NSString).stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
textView.textContainer.lineFragmentPadding = 0
textView.textContainerInset = UIEdgeInsetsZero
textView.font = PostCell.textViewFont
textView.frame.size = textView.sizeThatFits(CGSizeMake(prototypeTextViewWidth, CGFloat.max))
if textView.frame.size.height > maxHeight && !post.seeMore {
return maxHeight
} else {
return textView.frame.size.height
}
}
Some explanations of the code:
data is an array of Posts (the only thing that is relevant about the class Post in this case is that it has a property postText and seeMore.)
PostCell.textViewFont is a constant in one of my classes that returns the font I am using (System font size 13)
PostCell.additionalVertSpaceNeeded is a constant that returns the height of all other elements in my prototype cell except for the UITextView
prototypeTextViewWidth is defined as tableView.frame.width - 16 (16 is the value of the margins added together)
maxHeight is the max height of a post that does not want to show the full post (currently defined as tableView.frame.height * 0.625 - PostCell.additionalVertSpaceNeeded)
seeMore is a Bool property of Post that tells whether or not to show the full post or whether to show just a portion of the post (true shows full post, false shows a post of height maxHeight)
Any help would be appreciated... I have tried many solutions to resizing the UITextView and all have this same problem. Thanks in advance!
What's the best way to have UITableView cells with multiple lines ? Let's say 5.. or 6 ?
Instead of textLabel and la detailTextLabel ? Should I create a custom style ? or a custom view ?
Any tutorial / example is well accepted.
thanks
You can use the existing UILabel views in the UITableViewCell for this. The secret is to do the following:
cell.textLabel.numberOfLines = 0;
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
By default, the UILabel only allows 1 line of text. Setting numberOfLines to 0 basically removes any limitations on the number of lines displayed. This allows you to have multiple lines of text.
The setting of the lineBreakMode to Word Wrap tells it to word wrap long lines of text onto the next line in the label. If you don't want this, you can skip that line.
You may also have to adjust the height of the table view cell as needed to make more room for the multiple lines of text you add.
For iOS 6.0 and later, use NSLineBreakByWordWrapping instead of UILineBreakModeWordWrap, which has been deprecated.
Since Swift 3:
func allowMultipleLines(tableViewCell: UITableViewCell) {
tableViewCell.textLabel?.numberOfLines = 0
tableViewCell.textLabel?.lineBreakMode = .byWordWrapping
}
There is a method to accomplish this just using storyboard. First, select the cell and go to the attributes section on the right panel. The first option should be 'Style'. Change this from custom to basic. Now, in your cell you should see text that says 'Title'. Double click it and in the right panel you should be able to set the number of lines.
cell.textLabel.numberOfLines = 0
together with
tableView.rowHeight = UITableView.automaticDimension
Does work but only if the number of lines is limited (2-3 lines).
What I had to do as well was embed the cell fields in a StackView. That made all the difference. Now I can display as many lines as I want.
I found this worked for me on Xcode Version 8.0 (8A218a)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) UITableViewCell {
let cell = UITableViewCell()
//MARK: word wrapping in cell
cell.textLabel?.text = self.choices[(indexPath as NSIndexPath).row]
cell.textLabel?.numberOfLines=0 // line wrap
cell.textLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping
return cell
}