简体   繁体   English

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

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

I'm trying to save my data after being changed by the user, the thing im saving the data from my model also im fetching the data from my model class, I would like to save the data once the viewwilldissappear so all the data will be saved and I can fetch the data from my model(when I init it again), I kinda lost the way with implementing it:\ Here's my VC Code: where i set up my tableView cells + would like to use saveData() there:我正在尝试在用户更改后保存我的数据,我正在从我的 model 中保存数据,我也正在从我的 model 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)
    }
}

Here's my Model:这是我的 Model:

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
    }
    
}

Try this?尝试这个?

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

I would advise you to check if is saved.我建议您检查是否已保存。

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

if it remains, then check the keys:如果仍然存在,请检查密钥:

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") 

You can try something like writing a general save method in your FilterModel:您可以尝试在 FilterModel 中编写通用保存方法:

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

Then call it in your delegate methodes like that:然后像这样在您的委托方法中调用它:

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)
}

Make sure that the key you are using to save the values are the same as the key you are retrieving it.确保您用于保存值的密钥与您检索它的密钥相同。

Also saving data in deinit is not a good practice, as deinit have some other purpose, is called when an instance is deallocated to free memory.在 deinit 中保存数据也不是一个好习惯,因为 deinit 有其他用途,当实例被释放以释放 memory 时会调用它。 And this is not happening immediately after a view is disappeared from the screen.这不会在视图从屏幕上消失后立即发生。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM