Ok, so I know I can just leave out the NSLog but why does it give me a "EXC_BAD_ACCESS" error?
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex2
{
if(buttonIndex2 == 0 && waitForAction == NO)
{
waitForAction = YES;
[self showAbortAlert];
NSLog(#"%#",buttonIndex2); //This one does not crash the app
} else if (buttonIndex2 == 1 && waitForAction == NO)
{
waitForAction = YES;
[self addObject];
NSLog(#"%#",buttonIndex2); //This one crashes the app
} //else if
}
see the method signature again
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex2
buttonIndex2 is of type NSInteger. If you do %# in NSLog your code is calling the description method on the object. But buttonIndex2 is not an object.
use NSLog(#"%d",buttonIndex2);
The first one (with buttonIndex == 0) does not crash the app because you are calling description on an object at memory address 0, which is basically the same as [nil description] and this is perfectly legal in Objective-C.
buttonIndex2 is an integer use :
NSLog(#"%d",buttonIndex2);
And go read some good book on C.
Use %d as it is an integer, %# is for strings...
NSLog(#"%d", buttonIndex);
Related
I've been at this all day and just can't get my head around it. On a form in my app, I'm using -[UITextFieldDelegate textFieldDidEndEditing]: to register any errors and store them in an NSMutableArray instance variable formErrors.
I intend to use the formErrors when my submit button is pressed, or perhaps to disable the button disabled while there are errors on the form. The problem is the error count goes all over the place. I've just ended up confusing myself as you can see my code where I'm incrementing and decrementing in order to try and control what's going on but just confusing myself more.
Error messages get put on formErrors like this:
-(void)textFieldDidEndEditing:(UITextField *)textField {
if ( textField == [self nameField] ) {
if ( ([[textField text] isEqualToString:#""]) ) {
[formErrors addObject:#"What is your name?"];
errorCount++;
} else {
errorCount--;
if ( ([[textField text] length] < 2) || ([[textField text] length] > 20) ) {
[formErrors addObject:#"Name must contain a minimum of 2 and a maximum of 20 characters only."];
errorCount++;
} else {
errorCount--;
if ([[textField text] rangeOfCharacterFromSet:alphaSet].location != NSNotFound) {
[formErrors addObject:#"Name must contain letters and spaces only."];
errorCount++;
}
}
}
}
if (textField == [self ageField]) {
if ( ([[textField text] isEqualToString:#""]) ) {
[formErrors addObject:#"How old are you?"];
errorCount++;
} else {
errorCount--;
if ( ([[textField text] intValue]) < 1 || ([[textField text] intValue] > 120) ) {
[formErrors addObject:#"Please enter an age using a number between 1 and 120."];
errorCount++;
} else {
errorCount--;
if ([[textField text] rangeOfCharacterFromSet:numericSet].location != NSNotFound) {
[formErrors addObject:#"Age must be given in numbers."];
errorCount++;
}
}
}
}
My instance var:
{
NSMutableArray *formErrors;
}
Then initialise it in viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
formErrors = [[NSMutableArray alloc] init];
Then in prepareForSegue: I have some temporary code to check things are working:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
int errCount = [formErrors count];
// check if all textfield values are filled in if not then disallow form submit
for (NSString *error in formErrors) {
NSLog(#"Total %d errors, \n Error Message: %#", errCount, error);
}
All I want to do is, as I enter and leave fields, check if there are any errors; if there are, just store the error message in formErrors, so I can do what I need to do in the prepareForSegue:. Is this even the right approach? I've tried doing this many different ways but just keep on going in circles.
The submit button is linked to my segue and also is an outlet so I can enabled and disable it as I please.
Help would be appreciated
Kind regards
Your approach is a bit redundant. Validate your fields upon submission, cancel submission if there are any errors:
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
NSMutableArray *errors = [NSMutableArray array];
if (self.nameTextField.text.length > 21) {
[errors addObject:#"Name cannot be longer than 21 symbols"];
}
else if (!self.nameTextField.text.length) {
[errors addObject:#"Please enter your name"];
}
if (!self.passwordTextField.text.length) {
[errors addObject:#"Please enter password"];
}
else if (!self.confirmPasswordTextField.text.length) {
[errors addObject:#"Please confirm your password"];
}
else if (self.passwordTextField.text.length < 6) {
[errors addObject:#"Password is too short, use at least 6 characters."];
}
else if (![self.passwordTextField.text isEqualToString:self.confirmPasswordTextField.text]) {
[errors addObject:#"Passwords do not match"];
}
if (!self.emailTextField.text.length) {
[errors addObject:#"Please enter your e-mail"];
}
if (!self.image) {
[errors addObject:#"Please choose a photo"];
}
if (errors.count) {
[[[UIAlertView alloc] initWithTitle:#"Error"
message:[errors componentsJoinedByString:#"\n"]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil, nil] show];
return NO;
}
return YES;
}
You're on the right path, but here are a couple of observations.
First I would get rid of all the errorCount++ and errorCount-- lines. They don't make sense, because you can always count the items in your error array.
Every time you check your input fields, clear your error array, otherwise you will be having errors that might have been already corrected.
Inside your -(void)textFieldDidEndEditing:(UITextField *)textField method, don't only check the textfield that has been edited, because you wold need to keep track of the errors that were before, the ones that were corrected, and the new ones... too much trouble.
I think it's best to create a new method that checks all the information and returns an array of errors. Call this routine from textFieldDidEndEditing.
- (NSArray *)checkInputs
{
NSMutableArray *errors = [NSMutableArray array];
// Loop through the textfields and fill your array
return (NSArray *)errors;
}
I have the following code:
NSString *content = [[NSUserDefaults standardUserDefaults] stringForKey:#"mykey"];
NSLog(#"string is %#",content);
if ([content stringIsEmpty]){
NSLog(#"empty string");
}else{
NSLog(#"string is not empty");
}
stringIsEmpty is class category on NSString:
- (BOOL ) stringIsEmpty {
if ((NSNull *) self == [NSNull null]) {
return YES;
}
if (self == nil) {
return YES;
} else if ([self length] == 0) {
return YES;
}
return NO;
}
The output is:
string is (null)
string is not empty
How could it be null and not empty at the same time?
What happens is that:
[content stringIsEmpty:YES]
will return false (NO), when content is nil. So your code will take the
NSLog(#"string is not empty");
branch. This would be better:
if (content && [content stringIsEmpty:YES]){
...
A better way of doing this would be reversing the semantics of the method:
if ([content stringIsNotEmpty]) {
this would work finely because when content is nil it would return NO, when it is not nil, it would execute your method.
EDIT:
In Objective-C, sending a message to nil is legal and by definition will evaluate to nil. Google for "objective c sending message to nil".
In another language (C++), your code would crash (actually undefined behaviour, but to make things simple).
I use a small function to test for emptiness. It works on more than just strings:
static inline BOOL isEmpty(id thing) {
return thing == nil
|| ([thing respondsToSelector:#selector(length)]
&& [(NSData *)thing length] == 0)
|| ([thing respondsToSelector:#selector(count)]
&& [(NSArray *)thing count] == 0);
}
I usually import it in my pch file - you can see it along with attribution: https://gist.github.com/325926
As #sergio has already pointed out - when your string is nil you can't send it messages that test it for nil-ness - as sending messages to nil will do nothing for void methods, and return nil where the method returns something.
also
you are calling your method with a parameter
if ([content stringIsEmpty:YES])
but your method declaration doesn't take one:
- (BOOL ) stringIsEmpty {
What's that all about?
You have to check for the 'content == nil' case outside of the method.
If you want to be able to call just one method, change the method to something that tests for a positive, such as "stringHasContent", returning YES if self.length > 0.
I am practicing some bool functions and I seem to be stuck any help will be appreciated. I must be making some little mistake.
-(BOOL) checkForWin
{
if ([[dictionary valueForKey:[cowsShuffled objectAtIndex:cowsCard]] intValue] == 2{
return YES;
}
}
-(void) moo
{
if (checkForWin == YES) {
NSLog (#"foo");
}
}
You need to call the method (not function), and you don't need to compare to YES. The if statement does that implicitly:
if ([self checkForWin]) …
Also note that checkForWin has a problem: it doesn't return anything if the if statement fails. It should be simply:
- (BOOL)checkForWin{
return [[dictionary valueForKey:[cowsShuffled objectAtIndex:cowsCard]] intValue] == 2;
}
Footnote: Strictly speaking, if (x) … isn't exactly the same as if (x == YES) …. It's actually closer to if (x != NO) …, but of course that's the same thing for most intents and purposes (and those for which it isn't are largely pathological).
Your method call is wrong. You call a method like this: [object method].
In your case [self checkForWin].
I want to use a wrapping text field that can potentially contain carriage returns in my app. Is there any way to force the NSTextField object to write a carriage return into the text area instead of sending its action to its target when the Return key is pressed?
This is covered in Technical Q&A QA1454, which also enumerates reasons why one would use NSTextField instead of NSTextView in this case.
You can implement the following method in the text field delegate:
- (BOOL)control:(NSControl*)control
textView:(NSTextView*)textView
doCommandBySelector:(SEL)commandSelector
{
BOOL result = NO;
if (commandSelector == #selector(insertNewline:))
{
// new line action:
// always insert a line-break character and don’t cause the receiver
// to end editing
[textView insertNewlineIgnoringFieldEditor:self];
result = YES;
}
return result;
}
Okay, I figured out one way to do it, but this very well may not be the best (or even a good) way. I subclassed NSTextField, and overrode -textShouldEndEditing: like so:
-(BOOL)textShouldEndEditing:(NSText *)textObject {
NSEvent * event = [[NSApplication sharedApplication] currentEvent];
if ([event type] == NSKeyDown && [event keyCode] == 36) {
[self setStringValue:[[self stringValue] stringByAppendingString:#"\n"]];
return NO;
}
else {
return [super textShouldEndEditing:textObject];
}
}
I found a combination of Sean and Bevarious worked best for me. Sean's answer assumes that the new line is always wanted to be added to the end (instead of for instance where the user's cursor is placed).
-(BOOL)textShouldEndEditing:(NSText *)textObject
{
NSEvent * event = [[NSApplication sharedApplication] currentEvent];
if ([event type] == NSKeyDown && [event keyCode] == 36)
{
[textObject insertNewlineIgnoringFieldEditor:nil];
return NO;
}
else
{
return [super textShouldEndEditing:textObject];
}
}
Swift version:
override func textShouldEndEditing(textObject: NSText) -> Bool {
let event = NSApplication.sharedApplication().currentEvent
if event?.type == NSEventType.KeyDown && event?.keyCode == 36 {
self.stringValue = self.stringValue.stringByAppendingString("\n")
return false
} else {
return super.textShouldEndEditing(textObject)
}
}
I hope this question is some what self explanatory.
This works, returns YES and NO: note the NSLog()'s
- (BOOL)dateTestCourse:(NSDictionary *)listing {
BOOL result = ([self exammpleTest] == 0) ? YES : NO;
if (result) {
NSLog(#"Passes Test");
return YES;
}
NSLog(#"Failed Test");
return NO;
}
But below always return YES? Only difference is no NSLog();
- (BOOL)dateTestCourse:(NSDictionary *)listing {
BOOL result = ([self exammpleTest] == 0) ? YES : NO;
if (result) {
// NSLog(#"Passes Test");
return YES;
}
// NSLog(#"Failed Test");
return NO;
}
Is this something to do with C? I have no idea? I might expect it always to return NO (if I shouldn't be breaking in the conditional), but surely that would return YES.
I know I should be returning result in the above examples, but I'm curious to know why.
These two blocks of code should be running the same. Are you perhaps doing a find/replace all on NSLog? That could be causing issues elsewhere, say with the exammpleTest(sic) method.
Also, the ternary operator on the second line is redundant, consider reducing that line to:
BOOL result = ([self exammpleTest] == 0);