View reappears in UITableViewCell after scrolling off and on - ios

I have a UITableView with 2 sections. In the first section I do not want the UIView I have created to appear on the left. It works fine when it initially loads but when it goes off screen and back on again it reappears.
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
{
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier") as! ATableCell;
cell.delegate = self;
if (indexPath.section == 1)
{
// let height = cell.bounds.size.height;
let height = 100;
let turnsView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: height));
turnsView.backgroundColor = UIColor.purple;
cell.addSubview(turnsView);
}
else
{
let height = 100;
let turnsView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: height));
turnsView.backgroundColor = UIColor.clear;
cell.addSubview(turnsView);
}
// Configure the cell...
cell.backgroundColor = UIColor.clear;
cell.textLabel?.text = "texting";
cell.detailTextLabel?.text = "testing";
}
I don't want the purple view appearing in the first section at any point.

When using
tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
You should use Every "if" condition with its "else" clause as when you scroll on off it reuses the cell that cause it to reappear. So if you not write else condition and if condition become false it will use old cell data.

Always avoid adding subviews to cell in cellForRow. You need to understand that the cells are reused and when you add the subView in cellForRow, the subView is not removed each time. So if the cell appears for 10 times due to scrolling on and off, 10 subViews would be added to the reused cell.
In your case same thing is happening. I would suggest you to add the view in XIB/Prototype cell. Give it a reference and change its backgroundColor property in cellForRow.
Or if you are not using XIB or prototype cell, add subview in init method in your cell class.
var turnsView = UIView()
override init(style: UITableViewCellStyle, reuseIdentifier: String!) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
turnsView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 100))
self.contentView.addSubview(turnsView)
}

This should work for you:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
{
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier") as! ATableCell;
cell.delegate = self;
//Remove subviews before adding again
if let purpleV = cell.viewWithTag(1) {
purpleV.removeFromSuperview()
}
if let purpleV = cell.viewWithTag(2) {
purpleV.removeFromSuperview()
}
if (indexPath.section == 1)
{
// let height = cell.bounds.size.height;
let height = 100;
let turnsView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: height));
turnsView.tag = 1
turnsView.backgroundColor = UIColor.purple;
cell.addSubview(turnsView);
}
else
{
let height = 100;
let turnsView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: height));
turnsView.tag = 2
turnsView.backgroundColor = UIColor.clear;
cell.addSubview(turnsView);
}
// Configure the cell...
cell.backgroundColor = UIColor.clear;
cell.textLabel?.text = "texting";
cell.detailTextLabel?.text = "testing";
}
Read the documentation for dequeuereusablecell method to clarify.

This will be good to do as:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
// Config the cell...
if let turnsCell as? ATableCell {
turnsCell.turnsColor = (indexPath.section == 1 ? UIColor.purple : UIColor.clear)
}
cell.textLabel?.text = "texting"
cell.detailTextLabel?.text = "testing"
return cell
}
class ATableCell: UITableViewCell {
var turnsView = UIView()
var turnsColor: UIColor {
get {
return self.turnsView.backgroundColor
}
set {
self.turnsView.backgroundColor = newValue
}
}
override func awakeFromNib() {
super.awakeFromNib()
// let height = self.bounds.size.height;
let height = 100;
turnsView.frame = CGRect(x: 0, y: 0, width: 10, height: height)
self.contentView.addSubview(turnsView)
self.backgroundColor = UIColor.clear
}
}

Related

Elements of the FirstViewController still visible in DetailViewController after using pushViewController method

I first programmatically created a tableview :
private func setupTableView() {
tableView = UITableView(frame: CGRect(x: 0, y: 180, width: view.frame.width, height: view.frame.height), style: UITableView.Style.plain)
tableView.dataSource = self
tableView.delegate = self
tableView.register(ItemTableViewCell.self, forCellReuseIdentifier: "Cell")
view.addSubview(tableView)
}
and set the cellForRow method as below :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ItemTableViewCell
guard let finalItems = presenter.finalItems?[indexPath.row] else { return cell }
presenter.configure(cell: cell, FinalItem: finalItems)
return cell
}
Then I configure the ItemTableViewCell as below :
class ItemTableViewCell: UITableViewCell {
private var iconImageView : UIImageView = {
let imgView = UIImageView(image: #imageLiteral(resourceName: "Image"))
imgView.contentMode = .scaleAspectFit
imgView.clipsToBounds = true
return imgView
}()
private var titleLabel : UILabel = {
let lbl = UILabel()
lbl.textColor = .black
lbl.font = UIFont.boldSystemFont(ofSize: 12)
lbl.textAlignment = .left
return lbl
}()
func configure(finalItem: FinalItem) {
titleLabel.text = finalItem.title
iconImageView.downloaded(from: finalItem.images_url.small)
}
}
When I push to the DetailViewController with the uinavigationbar, the elements contained in the rows (titles, labels...) are still visible a few milli seconds:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let finalItem = finalItems[indexPath.row]
let detailViewController = ModuleBuilder.createDetailModuleWith(finalItem)
detailViewController.finalItem = finalItem
navigationController?.pushViewController(detailViewController, animated: true)
}
This is not what I am expecting. I never figure this problem out before.

