Add view dynamically to cells produce issues in table - swift

I have this function:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
let label = UILabel(frame: CGRect(x: 100, y: 14, width: 400, height: 30))
label.text = "\(data[indexPath.row])"
label.tag = indexPath.row
cell.contentView.addSubview(label)
return cell
}
Well, with this function I'm adding a list of rows dynamically.
Why dynamically? Because, the number of columns depends of the data.
Don't take focus on that.
The list has 244 elements.
The result is displayed ok, but once I started scrolling, I get this:
How I can add elements dynamically without get this error?

Cells get reused. You keep adding more and more labels to each cell.
The proper solution is to create a custom cell class that contains the desired label. Then simply set that label's text in cellForRowAt. Don't create and add subviews in cellForRowAt.
But keep in mind that you may just want to use the provided textLabel property of UITableViewCell. No need for a custom cell or your own label.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
cell.textLabel?.text = "\(data[indexPath.row])"
return cell
}

I found a solution, hope this works to you!!!
You have to add to your cellForRowAt this code:
for item in cell.contentView.subviews
{
item.removeFromSuperview()
}
Just after to get the cell.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
for item in cell.contentView.subviews
{
item.removeFromSuperview()
}
let label = UILabel(frame: CGRect(x: 100, y: 14, width: 300, height: 30))
label.text = "\(data[indexPath.row])"
label.tag = indexPath.row
let btn = UIButton(type: UIButtonType.custom) as UIButton
btn.backgroundColor = UIColor.red
btn.setTitle("boton", for: .normal)
btn.frame = CGRect(x:0, y:5, width: 80, height:40)
//btn.addTarget(self, action: "buttonPressed:", for: UIControlEvents.touchUpInside)
btn.tag = indexPath.row
cell.contentView.addSubview(btn)
cell.contentView.addSubview(label)
cell.tag = indexPath.row
return cell
}

UITableview create 10 cell at scroll down recreate cell view
You can using
cell.contentView.removeFromSuperview() before cell.contentView.addSubview(label) or using cell.textLabel?.text
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
cell.contentView.removeFromSuperview()
let label = UILabel(frame: CGRect(x: 100, y: 14, width: 400, height: 30))
label.text = "\(data[indexPath.row])"
label.tag = indexPath.row
cell.contentView.addSubview(label)
return cell
}
Note This solution will delete All subview in cell

Related

Add UIView To Tableview cell not updating

I am trying to add a UIView to an existing IBOutlet in a tableview cell. However this does not appear until I have scrolled away from the cell and scroll back to it.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let stockCell = tableView.dequeueReusableCell(withIdentifier: "stockCell", for: indexPath) as! customStockCell
let progressBar = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
progressBar.backgroundColor = .green
stockCell.progressContainerView.addSubview(progressBar)
return cell
}
class customStockCell: UITableViewCell {
#IBOutlet var progressContainerView: UIView!
}
Using contentView instead of containerView works fine for me.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let stockCell = tableView.dequeueReusableCell(withIdentifier: "stockCell", for: indexPath)
let progressBar = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
progressBar.backgroundColor = .green
stockCell.containerView.addSubview(progressBar)
return cell
}
You can also create a custom cell class and add this logic inside its' initialiser.
Alternatively, if the above solution doesn't work, you can add this logic inside willDisplayCell:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let progressBar = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
progressBar.backgroundColor = .green
cell.containerView.addSubview(progressBar)
}

Swift TableView Cell [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
I am trying to create tableView cells and customize the layout. But it seems anything I add inside my cells get stuck being the same size height and width as the cell itself. How can I make it to where I can design my cells however I like? i.e, make my images a specific size with margins and paddings. Even my text height is being stretched out to the cells height. I added the borders so that it's visible for what I mean. The height of my label should be just the size of the text itself but it is still stretching.
I am stuck on tableview cells and want to design my cell layout fully programmatically without storyboarding. I've been trying to teach myself all day and just keep going in circles.
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
self.tableView.delegate = self
self.tableView.dataSource = self
self.view.addSubview(self.tableView)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.frame = view.bounds
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 200
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 8
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "Here is some text"
cell.textLabel?.layer.cornerRadius = 10
cell.textLabel?.layer.borderWidth = 5
cell.textLabel?.layer.borderColor = UIColor.black.cgColor
cell.imageView?.image = UIImage(named: "spiderman")!
cell.imageView?.layer.cornerRadius = 10
cell.imageView?.layer.borderWidth = 5
cell.imageView?.layer.borderColor = UIColor.black.cgColor
return cell
}
}
Thats because you are using UITableViewCell's default textLabel
If you open a storyboard and play around with cell with 'basic' style, you will see that the text is always aligned to center.
I dont think you can adjust height or position of these two predefined properties (.textLabel and .imageView) directly.
Try to create imageView and label manually, and dont use cell.textLabel directly.
Example:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let label = UITextView(frame: CGRect(x: 200, y: 0, width: 500, height: 50))
label.text = "Here is some text"
label.layer.cornerRadius = 10
label.layer.borderWidth = 5
label.layer.borderColor = UIColor.black.cgColor
cell.addSubview(label)
let image = UIImage(named: "spiderman")!
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
imageView.image = image
imageView.layer.cornerRadius = 10
imageView.layer.borderWidth = 5
imageView.layer.borderColor = UIColor.black.cgColor
cell.addSubview(imageView);
return cell
}

TableviewCell: set constraints programmatically inside ContentView using autoresize

