I just recently got help on here getting the state of checkbox buttons to persist in my UIViewController (either checked or unchecked with the corresponding png changing with the bool value of true or false and saved using UserDefaults).
However, because of the way the project is set up, the state of the checkboxes persists across every item in the UITableViewController that segues to its own UIViewController with the checkboxes in it. So when I click on one checkbox, it checks the same checkbox in every UIViewController in the UITableViewController.
I'm not sure how to fix this as the CurrentGoalViewController (the one with the checkboxes) is segued by a dynamic tableview. Any help would be appreciated, thanks. Below is code for the corresponding views.
CurrentGoalsTableViewController (The tableview with all the goals listed)
import UIKit
class CurrentGoalsTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Current Goals"
//Setup a notification to let us know when the app is about to close, and that we should store the user items to persistence.
//This will call the applicationDidEnterBackground() function in
//this class
NotificationCenter.default.addObserver(self, selector: #selector(UIApplicationDelegate.applicationDidEnterBackground(_:)), name: NSNotification.Name.UIApplicationDidEnterBackground, object: nil)
do
{
//Try to load goalitems from persistence
goals = try[Goal].readFromPersistence() //read values saved for goal text
}
catch let error as NSError //catch errors
{
if error.domain == NSCocoaErrorDomain && error.code == NSFileReadNoSuchFileError
{
NSLog("No persistence file found, not necessarily an error")
}
else
{
let alert = UIAlertController(
title: "Error",
message: "Could not load the goal items",
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
NSLog("Error loading from persistence: \(error)")
}
}
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
private var goalitems = [Goal]()
@objc
public func applicationDidEnterBackground(_ notification: NSNotification)
{
do
{
try goals.writeToPersistence() //save goalitems to persistence
}
catch let error
{
NSLog("Error writing to persistence: \(error)")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData() //reload data every time we come to this view
self.tabBarController?.navigationItem.title = "Current Goals"
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return goals.count //count is amount of goalitems
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let currentGoal = goals[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "basic", for: indexPath)
cell.textLabel?.text = currentGoal.name
// Configure the cell...
return cell //for each goalitem, return cell with goal name
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if indexPath.row < goals.count {
goals.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .top)
}
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
if let currentGoalViewController = segue.destination as? CurrentGoalViewController {
if let indexPath = self.tableView.indexPathForSelectedRow {
currentGoalViewController.currentGoal = goals[indexPath.row]
} //segue from the clicked cell goalitem in CurrentGoalsTableViewController to the detail view of that item in CurrentGoalViewController
}
}
}
CurrentGoalViewController (for the views with the checkboxes)
import UIKit
let defaults = UserDefaults.standard
class CurrentGoalViewController: UIViewController {
var currentGoal: Goal?
var uncheckedBox = UIImage(named: "checkbox") //unchecked checkbox png
var checkedBox = UIImage(named: "checkedbox") //checked checkbox png
var isboxclicked: Bool!
var isbox2clicked: Bool!
var isbox3clicked: Bool!
@IBOutlet weak var progressView: UIProgressView!
var checkedBoxTotal = 0
@IBOutlet weak var goalCompletedLabel: UILabel!
@IBOutlet weak var deadlineLabel: UILabel!
@IBOutlet weak var uncheckBox: UIButton! //one button for each goal
@IBOutlet weak var uncheckBox2: UIButton!
@IBOutlet weak var uncheckBox3: UIButton!
private var checkMarkItems = [CheckmarkItem]()
@IBAction func clickBox(_ sender: UIButton) {
if isboxclicked == true {
isboxclicked = false //if box is checked, when you click on it it unchecks
defaults.set(isboxclicked, forKey:"checkboxstatus") //set bool for UserDefaults
uncheckBox.setImage(uncheckedBox, for: UIControlState.normal) //set image of checkbox to unchecked image
checkedBoxTotal = checkedBoxTotal - 1 //Amount of checkboxes checked is one less
defaults.set(checkedBoxTotal, forKey: "checkboxtotal") //save amount of checkbox total
showCompleteLabel()
progressView.progress = Float(checkedBoxTotal)/Float(3)
defaults.set(progressView.progress, forKey:"progressviewprogress")
} else {
isboxclicked = true //if not checked, check it
defaults.set(isboxclicked, forKey: "checkboxstatus")
uncheckBox.setImage(checkedBox, for:UIControlState.normal)
checkedBoxTotal = checkedBoxTotal + 1
defaults.set(checkedBoxTotal, forKey: "checkboxtotal")
progressView.progress = Float(checkedBoxTotal)/Float(3)
showCompleteLabel()
defaults.set(progressView.progress, forKey:"progressviewprogress")
}
}
@IBAction func clickBox2(_ sender: UIButton) { //second checkbox, same logic as first
if isbox2clicked == true {
isbox2clicked = false
sender.setImage(#imageLiteral(resourceName: "checkbox"), for: UIControlState.normal)
defaults.set(isbox2clicked, forKey: "checkboxstatus2")
checkedBoxTotal = checkedBoxTotal - 1
defaults.set(checkedBoxTotal, forKey: "checkboxtotal")
progressView.progress = Float(checkedBoxTotal)/Float(3)
showCompleteLabel()
defaults.set(progressView.progress, forKey:"progressviewprogress")
} else {
isbox2clicked = true
defaults.set(isbox2clicked, forKey: "checkboxstatus2")
sender.setImage(#imageLiteral(resourceName: "checkedbox"), for:UIControlState.normal)
checkedBoxTotal = checkedBoxTotal + 1
defaults.set(checkedBoxTotal, forKey: "checkboxtotal")
progressView.progress = Float(checkedBoxTotal)/Float(3)
showCompleteLabel()
defaults.set(progressView.progress, forKey:"progressviewprogress")
}
}
@IBAction func clickBox3(_ sender: UIButton) { //third checkbox, same logic
if isbox3clicked == true {
isbox3clicked = false
sender.setImage(#imageLiteral(resourceName: "checkbox"), for: UIControlState.normal)
defaults.set(isbox3clicked, forKey: "checkboxstatus3")
checkedBoxTotal = checkedBoxTotal - 1
defaults.set(checkedBoxTotal, forKey: "checkboxtotal")
progressView.progress = Float(checkedBoxTotal)/Float(3)
showCompleteLabel()
defaults.set(progressView.progress, forKey:"progressviewprogress")
} else {
isbox3clicked = true
sender.setImage(#imageLiteral(resourceName: "checkedbox"), for:UIControlState.normal)
defaults.set(isbox3clicked, forKey: "checkboxstatus3")
checkedBoxTotal = checkedBoxTotal + 1
defaults.set(checkedBoxTotal, forKey: "checkboxtotal")
progressView.progress = Float(checkedBoxTotal)/Float(3)
showCompleteLabel()
defaults.set(progressView.progress, forKey:"progressviewprogress")
}
}
func showCompleteLabel() {
if checkedBoxTotal == 3 { //if amount of boxes checked is 3, show goal complete label
goalCompletedLabel.isHidden = false
}
else { //otherwise, hide it
goalCompletedLabel.isHidden = true
}
}
@IBOutlet weak var currentGoalNameLabel: UILabel!
@IBOutlet weak var goalPoint1Label: UILabel!
@IBOutlet weak var goalPoint2Label: UILabel!
@IBOutlet weak var goalPoint3Label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Current Goal"
//boxes are initially unchecked
isboxclicked = false
isbox2clicked = false
isbox3clicked = false
goalCompletedLabel.isHidden = true //goal completed label originally hidden
}
override func viewWillAppear(_ animated: Bool) {
//loading state of checkbox
isboxclicked = defaults.bool(forKey: "checkboxstatus")
//if state is clicked, set checkbox to checked image
if isboxclicked == true {
uncheckBox.setImage(checkedBox, for: .normal)
}
//same logic as checkbox one
isbox2clicked = defaults.bool(forKey: "checkboxstatus2")
if isbox2clicked == true {
uncheckBox2.setImage(checkedBox, for: .normal)
}
//same logic as checkbox two
isbox3clicked = defaults.bool(forKey: "checkboxstatus3")
if isbox3clicked == true {
uncheckBox3.setImage(checkedBox, for: .normal)
}
//amount of checkboxes checked loaded
checkedBoxTotal = defaults.integer(forKey: "checkboxtotal")
//progress value loaded
progressView.progress = defaults.float(forKey: "progressviewprogress")
if let g = currentGoal { //text label values loaded into current CurrentGoalViewController
currentGoalNameLabel.text = g.name
goalPoint1Label.text = g.goalPoint1
goalPoint2Label.text = g.goalPoint2
goalPoint3Label.text = g.goalPoint3
deadlineLabel.text = g.deadline
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
Goals class (for the text labels)
import Foundation
var goals = [Goal]()
class Goal: NSObject, NSCoding {
var name: String
var goalPoint1: String
var goalPoint2: String
var goalPoint3: String
var deadline: String
init(name: String, goalPoint1: String, goalPoint2: String, goalPoint3: String, deadline: String)
{
self.name = name
self.goalPoint1 = goalPoint1
self.goalPoint2 = goalPoint2
self.goalPoint3 = goalPoint3
self.deadline = deadline
}
required init?(coder aDecoder: NSCoder)
{
if let name = aDecoder.decodeObject(forKey: "name") as? String {
self.name = name
}
else {
return nil
}
if let goalPoint1 = aDecoder.decodeObject(forKey: "goalPoint1") as? String {
self.goalPoint1 = goalPoint1
}
else {
return nil
}
if let goalPoint2 = aDecoder.decodeObject(forKey: "goalPoint2") as? String {
self.goalPoint2 = goalPoint2
}
else {
return nil
}
if let goalPoint3 = aDecoder.decodeObject(forKey: "goalPoint3") as? String {
self.goalPoint3 = goalPoint3
}
else {
return nil
}
if let deadline = aDecoder.decodeObject(forKey: "deadline") as? String {
self.deadline = deadline
}
else {
return nil
}
}
func encode(with aCoder: NSCoder) {
aCoder.encode(self.name, forKey: "name")
aCoder.encode(self.goalPoint1, forKey: "goalPoint1")
aCoder.encode(self.goalPoint2, forKey: "goalPoint2")
aCoder.encode(self.goalPoint3, forKey: "goalPoint3")
aCoder.encode(self.deadline, forKey: "deadline")
}
}
//Creates an extension of the Collection type (aka an Array),
//but only if it is an array of Goal objects
extension Collection where Iterator.Element == Goal
{
//Builds the persistence URL. This is a location inside the
//"Application Support" directory for the App.
private static func persistencePath() -> URL?
{
let url = try? FileManager.default.url(
for: .applicationSupportDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: true)
return url?.appendingPathComponent("goalitems.bin")
}
//Write the array to persistence
func writeToPersistence() throws
{
if let url = Self.persistencePath(), let array = self as? NSArray
{
let data = NSKeyedArchiver.archivedData(withRootObject: array)
try data.write(to: url)
}
else {
throw NSError(domain: "com.example.Goal", code: 10 , userInfo: nil)
}
}
//Read the array from persistence
static func readFromPersistence() throws -> [Goal]
{
if let url = persistencePath(), let data = (try Data(contentsOf: url) as Data?)
{
if let array = NSKeyedUnarchiver.unarchiveObject(with: data) as? [Goal]
{
return array
}
else
{
throw NSError(domain: "com.example.Goal", code: 11, userInfo: nil)
}
}
else
{
throw NSError(domain: "com.example.Goal", code: 12, userInfo: nil)
}
}
}
Aucun commentaire:
Enregistrer un commentaire