Search code examples
macosswift3xcode8nstableviewnstableviewcell

NSTableView Table View Cell Memory Issue


I have a very strange issue where cells when re-rendering the NSTableView seem to get random coloring and display text. This is MacOS not iPhone.

I use a dictionary in the variable data to display the results into the table.

It's easiest to show how it should look like this... enter image description here

When I run the simulation again and delete all the table columns and re-create them to match for a 'year' instead of months, then it looks like this.enter image description here

It's a very strange error. I'm not sure what I'm doing wrong that might be causing this but I suspect a memory issue with something I'm doing.

Here is my code for the controller.

import Cocoa

class ViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource {

    //@JA - Simulation Settings Tab
    @IBOutlet weak var theTableview: NSTableView!
    @IBOutlet weak var reportTypePopUpButton: NSPopUpButton!
    @IBOutlet weak var businessPopUpButton: NSPopUpButton!
    @IBOutlet weak var numberOfDaysToSimulateTextField: NSTextField!
    @IBOutlet weak var startDateTextField: NSTextField!
    @IBOutlet weak var startingBudgetTextField: NSTextField!

    //Default Multidimensional Dictionary
    var data = [
        [
            "name":"Click Start to Begin",
            "columninfo" :["0"]
        ]
    ]

    override func viewDidLoad() {
        super.viewDidLoad()

        //First remove all columns
        let columns = self.theTableview.tableColumns
        columns.forEach {
            self.theTableview.removeTableColumn($0)
        }

        //@JA - This fixes the last column not being shown correctly
        self.theTableview?.columnAutoresizingStyle = .noColumnAutoresizing

        for index in 0...1 {
            let column = NSTableColumn(identifier: "defaultheader")
            if(index != 0){
                column.title = "Month \(index)"
            }else{
                column.title = "Factors"
            }

            self.theTableview.addTableColumn(column)
        }


        let date = NSDate()
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd"
        let dateString = dateFormatter.string(from:date as Date)

        startDateTextField.stringValue = dateString

        // Do any additional setup after loading the view.

        self.theTableview.reloadData()
    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }

    //@JA - Tableview Delegate & Datasource Functions
    func numberOfRows(in tableView: NSTableView) -> Int {
        return data.count
    }

    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {

        let currentColumnIndex = tableView.tableColumns.index(of: tableColumn!)

        if let cell = tableView.make(withIdentifier: "defaultcell", owner: nil) as? NSTableCellView {
        //if let cell = tableView.reuse {
            if tableColumn == tableView.tableColumns[0]{ //@JA - If this is the first column then show the row names corespondingly
                cell.textField?.stringValue = data[row]["name"] as! String
            }else{
                let columnInfo = data[row]["columninfo"] as! [String]

                let isIndexValid = columnInfo.indices.contains(currentColumnIndex!-1)
                if(isIndexValid){
                    cell.textField?.stringValue = columnInfo[currentColumnIndex!-1]
                }
            }

            //Highlight Rows with rule to do so
            if data[row]["highlightrow"] != nil{
                let bghighlight = data[row]["highlightrow"] as! [String:NSNumber]
                cell.textField?.backgroundColor = NSColor.init(red: CGFloat(bghighlight["red"]!), green: CGFloat(bghighlight["green"]!), blue: CGFloat(bghighlight["blue"]!), alpha: CGFloat(bghighlight["alpha"]!))
                cell.textField?.textColor = NSColor.init(red: CGFloat(bghighlight["tred"]!), green: CGFloat(bghighlight["tgreen"]!), blue: CGFloat(bghighlight["tblue"]!), alpha: CGFloat(bghighlight["talpha"]!))
            }

            //Marked rows will show ----- for content to fill it out
            if data[row]["mark"] != nil{
                if data[row]["mark"] as! Bool == true && currentColumnIndex != 0{
                    cell.textField?.stringValue = "---------------"
                }
            }

            return cell
        }
        return nil
    }