Can't remove view from superview on reusable tableview cell

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: EventCommentsCustom = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! EventCommentsCustom
guard let release = array[exist: indexPath.section] else { return cell }
if release.user == "condition" {
let image = UIImage()
let imageView = UIImageView(image: image)
imageView.sd_setImage(with: URL(string: "https://example.com/" + TegKeychain.get("profile_pic")!))
imageView.frame = CGRect(x: 20, y: 10, width: 50, height:50)
imageView.layer.borderWidth = 0.4
imageView.layer.masksToBounds = false
imageView.layer.borderColor = UIColor.gray.cgColor
imageView.layer.cornerRadius = 25
imageView.clipsToBounds = true
imageView.tag = 3
cell.addSubview(imageView)
let button = UIButton(frame: CGRect(x: 90, y: 10, width: 200, height: 50))
button.contentHorizontalAlignment = .left
button.setTitleColor(UIColor.lightGray, for: .normal)
button.setTitle(NSLocalizedString("Say something...", comment: ""), for: .normal)
button.addTarget(self, action: #selector(EventComments.openInput), for: .touchUpInside)
button.tag = 3
cell.addSubview(button)
} else {
if let viewWithTag = cell.viewWithTag(3) {
if viewWithTag is UIImageView {
print("DONE")
viewWithTag.removeFromSuperview()
}
}
if let viewWithTag = cell.viewWithTag(3) {
if viewWithTag is UIButton {
print("DONE")
viewWithTag.removeFromSuperview()
}
}
}
return cell
}
I am trying to remove views that I created with a tag in a reusable tableview cell.
However, I still can see UIButton and UIImageview when first reused (5. section of tableview), then It starts to remove properly
Why don't they get removed at the first reuse?
I guess that reusing in your case could mean that the image view and button get added twice for a cell. You only remove one of them though. I think you should take a different approach (like different prototype cells as #vadian stated) into consideration but for now (assuming my assumption is correct) you could try this to fix your problem:
Replace ...
if let viewWithTag = cell.viewWithTag(3) {
if viewWithTag is UIImageView {
print("DONE")
viewWithTag.removeFromSuperview()
}
}
if let viewWithTag = cell.viewWithTag(3) {
if viewWithTag is UIButton {
print("DONE")
viewWithTag.removeFromSuperview()
}
}
With ...
while let viewToRemove = cell.viewWithTag(3) {
if viewToRemove is UIImageView || viewToRemove is UIButton {
viewToRemove.removeFromSuperview()
}
}
Update -
The approach with different cell types would look something like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let release = array[exist: indexPath.section] else { return cell }
if release.user == "condition" {
let cell = tableView.dequeueReusableCell(withIdentifier: "OneIdentifier", for: indexPath) as! OneCustomCellType
// configure your cell
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "AnotherIdentifier", for: indexPath) as! AnotherCustomCellType
// configure your cell
return cell
}
}

UITableview scrolling is not responding properly?

