Issue in comparing NSString inside NSMutable Array - objective-c

i write a method that loops through an array of buttons and checks if a string is equal to any of the buttons titles inside the array, but it doesn't work although the string passed to that method equals some strings inside the array, here's my code:
-(void)checkDuplicatesInSection:(NSString*)btnLabel
{
for (UIButton* btn in self.test) {
if([btnLabel isEqualToString:btn.titleLabel.text])
{
NSLog(#"Inside check Dublicates--->Title Existed");
} else {
NSLog(#"Inside check Dublicates--->Title Not Existed");
}
}
}
// self.test---> it's an array contains group of buttons
// btnLabel----> it's a string passed to that method
What I don't understand is why when I run the program, I get both Inside check Dublicates--->Title Existed and "Inside check Dublicates--->Title Not Existed.

The code:
if([btnLabel isEqualToString:btn.titleLabel.text])
{
NSLog(#"Inside check Dublicates--->Title Existed");
} else {
NSLog(#"Inside check Dublicates--->Title Not Existed");
}
will be executed multiple times because it is in a for loop. That's why you get both logs printed when you run your code.
To test whether self.test contains the string btn.titleLabel.text you should modify your code into:
-(void)checkDuplicatesInSection:(NSString*)btnLabel
{
BOOL found = NO;
for (UIButton* btn in self.test) {
if([btnLabel isEqualToString:btn.titleLabel.text]) {
found = YES;
break;
}
}
if (found) {
NSLog(#"Inside check Dublicates--->Title Existed");
} else {
NSLog(#"Inside check Dublicates--->Title Not Existed");
}
}
Or you can simply use the method -containsObject: *:
-(void)checkDuplicatesInSection:(NSString*)btnLabel
{
BOOL found = [self.test containsObject:btn.titleLabel.text];
if (found) {
NSLog(#"Inside check Dublicates--->Title Existed");
} else {
NSLog(#"Inside check Dublicates--->Title Not Existed");
}
}
* This will work even if btn.titleLabel.text is an NSString.

Related

Array Mutation whilst being Enumerated

so my issue is happening only when I have two enemies on a team in my game. If it's a one versus one.. I do not get the issue.
Please look at my code and see if you can gather as to why I'm getting this fatal error.
Removes Target from players target array
-(void)removeTarget:(PlayerClass *)target withSender:(PlayerClass *)sender {
if ([sender.targets containsObject:target]) {
[sender.targets removeObject:target];
}
}
Adds Target to players target array
-(void)addTarget:(PlayerClass *)target withSender:(PlayerClass *)sender {
//check if target already exists
if ([sender.targets count] > 0) {
for (PlayerClass *players in sender.targets) {
if ([players.name isEqualToString:target.name]) {
//Checked if exists, if target exists in list then move on.
goto outer;
}
}
}
[sender.targets addObject:target];
outer:;
}
In the Update to determine whether they're a target or not
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
for (PlayerClass *player in _players) {
....
if (player.health > 0) { //If player is alive!
....
//Checks if the player has targets, if so & visible - Engage.
if ([player.targets count] > 0) {
for (PlayerClass *enemy in player.targets) {
if (![player.team isEqualToString:enemy.team]) {
if (enemy.health > 0) {
if ([self lineOfSightBetweenPlayers:player andPlayer:enemy]) {
[self attackWithPlayer:player againstPlayer:enemy];
break;
} else {
[player removeTarget:enemy withSender:player];
}
} else {
[player removeTarget:enemy withSender:player];
}
} else {
[player removeTarget:enemy withSender:player];
}
}
}
}
Now from debugging I've gathered that the players don't add their team mates as targets. However, the player will gather more than one target if they can see more than one target on the opposing team. However, the issue I'm guessing lies in my technique to removing a target from the array? Can anyone check over and make sure I'm not delivering a school boy error here?
Thanks in advance.
Very simple fix. Wasn't thinking outside the box.. which tends to happen when code starts getting very large!
//Checks if the player has targets, if so & visible - Engage.
if ([player.targets count] > 0) {
for (PlayerClass *enemy in player.targets) {
if (![player.team isEqualToString:enemy.team]) {
if (enemy.health > 0) {
if ([self lineOfSightBetweenPlayers:player andPlayer:enemy]) {
[self attackWithPlayer:player againstPlayer:enemy];
break;
} else {
[player removeTarget:enemy withSender:player];
break;
}
} else {
[player removeTarget:enemy withSender:player];
break;
}
} else {
[player removeTarget:enemy withSender:player];
break;
}
}
}
Fixed my issue, I wasn't breaking out. Thus enumerating after removal.

What is a convenient way to check UITextFields for errors before allowing a form to submit?

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;
}

Dynamic NSOutlineView data source

So I have implemented a PXSourceList data source that pretty much is a duplicate of Apple's example of a NSOutlineView data source.
This is how it goes...
- (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item; {
if (item == nil) {
// item is nil so it's part of the very top hierarchy.
// return how many sections we need.
return 2;
}
else {
if ([item class] == [TSFileSystemItem class] ) {
return [item numberOfChildren];
// if item isn't nil and it's a TSFileSystemItem, then return it's children.
}
if ([item class] == [TSWorkspaceItem class]) {
return 2; // i don't know, random items.
}
else {
NSLog(#"this is a special object.");
}
}
}
- (BOOL)sourceList:(PXSourceList *)aSourceList isItemExpandable:(id)item {
if (item == nil) {
return YES;
}
else {
// if the number of children of the item is -1
BOOL gibberhook = ([item numberOfChildren] != -1);
return gibberhook;
}
}
-(id)sourceList:(PXSourceList *)aSourceList child:(NSUInteger)index ofItem:(id)item {
if (item == nil) {
return [TSFileSystemItem rootItem];
}
else {
return [(TSFileSystemItem *)item childAtIndex:index];
}
}
- (id)sourceList:(PXSourceList *)aSourceList objectValueForItem:(id)item {
if (item == nil) {
return #"/";
} else {
if (item == [TSFileSystemItem rootItem]) {
return PROJECT_FILES;
}
else {
return [item relativePath];
}
}
}
The mysterious TSFileSystemItem is from here: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/OutlineView/Articles/UsingOutlineDataSource.html.
All of this is OK, except I want to divide my source list to have multiple sections (root cells). One displaying a file hierarchy (check) and the other...
Well the other is going to contain a NSMutableArray that I add items to from the other section. Sounds complicated? Better explanation. Click an item from section with file hierarchy, and it is added to the other section.
I have tried to solve this mess with the help of Apple's docs, but I still can't find a simple, efficient, stable way of making 2 sections with the functions I mentioned above. If only it was as easy as configuring a data source for UITableView...
Can anybody kindly help me out?
When the childrenForItem delegate method is called and item in nil, this requests the root of the tree. If you return and array then the tree will have a root node for each element in that array.
- (NSArray *)childrenForItem:(id)item {
if (item == nil) {
return [self.rootTreeNode childNodes];
} else {
return [item childNodes];
}
}

How to break block loop in Objective - C?

I have these declarations in header file:
Note: I won't explain the whole code, I think it is easy to understand
typedef void (^loopCell)(id cell);
-(id)allCells:(loopCell)cell;
And allCells function implementation:
-(id)allCells:(loopCell)cell
{
for (AAFormSection *section in listSections)
{
for (id _cell in section.fields) {
cell(_cell);
}
}
return nil;
}
The usage of allCells function:
-(void)setFieldValue:(NSString *)value withID:(int)rowID
{
[self allCells:^(id cell) {
if([cell isKindOfClass:[AAFormField class]]) {
AAFormField *_cell = (AAFormField *)cell;
if(_cell.rowID == rowID) {
_cell.value = value;
//return; Here I want to terminate loop
}
}
}];
}
My problem is, I can't terminate allCells loop in the middle (actually when I found object I need in the loop, I don't want iterate through other objects)
How can I stop allCells loop in the middle?
Look at the docs for NSArray enumerateObjectsUsingBlock:. They setup the block signature to take a BOOL pointer. Set the stop BOOL to YES to cause the iteration to stop.
typedef void (^loopCell)(id cell, BOOL *stop);
-(id)allCells:(loopCell)cell {
BOOL stop = NO;
for (AAFormSection *section in listSections) {
for (id _cell in section.fields) {
cell(_cell, &stop);
if (stop) {
break;
}
}
if (stop) {
break;
}
}
return nil;
}
-(void)setFieldValue:(NSString *)value withID:(int)rowID {
[self allCells:^(id cell, BOOL *stop) {
if([cell isKindOfClass:[AAFormField class]]) {
AAFormField *_cell = (AAFormField *)cell;
if(_cell.rowID == rowID) {
_cell.value = value;
if (stop) {
*stop = YES;
}
}
}
}];
}
You can't break from setFieldValue, but you can from allCells.
It's up to the method you're using that calls the block — allCells in this case — to provide a mechanism for stopping the loop. Usually, it's a parameter to the block.
If allCells is yours and you don't mind modifying it, you modify the block signature to take a pointer to a BOOL, initialized to YES, and check if the block modified it to NO.
(Note: You can break from a for in loop.)

Anyway to make a (wrapping) NSTextField write a carriage return upon pressing return key?

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)
}
}