我在使用 USerDefault 保存数据时做错了什么
What am I doing wrong on using USerDefault to save data
我正在对用户选择的图像进行编码,然后保存到 USerDefault。因此,我通过解码并转换为 UIImages 数组来检索这些图像,并在其中填充我的 UITableView。我的目标是能够终止应用程序,当我再次打开时,UITableView 仍然存在,我想保留用户数据。
一切都很完美,我得到了我想要的,但在尝试了几次之后,我得到了以下错误:
Terminating app due to uncaught exception 'NSRangeException', reason:
' -[__NSArray0 objectAtIndex:]: index 0 beyond bounds for empty
NSArray'
什么是我做我想做的事的最佳方式?
这里是应该检索数据的地方:
class ViewController: UIViewController, CLLocationManagerDelegate, UITableViewDataSource, dataReceivedDelegate {
func dataReceived(nameSaved: NSArray) {
nameSaved1 = nameSaved
// imagemCell = fotoSaved
self.itensTableView.reloadData()
}
@IBOutlet weak var itensTableView: UITableView!
//Notification
let content = UNMutableNotificationContent()
//
var locationManager:CLLocationManager = CLLocationManager()
var currentLocation: CLLocation?
let region = CLBeaconRegion(proximityUUID: UUID(uuidString: "DD07D751-FBE1-430C-973A-281F9DA59A39")!, identifier: "Estimotes")
var arrayNomes = NSMutableArray()
var nomeReceived = ""
var qtd:Int = 0
var imagemCell = [NSData]()
var nameSaved1 = NSArray()
var dataSaved = [NSData]()
var imageSaved = [UIImage]()
override func viewDidLoad() {
super.viewDidLoad()
if isKeyPresentInUserDefaults(key: "namesSavedArray") == true{
nameSaved1 = UserDefaults.standard.array(forKey: "namesSavedArray") as! [String] as NSArray
itensTableView.reloadData()
}
if isKeyPresentInUserDefaults(key: "fotoSaved") == true{
// Load the image
imagemCell = UserDefaults.standard.object(forKey: "fotoSaved") as! [NSData]
for uiimage in imagemCell {
let imageConverted = UIImage(data: uiimage as Data)
imageSaved.append(imageConverted!)
itensTableView.reloadData()
}
}
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
//LocationManager
locationManager.delegate = self
if (CLLocationManager.authorizationStatus() != CLAuthorizationStatus.authorizedWhenInUse) {
locationManager.requestWhenInUseAuthorization()
}
locationManager.startRangingBeacons(in: region)
}
func isKeyPresentInUserDefaults(key: String) -> Bool {
return UserDefaults.standard.object(forKey: key) != nil
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addVc" {
let vc:adicionarNovoItemVc = segue.destination as! adicionarNovoItemVc
vc.delegate = self
}
}
func rangeBeacons(){
let uuid = UUID(uuidString: "DD07D751-FBE1-430C-973A-281F9DA59A39")
let major:Int = 1
let minor:Int = 1
let identifier = "Bruz IBeacon"
let region = CLBeaconRegion(proximityUUID: uuid!, major: CLBeaconMajorValue(major), minor: CLBeaconMinorValue(minor), identifier: identifier)
locationManager.startRangingBeacons(in: region)
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .restricted:
print("Location access was restricted.")
case .denied:
print("User denied access to location.")
case .notDetermined:
print("Location status not determined.")
case .authorizedAlways: fallthrough
case .authorizedWhenInUse:
print("Location status is OK.")
}
if status == .authorizedAlways{
rangeBeacons()
}
}
// Handle incoming location events.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location: CLLocation = locations.last!
print("Location: \(location)")
}
func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
guard let discoveredBeacon = beacons.first?.proximity else {
print("Beacon nao encontrado"); return}
switch discoveredBeacon {
case .immediate:
beaconMuitoProximo()
self.view.backgroundColor = .green
case .near: break
// beaconProximo()
// self.view.backgroundColor = .orange
case .far:
beaconLonge()
self.view.backgroundColor = .red
case .unknown:
self.view.backgroundColor = .black
}
}
// Handle location manager errors.
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
locationManager.stopUpdatingLocation()
print("Error: \(error)")
}
func beaconMuitoProximo(){
//Notificacoes de perda de objetos
// self.content.title = "Seguro"
// self.content.body = "Suas coisas estao perto de voce =)"
// self.content.sound = UNNotificationSound.default
// let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
// let request = UNNotificationRequest(identifier: "testIdentifierPerto", content: self.content, trigger: trigger)
// UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
//
}
func beaconProximo(){
}
func beaconLonge(){
// create a sound ID, in this case its the tweet sound.
let systemSoundID: SystemSoundID = 1016
// to play sound
AudioServicesPlaySystemSound (systemSoundID)
}
@IBAction func botaoAdicionar(_ sender: UIButton) {}
//TableView
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// let item = objetos[indexPath.row]
let cell = itensTableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! tableviewCell
cell.nameCell.text = nameSaved1[indexPath.row] as? String//Nil value
cell.imageViewCell.image = imageSaved[indexPath.row] //Nil value
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return imageSaved.count
}
}
这是我保存数据的地方:
protocol dataReceivedDelegate {
func dataReceived(nameSaved:NSArray)
}
class adicionarNovoItemVc: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITextFieldDelegate {
@IBOutlet weak var textFieldNome: UITextField!
@IBOutlet weak var namePreview: UILabel!
@IBOutlet weak var imagePreview: UIImageView!
let imagePicker = UIImagePickerController()
let picker = UIImagePickerController()
var arrayName = [String]()
var arrayFotoData = [NSData]()
var delegate:dataReceivedDelegate? = nil
override func viewDidLoad() {
super.viewDidLoad()
self.textFieldNome.delegate = self
if isKeyPresentInUserDefaults(key: "namesSavedArray") == true{
arrayName = UserDefaults.standard.array(forKey: "namesSavedArray") as! [String]
}
if isKeyPresentInUserDefaults(key: "fotoSaved") == true{
arrayFotoData = UserDefaults.standard.array(forKey: "fotoSaved") as! [NSData]
}
}
func isKeyPresentInUserDefaults(key: String) -> Bool {
return UserDefaults.standard.object(forKey: key) != nil
}
@IBAction func botaoAdcFoto(_ sender: UIButton) {
picker.allowsEditing = true
picker.delegate = self
picker.sourceType = .photoLibrary
if let mediaTypes = UIImagePickerController.availableMediaTypes(for: .photoLibrary) {
picker.mediaTypes = mediaTypes
}
present(picker, animated: true)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[.originalImage] as? UIImage {
self.imagePreview.image = image
self.namePreview.text = self.textFieldNome.text
//Encode Image
let dataSaved:NSData = image.pngData()! as NSData
arrayFotoData.append(dataSaved)
UserDefaults.standard.set(arrayFotoData, forKey: "fotoSaved")
}
self.picker.dismiss(animated: true, completion: nil)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.textFieldNome.resignFirstResponder()
return true
}
@IBAction func botaoAdcItem(_ sender: UIButton) {
if (self.namePreview!.text != nil) && (self.imagePreview!.image != nil) {
if delegate != nil {
arrayName.append(self.namePreview.text!)
delegate?.dataReceived(nameSaved: arrayName as NSArray)
UserDefaults.standard.set(arrayName, forKey: "namesSavedArray")
self.navigationController?.popViewController(animated: true)
}
}
else {return}
}
}
应用应该是这样工作的:
此屏幕是 UITableView 所在的位置,因此数据应该保留在这里:
这个屏幕是应该设置和保存数据的地方:
按下按钮后,应关闭屏幕并重新加载包含数据的 UITableView。
我认为您遇到的问题与as有关! [字符串]
仅仅因为密钥存在并不意味着你不会得到 nil。
用作? [细绳] ?? []
因此,如果返回 nil,您将得到一个空数组。
在为 UserDefault 设置新值后调用 UserDefaults.standard.synchronize(),它应该触发同步以实际存储新值。
我有您可能需要的 Objective-c 版本的代码,请尝试创建 swift 版本。
所以,要save/write图像数据到应用程序的文件夹,你可以这样做
//to write the image in folder
NSData *pngData = UIImagePNGRepresentation(image);
[pngData writeToFile:[[AlertHelper sharedAlertManager] documentsPathForFileName:@"The name for your image"] atomically:YES]; //Write the file
我在上面的代码中使用了一个辅助方法来简化这个过程,并在各种 class 文件中使用它。
并向读取图像数据,
NSData *pngData1 = [NSData dataWithContentsOfFile:[[AlertHelper sharedAlertManager] documentsPathForFileName:@"The same name you used to write for your image"]];
UIImage *image = [UIImage imageWithData:pngData1];
And the Helper methood is
// to write and read files in paths
- (NSString *)documentsPathForFileName:(NSString *)name
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSLog(@"%@",[documentsPath stringByAppendingPathComponent:name]); // to check if the name used for image.
return [documentsPath stringByAppendingPathComponent:name];
}
更新:-
对于我遇到的 Swift 版本 something 这可能有用。
我正在对用户选择的图像进行编码,然后保存到 USerDefault。因此,我通过解码并转换为 UIImages 数组来检索这些图像,并在其中填充我的 UITableView。我的目标是能够终止应用程序,当我再次打开时,UITableView 仍然存在,我想保留用户数据。 一切都很完美,我得到了我想要的,但在尝试了几次之后,我得到了以下错误:
Terminating app due to uncaught exception 'NSRangeException', reason: ' -[__NSArray0 objectAtIndex:]: index 0 beyond bounds for empty NSArray'
什么是我做我想做的事的最佳方式?
这里是应该检索数据的地方:
class ViewController: UIViewController, CLLocationManagerDelegate, UITableViewDataSource, dataReceivedDelegate {
func dataReceived(nameSaved: NSArray) {
nameSaved1 = nameSaved
// imagemCell = fotoSaved
self.itensTableView.reloadData()
}
@IBOutlet weak var itensTableView: UITableView!
//Notification
let content = UNMutableNotificationContent()
//
var locationManager:CLLocationManager = CLLocationManager()
var currentLocation: CLLocation?
let region = CLBeaconRegion(proximityUUID: UUID(uuidString: "DD07D751-FBE1-430C-973A-281F9DA59A39")!, identifier: "Estimotes")
var arrayNomes = NSMutableArray()
var nomeReceived = ""
var qtd:Int = 0
var imagemCell = [NSData]()
var nameSaved1 = NSArray()
var dataSaved = [NSData]()
var imageSaved = [UIImage]()
override func viewDidLoad() {
super.viewDidLoad()
if isKeyPresentInUserDefaults(key: "namesSavedArray") == true{
nameSaved1 = UserDefaults.standard.array(forKey: "namesSavedArray") as! [String] as NSArray
itensTableView.reloadData()
}
if isKeyPresentInUserDefaults(key: "fotoSaved") == true{
// Load the image
imagemCell = UserDefaults.standard.object(forKey: "fotoSaved") as! [NSData]
for uiimage in imagemCell {
let imageConverted = UIImage(data: uiimage as Data)
imageSaved.append(imageConverted!)
itensTableView.reloadData()
}
}
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
//LocationManager
locationManager.delegate = self
if (CLLocationManager.authorizationStatus() != CLAuthorizationStatus.authorizedWhenInUse) {
locationManager.requestWhenInUseAuthorization()
}
locationManager.startRangingBeacons(in: region)
}
func isKeyPresentInUserDefaults(key: String) -> Bool {
return UserDefaults.standard.object(forKey: key) != nil
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addVc" {
let vc:adicionarNovoItemVc = segue.destination as! adicionarNovoItemVc
vc.delegate = self
}
}
func rangeBeacons(){
let uuid = UUID(uuidString: "DD07D751-FBE1-430C-973A-281F9DA59A39")
let major:Int = 1
let minor:Int = 1
let identifier = "Bruz IBeacon"
let region = CLBeaconRegion(proximityUUID: uuid!, major: CLBeaconMajorValue(major), minor: CLBeaconMinorValue(minor), identifier: identifier)
locationManager.startRangingBeacons(in: region)
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .restricted:
print("Location access was restricted.")
case .denied:
print("User denied access to location.")
case .notDetermined:
print("Location status not determined.")
case .authorizedAlways: fallthrough
case .authorizedWhenInUse:
print("Location status is OK.")
}
if status == .authorizedAlways{
rangeBeacons()
}
}
// Handle incoming location events.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location: CLLocation = locations.last!
print("Location: \(location)")
}
func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
guard let discoveredBeacon = beacons.first?.proximity else {
print("Beacon nao encontrado"); return}
switch discoveredBeacon {
case .immediate:
beaconMuitoProximo()
self.view.backgroundColor = .green
case .near: break
// beaconProximo()
// self.view.backgroundColor = .orange
case .far:
beaconLonge()
self.view.backgroundColor = .red
case .unknown:
self.view.backgroundColor = .black
}
}
// Handle location manager errors.
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
locationManager.stopUpdatingLocation()
print("Error: \(error)")
}
func beaconMuitoProximo(){
//Notificacoes de perda de objetos
// self.content.title = "Seguro"
// self.content.body = "Suas coisas estao perto de voce =)"
// self.content.sound = UNNotificationSound.default
// let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
// let request = UNNotificationRequest(identifier: "testIdentifierPerto", content: self.content, trigger: trigger)
// UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
//
}
func beaconProximo(){
}
func beaconLonge(){
// create a sound ID, in this case its the tweet sound.
let systemSoundID: SystemSoundID = 1016
// to play sound
AudioServicesPlaySystemSound (systemSoundID)
}
@IBAction func botaoAdicionar(_ sender: UIButton) {}
//TableView
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// let item = objetos[indexPath.row]
let cell = itensTableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! tableviewCell
cell.nameCell.text = nameSaved1[indexPath.row] as? String//Nil value
cell.imageViewCell.image = imageSaved[indexPath.row] //Nil value
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return imageSaved.count
}
}
这是我保存数据的地方:
protocol dataReceivedDelegate {
func dataReceived(nameSaved:NSArray)
}
class adicionarNovoItemVc: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITextFieldDelegate {
@IBOutlet weak var textFieldNome: UITextField!
@IBOutlet weak var namePreview: UILabel!
@IBOutlet weak var imagePreview: UIImageView!
let imagePicker = UIImagePickerController()
let picker = UIImagePickerController()
var arrayName = [String]()
var arrayFotoData = [NSData]()
var delegate:dataReceivedDelegate? = nil
override func viewDidLoad() {
super.viewDidLoad()
self.textFieldNome.delegate = self
if isKeyPresentInUserDefaults(key: "namesSavedArray") == true{
arrayName = UserDefaults.standard.array(forKey: "namesSavedArray") as! [String]
}
if isKeyPresentInUserDefaults(key: "fotoSaved") == true{
arrayFotoData = UserDefaults.standard.array(forKey: "fotoSaved") as! [NSData]
}
}
func isKeyPresentInUserDefaults(key: String) -> Bool {
return UserDefaults.standard.object(forKey: key) != nil
}
@IBAction func botaoAdcFoto(_ sender: UIButton) {
picker.allowsEditing = true
picker.delegate = self
picker.sourceType = .photoLibrary
if let mediaTypes = UIImagePickerController.availableMediaTypes(for: .photoLibrary) {
picker.mediaTypes = mediaTypes
}
present(picker, animated: true)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[.originalImage] as? UIImage {
self.imagePreview.image = image
self.namePreview.text = self.textFieldNome.text
//Encode Image
let dataSaved:NSData = image.pngData()! as NSData
arrayFotoData.append(dataSaved)
UserDefaults.standard.set(arrayFotoData, forKey: "fotoSaved")
}
self.picker.dismiss(animated: true, completion: nil)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.textFieldNome.resignFirstResponder()
return true
}
@IBAction func botaoAdcItem(_ sender: UIButton) {
if (self.namePreview!.text != nil) && (self.imagePreview!.image != nil) {
if delegate != nil {
arrayName.append(self.namePreview.text!)
delegate?.dataReceived(nameSaved: arrayName as NSArray)
UserDefaults.standard.set(arrayName, forKey: "namesSavedArray")
self.navigationController?.popViewController(animated: true)
}
}
else {return}
}
}
应用应该是这样工作的: 此屏幕是 UITableView 所在的位置,因此数据应该保留在这里:
这个屏幕是应该设置和保存数据的地方: 按下按钮后,应关闭屏幕并重新加载包含数据的 UITableView。
我认为您遇到的问题与as有关! [字符串]
仅仅因为密钥存在并不意味着你不会得到 nil。
用作? [细绳] ?? []
因此,如果返回 nil,您将得到一个空数组。
在为 UserDefault 设置新值后调用 UserDefaults.standard.synchronize(),它应该触发同步以实际存储新值。
我有您可能需要的 Objective-c 版本的代码,请尝试创建 swift 版本。
所以,要save/write图像数据到应用程序的文件夹,你可以这样做
//to write the image in folder
NSData *pngData = UIImagePNGRepresentation(image);
[pngData writeToFile:[[AlertHelper sharedAlertManager] documentsPathForFileName:@"The name for your image"] atomically:YES]; //Write the file
我在上面的代码中使用了一个辅助方法来简化这个过程,并在各种 class 文件中使用它。
并向读取图像数据,
NSData *pngData1 = [NSData dataWithContentsOfFile:[[AlertHelper sharedAlertManager] documentsPathForFileName:@"The same name you used to write for your image"]];
UIImage *image = [UIImage imageWithData:pngData1];
And the Helper methood is
// to write and read files in paths
- (NSString *)documentsPathForFileName:(NSString *)name
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSLog(@"%@",[documentsPath stringByAppendingPathComponent:name]); // to check if the name used for image.
return [documentsPath stringByAppendingPathComponent:name];
}
更新:-
对于我遇到的 Swift 版本 something 这可能有用。