    //@JA - Helper Functions
    func calculateTableHeaders(){

        //First remove all columns
        let columns = self.theTableview.tableColumns
        columns.forEach {
            self.theTableview.removeTableColumn($0)
        }

        //Find out how many days we want to simulate
        let numOfDays = Int(numberOfDaysToSimulateTextField.intValue)
        let columnType = reportTypePopUpButton.titleOfSelectedItem

        //Generic Setting for Date
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd"

        let startDate = dateFormatter.date(from:startDateTextField.stringValue)
        var components = DateComponents()

        var numOfColumns = 150 //Default Column Setting.  Always overwritten

        print("columnType=\(columnType!)")

        if(columnType == "Daily"){
            numOfColumns = Int(numOfDays)
            components = DateComponents() //to reset it
        }
        if(columnType == "Monthly"){
            components.setValue(numOfDays, for: .day)
            let futureDate = Calendar.current.date(byAdding: components, to: startDate!)
            numOfColumns = futureDate!.interval(ofComponent: .month, fromDate: startDate!) //The number of months needed column wise
        }
        if(columnType == "Yearly"){
            components.setValue(numOfDays, for: .day)
            let futureDate = Calendar.current.date(byAdding: components, to: startDate!)
            numOfColumns = futureDate!.interval(ofComponent: .year, fromDate: startDate!) //The number of months needed column wise
        }

        dateFormatter.dateFormat = "dd MMM yyyy" //this is format we want to use for column names
        for index in 0...numOfColumns {
            let column = NSTableColumn(identifier: "defaultheader")
            if(index != 0){
                components = DateComponents() //reset it
                if(columnType == "Daily"){
                    components.setValue(index, for: .day)
                }else if(columnType == "Monthly"){
                    components.setValue(index, for: .month)
                }else if(columnType == "Yearly"){
                    components.setValue(index, for: .year)
                }
                let dayDate = Calendar.current.date(byAdding: components, to: startDate!)
                column.title = dateFormatter.string(from:dayDate!)
            }else{
                column.title = "Factors"
                column.width = 200
            }

            self.theTableview.addTableColumn(column)
        }

    }

    @IBAction func startsimulation(_ sender: NSButton) {
        calculateTableHeaders()
        let budget = startingBudgetTextField.doubleValue
        let businessname = businessPopUpButton.titleOfSelectedItem!

        var biz:Business?

        if ( businessname == "PoolService123.com"){
            biz = PoolService123(bizname: businessname, startbudget:Decimal(budget))
        }

        let sim = Simulator(biz: biz!)

        data = []
        data = sim.run() as! [Dictionary<String, Any>]


        theTableview.reloadData()
    }

}

extension Date {

    func interval(ofComponent comp: Calendar.Component, fromDate date: Date) -> Int {

        let currentCalendar = Calendar.current

        guard let start = currentCalendar.ordinality(of: comp, in: .era, for: date) else { return 0 }
        guard let end = currentCalendar.ordinality(of: comp, in: .era, for: self) else { return 0 }

        return end - start
    }
}

Relevant classes its depending on our defined like so:

Simulator.swift

import Foundation


class Simulator {

    var business: Business
    var daysToSimulate = 365

    //Must be initialized with a business
    init(biz: Business) {
        self.business = biz
    }

    public func run() -> Any{
        let data = self.business.data! //Grabs the default data structure from which to fill in details

        return data
    }
}

Business.swift

import Foundation

class Business {
    public var name: String = ""
    public var budget:Decimal = 0.0
    public var data: [Dictionary<String, Any>]?
    public var money:Decimal = 0.0 //This represents its current financial state

    init(bizname: String,startbudget: Decimal){
        self.name = bizname
        self.budget = startbudget
        self.money = startbudget
    }
}

PoolService123.swift

import Foundation

