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)
}
Related
In my swift code below each table view cell features a label. In that label I want to print the index path row. So in cell one the label should be 1 in cell to it should read 2. I tried to code cell.textLabel?.text = "(indexPath.row)" but that code is not complying. The code uses no storyboards all code.
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var numberOfRows = 3
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { numberOfRows }
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 118 }
var tableView = UITableView()
var selectedIndexPath = IndexPath(row: 0, section: 0)
override func viewDidLoad() {
super.viewDidLoad()
setTableVIew()
}
func setTableVIew(){
let VCframe = view.frame
let height = VCframe.height * 0.8
let widthx = VCframe.width
tableView.frame = CGRect(x: 10, y: 0, width: widthx - 20, height: height)
tableView.delegate = self
tableView.dataSource = self
view.addSubview(tableView)
tableView.register(customtv.self, forCellReuseIdentifier: "cell")
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! customtv
cell.textLabel?.text = "\(indexPath.row)"
return cell
}
}
class customtv: UITableViewCell {
lazy var backView : UIView = {
let view = UIView(frame: CGRect(x: 10, y: 6, width: self.frame.width , height: 110))
view.backgroundColor = .green
print(self.frame.width)
return view
}()
override func layoutSubviews() {
backView.clipsToBounds = true
backView.frame = CGRect(x: 0, y: 6, width: bounds.maxX , height: 110)
}
lazy var lbl : UILabel = {
let press = UILabel(frame: CGRect(x: 0, y: 3, width: 120 , height: 50))
press.backgroundColor = .yellow
press.text = String("1")
return press
}()
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(animated, animated: true)
addSubview(backView)
backView.addSubview(lbl)
}
}
You are almost there :)
You have a custom cell, so you need to set the value of custom label you created, instead of cell.textLabel?.text.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! customtv
//cell.textLabel?.text = "\(indexPath.row)" // Don't Do this.
cell.lbl.text = "\(indexPath.row)" // Do this.
return cell
}
To clarify, your cells will start with values 0, 1, 2. IndexPath.row is 0 based.
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
}
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
}
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
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
}