How to avoid Reusable Cell's copy behavior - ios

Here is the problem, when I use a TableViewController and add a behavior on cell been selected. The behavior showed twice
How can I avoid this?
// MARK: - Table Deleget
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
UIView.animate(withDuration: 0.2, animations: {
cell?.viewWithTag(100)?.isHidden = true
(cell?.viewWithTag(200) as! UILabel).textColor = UIColor.red
})
}
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
UIView.animate( withDuration: 0.3, animations: {
cell?.viewWithTag(100)?.isHidden = false
(cell?.viewWithTag(200) as! UILabel).textColor = UIColor(red: 0, green: 128/255, blue: 128/255, alpha: 1)
})
}

Move the animation from the 'cellForRow' method to 'willDisplayCell' method. I think it can help to you avoid the twice animation.

I have fixed it, add a var to remember the cell which has been taped, and use cellWillDisplay to refresh every cell will displayed, check each cell if it has been selected, if has, show it the selected way.
// MARK: - Table Deleget
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
index = indexPath.row
if let cell = tableView.cellForRow(at: indexPath){
UIView.animate(withDuration: 0.2, animations: {
cell.viewWithTag(100)?.isHidden = true
(cell.viewWithTag(200) as! UILabel).textColor = UIColor.red
})
}
}
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
UIView.animate( withDuration: 0.3, animations: {
cell.viewWithTag(100)?.isHidden = false
(cell.viewWithTag(200) as! UILabel).textColor = UIColor(red: 0, green: 128/255, blue: 128/255, alpha: 1)
})
}
}
// Added this
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if let index = index, index == indexPath.row {
cell.viewWithTag(100)?.isHidden = true
(cell.viewWithTag(200) as! UILabel).textColor = UIColor.red
} else {
cell.viewWithTag(100)?.isHidden = false
(cell.viewWithTag(200) as! UILabel).textColor = UIColor(red: 0, green: 128/255, blue: 128/255, alpha: 1)
}
}

Related

Deleting row by swipe not working propery in tableview