class PoolService123 : Business{
    override init(bizname: String, startbudget: Decimal) {
        //@JA - Call super to do generic business stuff that is the same
        super.init(bizname: bizname, startbudget: startbudget)

        self.data = [
            [
                "name":"Starting Budget",
                "columninfo" :["0"]
            ],
            [
                "name":"Regions Targeting",
                "columninfo" :["0"]
            ],
            [
                "name":"Marketing & Customers",
                "columninfo" :["-"],
                "highlightrow":["red":0.0,"green":0.0,"blue":0.0,"alpha":1.0,"tred":1.0,"tgreen":1.0,"tblue":1.0,"talpha":1.0],
                "mark":true
            ],
            [
                "name":"Adwords Clicks",
                "columninfo" :["0"]
            ],
            [
                "name":"Adwords Conversions",
                "columninfo" :["0"]
            ],
            [
                "name":"Adwords Customers Acquired",
                "columninfo" :["0"]
            ],
            [
                "name":"Customers Gained",
                "columninfo" :["0"]
            ],
            [
                "name":"Customers Lost",
                "columninfo" :["0"]
            ],
            [
                "name":"Total Customers",
                "columninfo" :["0"]
            ],
            [
                "name":"Expenses",
                "columninfo" :["-"],
                "highlightrow":["red":0.2,"green":0.0,"blue":0.0,"alpha":1.0,"tred":1.0,"tgreen":1.0,"tblue":1.0,"talpha":1.0],
                "mark":true
            ],
            [
                "name":"Adwords Cost",
                "columninfo" :["0"]
            ],
            [
                "name":"Total Expenses",
                "columninfo" :["0"]
            ],
            [
                "name":"Revenue",
                "columninfo" :["-"],
                "highlightrow":["red":0.0,"green":0.2,"blue":0.0,"alpha":1.0,"tred":1.0,"tgreen":1.0,"tblue":1.0,"talpha":1.0],
                "mark":true
            ],
            [
                "name":"Gross Income",
                "columninfo" :["0"]
            ],
            [
                "name":"Net Income",
                "columninfo" :["0"]
            ],
            [
                "name":"Net Worth",
                "columninfo" :["0"]
            ],
            [
                "name":"Business Partner Income",
                "columninfo" :["0"]
            ]
        ]
    }
}

Things I have tried:

When I re-simulate and keep it on months it seems to be okay and nothing goes wrong, this seems to only occur when I switch to doing it by years, which makes less columns overall.

Furthermore if I continue to click the simulation while on years it gets worse and worse. Looking like this ultimately...enter image description here

If I start on yearly and keep doing the simulation it works fine. Switch to monthly and it works fine! Switch back to yearly after doing this though and... it messes up again.

Clearly something I'm doing when coming from yearly simulation to monthly simulation is causing an issue.

Something else I noticed is the error colors and text data is always in the same position when I do the test. I did a break point on the data dictionary at each point and I don't see any corruption in it.

Some interesting Debug Results

