我正在对用户选择的图像进行编码,然后保存到USerDefault。因此,我通过解码并将其转换为UIImage数组来检索这些图像,并在其中填充UITableView。我的目标是能够终止该应用程序,并且当我再次打开该应用程序时,仍然填充UITableView,我想保留用户数据。 一切正常,我得到了想要的东西,但是经过几次尝试,我遇到了以下错误:
由于未捕获的异常“ NSRangeException”而终止应用程序,原因: '-[__ NSArray0 objectAtIndex:]:索引0超出范围为空 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。
答案 0 :(得分:0)
我认为您遇到的问题与as有关! [String]
仅仅因为密钥存在并不意味着您不会得到零。
用作? [String] ?? []
因此,如果返回nil,则将有一个空数组。
答案 1 :(得分:0)
我有您可能需要的Objective-c代码版本,请尝试创建快速版本。
因此,要保存/写入图像数据到应用程序的文件夹中,您可以执行以下操作
//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
我在上面的代码中使用了一个辅助方法来简化此过程,并在各种类文件中使用它。
并读取图像数据,
NSData *pngData1 = [NSData dataWithContentsOfFile:[[AlertHelper sharedAlertManager] documentsPathForFileName:@"The same name you used to write for your image"]];
UIImage *image = [UIImage imageWithData:pngData1];
助手的方法是
// 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,这可能会有用。
答案 2 :(得分:-3)
将新值设置为UserDefault后,只需调用 UserDefaults.standard.synchronize(),它就会触发一个同步,以实际存储新值。