UITableView in Swift - dequeueing of cells - ios

func tableView(tableView:UITableView!, numberOfRowsInSection section:Int) -> Int {
return 5
}
func tableView(tableView:UITableView!, cellForRowAtIndexPath indexPath:NSIndexPath!) -> UITableViewCell! {
let cell = tableView.dequeueReusableCellWithIdentifier("BookCell") as BookTableViewCell
println("ip: \(indexPath.row)")
cell.bookLabel.text = "Row #\(indexPath.row)"
return cell
}
I just see one cell, with the text overwritten 5 times, instead of 5 cells. I thought we didn't have to do the if (!cell) nonsense anymore? What am I doing wrong?

If you are seeing the UILabels overwritten 5 times, it is because your cell height has not been defined and it is being computed as 0 pixels tall. Any one of the following should help you explicitly or implicitly define the row height:
1) Set the row height within Interface Builder:
2) Implement the UITableViewDataSource protocol method:
func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
return 50.0
}
3) Set Auto Layout constraints between your cell's UILabel and the outer boundaries of the UITableViewCell:

Related

Swift: Expand UITableViewCell height depending on the size of the UICollectionView inside it

Hi everyone. I started learning programming 1 month ago so please be nice if I don't explain my problem well :)
My project is composed of a main UITableView. In each cell of the UITableView, I have a UICollectionView (on horizontal scrolling).
Img 1 : Main view
The width of each UICollectionViewCell is the same as the entire UITableViewCell. My first problem is about sizing the height of the UITableViewCell (which will depend of the size of the UICollectionView itself and the size of the content on top of it).
Img 2 : CollectionView
This has to be done automatically. In fact, the UICollectionViewCells will not have the same height, so when the user will scroll the UICollectionView horizontally, a new UICollectionViewCell will appear (with different height) and the height of the UITableViewCell will have to adapt.
The second problem I have is about sizing the UICollectionViewCell, in fact I will not know in advance what the height of it is going to be (the width is the same as the UITableView). I should import the content from a nib.
So now here is my ViewController file,
the variables:
#IBOutlet weak var tableView: UITableView!
var storedOffsets = [Int: CGFloat]()
the UITableView extension : Create the cell of the UITableView and set delegate of UICollectionView inside it
extension IndexVC: UITableViewDelegate, UITableViewDataSource {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 6 //Temp
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCellWithIdentifier("CellCollectionView") as? CellPost {
let post = self.posts[indexPath.row]
cell.configureCell(post)
return cell
} else {
return CellPost()
}
}
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
guard let tableViewCell = cell as? CellPost else { return }
tableViewCell.setCollectionViewDataSourceDelegate(self, forRow: indexPath.row)
tableViewCell.collectionViewOffset = storedOffsets[indexPath.row] ?? 0
}
func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
guard let tableViewCell = cell as? CellPost else { return }
storedOffsets[indexPath.row] = tableViewCell.collectionViewOffset
}
the part of the UICollectionView : Add the view from a Nib/xib to the cell
extension IndexVC: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 9 //Temp
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cellInCollectionView", forIndexPath: indexPath)
if let textPostView = NSBundle.mainBundle().loadNibNamed("textPostView", owner: self, options: nil).first as? textPostView {
textPostView.configurePost(post.descriptionLbl)
cell.addSubview(textPostView)
textPostView.translatesAutoresizingMaskIntoConstraints = false
cell.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[view]-0-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["view":textPostView]))
cell.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[view]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["view":textPostView]))
}
return cell
}
Make the size of the cell the same as the entire UICollectionView
extension IndexVC: UICollectionViewDelegateFlowLayout {
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSizeMake(collectionView.frame.width, collectionView.frame.height)
}
And I created this extension in the class dedicated for the UITableViewCell (not useful for the problem, but if any of you want to recreate it)
extension CellPost {
func setCollectionViewDataSourceDelegate<D: protocol<UICollectionViewDataSource, UICollectionViewDelegate>>(dataSourceDelegate: D, forRow row: Int) {
collectionView.delegate = dataSourceDelegate
collectionView.dataSource = dataSourceDelegate
collectionView.tag = row
collectionView.setContentOffset(collectionView.contentOffset, animated:false) // Stops collection view if it was scrolling.
collectionView.reloadData()
}
var collectionViewOffset: CGFloat {
set {
collectionView.contentOffset.x = newValue
}
get {
return collectionView.contentOffset.x
}
}
If anyone want to use this code, it works great, but I have to hardcode the height of the UITableView with a tableView( ... heightForRowAtIndexPath ...)
I tried everything to make the UITableViewCell adapt to what's inside it (I tried calculating the size of content sent by the Nib I'm putting in the cell, and then send it to tableView( ... heightForRowAtIndexPath ...)
but I can't make it work. I also tried Auto Layouts but maybe I'm doing it wrong. I also think the problem could come from the part that I imported the nib in my cell.
I also have no idea of a way to expand the cell after the user has swiped the UICollectionViewCell, is there any way to create an event when that happens? And maybe call tableView( ... heightForRowAtIndexPath ...) again?
As i understanding you need to auto adjust tableview cell height. so you can use autolayout for adjust cell height.
Write in ViewDidLoad
self.tableView.estimatedRowHeight = 80
self.tableView.rowHeight = UITableViewAutomaticDimension
Before Returning cell in CellForRowAtIndexPath
self.tableView.setNeedsLayout()
self.tableView.layoutIfNeeded()
You can find link for autolayout.
enter link description here
In order to solve my problem, I created an array to store the height of each cell of the UITableView (named cellHeight).
I also calculate the height of each cell at the start of the program in an array composed of array (named cellHeightForPost). The main array represent all the TableView cells, each array inside it represent the height of each cell of the 'collectionView'
In order to update the table view everytime I change the collectionView inside it, i used the fonction collectionView(... willDiplayCell...) :
self.tableView.beginUpdates()
cellHeight[collectionView.tag] = cellHeightForPost[collectionView.tag][indexPath.row]
self.tableView.setNeedsLayout()
self.tableView.layoutIfNeeded()
self.tableView.endUpdates()
My UITableView size is defined in tableView(... heightForRowAtIndexPath) and tableView(... estimatedHeightForRowAtIndexPath) :
return cellHeight[indexPath.row]
To set the size of the collectionViewCells :
return CGSize(width: collectionView.frame.width, height: cellHeightForPost[collectionView.tag][indexPath.row])

tableView not setting automatic row height

In my project, I have a static tableView with 3 sections. The cell in the second section holds a label that is filled dynamically and therefore has a dynamic height. The cell should adjust its height to the label's height. Here's what I tried, without success:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.section == 0 {
return 44
} else if indexPath.section == 1 {
return UITableViewAutomaticDimension
} else if indexPath.section == 2 {
return 80
} else {
return 50
}
}
The heights of all sections are set properly except the automatic dimensions. Any help?
set this line in viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
then write this table view method
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
return UITableViewAutomaticDimension
}
also make sure you have used the auto layout properly.
and you have set the number of lines for lable = 0
You also need to provide an estimated row height. You can do that by using the estimatedRowHeight property of your UITableView, or implementing the corresponding delegate method:
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return XXXXX // Provide your estimation here, or pass UITableViewAutomaticDimension (not the best for performances)
}
Reference: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithSelf-SizingTableViewCells.html

