How to save the state of a table view?

How do I save the state of a table view after a change to the table view has been made? When I switch to another view and switch back, the table view changes are lost and is back to default. I know there are multiple ways to do this but what is the easiest way? I am thinking core data but how do I do it?

Answered by DelawareMathGuy in 399909022

hi,


great! we've finally now gotten to what i think was your original question of "how to save the state ..." as i said above, "... if you really want to persist this data across application launches ... that's another journey."


what follows below is enough to get things working (albeit maybe not the best way to do this)


step 1: when the Database is first used, populate the allPlayers list from disk, or default to your hard-coded data if we have not yet written anything to disk.


first, change the name of your Data class to, perhaps, PlayerData -- you need to do this because we must use the Foundation class named Data -- and make it conform to the Codable protocol by writing


class PlayerData: Codable { // change name and add Codable


next, add the following functions to the Database class. you'll store the data in the Documents directory with the name playerData.json


fileprivate func playerDataURL() -> URL {
  let documentDirectoryURL = FileManager.default.urls(for: .documentDirectory, in:.userDomainMask).last!
  return documentDirectoryURL.appendingPathComponent("playerData.json")
}

fileprivate func readFromDisk() -> [PlayerData]? {
  let fileURL = playerDataURL()
  guard FileManager.default.fileExists(atPath: fileURL.path) else {
    return nil
  }

  do {
    let fileContents = try Data(contentsOf: fileURL)
    let list = try JSONDecoder().decode([PlayerData].self, from: fileContents)
    return list
  } catch let error as NSError {
    NSLog("Error reading file: \(error.localizedDescription)")
  }
  return nil
}


also change the init() method of Database slightly to be


init() {
  allPlayers = [PlayerData]()
  if let playerList = readFromDisk() {
    allPlayers = playerList
  } else {
    allPlayers = [
    // then all the data
    ]
  } // don't forget to add a closing curly brace here for the else
} // closes the init() method


step #2: add the following method in Database to be able to write data to disk.


fileprivate func writeToDisk() {
  do {
    let data = try JSONEncoder().encode(allPlayers)
    try data.write(to: playerDataURL())
  } catch let error as NSError {
    NSLog("Error reading file: \(error.localizedDescription)")
  }
}


step #3: you need to detect when changes have been made to the player data. one possible solution: explicitly tell the database that changes have been made whenever you make a change so that it will save to disk. so add the following method to the Database class:


func changeMade() {
  writeToDisk()
}


step #4: wherever in your code you make a change to any individual player, be sure to add a call to changeMade(). example: in addPlayer(), removePlayer(), and markAsTaken(), close the functions with both


top30QuarterbacksTable.reloadData()  // makes the visual changes on screen
Database.shared.changeMade() // triggers the data change on disk


i'm pretty sure i have all the code above correct on reading and writing (???)



hope that helps,

DMG

The right way to do it is to make sure the changes to the tableView are saved in the dataSource.


- if this dataSource (an Array in general, in your case should be [Quarterback]) is a global var, it will be persistent when you return.

- if not, save this array when you leave the view (may be in viewWillDisappear), in userDefaults, in a plist, in a file and read back if needed

Yes, that makes sense. I get to save the data source but how do I implement that?


My data source is an array of structs. It looks like this:


struct Data {
    var num: Int = 0
    var name: String = ""
    var team: String = ""
    var position: String = ""
    var strikeThrough: Bool = false
    var color: Bool = false
    var accessory: Bool = false
}

var objectsArray = [Data]()


My Data looks like this:


func quarterBacks() {
    objectsArray = [
        Data(num: 1, name: "Patrick Mahomes", team: "KC", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 2, name: "Deshaun Watson", team: "HOU", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 3, name: "Aaron Rodgers", team: "GB", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 4, name: "Matt Ryan", team: "ATL", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 5, name: "Baker Mayfield", team: "CLE", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 6, name: "Carson Wentz", team: "PHI", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 7, name: "Jared Goff", team: "LAR", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 8, name: "Cam Newton", team: "CAR", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 9, name: "Andrew Luck", team: "IND", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 10, name: "Drew Brees", team: "NO", position: "QB", strikeThrough: false, color: false, accessory: false),
    ]
}

func quarterBacks() is how you initialize the objectsArray.

So this must only be called when you start, not when you reload data (because you would loose all the changes).

=> advice : rename this func to make this explicit, such as func initialQuarterbacks()


What is the code ? The one of your other post ?


What type of change do you make on data ?

- add new player ?

- Change some property as strikeThrough ?

- which changes are lost ?

- to check what happens, add a log in

func initialQuarterbacks() {
     print("QuarterDeck initialized")
     objectsArray = [  /// with the content
}


Check if this is called when you return to the view.

If so, we'll see how to manage it by making objectsArray a global var.

Yes, that makes sense if I call quarterbacks() then it is going to load what is set to default. I call quarterbacks() in viewDidLoad. I changed it to initial_Quarterbacks().


override func viewDidLoad() {
        super.viewDidLoad()
        initial_QuarterBacks()
    }


If I call it like this, it loads the default attributes of initial_Quarterbacks(). Am I putting this function in the wrong place?


My function initial_Quarterbacks() is declared outside of a class so the changes should stay right?


It does call initial_Quarterbacks() when returning to the view but the table view changes do not stay.

You did not answer the most important questions:


What type of change do you make on data ?

- add new player ?

- Change some property as strikeThrough ?

- which changes are lost ?

- to check what happens, add a log in

func initialQuarterbacks() { 
     print("QuarterDeck initialized") 
     objectsArray = [  /// with the content 
}

Check if this is called when you return to the view.


If so, that's a problem because you reinitialize the array.


Then you should declare objectsArray as optional.

And in initialQuarterbacks, test for nil.

If not nil, exit immediately.


Of course, if you do so, you will have to unwrap objectsArray when you use in code:

For instance:

if let objects = objectsArray { // And use objects in the following

Another way wouyld be to avoid unloading the initial view ; but would need to get your complete code to tell exactly what and where to change (probably in thje way you transition between views)

I change some properties such as strikeThrough to true, color to true, and accessory to true. All of the changes are lost when returning to the view. Yes, initialQuarterbacks() is being called when I return to the view. The array is being reinitialized.

This is my Global file:

import Foundation
import UIKit

var tableViewData = [cellData]()

var myRoster: [String]? = []

var draftOrder: [String]? = []

func strikeThroughText (_ text:String) -> NSAttributedString {
    let attributeString: NSMutableAttributedString =  NSMutableAttributedString(string: text)
    attributeString.addAttribute(NSAttributedString.Key.strikethroughStyle, value: 1, range: NSMakeRange(0, attributeString.length))
    return attributeString
    }

func noStrikeThroughText (_ text: String) -> NSAttributedString {
    let attributeString: NSMutableAttributedString =  NSMutableAttributedString(string: text)
    attributeString.addAttribute(NSAttributedString.Key.strikethroughStyle, value: 0, range: NSMakeRange(0, attributeString.length))
    return attributeString
}

var strikeThroughTextBool: Bool = false

This is my TopPlayersData file that contains the Data Struct and the data:

import Foundation
import UIKit

struct Data {
    var num: Int = 0
    var name: String = ""
    var team: String = ""
    var position: String = ""
    var strikeThrough: Bool = false
    var color: Bool = false
    var accessory: Bool = false
}

var objectsArray = [Data]()

func quarterBacks() {
    objectsArray = [
        Data(num: 1, name: "Patrick Mahomes", team: "KC", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 2, name: "Deshaun Watson", team: "HOU", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 3, name: "Aaron Rodgers", team: "GB", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 4, name: "Matt Ryan", team: "ATL", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 5, name: "Baker Mayfield", team: "CLE", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 6, name: "Carson Wentz", team: "PHI", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 7, name: "Jared Goff", team: "LAR", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 8, name: "Cam Newton", team: "CAR", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 9, name: "Andrew Luck", team: "IND", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 10, name: "Drew Brees", team: "NO", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 11, name: "Ben Roethlisberger", team: "PIT", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 12, name: "Dak Prescott", team: "DAL", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 13, name: "Russell Wilson", team: "SEA", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 14, name: "Kyler Murray", team: "ARI", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 15, name: "Tom Brady", team: "NE", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 16, name: "Lamar Jackson", team: "BAL", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 17, name: "Mitchell Trubisky", team: "CHI", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 18, name: "Jameis Winston", team: "TB", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 19, name: "Philip Rivers", team: "LAC", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 20, name: "Kirk Cousins", team: "MIN", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 21, name: "Derek Carr", team: "OAK", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 22, name: "Sam Darnold", team: "NYJ", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 23, name: "Josh Allen", team: "BUF", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 24, name: "Matthew Stafford", team: "DET", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 25, name: "Marcus Mariota", team: "TEN", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 26, name: "Jimmy Garoppolo", team: "SF", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 27, name: "Andy Dalton", team: "CIN", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 28, name: "Eli Manning", team: "NYG", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 29, name: "Nick Foles", team: "JAC", position: "QB", strikeThrough: false, color: false, accessory: false),
        Data(num: 30, name: "Joe Flacco", team: "DEN", position: "QB", strikeThrough: false, color: false, accessory: false)
    ]
}

func runningBacks() {
    objectsArray = [
        Data(num: 1, name: "Saquon Barkley", team: "NYG", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 2, name: "Christian McCaffrey", team: "CAR", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 3, name: "Alvin Kamara", team: "NO", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 4, name: "Ezekiel Elliott", team: "DAL", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 5, name: "David Johnson", team: "ARI", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 6, name: "Le'Veon Bell", team: "NYJ", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 7, name: "Todd Gurley II", team: "LAR", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 8, name: "Kerryon Johnson", team: "DET", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 9, name: "Joe Mixon", team: "CIN", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 10, name: "James Conner", team: "PIT", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 11, name: "Dalvin Cook", team: "MIN", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 12, name: "Nick Chubb", team: "CLE", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 13, name: "Leonard Fournette", team: "JAC", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 14, name: "Josh Jacobs", team: "OAK", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 15, name: "Devonta Freeman", team: "ATL", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 16, name: "Melvin Gordon", team: "LAC", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 17, name: "Derrick Henry", team: "TEN", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 18, name: "Chris Carson", team: "SEA", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 19, name: "Marlon Mack", team: "IND", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 20, name: "Aaron Jones", team: "GB", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 21, name: "Damien Williams", team: "KC", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 22, name: "Mark Ingram II", team: "BAL", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 23, name: "James White", team: "NE", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 24, name: "Sony Michel", team: "NE", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 25, name: "Kenyan Drake", team: "MIA", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 26, name: "David Montgomery", team: "CHI", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 27, name: "Tarik Cohen", team: "CHI", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 28, name: "Phillip Lindsay", team: "DEN", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 29, name: "Derrius Guice", team: "WAS", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 30, name: "Lamar Miller", team: "HOU", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 31, name: "Tevin Coleman", team: "SF", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 32, name: "Miles Sanders", team: "PHI", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 33, name: "Austin Ekeler", team: "LAC", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 34, name: "Duke Johnson Jr.", team: "HOU", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 35, name: "Royce Freeman", team: "DEN", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 36, name: "Rashaad Penny", team: "SEA", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 37, name: "Nyheim Hines", team: "IND", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 38, name: "Peyton Barber", team: "TB", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 39, name: "Latavius Murray", team: "NO", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 40, name: "Jordan Howard", team: "PHI", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 41, name: "Matt Breida", team: "SF", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 42, name: "LeSean McCoy", team: "BUF", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 43, name: "Darrell Henderson", team: "LAR", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 44, name: "Darwin Thompson", team: "KC", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 45, name: "Ronald Jones", team: "TB", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 46, name: "Devin Singletary", team: "BUF", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 47, name: "Jalen Richard", team: "OAK", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 48, name: "Dion Lewis", team: "TEN", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 49, name: "Giovani Bernard", team: "CIN", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 50, name: "Kalen Ballage", team: "MIA", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 51, name: "Jaylen Samuels", team: "PIT", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 52, name: "Justice Hill", team: "BAL", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 53, name: "Tony Pollard", team: "DAL", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 54, name: "Chris Thompson", team: "WAS", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 55, name: "Justin Jackson", team: "LAC", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 56, name: "Carlos Hyde", team: "KC", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 57, name: "Jerick McKinnon", team: "SF", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 58, name: "Kareem Hunt", team: "CLE", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 59, name: "Malcolm Brown", team: "LAR", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 60, name: "Damien Harris", team: "NE", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 61, name: "Mike Davis", team: "CHI", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 62, name: "Mark Walton", team: "MIA", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 63, name: "C.J. Anderson", team: "DET", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 64, name: "Dexter Williams", team: "GB", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 65, name: "Adrian Peterson", team: "WAS", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 66, name: "Jamaal Williams", team: "GB", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 67, name: "Ito Smith", team: "ATL", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 68, name: "Alexander Mattison", team: "MIN", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 69, name: "Gus Edwards", team: "BAL", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 70, name: "Chase Edmonds", team: "ARI", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 71, name: "Frank Gore", team: "BUF", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 72, name: "Theo Riddick", team: "DEN", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 73, name: "Dontrell Hilliard", team: "CLE", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 74, name: "Darren Sproles", team: "PHI", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 75, name: "Benny Snell Jr.", team: "PIT", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 76, name: "Doug Martin", team: "OAK", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 77, name: "Rex Burkhead", team: "NE", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 78, name: "Ryquell Armstead", team: "JAC", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 79, name: "Ty Montgomery", team: "NYJ", position: "RB", strikeThrough: false, color: false, accessory: false),
        Data(num: 80, name: "Andre Ellington", team: "TB", position: "RB", strikeThrough: false, color: false, accessory: false)
    ]
}

This is my Top_30_Quarterbacks file. I have other views too such as running backs, wide receivers, tight ends, defenses, and kickers. I switch back and forth between the views.


import UIKit

@available(iOS 11.0, *)

class Top_30_Quarterbacks: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return objectsArray.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        let num = objectsArray[indexPath.row].num
        let name = objectsArray[indexPath.row].name
        let team = objectsArray[indexPath.row].team
        cell.textLabel?.text = "\(num).  \(name),  \(team)"
        cell.textLabel?.adjustsFontSizeToFitWidth = true
        cell.textLabel?.font = UIFont.systemFont(ofSize: 22)
        
        if objectsArray[indexPath.row].strikeThrough == false && objectsArray[indexPath.row].accessory == false && objectsArray[indexPath.row].color == false {
            cell.textLabel?.text = "\(num).  \(name),  \(team)"
            cell.textLabel?.attributedText = noStrikeThroughText("\(num).  \(name),  \(team)")
            cell.accessoryType = UITableViewCell.AccessoryType.none
            cell.backgroundColor = .none
        }
        else if objectsArray[indexPath.row].strikeThrough == true && objectsArray[indexPath.row].accessory == true && objectsArray[indexPath.row].color == true {
            cell.textLabel?.text = "\(num).  \(name),  \(team)"
            cell.textLabel?.attributedText = strikeThroughText("\(num).  \(name),  \(team)")
            cell.accessoryType = UITableViewCell.AccessoryType.checkmark
            cell.backgroundColor = .systemGray3
        } else {
            cell.textLabel?.text = "\(num).  \(name),  \(team)"
            cell.textLabel?.attributedText = strikeThroughText("\(num).  \(name),  \(team)")
            cell.accessoryType = UITableViewCell.AccessoryType.none
            cell.backgroundColor = .systemGray2
        }
        return cell
    }
    
    func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        let cell = self.top30QuarterbacksTable.cellForRow(at: indexPath)
        let str: String = (cell?.textLabel!.text)!
        let player: String = objectsArray[indexPath.row].name
        
        if cell?.textLabel?.attributedText == strikeThroughText(str) {
            strikeThroughTextBool = true
        } else {
            strikeThroughTextBool = false
        }
        if strikeThroughTextBool == false {
            let add = UIContextualAction(style: .normal, title: "Add") { (contextualAction, view, actionPerformed: @escaping (Bool) -> Void) in
                let alert = UIAlertController(title: "Add Player", message: "Are you sure you want to add '\(player)' to your roster?", preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { (alertAction) in
                    actionPerformed(false)
                }))
                alert.addAction(UIAlertAction(title: "Yes", style: .destructive, handler: { (alertAction) in
                    self.addPlayer(index: indexPath)
                    let okayAlert = UIAlertController(title: "Player Added!", message: "You added '\(player)' to your roster?", preferredStyle: .alert)
                    okayAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (alertAction) in
                        actionPerformed(true)
                    }))
                    self.present(okayAlert, animated: true)
                }))
                self.present(alert, animated: true)
            }
            add.backgroundColor = .systemGreen
            let taken = UIContextualAction(style: .normal, title: "Taken") { (contextualAction, view, actionPerformed: @escaping (Bool) -> Void) in
                let alert = UIAlertController(title: "Player Taken", message: "Are you sure you want to mark '\(player)' as taken and not available?", preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { (alertAction) in
                    actionPerformed(false)
                }))
                alert.addAction(UIAlertAction(title: "Yes", style: .destructive, handler: { (alertAction) in
                    self.markAsTaken(index: indexPath)
                    let okayAlert = UIAlertController(title: "Taken Player", message: "You marked '\(player)' as taken and not available!", preferredStyle: .alert)
                    okayAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (alertAction) in
                        actionPerformed(true)
                    }))
                    self.present(okayAlert, animated: true)
                }))
                self.present(alert, animated: true)
            }
            taken.backgroundColor = .systemRed
            let config = UISwipeActionsConfiguration(actions: [taken, add])
            config.performsFirstActionWithFullSwipe = false
            return config
        } else {
            let undo = UIContextualAction(style: .normal, title: "Undo") { (contextualAction, view, actionPerformed: @escaping (Bool) -> Void) in
                let alert = UIAlertController(title: "Undo", message: "Are you sure you want to undo the action for '\(player)'?", preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { (alertAction) in
                    actionPerformed(false)
                }))
                alert.addAction(UIAlertAction(title: "Yes", style: .destructive, handler: { (alertAction) in
                    self.removePlayer(index: indexPath)
                    let okayAlert = UIAlertController(title: "Action Undone", message: "The previous action for '\(player)' has been undone!", preferredStyle: .alert)
                    okayAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (alertAction) in
                        actionPerformed(true)
                    }))
                    self.present(okayAlert, animated: true)
                }))
                self.present(alert, animated: true)
            }
            undo.backgroundColor = .systemBlue
            let config = UISwipeActionsConfiguration(actions: [undo])
            config.performsFirstActionWithFullSwipe = false
            return config
        }
    }
    
    override func viewWillAppear(_ animated: Bool) {
        initial_QuarterBacks()
    }
    
    func addPlayer(index: IndexPath) {
        _ = top30QuarterbacksTable.cellForRow(at: index)
        let name: String = objectsArray[index.row].name
        let team: String = objectsArray[index.row].team
        let position: String = objectsArray[index.row].position
        
        objectsArray[index.row].strikeThrough = true
        objectsArray[index.row].accessory = true
        objectsArray[index.row].color = true
        
        if myRoster == [] {
            myRoster?.insert("\(name),  \(team) - \(position)", at: 0)
        } else {
            myRoster?.append("\(name),  \(team) - \(position)")
        }
        
        if draftOrder == [] {
            draftOrder?.insert("\(name),  \(team) - \(position)", at: 0)
        } else {
            draftOrder?.append("\(name),  \(team) - \(position)")
        }
        top30QuarterbacksTable.reloadData()
    }
    
    func removePlayer(index: IndexPath) {
        _ = top30QuarterbacksTable.cellForRow(at: index)
        let name: String = objectsArray[index.row].name
        let team: String = objectsArray[index.row].team
        let position: String = objectsArray[index.row].position
        let fullName: [String] = ["\(name),  \(team) - \(position)"]
        
        objectsArray[index.row].strikeThrough = false
        objectsArray[index.row].color = false
        objectsArray[index.row].accessory = false
        
        myRoster?.removeAll(where: { fullName.contains($0) })
        draftOrder?.removeAll(where: { fullName.contains($0) })
        top30QuarterbacksTable.reloadData()
    }
        
    func markAsTaken(index: IndexPath) {
        _ = top30QuarterbacksTable.cellForRow(at: index)
        let name: String = (objectsArray[index.row].name)
        let team: String = (objectsArray[index.row].team)
        let position: String = (objectsArray[index.row].position)
        
        objectsArray[index.row].strikeThrough = true
        objectsArray[index.row].color = true
        objectsArray[index.row].accessory = false
            
            if draftOrder == [] {
                draftOrder?.insert("\(name),  \(team) - \(position)", at: 0)
            } else {
                draftOrder?.append("\(name),  \(team) - \(position)")
            }
        top30QuarterbacksTable.reloadData()
        }

    @IBOutlet weak var top30QuarterbacksTable: UITableView!
    @IBOutlet var view1: UIView!
    @IBOutlet weak var view2: UIView!
    @IBOutlet weak var label: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
    }
}