I built an app using mvvm architecture. I don't know why
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath)
it's not getting called on my view controller (basically swipe doesn't work, it can work like 1/20). If I build the tableview (basic one) with its methods to another view controller it works well. I tried to build one more time from scratch my view controller, but also it doesn't affect the result, so the problem is definitely in the functionality of the controller.
My code where swipe works well (I did that only to check if that works in my app:)
class tbViewController: UIViewController {
#IBOutlet weak var tableview: UITableView!
private var data = ["1","2","3","4"]
override func viewDidLoad() {
super.viewDidLoad()
tableview.delegate = self
tableview.dataSource = self
}
}
extension tbViewController: UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = self.data[indexPath.row]
return cell
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
return .delete
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
tableView.beginUpdates()
self.data.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
tableView.endUpdates()
}
}
}
And my original view controller where swipe doesn't work, I also swapped original tableview with above one, only to verify if the problem is from tableview... I can't get it :C. (Please check first tableview methods) :
class ExpensesViewController: UIViewController, ChartViewDelegate {
#IBOutlet private weak var thisWeek: UIButton!
#IBOutlet private weak var thisMonth: UIButton!
#IBOutlet private weak var thisYear: UIButton!
#IBOutlet private weak var totalExpensedAmount: UILabel!
#IBOutlet private weak var pieChart: PieChartView!
#IBOutlet private weak var tableView: UITableView!
private var menu: SideMenuNavigationController?
private var tableViewActions = [Action]()
private var expensesViewModel = ExpensesViewModel()
private var data = ["1","2","3","4"]
override func viewDidLoad() {
super.viewDidLoad()
initSideBar()
customizeButtons()
customizePieChart()
self.tableView.delegate = self
self.tableView.dataSource = self
}
#IBAction func didTapMenu(){
present(self.menu!, animated: true)
}
#IBAction func onClickThisWeek(_ sender: UIButton) {
setGrayBackgroundAndBlackTitleColor(for: self.thisMonth, and: self.thisYear)
sender.backgroundColor = #colorLiteral(red: 0.05509413034, green: 0.7074701786, blue: 0.4755263329, alpha: 1)
sender.setTitleColor(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1), for: UIControl.State.normal)
}
#IBAction func onClickThisMonth(_ sender: UIButton) {
setGrayBackgroundAndBlackTitleColor(for: self.thisWeek, and: self.thisYear)
sender.backgroundColor = #colorLiteral(red: 0.05509413034, green: 0.7074701786, blue: 0.4755263329, alpha: 1)
sender.setTitleColor(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1), for: UIControl.State.normal)
}
#IBAction func onClickThisYear(_ sender: UIButton) {
setGrayBackgroundAndBlackTitleColor(for: self.thisWeek, and: self.thisMonth)
sender.backgroundColor = #colorLiteral(red: 0.05509413034, green: 0.7074701786, blue: 0.4755263329, alpha: 1)
sender.setTitleColor(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1), for: UIControl.State.normal)
}
private func setGrayBackgroundAndBlackTitleColor(for button1: UIButton, and button2: UIButton){
button1.backgroundColor = #colorLiteral(red: 0.921431005, green: 0.9214526415, blue: 0.9214410186, alpha: 1)
button2.backgroundColor = #colorLiteral(red: 0.921431005, green: 0.9214526415, blue: 0.9214410186, alpha: 1)
button1.setTitleColor(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 1), for: UIControl.State.normal)
button2.setTitleColor(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 1), for: UIControl.State.normal)
}
private func initSideBar(){
self.menu = SideMenuNavigationController(rootViewController: MenuListController())
self.menu?.leftSide = true
self.menu?.setNavigationBarHidden(true, animated: false)
SideMenuManager.default.leftMenuNavigationController = self.menu
SideMenuManager.default.addPanGestureToPresent(toView: self.view)
}
private func customizeButtons(){
self.thisWeek.setTitleColor(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1), for: UIControl.State.normal)
self.thisWeek.backgroundColor = #colorLiteral(red: 0.05509413034, green: 0.7074701786, blue: 0.4755263329, alpha: 1)
self.thisWeek.layer.borderColor = #colorLiteral(red: 0.05509413034, green: 0.7074701786, blue: 0.4755263329, alpha: 1)
self.thisMonth.layer.borderColor = #colorLiteral(red: 0.05509413034, green: 0.7074701786, blue: 0.4755263329, alpha: 1)
self.thisYear.layer.borderColor = #colorLiteral(red: 0.05509413034, green: 0.7074701786, blue: 0.4755263329, alpha: 1)
}
private func customizePieChart(){
self.pieChart.delegate = self
self.pieChart.noDataText = "You don't have any income, to display quarters income."
self.pieChart.legend.enabled = false
let pieChartAttribute = [ NSAttributedString.Key.font: UIFont(name: "Arial", size: 16.0)!, NSAttributedString.Key.foregroundColor: UIColor.init(displayP3Red: 0.462, green: 0.838, blue: 1.000, alpha: 1) ]
let pieChartAttrString = NSAttributedString(string: "Quarterly Revenue", attributes: pieChartAttribute)
self.pieChart.centerAttributedText = pieChartAttrString
}
}
extension ExpensesViewController: UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = self.data[indexPath.row]
return cell
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
return .delete
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
tableView.beginUpdates()
self.data.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
tableView.endUpdates()
}
}
}
How original screen should be:
How my screen is now, I commended basically all functionality to not confuse you, where my all business logic is, that's why nothing is displayed, it's just some UI changing, view model doesn't interact with controller(as you see in my code above):
If you have encountered such errors, please help me. Thanks a lot.
I would try using this: https://developer.apple.com/documentation/uikit/uitableviewdelegate/2902367-tableview
There’s also a similar function for leading swipes.
"I don't know why
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) it's not getting called on my view controller".
The reason your method it is not getting called is that you forgot to add at the declaration of your view controller that it should be the UITableViewDelegate.
Note: using beginUpdates method is pointless to delete/remove a single row. Btw when doing multiple insertions/deletions better to use performBatchUpdastes. Last but not least, it is Swift naming convention to name your classes starting with an uppercase letter.

Default highlight tableview cell not showing up

I created a table view and each cell contains an image (covers the entire cell) and a title in the center. However, when I highlight one of the cells, nothing happens. It doesn't get darker which I thought was default. I tried using the code below to fix this, but it doesn't work.
func tableView(_ tableView: UITableView, didHighlightRowAt indexPath: IndexPath) {
let cell = (tableView.cellForRow(at: indexPath) as! MainPackCell)
UIView.animate(withDuration: 0.4) {
cell.packImageView.highlightedImage = cell.packImageView.image?.withRenderingMode(.alwaysTemplate)
if #available(iOS 10.0, *) {
cell.packImageView.tintColor = UIColor(displayP3Red: 0, green: 0, blue: 0, alpha: 0.3)
} else {
// Fallback on earlier versions
}
UIView.animate(withDuration: 0.4, animations: {
tableView.deselectRow(at: indexPath, animated: true)
})
}
}
Populate the TableVIew and refer to the Screenshot below:---
Use this and it is Done:-- Man
implement it after cellForRowAt
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}

How to change particular cell color in ios swift3

I have an String array like [1,2,3,4,5]. I am displaying this array in tableview. My Question is If I want to change the color of 3rd cell only, How can I compare 3rd element with array in ios swift?
on your tableview delegate method based on the condition change the color what you want
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.backgroundColor = indexPath.row == 2 ? .red : yellow //or use cell.contentView.backgroundColor = = indexPath.row == 2 ? .red : yellow
}
if you want to change the array contains 3 condition based then use like
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.backgroundColor = yourArrayName[indexPath.row] == "3" ? .red : yellow //or use cell.contentView.backgroundColor = = yourArrayName[indexPath.row] == "3" ? .red : yellow
}
okay we go with alternate way
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.backgroundColor = .yellow
if let index = yourArrayName.index(of: "3") {
cell.backgroundColor = indexPath.row == index ? .red : yellow
}
}
You can check the condition inside your cellforrow function like this too :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ExampleCellIndentifier", for: indexPath) as! ExampleCell
if indexPath.row == 2{
// Do your modification on the 3rd cell item
cell.backgroundColor = .red
}
return cell
}
I just applied condition under cellForRowAt
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TextFieldInTableViewCell") as! myProfieTabelViewCellTableViewCell
if indexPath[1] % 2 == 0{
cell.backgroundColor = #colorLiteral(red: 0.9088078997, green: 0.9088078997, blue: 0.9088078997, alpha: 1)
print(indexPath[1])
}
else{
cell.backgroundColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
}
cell.selectionStyle = .none
return cell
}