Static UITableView, make a single cell with dynamic height in Swift

I have a static UITableView which has 12 rows and for 11 rows I know what the height needs to be.
I have a UILabel which has dynamic text which sits inside the 12 row, how do i go about making just that one cell to have a dynamic height based on the text in the UILabel.
I have tried the below code inside viewDidLoad, but it didn't work. The rows remained the same height. I have also set the lines = 0 for the UILabel
tableView.estimatedRowHeight = 100.0
tableView.rowHeight = UITableViewAutomaticDimension
Have you tried this?
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if (indexPath.row < 11) {
// Everything starts at 0, so this covers 0-10
// Whatever your cell height is
return <#fixedCellHeight#>
} else {
// This is your 12th cell (indexPath.row 11), as we start counting at 0
return UITableViewAutomaticDimension
}
}
Adding to #Adrian answer , if you are using static Cells , changing one cell to dynamic height , and others as they are you can edit it to this .
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.row == 11 {
// This is your 12th cell (indexPath.row 11), as we start counting at 0
return UITableViewAutomaticDimension
} else {
return super.tableView(tableView, heightForRowAtIndexPath: indexPath)
}
}
Try this:
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
You can create 2 cell prototype cells for the table view. You give them 2 different id.
And then in your code you override this fonction
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = UITableViewCell()
if indexPath.row < 12 {
cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
} else {
cell = tableView.dequeueReusableCellWithIdentifier("cell12", forIndexPath: indexPath)
}
return cell
}
You Can create the dynamic cell with Text height then you can set the static and dynamic cell both in on table view
Here is link to dynamic cell with text How to change cell height dynamically in UITableView static cell
More elegant might be to just implement as suggested:
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
Or in Objective C:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
But rather than the row specific logic, add constraints to your static cell's content in the Storyboard to keep the row heights constant. That way if you move rows or change content you don't need to change any code.