So I added this line to this function func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {

print("currentColumnIndex=\(currentColumnIndex ?? -1) , row=\(row)")

I get the following debug output when I'm doing the yearly which is very strange, somehow its rendering the columns in order then sequentially again?

columnType=Yearly
currentColumnIndex=0 , row=0
currentColumnIndex=0 , row=1
currentColumnIndex=0 , row=2
currentColumnIndex=0 , row=3
currentColumnIndex=0 , row=4
currentColumnIndex=0 , row=5
currentColumnIndex=0 , row=6
currentColumnIndex=0 , row=7
currentColumnIndex=0 , row=8
currentColumnIndex=0 , row=9
currentColumnIndex=0 , row=10
currentColumnIndex=0 , row=11
currentColumnIndex=0 , row=12
currentColumnIndex=0 , row=13
currentColumnIndex=0 , row=14
currentColumnIndex=0 , row=15
currentColumnIndex=0 , row=16
currentColumnIndex=1 , row=0
currentColumnIndex=1 , row=1
currentColumnIndex=1 , row=2
currentColumnIndex=1 , row=3
currentColumnIndex=1 , row=4
currentColumnIndex=1 , row=5
currentColumnIndex=1 , row=6
currentColumnIndex=1 , row=7
currentColumnIndex=1 , row=8
currentColumnIndex=1 , row=9
currentColumnIndex=1 , row=10
currentColumnIndex=1 , row=11
currentColumnIndex=1 , row=12
currentColumnIndex=1 , row=13
currentColumnIndex=1 , row=14
currentColumnIndex=1 , row=15
currentColumnIndex=1 , row=16
currentColumnIndex=2 , row=0
currentColumnIndex=2 , row=1
currentColumnIndex=2 , row=2
currentColumnIndex=2 , row=3
currentColumnIndex=2 , row=4
currentColumnIndex=2 , row=5
currentColumnIndex=2 , row=6
currentColumnIndex=2 , row=7
currentColumnIndex=2 , row=8
currentColumnIndex=2 , row=9
currentColumnIndex=2 , row=10
currentColumnIndex=2 , row=11
currentColumnIndex=2 , row=12
currentColumnIndex=2 , row=13
currentColumnIndex=2 , row=14
currentColumnIndex=2 , row=15
currentColumnIndex=2 , row=16
currentColumnIndex=3 , row=0
currentColumnIndex=3 , row=1
currentColumnIndex=3 , row=2
currentColumnIndex=3 , row=3
currentColumnIndex=3 , row=4
currentColumnIndex=3 , row=5
currentColumnIndex=3 , row=6
currentColumnIndex=3 , row=7
currentColumnIndex=3 , row=8
currentColumnIndex=3 , row=9
currentColumnIndex=3 , row=10
currentColumnIndex=3 , row=11
currentColumnIndex=3 , row=12
currentColumnIndex=3 , row=13
currentColumnIndex=3 , row=14
currentColumnIndex=3 , row=15
currentColumnIndex=3 , row=16
currentColumnIndex=4 , row=0
currentColumnIndex=4 , row=1
currentColumnIndex=4 , row=2
currentColumnIndex=4 , row=3
currentColumnIndex=4 , row=4
currentColumnIndex=4 , row=5
currentColumnIndex=4 , row=6
currentColumnIndex=4 , row=7
currentColumnIndex=4 , row=8
currentColumnIndex=4 , row=9
currentColumnIndex=4 , row=10
currentColumnIndex=4 , row=11
currentColumnIndex=4 , row=12
currentColumnIndex=4 , row=13
currentColumnIndex=4 , row=14
currentColumnIndex=4 , row=15
currentColumnIndex=4 , row=16
currentColumnIndex=5 , row=0
currentColumnIndex=5 , row=1
currentColumnIndex=5 , row=2
currentColumnIndex=5 , row=3
currentColumnIndex=5 , row=4
currentColumnIndex=5 , row=5
currentColumnIndex=5 , row=6
currentColumnIndex=5 , row=7
currentColumnIndex=5 , row=8
currentColumnIndex=5 , row=9
currentColumnIndex=5 , row=10
currentColumnIndex=5 , row=11
currentColumnIndex=5 , row=12
currentColumnIndex=5 , row=13
currentColumnIndex=5 , row=14
currentColumnIndex=5 , row=15
currentColumnIndex=5 , row=16
currentColumnIndex=6 , row=0
currentColumnIndex=6 , row=1
currentColumnIndex=6 , row=2
currentColumnIndex=6 , row=3
currentColumnIndex=6 , row=4
currentColumnIndex=6 , row=5
currentColumnIndex=6 , row=6
currentColumnIndex=6 , row=7
currentColumnIndex=6 , row=8
currentColumnIndex=6 , row=9
currentColumnIndex=6 , row=10
currentColumnIndex=6 , row=11
currentColumnIndex=6 , row=12
currentColumnIndex=6 , row=13
currentColumnIndex=6 , row=14
currentColumnIndex=6 , row=15
currentColumnIndex=6 , row=16
currentColumnIndex=7 , row=0
currentColumnIndex=7 , row=1
currentColumnIndex=7 , row=2
currentColumnIndex=7 , row=3
currentColumnIndex=7 , row=4
currentColumnIndex=7 , row=5
currentColumnIndex=7 , row=6
currentColumnIndex=7 , row=7
currentColumnIndex=7 , row=8
currentColumnIndex=7 , row=9
currentColumnIndex=7 , row=10
currentColumnIndex=7 , row=11
currentColumnIndex=7 , row=12
currentColumnIndex=7 , row=13
currentColumnIndex=7 , row=14
currentColumnIndex=7 , row=15
currentColumnIndex=7 , row=16
currentColumnIndex=8 , row=0
currentColumnIndex=8 , row=1
currentColumnIndex=8 , row=2
currentColumnIndex=8 , row=3
currentColumnIndex=8 , row=4
currentColumnIndex=8 , row=5
currentColumnIndex=8 , row=6
currentColumnIndex=8 , row=7
currentColumnIndex=8 , row=8
currentColumnIndex=8 , row=9
currentColumnIndex=8 , row=10
currentColumnIndex=8 , row=11
currentColumnIndex=8 , row=12
currentColumnIndex=8 , row=13
currentColumnIndex=8 , row=14
currentColumnIndex=8 , row=15
currentColumnIndex=8 , row=16
currentColumnIndex=9 , row=0
currentColumnIndex=9 , row=1
currentColumnIndex=9 , row=2
currentColumnIndex=9 , row=3
currentColumnIndex=9 , row=4
currentColumnIndex=9 , row=5
currentColumnIndex=9 , row=6
currentColumnIndex=9 , row=7
currentColumnIndex=9 , row=8
currentColumnIndex=9 , row=9
currentColumnIndex=9 , row=10
currentColumnIndex=9 , row=11
currentColumnIndex=9 , row=12
currentColumnIndex=9 , row=13
currentColumnIndex=9 , row=14
currentColumnIndex=9 , row=15
currentColumnIndex=9 , row=16
currentColumnIndex=10 , row=0
currentColumnIndex=10 , row=1
currentColumnIndex=10 , row=2
currentColumnIndex=10 , row=3
currentColumnIndex=10 , row=4
currentColumnIndex=10 , row=5
currentColumnIndex=10 , row=6
currentColumnIndex=10 , row=7
currentColumnIndex=10 , row=8
currentColumnIndex=10 , row=9
currentColumnIndex=10 , row=10
currentColumnIndex=10 , row=11
currentColumnIndex=10 , row=12
currentColumnIndex=10 , row=13
currentColumnIndex=10 , row=14
currentColumnIndex=10 , row=15
currentColumnIndex=10 , row=16
currentColumnIndex=0 , row=0
currentColumnIndex=1 , row=0
currentColumnIndex=2 , row=0
currentColumnIndex=3 , row=0
currentColumnIndex=4 , row=0
currentColumnIndex=5 , row=0
currentColumnIndex=6 , row=0
currentColumnIndex=7 , row=0
currentColumnIndex=8 , row=0
currentColumnIndex=9 , row=0
currentColumnIndex=10 , row=0
currentColumnIndex=0 , row=1
currentColumnIndex=1 , row=1
currentColumnIndex=2 , row=1
currentColumnIndex=3 , row=1
currentColumnIndex=4 , row=1
currentColumnIndex=5 , row=1
currentColumnIndex=6 , row=1
currentColumnIndex=7 , row=1
currentColumnIndex=8 , row=1
currentColumnIndex=9 , row=1
currentColumnIndex=10 , row=1
currentColumnIndex=0 , row=2
currentColumnIndex=1 , row=2
currentColumnIndex=2 , row=2
currentColumnIndex=3 , row=2
currentColumnIndex=4 , row=2
currentColumnIndex=5 , row=2
currentColumnIndex=6 , row=2
currentColumnIndex=7 , row=2
currentColumnIndex=8 , row=2
currentColumnIndex=9 , row=2
currentColumnIndex=10 , row=2
currentColumnIndex=0 , row=3
currentColumnIndex=1 , row=3
currentColumnIndex=2 , row=3
currentColumnIndex=3 , row=3
currentColumnIndex=4 , row=3
currentColumnIndex=5 , row=3
currentColumnIndex=6 , row=3
currentColumnIndex=7 , row=3
currentColumnIndex=8 , row=3
currentColumnIndex=9 , row=3
currentColumnIndex=10 , row=3
currentColumnIndex=0 , row=4
currentColumnIndex=1 , row=4
currentColumnIndex=2 , row=4
currentColumnIndex=3 , row=4
currentColumnIndex=4 , row=4
currentColumnIndex=5 , row=4
currentColumnIndex=6 , row=4
currentColumnIndex=7 , row=4
currentColumnIndex=8 , row=4
currentColumnIndex=9 , row=4
currentColumnIndex=10 , row=4
currentColumnIndex=0 , row=5
currentColumnIndex=1 , row=5
currentColumnIndex=2 , row=5
currentColumnIndex=3 , row=5
currentColumnIndex=4 , row=5
currentColumnIndex=5 , row=5
currentColumnIndex=6 , row=5
currentColumnIndex=7 , row=5
currentColumnIndex=8 , row=5
currentColumnIndex=9 , row=5
currentColumnIndex=10 , row=5
currentColumnIndex=0 , row=6
currentColumnIndex=1 , row=6
currentColumnIndex=2 , row=6
currentColumnIndex=3 , row=6
currentColumnIndex=4 , row=6
currentColumnIndex=5 , row=6
currentColumnIndex=6 , row=6
currentColumnIndex=7 , row=6
currentColumnIndex=8 , row=6
currentColumnIndex=9 , row=6
currentColumnIndex=10 , row=6
currentColumnIndex=0 , row=7
currentColumnIndex=1 , row=7
currentColumnIndex=2 , row=7
currentColumnIndex=3 , row=7
currentColumnIndex=4 , row=7
currentColumnIndex=5 , row=7
currentColumnIndex=6 , row=7
currentColumnIndex=7 , row=7
currentColumnIndex=8 , row=7
currentColumnIndex=9 , row=7
currentColumnIndex=10 , row=7
currentColumnIndex=0 , row=8
currentColumnIndex=1 , row=8
currentColumnIndex=2 , row=8
currentColumnIndex=3 , row=8
currentColumnIndex=4 , row=8
currentColumnIndex=5 , row=8
currentColumnIndex=6 , row=8
currentColumnIndex=7 , row=8
currentColumnIndex=8 , row=8
currentColumnIndex=9 , row=8
currentColumnIndex=10 , row=8
currentColumnIndex=0 , row=9
currentColumnIndex=1 , row=9
currentColumnIndex=2 , row=9
currentColumnIndex=3 , row=9
currentColumnIndex=4 , row=9
currentColumnIndex=5 , row=9
currentColumnIndex=6 , row=9
currentColumnIndex=7 , row=9
currentColumnIndex=8 , row=9
currentColumnIndex=9 , row=9
currentColumnIndex=10 , row=9
currentColumnIndex=0 , row=10
currentColumnIndex=1 , row=10
currentColumnIndex=2 , row=10
currentColumnIndex=3 , row=10
currentColumnIndex=4 , row=10
currentColumnIndex=5 , row=10
currentColumnIndex=6 , row=10
currentColumnIndex=7 , row=10
currentColumnIndex=8 , row=10
currentColumnIndex=9 , row=10
currentColumnIndex=10 , row=10
currentColumnIndex=0 , row=11
currentColumnIndex=1 , row=11
currentColumnIndex=2 , row=11
currentColumnIndex=3 , row=11
currentColumnIndex=4 , row=11
currentColumnIndex=5 , row=11
currentColumnIndex=6 , row=11
currentColumnIndex=7 , row=11
currentColumnIndex=8 , row=11
currentColumnIndex=9 , row=11
currentColumnIndex=10 , row=11
currentColumnIndex=0 , row=12
currentColumnIndex=1 , row=12
currentColumnIndex=2 , row=12
currentColumnIndex=3 , row=12
currentColumnIndex=4 , row=12
currentColumnIndex=5 , row=12
currentColumnIndex=6 , row=12
currentColumnIndex=7 , row=12
currentColumnIndex=8 , row=12
currentColumnIndex=9 , row=12
currentColumnIndex=10 , row=12
currentColumnIndex=0 , row=13
currentColumnIndex=1 , row=13
currentColumnIndex=2 , row=13
currentColumnIndex=3 , row=13
currentColumnIndex=4 , row=13
currentColumnIndex=5 , row=13
currentColumnIndex=6 , row=13
currentColumnIndex=7 , row=13
currentColumnIndex=8 , row=13
currentColumnIndex=9 , row=13
currentColumnIndex=10 , row=13
currentColumnIndex=0 , row=14
currentColumnIndex=1 , row=14
currentColumnIndex=2 , row=14
currentColumnIndex=3 , row=14
currentColumnIndex=4 , row=14
currentColumnIndex=5 , row=14
currentColumnIndex=6 , row=14
currentColumnIndex=7 , row=14
currentColumnIndex=8 , row=14
currentColumnIndex=9 , row=14
currentColumnIndex=10 , row=14
currentColumnIndex=0 , row=15
currentColumnIndex=1 , row=15
currentColumnIndex=2 , row=15
currentColumnIndex=3 , row=15
currentColumnIndex=4 , row=15
currentColumnIndex=5 , row=15
currentColumnIndex=6 , row=15
currentColumnIndex=7 , row=15
currentColumnIndex=8 , row=15
currentColumnIndex=9 , row=15
currentColumnIndex=10 , row=15
currentColumnIndex=0 , row=16
currentColumnIndex=1 , row=16
currentColumnIndex=2 , row=16
currentColumnIndex=3 , row=16
currentColumnIndex=4 , row=16
currentColumnIndex=5 , row=16
currentColumnIndex=6 , row=16
currentColumnIndex=7 , row=16
currentColumnIndex=8 , row=16
currentColumnIndex=9 , row=16
currentColumnIndex=10 , row=16

What is interesting about this result is when rendering monthly the default render I do it seems to render the tableView in different order?

columnType=Monthly
currentColumnIndex=0 , row=0
currentColumnIndex=1 , row=0
currentColumnIndex=2 , row=0
currentColumnIndex=3 , row=0
currentColumnIndex=4 , row=0
currentColumnIndex=5 , row=0
currentColumnIndex=6 , row=0
currentColumnIndex=7 , row=0
currentColumnIndex=8 , row=0
currentColumnIndex=9 , row=0
currentColumnIndex=10 , row=0
currentColumnIndex=11 , row=0
currentColumnIndex=12 , row=0
...

currentColumnIndex=0 , row=0
currentColumnIndex=1 , row=0
currentColumnIndex=2 , row=0
currentColumnIndex=3 , row=0
currentColumnIndex=4 , row=0
currentColumnIndex=5 , row=0
currentColumnIndex=6 , row=0
currentColumnIndex=7 , row=0
currentColumnIndex=8 , row=0
currentColumnIndex=9 , row=0
currentColumnIndex=10 , row=0
currentColumnIndex=11 , row=0
currentColumnIndex=12 , row=0
...
currentColumnIndex=0 , row=1
currentColumnIndex=1 , row=1
currentColumnIndex=2 , row=1
currentColumnIndex=3 , row=1
currentColumnIndex=4 , row=1
currentColumnIndex=5 , row=1
currentColumnIndex=6 , row=1
currentColumnIndex=7 , row=1
currentColumnIndex=8 , row=1
currentColumnIndex=9 , row=1
currentColumnIndex=10 , row=1
currentColumnIndex=11 , row=1
currentColumnIndex=12 , row=1
....
currentColumnIndex=0 , row=2
currentColumnIndex=1 , row=2
currentColumnIndex=2 , row=2
currentColumnIndex=3 , row=2
currentColumnIndex=4 , row=2
currentColumnIndex=5 , row=2
currentColumnIndex=6 , row=2
currentColumnIndex=7 , row=2
currentColumnIndex=8 , row=2
currentColumnIndex=9 , row=2
currentColumnIndex=10 , row=2
currentColumnIndex=11 , row=2
currentColumnIndex=12 , row=2

... currentColumnIndex=120 , row=16

I did not have enough characters to show full pattern but summarized it here.

Clearing the results of the table only seems to help temporarily, I do this by setting data = [] and reloading the table.


Solution

  • First of all it's not a memory issue.

    Table view cells are reused, you have to ensure that all UI elements are set to a defined state.

    The code contains an if clause, so you have to add a balancing else clause to set the colors to default values for example:

       if let bghighlight = data[row]["highlightrow"] as? [String:NSNumber] {
            cell.textField?.backgroundColor = NSColor(red: CGFloat(bghighlight["red"]!), green: CGFloat(bghighlight["green"]!), blue: CGFloat(bghighlight["blue"]!), alpha: CGFloat(bghighlight["alpha"]!))
            cell.textField?.textColor = NSColor(red: CGFloat(bghighlight["tred"]!), green: CGFloat(bghighlight["tgreen"]!), blue: CGFloat(bghighlight["tblue"]!), alpha: CGFloat(bghighlight["talpha"]!))
       } else {
           cell.textField?.backgroundColor = .white
           cell.textField?.textColor = .textColor
       }
    

    I changed the code a bit to use optional bindings, that's more efficient.

    PS: I recommend to map the JSON to a data model, creating the same colors again and again is unnecessarily expensive.