Do you use the same objectsArray to store quarterBacks and runningBacks ?


You have:

    override func viewWillAppear(_ animated: Bool) { 
        initial_QuarterBacks() 
    }

So, each time the view is presented, it is reset !!!


Just suppress this func. And tell if it works now.


Finally, some programming style remarks.

I personally prefer initilizing func that return a value. Doing so, you know when you use, what var you are loading:


var objectsArray = [Data]()

func initial_QuarterBacks() -> [Data]{
    let dataArray = [
        Data(num: 1, name: "Patrick Mahomes", team: "KC", position: "QB", strikeThrough: false, color: false, accessory: false),
     // All the stuff
     ]
  return dataArray
}

and when needed:

objectsArray = initial_QuarterBacks()

I do use the same objectsArray to store quarterbacks and runningBacks but I tried using a different variable to store quarterbacks and runningBacks and it still doesn't work.


I have a main view that uses a table view that shows quarterbacks is one row and running backs in another row. I click on quarterbacks and it shows the quarterbacks view. I make changes and then hit the back button to go back the the main view and then click on quarterbacks to return to the quarterbacks view and all changes are lost.

Did you do what I proposed ?


You have:

    override func viewWillAppear(_ animated: Bool) { 
        initial_QuarterBacks() 
    }

So, each time the view is presented, it is reset !!!

Just suppress this func. And tell if it works now.


You don't answer. Please read completely the answers, otherwise it makes it pretty tough to undrstand what's going on.


The best would be you to give me an email so that we can exchange complete code.

hi,


from the extended back-and-forth below, and your previous thread in which both Claude31 and i were trying to move you forward, we now know more about what you are trying to do. i'd suggest that ALL the Data records for ALL the players you want to work with should probably be held globally, in a central database.


i'd suggest that you want a singleton Database class that manages all the data of all the players. any ViewController that needs to present and/or modify this data can access it here.


step #1: make sure the Data struct is now a CLASS, with a convenient init() method that let's you abbreviate its use, since all three boolean values are false upon creations


class Data {
    var num: Int = 0
    var name: String = ""
    var team: String = ""
    var position: String = ""
    var strikeThrough: Bool = false
    var color: Bool = false
    var accessory: Bool = false

  init(num: Int, name: String, team: String, position: String) {
    self.num = num
    self.name = name
    self.team = team
    self.position = position
  }
}


step #2: create a singleton, global Database class with


class Database { // this is a singleton class, exposing its shared instance

  static let shared = Database()

  fileprivate var allPlayers: [Data]

  func playerList(position: String) -> [Data] {
    return allPlayers.filter({ $0.position == position })
  }

