不知道在哪里放置 saveData 以在 Swift 中使用 userDefaults 保存数据

Don't know where to place saveData to save data with userDefaults in Swift

我正在尝试在被用户更改后保存我的数据,我正在从我的模型中保存数据,同时我也在从我的模型中获取数据 class,我想保存数据一旦视图消失,所有数据都将被保存,我可以从我的模型中获取数据(当我再次初始化它时),我有点迷失了实现它的方式:\ 这是我的 VC 代码:我在哪里设置我的 tableView 单元格 + 想在那里使用 saveData() :

import UIKit
import PanModal


class FilterTableViewController: UIViewController, UITableViewDelegate,UITableViewDataSource, PanModalPresentable {
    
    //Instance of our ViewModel
    private var filterViewModel = FilterViewModel()
    
    //Our Model Instance:
//    private var filterModel = FilterModel()

    let tableView = UITableView()
    var safeArea: UILayoutGuide!
    let buttonView = UIView()
    
    var panScrollable: UIScrollView? {
        return tableView
    }
    
    
    var albumsPickerIndexPath: IndexPath? //  indexPath of the currently shown albums picker in tableview.
    //    var delegate: FilterTableViewDelegate?
    
    
    var datesCell = DatesCell()
    var selectedAlbumByUser = ""
    
    
    //    var userFilter: FilterModel?
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        safeArea = view.layoutMarginsGuide
        