booktable.frame = CGRect(x: 0, y: booktopview.bounds.height, width: screenWidth, height: screenHeight-booktopview.bounds.height-tabbarView.bounds.height)
booktable.register(UITableViewCell.self, forCellReuseIdentifier: "mycell")
booktable.dataSource = self
booktable.delegate = self
booktable.separatorColor = UIColor.lightGray
booktable.backgroundColor = UIColor.clear
booktable.separatorStyle = .singleLine
bookview.addSubview(booktable)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if(tableView == booktable)
{
let cell1 = booktable.dequeueReusableCell(withIdentifier: "mycell")
for object in (cell1?.contentView.subviews)!
{
object.removeFromSuperview();
}
let img :UIImageView = UIImageView()
let lbl : UILabel = UILabel()
img.frame = CGRect(x: 15, y: 15, width: 80, height: 130)
img.image = imgarray[indexPath.row]
img.layer.borderWidth = 1.0
img.layer.borderColor = UIColor.lightGray.cgColor
cell1?.contentView.addSubview(img)
imgheight = img.bounds.height
lbl.frame = CGRect(x: img.bounds.width + 40, y: (imgheight+40-80)/2, width: booktable.bounds.width-img.bounds.width + 40 - 100, height: 80)
lbl.text = imgname[indexPath.row]
lbl.numberOfLines = 0
lbl.textAlignment = .left
lbl.font = UIFont(name: "Arial", size: 23)
lbl.textColor = UIColor.black
cell1?.selectionStyle = .none
cell1?.contentView.addSubview(lbl)
return cell1!
}
The code shown above is for book table, which sometimes scrolls like normal and sometimes not scrolling at all. I am doing all the code programatically. I have tested this on both simulators and devices but still the problem exists. Any help is appreciated...
Create Custom UITableViewCell, let's say it is ListTableCell
class ListTableCell: UITableViewCell {
#IBOutlet weak var lblTemp: UILabel!
#IBOutlet weak var imgTemp: UIImage!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
I've created UITableViewCell with xib like this and bind IBOutlets
Let's say we have struct Model and array like this
struct Model {
let image : UIImage
let name: String
}
for i in 0...10 {
let model = Model(image: #imageLiteral(resourceName: "Cat03"), name: "Temp \(i)")
array.append(model)
}
Now on ViewController viewDidLoad() method,
tableView.register(UINib(nibName: "ListTableCell", bundle: nil), forCellReuseIdentifier: "ListTableCell")
Implement UITableViewDataSource methods like this,
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ListTableCell") as! ListTableCell
let model = array[indexPath.row]
cell.lblTemp.text = model.name
cell.imgTemp.image = model.image
return cell
}
FYI
For different tableviews, you can create different custom cell the same way and cellForRowAt indexPath and numberOfRowsInSection method will change appropriately.
Let me know in case of any queries.
UPDATE
Follow this and this to create CustomTableCell programmatically

Cell width too big for tableview width

I have a programmatic tableview nested in a caraousel, but when it displays, the programmatic cell is always set wider than the tableview its in. If I print the carousel item width, tableview width and cell width I get: 281, 281, 320(iphone5 screen width). I tried adding constraints to the cell but keep getting nil errors, I believe because the cell hasn't been made yet. So I'm not sure where to put the constraints so that I stop getting these errors, I only need the cell content view width to match the tableview width, but how do I get the tableview width in my custom cell which has been made programmatically? Heres my current code without any constraints:
TableView:
import UIKit
class SplitterCarouselItemTableView: UITableView {
var splitter: BillSplitter?
required init(frame: CGRect, style: UITableViewStyle, splitter: BillSplitter) {
super.init(frame: frame, style: style)
self.splitter = splitter
setupView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupView() {
if Platform.isPhone {
let tableViewBackground = UIImageView(image: UIImage(data: splitter?.image as! Data, scale:1.0))
self.backgroundView = tableViewBackground
tableViewBackground.contentMode = .scaleAspectFit
tableViewBackground.frame = self.frame
}
self.backgroundColor = .clear
self.separatorStyle = .none
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
tableView.register(SplitterCarouselItemTableViewCell.classForCoder(), forCellReuseIdentifier: "splitterCarouselItemTableViewCell")
let cell: SplitterCarouselItemTableViewCell = tableView.dequeueReusableCell(withIdentifier: "splitterCarouselItemTableViewCell") as! SplitterCarouselItemTableViewCell
var item = ((allBillSplitters[tableView.tag].items)?.allObjects as! [Item])[indexPath.row]
if allBillSplitters[tableView.tag].isMainBillSplitter {
getMainBillSplitterItems(splitter: allBillSplitters[tableView.tag])
item = mainBillSplitterItems[indexPath.row]
}
let count = item.billSplitters?.count
if count! > 1 {
cell.name!.text = "\(item.name!)\nsplit \(count!) ways"
cell.price!.text = "£\(Double(item.price)/Double(count!))"
} else {
cell.name!.text = item.name!
cell.price!.text = "£\(item.price)"
}
return cell
}
Cell:
import UIKit
class SplitterCarouselItemTableViewCell: UITableViewCell {
var name: UILabel!
var price: UILabel!
var view: UIView!
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:)")
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: "splitterCarouselItemTableViewCell")
self.backgroundColor = .clear
let width = Int(contentView.bounds.width)
let height = Int(contentView.bounds.height)
view = UIView(frame: CGRect(x: 0, y: 2, width: width, height: height - 4 ))
view.backgroundColor = UIColor.white.withAlphaComponent(0.5)
let viewHeight = Int(view.bounds.height)
let viewWidth = Int(view.bounds.width)
price = UILabel(frame: CGRect(x: viewWidth - 80, y: 0, width: 75, height: viewHeight))
price.font = UIFont.systemFont(ofSize: 15.0)
price.textAlignment = .right
name = UILabel(frame: CGRect(x: 5, y: 0, width: width, height: viewHeight))
name.font = UIFont.systemFont(ofSize: 15.0)
name.numberOfLines = 0
view.addSubview(name)
view.addSubview(price)
contentView.addSubview(view)
}
}
I understand the init shouldn't be filled with all that code and will be extracted later. Its just there until i resolve the width issue.
Thanks for any help.
The challenge is that the tableView.dequeueReusableCell(withIdentifier:) method indirectly calls init(frame:) on your UITableViewCell class. There are a couple of ways to solve this:
Override init(frame:) in SplitterCarouselItemTableViewCell and set your width there when you call super.init(frame:)
Modify your cell.frame in your func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) method
Using Autolayout is problematic with UITableView cells. You don't want to go messing with all the subviews there.
Try adding this
cell.frame = CGRect(x: CGFloat(Int(cell.frame.minX)),
y: CGFloat(Int(cell.frame.minY)),
width: tableView.frame.width,
height: cell.frame.height)
to your
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
This fixed the problem for me.
My UITableViewController and UITableViewCell were done programmatically
in viewDidLoad Grab your tableView Outlet or your tableView Instance if you made it programmatically, then access to rowHeight and give an Value to it : Like This code Below:
enter code here
private let MenuTableView:UITableView = {
let tableView = UITableView(frame: .zero, style: .grouped)
tableView.register(FoodTableViewCell.self, forCellReuseIdentifier: FoodTableViewCell.identifier)
tableView.showsVerticalScrollIndicator = false
return tableView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(MenuTableView)
MenuTableView.rowHeight = 50
}