Why are my tableVeiws Rows so Jumpy while scrolling?

Below is the code of my tableView controller which takes an image from a UIImage array resizes it to aspect ratio and displays it on screen. While I scroll the images are very choppy? Is there anyway to reduce the choppiness?
import UIKit
class TableViewController: UITableViewController {
var images = imagePost.defaultimages //An array of a class containing images
var indexPathPass = IndexPath(row: 0, section: 0)
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 100
tableView.backgroundColor = colors.backgroundColor
DispatchQueue.main.async {
self.tableView.scrollToRow(at: self.indexPathPass, at: .top, animated: false)
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TableViewCell
// Configure the cell...
cell.cellImageView.image = self.images[indexPath.row].image
cell.cellImageView.image = cell.cellImageView.image?.resizeImageWith()
cell.backgroundColor = UIColor(displayP3Red: 0, green: 20, blue: 1, alpha: 0.99)
cell.layer.shouldRasterize = true
return cell
}
override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
}
Check for the difference in your estimatedRowHeight and the actualRowHeight, the more is the difference the more it will jump while scrolling.
Solution 1.
If possible, try to implement 'heightForItem at indexPath' method. And return calculated height
Solution 2.
You can implement height caching mechanism. Like below code.
class TableViewController: UITableViewController {
var images = imagePost.defaultimages //An array of a class containing images
var indexPathPass = IndexPath(row: 0, section: 0)
fileprivate var cachedHeight = [IndexPath: CGFloat]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 100
tableView.backgroundColor = colors.backgroundColor
DispatchQueue.main.async {
self.tableView.scrollToRow(at: self.indexPathPass, at: .top, animated: false)
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TableViewCell
// Configure the cell...
cell.cellImageView.image = self.images[indexPath.row].image
cell.cellImageView.image = cell.cellImageView.image?.resizeImageWith()
cell.backgroundColor = UIColor(displayP3Red: 0, green: 20, blue: 1, alpha: 0.99)
cell.layer.shouldRasterize = true
return cell
}
override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return cachedHeight[indexPath] ?? 100
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cachedHeight[indexPath] = cell.frame.height
}
}

Swift UITableView visually deselects cells on scroll

I made an app that lists items in a UITableView. When I select items and scroll down till they go off screen, they will become visually deselected, meaning:
The checkbox image we set and the backgroundcolor are reset to original state.
The system itself however, does actually know what was selected and what wasnt.
Code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:TblCell! = self.tableView.dequeueReusableCellWithIdentifier("cell") as TblCell!
cell.lblCarName.text = tableData[indexPath.row]
cell.lblPrice.text = tablePrice[indexPath.row]
if (tableAvailability[indexPath.row] == "NO") {
cell.imgCarName.image = UIImage(named: "nonselectable")
cell.lblPrice.textColor = UIColor(red: 172/255, green: 76/255, blue: 67/255, alpha: 1);
} else {
cell.imgCarName.image = UIImage(named: "deselected")
}
cell.selectionStyle = UITableViewCellSelectionStyle.None;
return cell
}
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
let cell:TblCell = tableView.cellForRowAtIndexPath(indexPath) as TblCell
if (tableAvailability[indexPath.row] == "YES") {
println("Row \(indexPath.row) selected")
//var myBackView = UIView(frame: cell.frame)
cell.backgroundColor = UIColor(red: 190/255, green: 225/255, blue: 255/255, alpha: 1);
//cell.selectedBackgroundView = myBackView
cell.imgCarName.image = UIImage(named: "selected")
}
}
func tableView(tableView: UITableView!, didDeselectRowAtIndexPath indexPath: NSIndexPath!) {
let cell:TblCell = tableView.cellForRowAtIndexPath(indexPath) as TblCell
if (tableAvailability[indexPath.row] == "YES") {
println("Row \(indexPath.row) deselected")
//var myBackView = UIView(frame: cell.frame)
cell.backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 1);
//cell.selectedBackgroundView = myBackView
cell.imgCarName.image = UIImage(named: "deselected")
}
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 70
}
Any idea on how to fix this?
thanks in advance.
On 'didSelectRowAtIndexPath', you change the backgroundColor and imgCarName straight on the cell.
When you scroll, your cell gets reused! Meaning that the same cell is destroyed and used to present new content.
To keep track of what is selected, you need to save that state somewhere else than against the cell, maybe in your tableAvailability object, or any other object that handles the content of your cells.

Resources