Reputation: 645
I have a UITableView
that UITableViewCell
has a dynamic UILabel
which will be added based on data i get from the database.
I'm using something like
let testing = ["A", "B", "C", "D"] // This array is dynamic data
self.dictionaryTableView.estimatedRowHeight = 44
self.dictionaryTableView.rowHeight = UITableViewAutomaticDimension
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "vocabCell", for: indexPath)
var y = 0
for var i in 0..<self.testing.count {
let lbl = UILabel(frame: CGRect(x: 0, y: y, width: 50, height: 25))
lbl.text = self.testing[i]
lbl.numberOfLines = 0
cell.addSubview(lbl)
y += 20
}
return cell
}
But UITableViewCell
height does not stretch automatically to display all content cell. Please help
Here is the result
EDIT I added constraint for the uilabel in UITableView cellForRowAtIndexPath, the constraints are working (I knew it when I expend the cell height), but cell height not automaticly strech out
var bottomAnchor: NSLayoutYAxisAnchor = NSLayoutYAxisAnchor()
for var i in 0..<self.testing.count {
let lbl = UILabel()
lbl.text = self.testing[i]
lbl.numberOfLines = 0
lbl.translatesAutoresizingMaskIntoConstraints = false
cell.addSubview(lbl)
lbl.leftAnchor.constraint(equalTo: cell.leftAnchor, constant: 16).isActive = true
lbl.widthAnchor.constraint(equalTo: cell.widthAnchor).isActive = true
lbl.heightAnchor.constraint(equalToConstant: 25).isActive = true
if i == 0 {
lbl.topAnchor.constraint(equalTo: cell.topAnchor).isActive = true
} else {
lbl.topAnchor.constraint(equalTo: bottomAnchor).isActive = true
}
bottomAnchor = lbl.bottomAnchor
}
return cell
Many thanks
Upvotes: 0
Views: 1862
Reputation: 645
I found the answers here
I added a constraint to my UITableViewCell bottom with the following code and it's working, but I don't know what exactly this line doing (I don't know the parameters too)
Could anyone help me explain this code
let bottomSpaceConstraint: NSLayoutConstraint = NSLayoutConstraint(item: lbl, attribute: NSLayoutAttribute.bottomMargin, relatedBy: NSLayoutRelation.equal, toItem: cell.contentView, attribute: NSLayoutAttribute.bottomMargin, multiplier: 1, constant: -8)
cell.contentView.addConstraint(bottomSpaceConstraint)
Upvotes: 0
Reputation: 17241
There are two things that you need to fix in your implementation:
Create the layout of your cell before dequeueing it in tableView(_:, cellForRowAt:)
.
For efficiency reasons, table views reuse the same cells over and over again when the user scrolls. That's why you use this weird "dequeuing" function rather than simply instantiating a new cell.
let cell = tableView.dequeueReusableCell(withIdentifier: "vocabCell", for: indexPath)
will always try to return a used cell that just scrolled out of the view. Only if there are no recycled cells available (for example when dequeueing the first couple of cells) the table view will create new instances of a cell.
The recycling of cells is the very reason why you should never create your cell's layout inside tableView(_:, cellForRowAt:)
, for example by adding a label. When the cell is being reused, another label will be added on top of the label that you added before and when it's being reused a second time, you'll end up with three overlapping labels etc.
The same applies to constraints. When you add constraints to a cell in tableView(_:, cellForRowAt:)
without removing existing ones, more and more constraints will be added while the user is scrolling, most likely resulting in serious constraint conflicts.
Instead, setup your cell's layout before dequeueing it. There are several ways to achieve this:
If you use a UITableViewController
inside a storyboard, you can create dynamic prototypes and lay out the cells directly in the storyboard. You could, for example, drag a label to a prototype cell there and create an outlet for it in a custom UITableViewCell
subclass.
You can create a XIB file for your cell, open it in Interface Builder and create your layout there. Again, you need to create a UITableViewCell
subclass with the appropriate outlets and associate it with your XIB file.
You can create a UITableViewCell
subclass and set up your layout purely in code, for example inside the cell's initializer.
You need to use Auto Layout.
If you create your layout in Interface Builder, you just need to add the necessary constraints and you're good to go. If you create your layout in code, you need to set the translatesAutoresizingMaskIntoConstraints
property to false
for all views that you wish to constrain, for example:
lbl.translatesAutoresizingMaskIntoConstraints = false
In your particular layout, you need to constrain the label at the left, right, top and bottom with the corresponding edges of your cell's contentView
.
If you don't know how to do that, please read Apple's Auto Layout Guide. (It's usually a better idea to do this in Interface Builder rather than in code.)
A very detailed description of how to use "Auto Layout in UITableView for dynamic cell layouts & variable row heights" can be found here.
Upvotes: 2
Reputation: 954
You can use UITableView section to render the data instead of adding the view programmatically. Here's the example:
class ViewController: UITableViewController {
private var dataSectionOne = ["Data 1", "Data 2"]
// This can be your dynamic data.
// Once the data changed, called tableView.reloadData() to update the view.
private var dataSectionTwo = ["A", "B", "C"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 44
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return dataSectionOne.count
} else if section == 1 {
return dataSectionTwo.count
}
return 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
if indexPath.section == 0 {
cell.textLabel?.text = dataSectionOne[indexPath.row]
} else if indexPath.section == 1 {
cell.textLabel?.text = dataSectionTwo[indexPath.row]
}
return cell
}
}
The results:
Upvotes: 1
Reputation: 861
used this code.
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
Upvotes: 1