        setupTableView()
        self.tableView.dataSource = self
        self.tableView.delegate = self
        setupButtonView()
    }
    
    
    
    
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }
    
    
    
    // MARK: - Views Configurations
    
    func setupTableView() {
        view.addSubview(tableView)
        tableView.translatesAutoresizingMaskIntoConstraints = false
        
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor,constant: 120).isActive = true
        tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        
        
        tableView.separatorStyle = .singleLine
        tableView.isScrollEnabled = false
        tableView.allowsSelection = true
        
        
        tableView.rowHeight = UITableView.automaticDimension
        tableView.estimatedRowHeight = 600
        
        
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        tableView.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
        
        tableView.tableFooterView = UIView(frame: .zero)
        
    }
    
    
    func setupButtonView() {
        
        view.addSubview(buttonView)
        buttonView.translatesAutoresizingMaskIntoConstraints = false
        buttonView.backgroundColor = .white
        buttonView.layer.cornerRadius = 15;
        //        buttonView.clipsToBounds  =  true
        NSLayoutConstraint.activate([
            buttonView.topAnchor.constraint(equalTo: tableView.bottomAnchor),
            buttonView.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor,constant: 10),
            buttonView.leadingAnchor.constraint(equalTo: view.leadingAnchor,constant: 16),
            buttonView.trailingAnchor.constraint(equalTo: view.trailingAnchor,constant: -16),
            buttonView.widthAnchor.constraint(equalToConstant: 100),
            buttonView.heightAnchor.constraint(equalToConstant: 40),
        ])
        
        
        let applyFiltersButton = UIButton()
        applyFiltersButton.setTitle("Apply Filters", for: .normal)
        applyFiltersButton.setTitleColor(.white, for: .normal)
        applyFiltersButton.layer.cornerRadius = 15;
        //        myFirstButton.clipsToBounds  =  true
        applyFiltersButton.backgroundColor = #colorLiteral(red: 0.1957295239, green: 0.6059523225, blue: 0.960457623, alpha: 1)
        applyFiltersButton.contentHorizontalAlignment = .center;
        applyFiltersButton.titleLabel?.font = UIFont(name: "Helvetica", size:16)
        
        
        self.buttonView.addSubview(applyFiltersButton)
        applyFiltersButton.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            applyFiltersButton.topAnchor.constraint(equalTo: buttonView.topAnchor),
            applyFiltersButton.bottomAnchor.constraint(equalTo: buttonView.bottomAnchor),
            applyFiltersButton.leadingAnchor.constraint(equalTo: buttonView.leadingAnchor),
            applyFiltersButton.trailingAnchor.constraint(equalTo: buttonView.trailingAnchor),
            applyFiltersButton.widthAnchor.constraint(equalToConstant: 100),
            applyFiltersButton.heightAnchor.constraint(equalToConstant: 40),
            applyFiltersButton.centerXAnchor.constraint(equalTo: buttonView.centerXAnchor),
            applyFiltersButton.centerYAnchor.constraint(equalTo: buttonView.centerYAnchor)
            
        ])
    }
    
    
    func indexPathToInsertDatePicker(indexPath: IndexPath) -> IndexPath {
        if let albumsPickerIndexPath = albumsPickerIndexPath, albumsPickerIndexPath.row < indexPath.row {
            return indexPath
        } else {
            return IndexPath(row: indexPath.row + 1, section: indexPath.section)
        }
    }
    
    
    
    // MARK: - UITableViewDataSource
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // If datePicker is already present, we add one extra cell for that
        if albumsPickerIndexPath != nil {
            return 5 + 1
        } else {
            return 5
        }
    }
    
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        switch indexPath.row {
        case 0:
            
            let byActivityCell = UINib(nibName: "byActivityCell",bundle: nil)
            self.tableView.register(byActivityCell,forCellReuseIdentifier: "byActivityCell")
            let activityCell = tableView.dequeueReusableCell(withIdentifier: "byActivityCell", for: indexPath) as! byActivityCell
            activityCell.selectionStyle = .none
            activityCell.activityDelegate = filterViewModel
            
            
            return activityCell
            
        case 1:
            let byTypeCell = UINib(nibName: "ByType",bundle: nil)
            self.tableView.register(byTypeCell,forCellReuseIdentifier: "byTypeCell")
            let typeCell = tableView.dequeueReusableCell(withIdentifier: "byTypeCell", for: indexPath) as! ByType
            typeCell.selectionStyle = .none
            typeCell.byTypeDelegate = filterViewModel
            
            
            return typeCell
            
            
        case 2:
            let byHashtagsCell = UINib(nibName: "ByHashtags",bundle: nil)
            self.tableView.register(byHashtagsCell,forCellReuseIdentifier: "byHashtagsCell")
            let hashtagsCell = tableView.dequeueReusableCell(withIdentifier: "byHashtagsCell", for: indexPath) as! ByHashtags
            hashtagsCell.selectionStyle = .none
            hashtagsCell.byHashtagsDelegate = filterViewModel
            
            return hashtagsCell
            
        case 3:
            let byDatesCell = UINib(nibName: "DatesCell",bundle: nil)
            self.tableView.register(byDatesCell,forCellReuseIdentifier: "byDatesCell")
            let datesCell = tableView.dequeueReusableCell(withIdentifier: "byDatesCell", for: indexPath) as! DatesCell
            datesCell.selectionStyle = .none
            datesCell.datesTableViewCellDelegate = self
            
            
            
            return datesCell
            
            
        case 4:
            let byAlbumCell = UINib(nibName: "AlbumCell",bundle: nil)
            self.tableView.register(byAlbumCell,forCellReuseIdentifier: "byAlbumCell")
            let albumCell = tableView.dequeueReusableCell(withIdentifier: "byAlbumCell", for: indexPath) as! AlbumCell
//            albumCell.configureCell(choosenAlbum: filterViewModel.getSelectedAlbum())
            albumCell.selectionStyle = .none
            
            filterViewModel.getAlbum = { selectedAlbum in
                albumCell.configureCell(choosenAlbum: selectedAlbum)
            }
            
            
            return albumCell
            
            
        case 5:
            let albumPickerCell = UINib(nibName: "AlbumsPickerTableViewCell", bundle: nil)
            self.tableView.register(albumPickerCell, forCellReuseIdentifier: "albumPickerCell")
            let albumsPicker = tableView.dequeueReusableCell(withIdentifier: "albumPickerCell", for: indexPath) as! AlbumsPickerTableViewCell
            //            albumsPicker.albumsPickerCellDelegate = self
            albumsPicker.albumsPickerCellDelegate = filterViewModel
            
            
            return albumsPicker
            
            
        default:
            return UITableViewCell()
        }
        
    }
    
    
    // MARK: TableViewDelegate Methods:
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: false)
        
        tableView.beginUpdates()
        
        // 1 - We Delete the UIPicker when the user "Deselect" thr row.
        if let datePickerIndexPath = albumsPickerIndexPath,   datePickerIndexPath.row - 1 == indexPath.row {
            tableView.deleteRows(at: [datePickerIndexPath], with: .fade)
            self.albumsPickerIndexPath = nil
            let albumCell = tableView.cellForRow(at: indexPath) as! AlbumCell
            UIView.animate(withDuration: 1) {
                albumCell.arrowPickerView.transform = CGAffineTransform.identity
            }
        } else {
            // 2
            //            if let datePickerIndexPath = albumsPickerIndexPath {
            //                tableView.deleteRows(at: [datePickerIndexPath], with: .fade)
            //            }
            
            let albumCell = tableView.cellForRow(at: indexPath) as! AlbumCell
            
            UIView.animate(withDuration: 1) {
                albumCell.arrowPickerView.transform = CGAffineTransform(rotationAngle: .pi)
            }
            albumsPickerIndexPath = indexPathToInsertDatePicker(indexPath: indexPath)
            tableView.insertRows(at: [albumsPickerIndexPath!], with: .fade)
            tableView.deselectRow(at: indexPath, animated: true)
        }
        tableView.endUpdates()
    }
    
    
    func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
        if indexPath.row == 4  {
            return indexPath
        } else {
            return nil
        }
    }
}


extension FilterTableViewController: DatesTableViewCellDelegate {
    