Swift sidebar menu creation

I am trying to follow the guide found here to create a swift sidebar menu: https://www.youtube.com/watch?v=qaLiZgUK2T0
I have reached the following function:
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
var cell:UITableViewCell? = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell
if cell == nil{
cell = UITableViewCell(style :UITableViewCellStyle.Default, reuseIdentifier: "Cell")
// Configure the cell...
cell!.backgroundColor = UIColor.clearColor()
cell!.textLabel.textColor = UIColor.darkTextColor()
let selectedView:UIView = UIView(frame: CGRect (x: 0, y:0, width: cell!.frame.size.width, height: cell!.frame.size.height))
selectedView.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.3)
cell!.selectedBackgroundView = selectedView
}
cell!.textLabel.text = tableData[indexPath.row]
return cell
}
I have gotten a "Overriding method with selector 'tableView:cellForRowAtIndexPath..." error; being fairly new to swift i am not sure what to do at this point. Please let me know what to do, if you happen to know of a better guide please let me know.
For anyone who may have this issue in the future here is a complete solution of what worked for me, thanks to zisoft for the help with this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell? = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell
if cell == nil{
cell = UITableViewCell(style :UITableViewCellStyle.Default, reuseIdentifier: "Cell")
// Configure the cell...
cell!.backgroundColor = UIColor.clearColor()
cell!.textLabel.textColor = UIColor.darkTextColor()
let selectedView:UIView = UIView(frame: CGRect (x: 0, y:0, width: cell!.frame.size.width, height: cell!.frame.size.height))
selectedView.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.3)
cell!.selectedBackgroundView = selectedView
}
cell!.textLabel.text = tableData[indexPath.row]
return cell!
}
The function signature has changed during the evolving of swift. The correct signature now is:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
...
}
So remove the exclamation marks.
Were the same mistakes were not removed by these methods and found this:
if cell == nil{
cell = UITableViewCell(style :UITableViewCellStyle.Default, reuseIdentifier: "Cell")
// Configure the cell...
cell!.backgroundColor = UIColor.clearColor()
cell!.textLabel?.textColor = UIColor.darkGrayColor()
let selectedView:UIView = UIView(frame: CGRect (x: 0, y:0, width: cell!.frame.size.width, height: cell!.frame.size.height))
selectedView.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.3)
cell!.selectedBackgroundView = selectedView
}
cell!.textLabel?.text = tableData[indexPath.row]
return cell!
}
Hope this might help.

Resources