  init() {
  allPlayers = [ // edited after posting to correct an error
    Data(num: 1, name: "Patrick Mahomes", team: "KC", position: "QB"),
    Data(num: 2, name: "Deshaun Watson", team: "HOU", position: "QB"),
    Data(num: 3, name: "Aaron Rodgers", team: "GB", position: "QB"),
    Data(num: 4, name: "Matt Ryan", team: "ATL", position: "QB"),
    // add all the other QB data you have and then just keep on going
    // with all the RB data you have
    Data(num: 1, name: "Saquon Barkley", team: "NYG", position: "RB"),
    Data(num: 2, name: "Christian McCaffrey", team: "CAR", position: "RB"),
    Data(num: 3, name: "Alvin Kamara", team: "NO", position: "RB"),
    Data(num: 4, name: "Ezekiel Elliott", team: "DAL", position: "RB"),
    // and finish out all the RB data
    Data(num: 80, name: "Andre Ellington", team: "TB", position: "RB")
    ]
  }


}


step #3: for your second-level VCs that need data either for "QB" or "RB" players, include the following to initialize its objectsArray: [Data] in viewWillAppear()


override func viewWillAppear(_ animated: Bool) {
  super.viewWillAppear(animated)
  objectsArray = Database.shared.playerList(position: "QB") // edited after original post
  // and whatever else you need to do; and also note that you may want to
  // sort the objectsArray perhaps by their "num" value at this point
  }


step #4: i'm not sure there is a step 4, because any change you now make directly to a Data object will be made in the Database, because Data objects are classes and accessed by reference.


that solves your immediate problem of having the list of players in a second-level VC being reset each time it opens: the data is kept globally, and you pull an updated list of players everytime such a VC comes on screen. and, of course, you may now want to access the Database in your main-level VC ...


still more to do, though, if you allow adding and deleting rows in your VC (you do, right?), and if you really want to persist this data across application launches. that's another journey ⚠ that will require more discussion about what you are trying to accomplish in your app.



hope that helps,

DMG

I deleted

override func viewWillAppear(_ animated: Bool) {   
        initial_QuarterBacks()   
    }  


It doesn't load anything in the view now unless I put the function in viewDidLoad()

override func viewDidLoad() {
        super.viewDidLoad()
        initial_QuarterBacks()
    }

Of course, you have to put somewhere ! viewDidLoad is the right place.

How to save the state of a table view?
 
 
Q