iOS8 Swift - Problems dynamically resizing tableview cells - swift

Here's what my application looks like currently.
You'll see that when the subtitle text label is only one line is resizes correctly, but when there's multiple lines it gets all messed up. I think this has to do with the constraints possibly? Right now I'm using the auto-layout constraints. You can see them in the screenshot. Here's the code that creates my cells.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> DealCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Deal Cell", forIndexPath: indexPath) as DealCell
let currentBar = bars[indexPath.row] as BarAnnotation
cell.barName.text = currentBar.name
cell.deal.text = currentBar.deal
cell.distanceToBar.text = String(format: "%.3f mi", currentBar.distance)
// Set the height of the table view cells
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 44.0;
return cell
}
Any ideas?

Use tableView:heightForRowAtIndexPath: function in the UITableViewDelegate.
Using this you can individually set the height of each row separately.
For more information : https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableViewDelegate_Protocol/index.html#//apple_ref/occ/intfm/UITableViewDelegate/tableView:heightForRowAtIndexPath:
Or you can use auto layout settings like in this article.
http://useyourloaf.com/blog/2014/02/14/table-view-cells-with-varying-row-heights.html

Related

swift tableview cell change width frame not work

This is my code but not work!
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyChatCell
var frame = cell.frame
if(indexPath%2 == 0){
frame.size.width = CGFloat(200)
} else {
frame.size.width = CGFloat(100)
}
cell.frame = frame
return cell
}
I want change cell width to 100 or 200 per cell
But frame.size.width not work
You can't change that, because all cells in a table view must have the same width, but can have different heights if needed.
However, you can try to make the table view's .backgroundColor transparent, and maybe the content view of the cell, than add another "wrapper view" on the cell's content view and make it have different widths. It will create the "visual impression" that cells indeed have different widths.

Setting the height: TableViewCells with custom Nib that are different sizes

I have a TableViewController that has a custom TableViewCell under the identifier "customCell". Heres an image of the configuration of the cell along with the IBOulets connected to it:
The cell takes information from my backend and presents it. The description text view (just noticed that I accidentally named it descriptionLabel) doesn't allow scrolling so it expands based off of the content that it's holding. The database is being processed correctly from the database, and it's displaying on the app. The only problem is that the cell is not its correct height. On the TableViewControl that's registering this cell through its identifier, I automatically set the height of the cell using UITableViewAutomaticDimension in heightForRow:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
but that's not working either. *The only thing that works is when I set the height of each cell to a standard value such as 200. That doesn't work for me because each cell will be a different height because of the length of the textView.
How do I set the height of a custom nib (tableViewCell) so that it adjusts based off of the content within it instead of setting the height to a specific value?
1-Remove both textViews and replace them with labels
2- Title lbl with theses constraints
top,leading,trailing , .lines = 0
3- Description lbl with theses constraints
bottom ,leading,trailing to contentView,top to bottom of title lbl , .lines = 0
Off course you can leave the 2 textviews , but you have to give each one an initial height and do this in
override func layoutSubviews() {
super.layoutSubviews()
self.titleTvHeight.constant = self.titleTv.contentSize.height
self.desTVheight.constant = self.desTv.contentSize.height
}
//
Don't forget to set this in viewDidLoad of the VC
tableView.estimatedRowHeight = 200
tableView.rowHeight = UITableViewAutomaticDimension
Anyway you can remove heightForRowAt

in UITableviewcell in Xcode 9.0.1 cell size it not expanding at runtime?

I have tried with drag and drop UITableviewcell and change row height from file inspector cannot expanded cell height at runtime.
in Xcode 9.0.1 I am facing issue regarding Cell height not increasing at run time.
Swift 3+
If you want dynamic row height, you can define your constraints (making sure they're unambiguous), set the label's numberOfLines to zero, and then in viewDidLoad, tell it that rows should automatically adjust their height:
override func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = your height here
tableView.rowHeight = UITableViewAutomaticDimension
}
OR Try this.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
You have to configure your cells to be self-sizing:
1: Add the following lines to viewDidLoad(). Specify a tableViewRowHieght that you think will be the average height of the cells displayd in your tableView:
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 200
2: Makes sure all of the labels in your cell are capable of expanding to as many lines as needed. This is done by setting Number Of Lines in Attribute Inspector to 0 as I have done for my Title label in the image below:
3: Finally, you must set constraints for every element in your cell so that the compiler can correctly calculate the size of the cell at runtime. Here I've added two lables contrained the height of the first to the top of the view, and bottom to label 2 below. Then label 2 must be contrained to the bottom of the view. As long as your contraints contain no "gaps" between the top of the cell to the bottom, the compiler can calculate the height of the cell:

How does changing the intrinsicContentSize of a UITableViewCell work?

