Picker saves random data to NSUserDefaults - ios

I've been working on a new tips & tricks project for a game called Dofus. In the first view the user is asked to pick their class out of a picker. Then they go to a next view and pick their level and stuff like that. The problem with the picker however is that its getting the wrong class out of the picker.
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return [self.classNames count];
}
- (NSString *)pickerView:(UIPickerView *)pickerView
titleForRow:(NSInteger)row
forComponent:(NSInteger)component {
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *_myClass = self.classNames[row];
[prefs setObject:_myClass forKey:#"class"];
return self.classNames[row];
}
In the ViewDidLoad i declared my classes:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"background"]];
self.classNames = #[#"Cra", #"Ecaflip", #"Eliotrope", #"Eniripsa",
#"Enutrof", #"Feca", #"Foggernout", #"Iop", #"Masqueraider", #"Osamodas", #"Pandawa", #"Rogue", #"Sacrier", #"Sadida", #"Sram", #"Xelor"];
}
Then in the next view I get the information like this (different .m file):
- (void)viewDidLoad {
[super viewDidLoad];
// Do view setup here.
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"background"]];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
// getting an NSString
NSString *myClass = [prefs stringForKey:#"class"];
_textLabel.text = [NSString stringWithFormat:#"So you are a %# huh? Pretty cool! Its a good class. Fill out the rest so we are ready to go!", myClass];
}
Anyone any idea's how this is happening? Thanks in advance!

You are setting your preferences on the Picker's datasource call for the row title, instead of the delegate call for when the row is selected. For every row that is loaded into the view, pickerView:titleForRow:forComponent: is called, at which point you set the class preference, however, this does not correspond to the row that is selected in the picker, it corresponds to the next row to be displayed.
Instead, respond to the
pickerView:didSelectRow:inComponent: method, and set your preference there (you will still need to respond to the other one to set the row titles, just don't set the preference).
- (NSString *)pickerView:(UIPickerView *)pickerView
didSelectRow:(NSInteger) row
inComponent:(NSInteger) component {
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *_myClass = self.classNames[row];
[prefs setObject:_myClass forKey:#"class"];
}

If you want store the selected row by the user you should this method from UIPickerViewDelegate
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component;
Something like that would work:
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *_myClass = self.classNames[row];
[prefs setObject:_myClass forKey:#"class"];
[prefs synchronize];
}
}
Take a look at these links to better understand how works UIPickerView :
http://nscookbook.com/2013/01/ios-programming-recipe-7-using-the-uipickerview/
http://www.techotopia.com/index.php/An_iOS_7_UIPickerView_Example
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIPickerView_Class/index.html#//apple_ref/doc/uid/TP40006842-CH3-SW9

Related

How to set alpha of static UITableViewCell on startup?

I'm making an application, which has a Settings page. For the Settings page, I am using a UITableViewController with static UITableViewCells. I am using NSUserDefaults to store the settings.
Now, this is the Settings View Controller :
According to the code in the viewDidLoad function in the SettingsTableViewController.m, there is supposed to be a check on the saved setting, and depending on that, the alpha property of the Notification Interval cell is supposed to change. This works fine if I do it in the tableView:didSelectRowAtIndexPath method, however, it is not working in the viewDidLoad method. Here's the source :
#import "SettingsTableViewController.h"
#interface SettingsTableViewController ()
#end
#implementation SettingsTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// Check if setting is already saved.
if ([defaults stringForKey:#"notification"]) {
self.notificationTableViewCell.detailTextLabel.text = [defaults stringForKey:#"notification"];
// Disable the interval cell, if the notifications are turned off.
if([[defaults stringForKey:#"notification"] isEqualToString:#"Off"]) {
self.intervalTableViewCell.alpha = 0.439216f;
self.intervalTableViewCell.userInteractionEnabled = NO;
}
} else {
// If no setting is saved, reset to default settings.
self.notificationTableViewCell.detailTextLabel.text = #"On";
[defaults setObject:#"On" forKey:#"notification"];
[defaults synchronize];
}
// Check if setting is already saved.
if([defaults stringForKey:#"interval"]) {
self.intervalTableViewCell.detailTextLabel.text = [defaults stringForKey:#"interval"];
} else {
// If no setting is saved, reset to default settings.
self.intervalTableViewCell.detailTextLabel.text = #"5 min";
[defaults setObject:#"5 min" forKey:#"interval"];
[defaults synchronize];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Get selected cell.
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
// Reset the state of the cell.
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
if(cell == self.notificationTableViewCell) {
if([cell.detailTextLabel.text isEqual: #"On"]) {
cell.detailTextLabel.text = #"Off";
[[NSUserDefaults standardUserDefaults] setObject:#"Off" forKey:#"notification"];
self.intervalTableViewCell.alpha = 0.439216f;
self.intervalTableViewCell.userInteractionEnabled = NO;
} else {
cell.detailTextLabel.text = #"On";
[[NSUserDefaults standardUserDefaults] setObject:#"On" forKey:#"notification"];
self.intervalTableViewCell.alpha = 1.0f;
self.intervalTableViewCell.userInteractionEnabled = YES;
}
} else if(cell == self.intervalTableViewCell) {
NSArray *possibleTimes = #[#"1 min", #"5 min", #"10 min", #"30 min"];
int indexOfCurrent = 0;
for (int i = 0; i < possibleTimes.count; i++) {
if([self.intervalTableViewCell.detailTextLabel.text isEqualToString:possibleTimes[i]]) {
indexOfCurrent = i;
}
}
// Return to the starting value of the array.
if(++indexOfCurrent == possibleTimes.count) {
indexOfCurrent = 0;
}
self.intervalTableViewCell.detailTextLabel.text = possibleTimes[indexOfCurrent];
[[NSUserDefaults standardUserDefaults] setObject:possibleTimes[indexOfCurrent] forKey:#"interval"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
- (BOOL)prefersStatusBarHidden {
return YES;
}
#end
First of as NSString comparation method you should use this:
isEqualToString:
instead of
isEqualToString:
Second thing is the way you find if selected cell is the one you want to check is not the best idea, I mean this:
if(cell == self.notificationTableViewCell)
Don't do that - you will find yourself in a huge troubles pretty quickly.
Proper way of handling that is to use enumerations, and switch case based on indexPath section, row values. Otherwise you keep a reference to a memory where cell is, but this cell is gonna be reused, by queuing mechanism of UITableView, for a different row - so you're gonna end up keeping some random reference to random cells at the end here.
For the alpha problem I think you should reload table data after you synchronize NSUserDefaults inside didSelectRowAtIndexPath method.
Use reloadData method from UITableView, or even better reload only the section you know that's gonna change on click (optimized version, but you can start with reloading whole table).
Move the code that changes the alpha property of the cell into a method that occurs later in the view lifecycle. A good one to try would be viewWillAppear:.

UIPickerView and empty core data array

I have a viewcontroller showing items from a core data entity. I also have a tableview listing records from the same entity. The table is editable, and the user could remove all the records. When this happens, the view controller holding the pickerview bombs because it's looking for records in an empty array. How to prevent this? I'm assuming I need to do something different at objectAtIndex:row...
# pragma mark PickerView Section
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1; // returns the number of columns to display.
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return [profiles count]; // returns the number of rows
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
// Display the profiles we've fetched on the picker
Profiles *prof = [profiles objectAtIndex:row];
return prof.profilename;
}
//If the user chooses from the pickerview
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
selectedProfile = [[profiles objectAtIndex:row]valueForKey:#"profilename"];
}
EDIT**
Ok, I found what was causing the problem...on the PickerView I was setting a default..
self.profiles = [[self.managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
selectedProfile = [[profiles objectAtIndex:0]valueForKey:#"profilename"];
[pickerView reloadAllComponents];
When I take that out, it works like a champ.

Getting row without selection from UIPickerView

I have a pickerview that loads from a core data fetch on the view controller did load method. Everything works great, however I'm finding that I'm passing null values quite often, and that I'm having to spin the picker back and forth and click several times to get the value to hold.
I also have a problem if the user doesn't manually pick something, I need it to pass the value that the picker is on. I have a datepicker on the same viewcontroller and it passes whatever date it is sitting on without any problems. I'm probably overlooking something..any help would be appreciated
# pragma mark PickerView Section
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1; // returns the number of columns to display.
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return [profiles count]; // returns the number of rows
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
// Display the profiles we've fetched on the picker
Profiles *prof = [profiles objectAtIndex:row];
return prof.profilename;
}
//If the user chooses from the pickerview
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
selectedProfile = [[profiles objectAtIndex:row]valueForKey:#"profilename"];
}
# pragma mark Segue Section
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"resultsSegue"]) {
ResultsVC *result = segue.destinationViewController;
result.profileName = [NSString stringWithFormat:#"%#",selectedProfile];
NSDate *selectedDate = [datePicker date];
result.trialDate = selectedDate;
}
}
You need to initialize selectedProfile to the 1st value in the picker. Do this in viewDidLoad. This way if the user never explicitly picks a value, the selectedProfile will be set to the 1st value by default.

UIPickerView selectRow won't select last row during initialization

I have found similar questions here and here. I have tried the answers to these and other questions found on stackoverflow and Apple's developer forums, with no luck.
I created a fresh project to test this, and it works fine in that new project. I am iteratively removing differences between my new test project and the one exhibiting this issue. Any pointers would be most helpful.
I have a UIPickerView connected to my view controller. I want to initialize the UIPickerView to the last value selected by the user. However if this row is the last row in the picker, it shows the second-to-last row selected in the UI instead.
Meanwhile, logging shows the correct row index selected in the UIPickerView object instance.
Selecting any row besides the last row in the picker works fine.
I have tried calling the selectRow:inComponent:animated method from viewDidLoad, viewWillAppear, and viewDidAppear - none of these make any difference. I have also tried calling reloadAllComponents in various orders relative to row selection and data initialization - again, no difference.
Some code:
- (void)viewDidLoad
{
[super viewDidLoad];
NSParameterAssert(self.pickerView);
// Set pickerView data
self.pickerOptions = #[#"zero", #"one", #"two", #"three", #"four"];
// Picker View config
self.pickerView.dataSource = self;
self.pickerView.delegate = self;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSInteger selectedIndex = ([self.pickerOptions count] -1);
NSLog(#"About to select row: %d", selectedIndex);
[self.pickerView selectRow:selectedIndex inComponent:0 animated:NO];
NSLog(#"Selected value is %d", [self.pickerView selectedRowInComponent:0]);
}
#pragma mark UIPickerViewDataSource
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
NSInteger numRows = [self.pickerOptions count];
NSLog(#"numRows: %d", numRows);
return numRows;
}
#pragma mark UIPickerViewDelegate
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
NSString *rowTitle = [self.pickerOptions objectAtIndex:(NSUInteger)row];
return rowTitle;
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
NSString *value = [self.pickerOptions objectAtIndex:(NSUInteger)row];
NSLog(#"User selected value: %#", value);
NSLog(#"index: %d", row);
}
Log output:
-[CCSelectorViewController viewDidAppear:] About to select row index: 4
-[CCSelectorViewController pickerView:numberOfRowsInComponent:] numRows: 5
-[CCSelectorViewController viewDidAppear:] Selected row index is 4
Here is the screenshot of the wrong row being selected:
http://imgur.com/FVvu9RA
This is using iOS6, XCode 4.6.1.
Any ideas?

How do I call the delegate of the pickerview when viewDidAppear?

I need the last selected row in my pickerview to be totally recalled when the view appears! I have this code in my viewDidAppear it animates to the last selected row but it doesn't really call the delegate and the NSLog in my rows won't print without touching my picker and reselect them.
How do I do that?
- (void)viewDidAppear:(BOOL)animated {
NSUserDefaults *pickerViewSelectionDefaults = [NSUserDefaults standardUserDefaults];
[tasbeehPicker selectRow:[pickerViewSelectionDefaults integerForKey:#"picker"]
inComponent:0 animated:YES];
[pickerViewSelectionDefaults synchronize];
[UIPicker reloadAllComponents];
NSLog(#"Last selcted row was %d ",[[NSUserDefaults standardUserDefaults] integerForKey:#"picker"]);
}
The delegate is not supposed to be called when you update the picker through code. The delegates are only called when the user interacts with the picker. If you need the same code called in both cases then do something like this:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[myPicker selectRow:4 inComponent:0 animated:animated];
[self handlePickerSelection:myPicker];
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
[self handledPickerSelection:pickerView];
}
- (void)handlePickerSelection:(UIPickerView *)pickerView {
// process selection
}
You can get selected row from below method,
Add this code in viewDidAppear,
int index = [pickerViewObject selectedRowInComponent:0];
[pickerViewObject selectRow:index inComponent:0 animated:YES];
selectedRowInComponent will give you selected row number, and then you can use selectRow:Row_number inComponent:Component_number animated:YES to select any row, picker delegate method will not get called.
If you want to save selected row in NSUserDefaults than you can do in in viewWillDisappear,
int index = [pickerViewObject selectedRowInComponent:0];
now save this index value in NSUserDefaults.

Resources