How to dynamically change the height of a cell in a UITableView?

I have:
A UITableView in my UIViewcontroller
var searchResults: UITableView = UITableView();
A custom UITableViewCell class:
class CellSearchResult: UITableViewCell { ... }
I register my cell for the tableview:
searchResults.registerClass(CellSearchResult.self, forCellReuseIdentifier: "Cell");
I have my tableView method to fill my table:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! CellSearchResult
cell.frame.height = 100; <<<< ERROR
...
}
I would like to change the height of my cell like:
cell.frame.height = 100; <<<< ERROR
How to do this ?
You need to override heightForRowAtIndexPath function. Example:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 100
}
EDIT:
If you are not subclassing UITableViewController, you can't access tableView functions unless you conform UITableViewDelegate, UITableViewDataSource protocols. You can do this by
class myViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { ....
.
tableview.delegate = self
tableview.datasource = self
You have to implement the following to increase a particular row height at run time without calling the reloadData()
Example to increase height of row 10:
rowHightUpdateReqd = true
tableView.beginUpdates()
tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: 10, inSection: 0)], withRowAnimation: UITableViewRowAnimation.Fade)
tableView.endUpdates()
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
var rowHight: CGFloat = 50
if rowHightUpdateReqd == true{
rowHight = 100
rowHightUpdateReqd = false
}
return rowHight
}
If the heights of all cells are the same the most efficient way is to set the rowHeight property of the UITableView itself.
tableView.rowHeight = 100.0
There are performance implications to using
tableView:heightForRowAtIndexPath: instead of rowHeight. Every time a
table view is displayed, it calls tableView:heightForRowAtIndexPath:
on the delegate for each of its rows, which can result in a
significant performance problem with table views having a large number
of rows (approximately 1000 or more).
If the cells have variable heights you will need to implement the UITableViewDelegate method tableView:heightForRowAtIndexPath:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
var height = // do something to calculate height
return height
}
If you need to do expensive calculations in tableView:heightForRowAtIndexPath: it is also advisable to implement another UITableViewDelegate method, tableView:estimatedHeightForRowAtIndexPath: (available since iOS 7). Any code you put in this method needs to be efficient.
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 100.0
}
Providing an estimate the height of rows can improve the user
experience when loading the table view. If the table contains variable
height rows, it might be expensive to calculate all their heights and
so lead to a longer load time. Using estimation allows you to defer
some of the cost of geometry calculation from load time to scrolling
time.
override func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
return 100.0;
}

Wrap Text in UITableView Custom Cell When It Reaches End of Screen (Swift)

I have a table that loads data from a database but the problem is if the text being loaded is too long, it goes off the screen. I'm trying to find a way for the text to go onto the next line and have the cell resize automatically to fit this change. Does anyone know how this is done? The cell has three labels but one of them is allowed to be multi-lined.
EDIT:
I got it to work using auto constraints but how can I resize the table cell so that the actual items fit inside the cell and do not go over the cell boundary?
Set label number of "Lines" to ZERO. And set "Line Breaks" to "Word Wrap"
Adding these two methods along with above code fixed the problem with custom font
tamilRow.textLabel?.numberOfLines=0
tamilRow.textLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping
func tableView(tableView: UITableView, heightForRowAtIndexPath
indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
swift 5
let unselectedCellHeight: CGFloat = 130.0
func tableView(_: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if selectedCellIndexPath == indexPath {
//return selectedCellHeight
return UITableView.automaticDimension
}
return unselectedCellHeight
}

Resources