I made a UITableView with cells that expand when you tap on them. It is modeled off the following project: https://github.com/rushisangani/TableViewCellExpand
Basically, the cell's container view has two subviews which act as containers for the expanded/contracted states - "front" and "back", each of which is constrained to the top, bottom, leading, and trailing edges of the cell's main content view. To make the cell expand or contract, I just toggle the isActive property on the bottom constraint of the front and back views. This works, but only if I reload the cell when it is tapped. If I just change the constraints and then try to call invalidateIntrinsicContentSize(), nothing happens.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
// Expand/contract the cell and invalidate size (doesn't work)
// let cell = tableView.cellForRow(at: indexPath) as! ExpandingCell
// cell.tap()
// cell.invalidateIntrinsicContentSize()
// Keep track of the selected index and configure the expand/contract state when the cell is remade
tableView.deselectRow(at: indexPath, animated: false)
expandedIndexPath = indexPath
if(!expandedIndexPathArray.contains(indexPath)){
expandedIndexPathArray.append(indexPath)
}
else{
expandedIndexPathArray = expandedIndexPathArray.filter({$0 != indexPath})
}
// Whenever a cell's intrinsicContentSize changes, it must be reloaded
tableView.reloadRows(at: [indexPath], with: .none)
}
What's going on behind the scenes? Why can't the cell recalculate its size without being reloaded?
If you want to contract/expand cells based on their intrinsic content size, you need to do the following 3 steps:
Configure the table view:
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 44
For static table view controller, you will need to implement heightForRowAtIndexPath and return UITableViewAutomaticDimension.
Update cell constraints to reflect contracted/expanded state
Ask the table view to reflect changes:
tableView.beginUpdates()
tableView.endUpdates()

How to increase the UITableView separator height?

