问题
I made a first try to use NSUserDefaults in an App with UIPickerViews. But the App is crashing while testing / the data isn't displayed in the PickerView. I still try to fix it myself but some help would be great.
ViewController3: Where the user should save data to the NSUserDefault. Therefore there is an textField to write down something and a "Savebutton"
class ViewController3: UIViewController, UITextFieldDelegate {
@IBOutlet weak var textField: UITextField!
...
@IBAction func tappedAddButton(sender: AnyObject) {
var userDefaults:NSUserDefaults = NSUserDefaults.standardUserDefaults()
var exercisesList:NSMutableArray? = userDefaults.objectForKey("exercisesList") as? NSMutableArray
var dataSet:NSMutableDictionary = NSMutableDictionary()
dataSet.setObject(textField.text, forKey: "exercises")
if ((exercisesList) != nil){
var newMutableList:NSMutableArray = NSMutableArray();
for dict:AnyObject in exercisesList!{
newMutableList.addObject(dict as NSDictionary)
}
userDefaults.removeObjectForKey("exercisesList")
newMutableList.addObject(dataSet)
userDefaults.setObject(newMutableList, forKey: "exercisesList")
}else{
userDefaults.removeObjectForKey("exercisesList")
exercisesList = NSMutableArray()
exercisesList!.addObject(dataSet)
userDefaults.setObject(exercisesList, forKey: "exercisesList")
}
userDefaults.synchronize()
self.view.endEditing(true)
textField.text = ""
}
ViewController 1: Where the saved data should be loaded from NSUserDefaults and displayed in pickerView1
class ViewController: UIViewController,UIPickerViewDelegate {
@IBOutlet weak var pickerView1: UIPickerView!
@IBOutlet weak var pickerView2: UIPickerView!
@IBOutlet weak var pickerView3: UIPickerView!
var reps = [["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25"],["x"]]
var weight = [["0","1","2","3","4","5"],["0","1","2","3","4","5","6","7","8","9"],["0","1","2","3","4","5","6","7","8","9"],[","],["0","25","5","75"],["kg","lbs"]]
var exercises:NSMutableArray = NSMutableArray();
override func viewDidLoad() {
super.viewDidLoad()
var userDefaults:NSUserDefaults = NSUserDefaults.standardUserDefaults()
var exercisesListFromUserDefaults:NSMutableArray? = userDefaults.objectForKey("exercisesList") as? NSMutableArray
if ((exercisesListFromUserDefaults) != nil){
exercises = exercisesListFromUserDefaults!
}
self.pickerView1.reloadAllComponents()
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
switch pickerView {
case pickerView1:
return 1
case pickerView2:
return reps.count
case pickerView3:
return weight.count
default:
assertionFailure("Unknown pickerView")
}
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
switch pickerView {
case pickerView1:
return exercises.count
case pickerView2:
return reps[component].count
case pickerView3:
return weight[component].count
default:
assertionFailure("Unknown pickerView")
}
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
switch pickerView {
case pickerView1:
return exercises[row] as String
case pickerView2:
return reps[component][row]
case pickerView3:
return weight[component][row]
default:
assertionFailure("Unknown pickerView")
}
}
}
回答1:
The reason that your pickerViews are empty is most likely because you have not wired them up to viewController1 as dataSource and delegate. You can do this in the storyboard or in code:
override func viewDidLoad() {
super.viewDidLoad()
let pickers = [pickerView1,pickerView2,pickerView3]
for i in 0...2 {
let pickerView : UIPickerView = pickers[i]
pickerView.dataSource = self
pickerView.delegate = self
}
}
update (part 3)
exercisesList is an array of dictionaries. Here for example you are adding a new item to exercisesList:
newMutableList.addObject(dataSet)
where dataSet is created/defined as follows:
var dataSet:NSMutableDictionary = NSMutableDictionary()
dataSet.setObject(textField.text, forKey: "exercises")
so you have a single-item dictionary, with the key "exercises".
Then in viewController1 you are unpacking the array and assuming it is populated with strings, not dictionaries:
return exercises[row] as String
Here you are casting a dictionary to a string, which is crashing out.
The clue is in the stack trace line swift dynamic cast failed
and in these lines
(ObjectiveC.UIPickerView, titleForRow : Swift.Int, forComponent:
Swift.Int) => Swift.ImplicitlyUnwrappedOptional<Swift.String>
It will work if you correctly extract the string as a value from the dictionary key "exercises":
return exercises[row]["exercises"] as String
Alternatively you could populate the array with strings instead of dictionaries (as you only have one entry per entry).
Hopefully you can run this without crashing now!
回答2:
First, make sure datasource
& delegate
are set for your pickerViews. You can set them in the storyboard by selecting the pickerView and dragging from dataSource and delegate to your viewController:
Or you can set them in viewDidLoad
like this:
self.pickerView1.dataSource = self
self.pickerView2.delegate = self
Update
You are crashing in the titleForRow
method. I would guess your exercises array doesn't have any data when that method fires. Override viewDidLoad
and move your code from viewDidAppear
to that method instead. You need to have the data in your exercises array before the picker tries to display it.
Another thing I noticed in your viewDidAppear
method, the first thing you must do in view overrides is call to super like super.viewDidAppear(animated)
before your own code. Likewise, you must first call super.viewDidLoad()
in viewDidLoad
.
Update 2
In your switch
statements, make pickerView3
the default case and get rid of those assertionFailure
lines. The pickerView delegate methods are expecting return values, so this is probably throwing things off as well.
来源:https://stackoverflow.com/questions/28175743/uipickerview-wont-display-the-data-loaded-from-nsuserdefaults-app-is-crashing