IBAction button crashes when using it the second time without changing view - ios

So I'm building a notes app saving the notes to a NSUSerDefaults after the click of a button. Well when I load that view and add the note and click on the button to save it the first time, it works properly. However if I don't leave the view and add a new message and try to click the button to save it, my app crashes. which I really don't understand why. The xcode version I'm using right now doesn't give me a lot of details on my errors, but I do see this
0x00015a9b <+1175> xor %eax,%eax
Program received signal 'SIGABRT'
This is the code of the IBAction that is triggered after the button click (sent event)
-(IBAction)saveNote{
NSUserDefaults *prefs=[NSUserDefaults standardUserDefaults];
//If empty add text to synthesized array and then add the array to the USer defaults
if([[prefs stringArrayForKey:#"Subjects"] count]==0){
NSLog(#"Went through here");
[notesSubject addObject:writeNote.text];
[prefs setObject:notesSubject forKey:#"Subjects"];
NSLog(#"The length of the array is %d",[notesSubject count]);
UIAlertView *alert=[[UIAlertView alloc]initWithTitle:#"Note Saved" message:#"Your note was saved" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
//[notesView.tableView reloadData];
}
//If not empty, get the array stored in the user defaults and set it to a temp array, add text to that temp array and then place the temp array back to the user defaults.
else{
newSubjects=[[NSMutableArray alloc]init];
beforeSubjects=[[NSArray alloc]init];
newSubjects= [prefs stringArrayForKey:#"Subjects"];
[newSubjects addObject:writeNote.text];
[prefs setObject:newSubjects forKey:#"Subjects"];
UIAlertView *alert=[[UIAlertView alloc]initWithTitle:#"Note Saved" message:#"Your note was saved" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
//[notesView.tableView reloadData];
}
}
Yeah, I don't really understand why the app crashes on second click.

You're overwriting the mutable newSubjects instance with an immutable instance with this line:
newSubjects= [prefs stringArrayForKey:#"Subjects"];
Instead, it should be this:
newSubjects = [[prefs stringArrayForKey:#"Subjects"] mutableCopy];

Related

Obtain an array with notifications

I am trying to put notifications in an array as they become available, but the count of the array is reset to 1 when I push a new notification.
This is the code:
int r = 0;
listMsgReceived = [[NSMutableArray alloc] init];
if (notification)
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Notification received" message:[NSString stringWithFormat:#"%#", message] delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertView show];
[listMsgReceived insertObject:message atIndex:r];
r++;
NSLog(#"apres: %d \n", [listMsgReceived count]);
}
It looks like you are initializing the variables r and listMsgReceived each time your notification is received (though it's hard to tell from the context you provided).
You should not do that, because that gets you a new array each time, where you insert one object - hence the count will be one after each notification.
You could try moving your array initialization outside of your method; declare it as a property on your class and initialize it in the initializer.

How to set UITextField default value to user's previous input?

My iOS app starts off with a pop-up message (UIAlertView) where you have to enter text in a UITextField. I would like the default value of the UITextField to be equal to the user's previous input.
Example: at 1pm, the user types "Happy" and clicks OK. When he re-opens the app at 2pm, the Text Field already has "Happy" as default value, he just needs to click OK. At 3pm, he opens the app, the default value is "Happy" but he changes it to "Angry", and then clicks OK. At 4pm, "Angry" is the default value when he opens the app.
- (void)viewDidLoad
{
[super viewDidLoad];
//Pop-up TextField
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:#"Hello" message:#"How are you feeling ?" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
alert.alertViewStyle = UIAlertViewStylePlainTextInput;
UITextField * machineTextFieldInit = [alert textFieldAtIndex:0];
machineTextFieldInit.placeholder = #"Mood";
[alert show];
}
Many thanks in advance for your help and advice !
It appears what you're looking for is a method to persist a user's data across multiple app sessions. This can be done a number of different ways:
Core Data
User Defaults
Plist File
External Server
Since the code you've supplied looks rather simple, perhaps using the NSUserDefaults will be the way to go.
To store a value in NSUserDefaults use this bit of code when the user presses the button:
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
UITextField * machineTextField = [alertView textFieldAtIndex:0];
NSString * input = machineTextField.text;
if (input != nil)
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:input forKey:#"my.app.domain.machineTextField"];
[defaults synchronize];
}
}
And when you present the dialog, load the value from the defaults.
- (void)viewDidLoad
{
[super viewDidLoad];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString * previous = [defaults objectForKey:#"my.app.domain.machineTextField"];
//Pop-up TextField
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:#"Hello" message:#"How are you feeling ?" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
alert.alertViewStyle = UIAlertViewStylePlainTextInput;
UITextField * machineTextFieldInit = [alert textFieldAtIndex:0];
machineTextFieldInit.placeholder = previous ? previous : #"Mood";
[alert show];
}
The simplest answer I can think of is the store the previous value in a class property, then when the user clicks cancel, store the value into their property
#property NSString *previousValue;
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == [alertView cancelButtonIndex]){
previousText = machineTextFieldInit.text;
}
}
Then on the init of your alertview test field set
machineTextFiledInit.text = previousValue;
Haven't tested and there may be an easier way :)
Also don't forget to set previousValue to a default value for when the user clicks the first time.
Edit: If you want it to store it passed the close of the app
-(void)encodeRestorableStateWithCoder:(NSCoder *)coder
{
[coder encodeObject:previousValue forKey:#"previousVal"];
}
-(void)decodeRestorableStateWithCoder:(NSCoder *)coder
{
previousValue = [coder decodeObjectForKey#"previousVal"];
}

Placeholders for variables in UIAlertView?

I want a UIAlertView to warn the user if there are no items matching his/her chosen search criteria. My initial idea was to use this code:
if (aOiCount == 0)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"No instances of %#",self.thisSpec.activityOfInterest message:#"Please select an activity or Cancel" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
The idea being to slip an actual activity name into the title, like in an NSLog string.
Unfortunately, this doesn't work. Compiler tells me Expected ":"
Is it possible to use a variable like this, and if so, how?
Thanks!
call this line
#"No instances of %#",self.thisSpec.activityOfInterest
in one NSString
NSString *alertstr=[NSString stringWithFormat:#"No instances of %#",self.thisSpec.activityOfInterest];
after that call your UIAlertView and then rearrange the word delegate:nil into delegate:self
if (aOiCount == 0)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:alertStr message:#"Please select an activity or Cancel" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
I don't think this is valid Objective C syntax:
initWithTitle:#"No instances of %#",self.thisSpec.activityOfInterest
You need to wrap it in an NSString, or a self-contained form.
Consider one of the many NSString methods, such as stringWithFormat or construct one in a different way. Either way, you should pass a complete string here.

How To Save UILabel Information

I have a calculator that multiplies a value with a certain $ rate. The user can edit the $ rate, and the value is multiplied with the user-entered rate. Here is my code for this:
- (IBAction)edit
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Edit New Amount"
message:#"Enter new rate"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Ok", nil];
alert.alertViewStyle = UIAlertViewStylePlainTextInput;
[[alert textFieldAtIndex:0] setKeyboardType:UIKeyboardTypeNumberPad];
[alert show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex != alertView.cancelButtonIndex) {
UITextField *field = [alertView textFieldAtIndex:0];
field.placeholder = #"Enter New Rate";
NSCharacterSet * set = [[NSCharacterSet characterSetWithCharactersInString:#"0123456789."] invertedSet];
if ([field.text rangeOfCharacterFromSet:set].location != NSNotFound)
{
UIAlertView *errorAlert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Only numbers are allowed in this field."delegate:self cancelButtonTitle:#"OK."otherButtonTitles:nil];
[errorAlert show];
rate.text=#"";
}
else
{
rate.text = field.text;
}
}
else
{
//Cancel
}
}
However, when the app closes and is restarted, the old rate is displayed, not the new user-entered rate. How can I save the new user-entered rate, so that it is retained within the app, even when it is restarted?
If you want the value to be retained after app is closed, then you got to store it somewhere in file system. Easiest and convenient way is to use NSUserDefaults:
//Storing in user defaults
[[NSUserDefaults standardUserDefaults] setObject:field.text forKey:RATE_KEY];
[[NSUserDefaults standardUserDefaults] synchronize];
//Retrieving (possibly in app start or viewdidload)
self.rate.text = [[NSUserDefaults standardUserDefaults] objectForKey:RATE_KEY];
One note on usage of NSUserDefaults is that it doesn't support storing every object type, it supports NSString,NSArray,NSNumber etc and primitive types. Since you used NSStrings in your code I assumed you can save it as string to defaults.
Have you tried creating a new variable to hold the new input as the user enters the new input, and then setting whatever the new input is to that new variable? It sounds like it is restarting from the last variable that was set in the input.

UITableView didSelectRowAtIndexPath error - when selecting item again

Loaded a list of items on to the UITableview and was able to click and show an alert for the row selected . But however after saying "ok" on the alert and i reclick on the already selected row my code breaks down saying "Thread 1:Program received signal:EXC_BAD_ACCESS".please take a look at the code below.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *playerselected = [exercises objectAtIndex:indexPath.row];
NSString *video = [playerselected valueForKey:#"video"];
NSString *msg = [[NSString alloc] initWithFormat:#"You have selected %#", video];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Player selected"
message:msg
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
[video release];
[msg release];
}
PLease suggest me what could be the issue here.
Don't release video.
When you retrieve a value from an NSDictionary you don't own it unless you explicitly retain it.
To be more specific, when you retrieve your string it is still owned by the dictionary. When you release it, you are releasing an object that you do not own, resulting in it being over-released. As a result it is deallocated, and when you next try to access it the memory is no longer valid and your app crashes.

Resources