    func didButtonFromPressed() {
        let pickerController = CalendarPickerViewController(
            baseDate: Date(),
            selectedDateChanged: { [weak self] date in
                guard let self = self else { return }
                
                //          self.item.date = date
                
                
//                self.filterModel.startDate = date
                self.tableView.reloadRows(at: [IndexPath(row: 3, section: 0)], with: .fade)
            })
        present(pickerController, animated: true, completion: nil)
    }
    
    
    func didButtonToPressed() {
        //TODO: Present our custom calendar
        let calendarController = CalendarPickerViewController(
            baseDate: Date(),
            selectedDateChanged: { [weak self] date in
                guard let self = self else { return }
                
                
//                self.filterModel.endDate = date
                self.tableView.reloadRows(at: [IndexPath(row: 3, section: 0)], with: .fade)
            })
        self.present(calendarController, animated: true, completion: nil)
    }
}

这是我的模型:

import Foundation
import UIKit



class FilterModel {
    let userDefaults = UserDefaults.standard
    
    
    var byActivity: String = ""
    var postType: [String] = []
    var hashTags: [String] = []
    var startDate: Date = Date()
    var endDate: Date = Date()
    var album: String = ""
    
    
    init() {
        // First we need to fetch the data from the local storeage and insert into the vars:
        
        self.fetchData()
    }
    
    
    deinit {
        //Second we need to save the data in the local storeage:
        self.saveData()
        print("Im being deinited")
    }
    
    
    
    // MARK: - UserDefaults Section:
    //Fetch - Retrieve from NSUserDefaults(local):
    func fetchData() {
        self.byActivity = userDefaults.string(forKey: k.UserDefaultsSaveKeys.byActivitySaveKey) ?? self.byActivity
        self.postType = userDefaults.stringArray(forKey: k.UserDefaultsSaveKeys.postTypeSaveKey) ?? self.postType
        self.hashTags = userDefaults.stringArray(forKey: k.UserDefaultsSaveKeys.hashTagsSaveKey) ?? self.hashTags
        self.startDate = userDefaults.object(forKey: k.UserDefaultsSaveKeys.startDateSaveKey) as? Date ?? self.startDate
        self.endDate = userDefaults.object(forKey: k.UserDefaultsSaveKeys.endDateSaveKey) as? Date ?? self.endDate
        self.album = userDefaults.string(forKey: k.UserDefaultsSaveKeys.albumSaveKey) ?? self.album
        
        
        
        print("Checking \(userAlreadyExist(kUsernameKey: k.UserDefaultsSaveKeys.albumSaveKey) )")
    }
    
    
    //Save - Save to local(NSUserDefaults):
    func saveData() {
        userDefaults.set(self.byActivity, forKey: "SavedByActivity")
        userDefaults.set(self.postType, forKey: "SavedPostType")
        userDefaults.set(self.hashTags, forKey: "SavedHashTags")
        userDefaults.set(self.startDate, forKey: "SavedStartDate")
        userDefaults.set(self.endDate,forKey: "SavedEndDate")
        userDefaults.set(self.album,forKey: k.UserDefaultsSaveKeys.albumSaveKey)
    }
    
    
    
    //Testing function
    func userAlreadyExist(kUsernameKey: String) -> Bool {
        return userDefaults.object(forKey: kUsernameKey) != nil
    }
    
}

试试这个?

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    filterViewModel.fetchData()
}
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisppear(animated)
    filterViewModel.saveData()
}

我建议你检查是否已保存。

userDefaults.string (forKey: k.UserDefaultsSaveKeys.albumSaveKey) 

如果仍然存在,则检查密钥:

userDefaults.set (self.byActivity, forKey: "SavedByActivity")
userDefaults.set (self.postType, forKey: "SavedPostType")
userDefaults.set (self.hashTags, forKey: "SavedHashTags")
userDefaults.set (self.startDate, forKey: "SavedStartDate")
userDefaults.set (self.endDate, forKey: "SavedEndDate") 

您可以尝试在您的 FilterModel 中编写通用保存方法:

func saveValue(_ value: Any, forKey key: String) {
    UserDefaults.standard.set(value, forKey: key)
}

然后像这样在你的委托方法中调用它:

func didButtonToPressed() {
   //TODO: Present our custom calendar
   let calendarController = CalendarPickerViewController(
       baseDate: Date(),
       selectedDateChanged: { [weak self] date in
          guard let self = self else { return }
          self.filterModel.saveValue(date, forKey: "SavedEndDate")
          self.tableView.reloadRows(at: [IndexPath(row: 3, section: 0)], with: .fade)
        })
   self.present(calendarController, animated: true, completion: nil)
}

确保您用于保存值的密钥与您检索它的密钥相同。

在 deinit 中保存数据也不是一个好的做法,因为 deinit 有一些其他目的,当实例被释放以释放内存时调用。这不会在视图从屏幕上消失后立即发生。