I want more space(10px) between each cell. How can I do this?
And I have added this code
tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
The best way for me, just add this in cellForRowAtIndexPath or in willDisplayCell
CGRect sizeRect = [UIScreen mainScreen].applicationFrame;
NSInteger separatorHeight = 3;
UIView * additionalSeparator = [[UIView alloc] initWithFrame:CGRectMake(0,cell.frame.size.height-separatorHeight,sizeRect.size.width,separatorHeight)];
additionalSeparator.backgroundColor = [UIColor grayColor];
[cell addSubview:additionalSeparator];
For Swift 3.0:
let screenSize = UIScreen.main.bounds
let separatorHeight = CGFloat(3.0)
let additionalSeparator = UIView.init(frame: CGRect(x: 0, y: self.frame.size.height-separatorHeight, width: screenSize.width, height: separatorHeight))
additionalSeparator.backgroundColor = UIColor.gray
self.addSubview(additionalSeparator)
You should add this to cell's method awakeFromNib() to avoid re-creation.
I have seen many clunky solutions like subclassing UITableView with hidden cells, and other less optimal ones incl. in this thread.
When initializing the UITableView, Set the rowHeight property of UITableView to a height that equals = cell height + desired separator/space height.
Do not use standard UITableViewCell class though, instead, subclass the UITableViewCell class and override its layoutSubviews method. There, after calling super (don't forget that), set the height of the cell itself to desired height.
BONUS UPDATE 18/OCT/2015:
You can be a bit smart about this. The solution above basically puts the "separator" at the bottom of the cell. What really happens is, the row height is managed by the TableViewController but the cell is resized to be a bit lower. This results in the separator/empty space being at the bottom. But you can also centre all the subviews vertically so that you leave the same space at the top and the bottom. For example 1pt and 1pt.
You can also create isFirst, isLast convenience properties on your cell subclass. You would set these to yes in the cellForRowAtIndexPath.
This way you can handle the edge cases for top and bottom separators inside the layoutSubviews method as this would have access to these properties.
This way you can handle the edge cases for top or bottom - because sometimes the design department wants N+1 separators while the number of cells is only N. So you have to either deal with the top one or the boot one in a special way. But it's best do this inside cells instead tableViewHeader or TableViewFooter.
I don't think it's possible using standard API. I guess you would need to subclass the UITableViewCell and add a view that simulates a separator at the bottom of the cell.
You may want to check this question, it seems related and has some sample code:
iPhone + UITableView + place an image for separator
In Swift
The easiest and shortest way for me was to add the snippet below in cellForRowAtIndexPath or in willDisplayCell:
override func tableView(tableView: UITableView,
willDisplayCell cell: UITableViewCell,
forRowAtIndexPath indexPath: NSIndexPath)
{
let additionalSeparatorThickness = CGFloat(3)
let additionalSeparator = UIView(frame: CGRectMake(0,
cell.frame.size.height - additionalSeparatorThickness,
cell.frame.size.width,
additionalSeparatorThickness))
additionalSeparator.backgroundColor = UIColor.redColor()
cell.addSubview(additionalSeparator)
}
this is quite old. Nevertheless I will post my approach.
Simply increase your cell height a bit and assign a mask layer to the cell, like that:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "...", for: indexPath)
// Configure the cell...
let maskLayer = CAShapeLayer()
let bounds = cell.bounds
maskLayer.path = UIBezierPath(roundedRect: CGRect(x: 2, y: 2, width: bounds.width-4, height: bounds.height-4), cornerRadius: 5).cgPath
cell.layer.mask = maskLayer
return cell
}
So in this example my seperator height will be 4.
Have fun!
You can do this entirely in the storyboard. Here is how:
go to the storyboard and select the tableview
Show the Size Inspector and from there set row height to say 140.
then show the Attributes Inspector and from there set your separator to Single Line and Style Plain and choose a color
then in the storyboard (or in Document Outline) select the cell
and again in the Size Inspector, under the Table View Cell, set custom Row Height to say 120.
That’s all. Your separator will be 20 units tall.
Kinda old thread, but since I only found hacky solutions in this thread,
here the solution that worked best for me (without additional UIView in every cell)
Swift 3:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//configure your cell...
cell.layer.shadowColor = UIColor.red.cgColor
cell.layer.shadowOffset = CGSize(width: 0, height: 1)
cell.layer.shadowOpacity = 1
cell.layer.shadowRadius = 0
cell.layer.masksToBounds = false
return cell
}
EDIT: Unfortunately this does not work if you scroll up in a table. I leave the answer here anyway, since it might be a solution if your table has limited content.
See Shadow on a UITableViewCell disappears when scrolling for more info.
For a table cell with height of 50 and a space of 5 pix between the rows. Width is 320.
Define the background of the cells to be clear:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
cell.backgroundColor = [UIColor clearColor];
}
Set the height of the cells, this is the size of the row PLUS the delimiter:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 55;
}
And define in cellForRowAtIndexPath a box, with the size of the row (MINUS delimiter) to draw in the background color:
UILabel *headerBackgroundLabel = [[UILabel alloc] initWithFrame:CGRectMake(0,0,320,50)];
backgroundBox.backgroundColor = [UIColor grayColor];
[cell addSubview:backgroundBox];
I do it a much simpler and more flexible way. Some may call it a hack. I call it pragmatic.
I hide the standard UITableViewSeparator. I then add a subview to my cell, using auto layout pin it to the top. Fix the height to what I desire. Pin it to the edges with a margin either side. Change it's background colour. I have a plain separator with the height i desire.
You may question how efficient this is having another UIView in the cell hierarchy. Is it really going to make a noticeable difference? Probably not - you've just taken the standard separator out of the table hierarchy anyway.
Swift 4
It's not possible to make the default separator higher. Instead you need to add a subview that will look as a separator to each cell (and optionally make the cell higher). You can do it for example in cellForRowAtIndexPath or in a UITableViewCell subclass.
In case you allow to select the cell, you need to add the subview for selected state as well, otherwise the separator would disappear when the cell is selected. That's why selectedBackgroundView is also configured.
Add this into your UITableViewController subclass:
override func viewDidLoad() {
super.viewDidLoad()
tableView.separatorStyle = .none
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.backgroundView = UIView(backgroundColor: .white)
cell.backgroundView?.addSeparator()
cell.selectedBackgroundView = UIView(backgroundColor: .blue)
cell.selectedBackgroundView?.addSeparator()
// configure the cell
return cell
}
Add this extensions into the same file at the bottom:
private extension UIView {
convenience init(backgroundColor: UIColor) {
self.init()
self.backgroundColor = backgroundColor
}
func addSeparator() {
let separatorHeight: CGFloat = 2
let frame = CGRect(x: 0, y: bounds.height - separatorHeight, width: bounds.width, height: separatorHeight)
let separator = UIView(frame: frame)
separator.backgroundColor = .gray
separator.autoresizingMask = [.flexibleTopMargin, .flexibleWidth]
addSubview(separator)
}
}
Here's an option that might work for some people
cell.layer.borderColor = UIColor.white.cgColor
cell.layer.borderWidth = 4.0
cell.layer.masksToBounds = true
The easier and safest solution to this problem is to turn off the table separator and use a UITableViewCell as a separator of variable height. Sure, you'll have to do some index math to figure out where items are, but really it's odd / even.
It won't break and you get the benefit of recyclable cells (no extraneous views to clean up).
First make tableview separator none from the storyboard. Then add UILabel/UIView at bottom of cell of height(you needed) using storyboard or Xib
For Swift 4
Implemented King-Wizard's solution to Swift 4:
public func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let additionalSeparatorThickness = CGFloat(4)
let additionalSeparator = UIView(frame: CGRect(x: 0,
y: cell.frame.size.height - additionalSeparatorThickness, width: cell.frame.size.width, height: additionalSeparatorThickness))
additionalSeparator.backgroundColor = UIColor.groupTableViewBackground
cell.addSubview(additionalSeparator)
}
This is the easiest solution I've found:
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
" "
}
then just set the height to whatever you want:
tableView.sectionHeaderHeight = 30.0
I came across a way that has allowed me to effectively change the gap between cells.
In Interface builder I set the row height to be 46.
In the cellForRowAtIndexPath method of my TableView delegate I set the frame of the cell to be a smaller value.
cell.frame=CGRectMake(44,0,tableView.bounds.size.width,44)
This gives me a cell with a height of 44 that fits the width of the tableView but the space provided for the row will be 46 as defined in IB.
I was filling the cell programmatically anyway so this suited me fine.
You should implement
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
delegate method. and return 100.0 there.