I am trying to constraint elements (UIView or UITextfield) in a TableViewCell. If i am using a frame like
UITextField(frame: CGRect(x: 150, y: 0, width: 500, height: 110))
everything works fine.
But i am trying to use autoresize and somehow my UITextfield added as SubView in my Cell don't have the same ancestor. The error message is: "they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies?"
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var Testtext: UITextField{
let test = UITextField(frame: CGRect(x: 150, y: 0, width: 500, height: 110))
test.textColor = .red
test.font = .boldSystemFont(ofSize: 22)
test.text = "Hello World"
return test
}
var cell:UITableViewCell {
let createdCell = UITableViewCell()
createdCell.contentView.addSubview(Testtext)
// Testtext.translatesAutoresizingMaskIntoConstraints = false
// NSLayoutConstraint.activate([
// Testtext.topAnchor.constraint(equalTo: createdCell.topAnchor),
// Testtext.bottomAnchor.constraint(equalTo: createdCell.bottomAnchor),
// Testtext.trailingAnchor.constraint(equalTo: createdCell.trailingAnchor),
// Testtext.leadingAnchor.constraint(equalTo: createdCell.leadingAnchor)
// ])
return createdCell
}
BTW: I don't want to create a Cellclass, because i need to adjust the number of Textfields and UIViews depending on the input arrayscount.
You need
var cell:UITableViewCell {
let createdCell = UITableViewCell()
createdCell.contentView.addSubview(Testtext)
Testtext.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
Testtext.topAnchor.constraint(equalTo: createdCell.contentView.topAnchor),
Testtext.bottomAnchor.constraint(equalTo: createdCell.contentView.bottomAnchor),
Testtext.trailingAnchor.constraint(equalTo: createdCell.contentView.trailingAnchor),
Testtext.leadingAnchor.constraint(equalTo: createdCell.contentView.leadingAnchor)
])
return createdCell
}
but this isn't the correct way you need to dequeueResuableCell inside cellForRowAt to generate the cells instead of creating vars
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! CustomCell
return cell
}

Put a UIButton in tableView

I made a code for added a array of UIView in my TableView, but when I add them, it doesn't show them row by row while I made a loop for it to add each item in my TableView but it adds them one above the other, another solution?
my view controller :
class mainViewController: UIViewController {
private var myArray: [UIView] = []
private var myTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let barHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height
let displayWidth: CGFloat = self.view.frame.width
let displayHeight: CGFloat = self.view.frame.height
myTableView = UITableView(frame: CGRect(x: 50, y: barHeight, width: displayWidth, height: displayHeight - barHeight))
myTableView.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
myTableView.dataSource = self
myTableView.delegate = self
self.view.addSubview(myTableView)
let DoneBut: UIButton = UIButton(frame: CGRect(x: 50, y: 0, width: 150, height: 50))
DoneBut.setTitle("Done", for: .normal)
DoneBut.backgroundColor = UIColor.blue
let DoneBut2: UIButton = UIButton(frame: CGRect(x: 50, y: 0, width: 50, height: 50))
DoneBut2.setTitle("Done2", for: .normal)
DoneBut2.backgroundColor = UIColor.blue
let view1 = UIView()
view1.addSubview(DoneBut)
myArray.append(view1)
let view2 = UIView()
view2.addSubview(DoneBut2)
myArray.append(view2)
}
}
extension mainViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier:
"MyCell", for: indexPath as IndexPath)
for array in myArray {
cell.contentView.addSubview(array)
}
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
return myArray.count
}
}
extension mainViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath:
IndexPath) {
print("Num: \(indexPath.row)")
print("Value: \(myArray[indexPath.row])")
}
}
Your issue is that each time the cellForRowAt function is asking for a cell, you are looping through ALL the views and adding them to each cell. Instead you should be indexing in it using the indexPath. See the following:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath as IndexPath)
// check if there is a view at the indexPath
if indexPath.row < myArray.count {
// there is a view, add it to the cells contentView
cell.contentView.addSubview(myArray[indexPath.row])
} else {
print("no view at index")
}
return cell
}

How to remove UIView from the superView with specific position programmatically

I am new to swift, using swift 3, I built tableview that reads group of images and I want to have it as below:
when user select an image in the tableview I display it in an UIImageView and when Deselect an image he already selected in the tableview to be removed from the UIImageView ( Multiple Selection is enabled). My code below is working for one image selection only, however if multiple images are selected and then deselected the last selected one only removed from the UIImageview. So my question is how can I remove the UIImageview that belongs to the deselected record in the UItableview
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
image_view.image = UIImage(data: Saveddata[indexPath.row].photo as! Data)
//create image view programmatically
currentImageView = UIImageView(frame: CGRect(x: 130 + (i * 10), y: 50 + (i * 5), width: 50, height: 100))
currentImageView.contentMode = .scaleAspectFit
currentImageView.image = image_view.image
view.addSubview(currentImageView as UIView)
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
currentImageView.removeFromSuperview()
}
You could use tag for each UIImageView based on the indexPath.row so you could refer by tag.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let image = UIImage(data: Saveddata[indexPath.row].photo as! Data)
//create image view programmatically
let imageView = UIImageView(frame: CGRect(x: 130 + (i * 10), y: 50 + (i * 5), width: 50, height: 100))
imageView.contentMode = .scaleAspectFit
imageView.tag = 100 + indexPath.row
imageView.image = image
view.addSubview(imageView as UIView)
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
// get imageView by its tag
let deselectedImageView = view.viewWithTag(100 + indexPath.row) as! UIImageView
deselectedImageView.removeFromSuperview()
}
If you want to update your cell appearance when selected/deselected, you should override the method setSelected(_:animated:) inside your UITableViewCell subclass instead of trying to update it directly from your UITableViewDelegate method:
override func setSelected(_ selected: Bool, animated: Bool) {
// Do whatever you want depending on the selected value
}