NSUInteger oddities with for loops [duplicate] - ios

This question already has answers here:
NSUInteger in reversed loop confusion?
(2 answers)
Closed 6 years ago.
I use AppCode to tweak the code I've written in XCode. AppCode does awesome code inspections, and tells you where things can be improved.
One of the frequent inspections I come across points out that [SomeObjCFrameworkClass objectAtIndex] is expecting an NSUInteger which is in fact true...
- (ObjectType)objectAtIndex:(NSUInteger)index
however, I keep finding myself getting screwed by trying to follow this advice and change my ints to NSUIntegers.
For example, here's one piece of code that exploded when I did this change...
-(void)removeBadge
{
if ([[theButton subviews] count]>0)
{
NSUInteger initalValue = [[theButton subviews] count]-1;
//Get reference to the subview, and if it's a badge, remove it from it's parent (the button)
for (NSUInteger i=initalValue; i>=0; i--) {
if ([[[theButton subviews] objectAtIndex:i] isMemberOfClass:[MKNumberBadgeView class]])
{
[[[theButton subviews] objectAtIndex:i] removeFromSuperview];
[theButton setTitleColor:[UIColor lightTextColor] forState:UIControlStateNormal];
}
}
}
}
Any idea why this is happening.
There is a clue in the debug data pictured below, but I can't make sense of it.

NSUInteger in unsigned, so i>=0 condition in your for loop always evaluates to YES. After i reach 0, on next iteration you will get integer underflow, and i becomes NSUIntegerMax.
Updated: As far as I can tell from your code, there is no reason in processing subviews in reverse order. So, you can simply do
for (NSUInteger i=0; i<theButton.subviews.count; i++)
Otherwise, you can use something like
if (0 == i) {
break;
}
inside your loop or use do/while for example.

Related

How to find number of subviews?

I have a Xcode project and in it i have dragged two views and both of them inherit from a class LabelsView. However when I try and run the code to find out number of subviews, I get 4. Can anyone explain why is this happening.
The code is
NSLog(#"no. of subviews:%#",[NSString stringWithFormat:#"%d",[self.superview.subviews count]]);
You're probably getting a weird subview count because you're accessing self.superview.subviews. You likely just want self.subviews.
If, like you said, you only care about subviews of type LabelsView, you can filter those out like this:
int labelViewCount = 0;
for(LabelsView *subview in self.subviews) {
if([subview isKindOfClass:[LabelsView class]]) {
labelViewCount++;
}
}
NSLOG(#"label count: %d", labelViewCount);
If you want the amount of all subviews in swift, you can just go with
self.subviews.count

Keeping track of objects on the screen

Even though I’m writing in Objective C, most of my code is still written in a procedural style. However, now I want to do something where that approach will not work. So I need some advice on how to deal with an indeterminate number of objects on the screen at the same time. I’m sure that this problem has been solved, I just haven’t been able to find out how.
I have a bunch of games where I put two or four pictures on the screen and then the user interacts with the picture. When they are done with a page they swipe to the next one and I use a transition to slide the pictures off the screen. I can control the movement of the pictures because when they were created I name them self.picture_1 and self.picture_2. The movement method knows about them even though that method didn’t create them.
Now suppose I want to have an indeterminate number of pictures on the screen. I can’t call them self.picture_1. through self.picture_n because ObjectiveC won’t let you dynamically create variable names. But I still need to move them in a method where they weren’t created.
I can make it work with two techniques, neither of which seem ideal. First, I look at all the objects on the screen and then do something with the ones that I want to target. Note: pictures are in buttons.
for ( id subview in self.parentView.subviews ) {
if ( [subview isKindOfClass:[UIButton class]] ) {
UIButton *pictureButton = subview;
for (NSUInteger i=0; i<self.totalItems; i++) {
NSUInteger row = (i % 2) + 1;
NSUInteger column = (i/2) + 1;
NSString *pictureTitle = [NSString stringWithFormat:#"pictureR%iC%i", row, column];
if ( [pictureButton.titleLabel.text isEqualToString:pictureTitle] ) [pictureButton removeFromSuperview];
}
}
}
This works for removing them from the view, but gets cumbersome when I try to make the pictures slide off the screen.
The second way is to make an array that holds the picture objects when they are created. I’ve been playing with something like this.
self.gridImages = [NSMutableArray arrayWithCapacity:4];
for (NSUInteger i = 0; i < itemsOnScreen; i++) {
Word *word = [wordListArray objectAtIndex:i];
self.gridImages[i] = word.image;
}
And then to do things with the pictures I loop through the array.
for (NSUInteger i = 0; i < itemsOnScreen; i++) {
Picture *picture = self.gridImages[i];
// do something with picture
}
Neither of these methods seems ‘right’ so I’m wondering if there is a preferred method for manipulating an indeterminate number of objects on the screen?
#Hot Licks, that works, so I put you answer into an answer.
I have it working for checkBoxes. They are created by the main view controller and it passes in a tag. I'm using tags starting at 1000 for checkboxes, 2000 for pictures, etc. Just before I put the checkBox on the screen, I assign it the tag.
[self.checkBox setTag:checkBoxTag];
You could also use: self.checkbox.tag = checkBoxTag;
When it is time to remove the checkBoxes, I loop through all of the tags starting at 1000 up to the total number of items on the screen. I have warnings turned up to 11 so I need to cast the counter to an NSInteger.
- (void)removeCheckboxes {
for (NSUInteger i = 0; i < self.totalItems; i++) {
NSInteger tagNumber = 1000 + (NSInteger)i;
[ [self.parentView viewWithTag:tagNumber] removeFromSuperview];
}
}

ios next and previous buttons

I have a next button that I want to disappear when I get to the last result of an array and I want my previous button to disappear when I am at the first result of the array. I have figured out how to make them disappear when only one result is found.
Contacts is the array.
next Button:
//button look
if (contacts != nil) {
if ([contacts count] > 1 ) [self.view addSubview:nextButton];
Previous button:
//button look
if (contacts != nil) {
if ([contacts count] > 1 ) [self.view addSubview:previousButton];
I have tried this
if ([contacts count] < (index-1)) [self.view addSubview:nextButton];
I get this error "Ordered comparison between pointer and integer ('NSUInterger'(aka 'unsigned int') and 'char * (*)(const char *,int)')" The button is there it just doesn't disappear when it gets to the last result.
Any help would be greatly appreciated.
It sounds like index is a NSUInterger. count is a int and NSUInteger is typedef to unsigned integer. try changing index where ever your setting it to a int or typecasting it to a int where your using it for comparison.
For the part about the button, if your logic is right and you are trying to remove "nextButton" from view you can either hide or remove it from the view like so:
// To remove button
if ([contacts count] < (index-1)) [nextButton removeFromSuperview];
or
//To hide button
if ([contacts count] < (index-1)) nextButton.hidden = YES;
You never remove it from the superView, so it will never go away. You should use the hidden property of the button i.e.
[nextButton setHidden:NO];
or YES if you want to hide it.
The error is probably from the index entity, did you typecast that as a NSUInterger? change that to a uint or an int. (just posted a bit late, but this has been mentioned in other answer :D)
if ([contacts objectAtIndex:[contacts count]-1])
{
[self.view addSubview:nextButton];
}
if([contacts objectAtIndex:0)
{
[self.view addSubview:previousButton];
}
Hope this helps...nil just tells it there are no more elements to be included in the array. U can't use it to check..

hittin UIImageViews with another UIImageView

I have 10 UIImageViews on the screen. They are all in an array called posArray. I also have another UIImageView that is dragged by user and can hit those other 10. What is the easies way of displaying a simple NSlog message if the one object hits any of the other 10?
Right now i'm using touchesBegin, touchesMoved to move my one object and this array below to test if the one objects hits any of the other ten.
I'm just thinking that there is an easier, less memory spending way, way of doing this for some reason.
for (int i = 0; i < [posArray count]; i++) {
UIImageView *tempPos;
tempPos = [posArray objectAtIndex:i];
if (CGRectIntersectsRect(red1.frame, tempPos.frame)) {
red1.center = CGPointMake(tempPos.center.x, tempPos.center.y);
NSLog(#"position piece touched");
}
}
You can also use fast enumeration to get some more speed. Also you can add a break statement after you found one match (if you just need one match):
for (UIImageView * tempPos in posArray){
if (CGRectIntersectsRect(red1.frame, tempPos.frame)) {
red1.center = CGPointMake(tempPos.center.x, tempPos.center.y);
NSLog(#"position piece touched");
break;
}
}

Prevent choosing of certain objects in NSMutableArray?

I am kind of in a pickle here since I have tried many things to accomplish this and have failed.
What I want to do: I have a NSString in my app which is the answer to a question to a round in a quiz. My answers are stored in a NSMutableArray. These answers are chosen at random. I have 4 buttons in my app that are options (possible answers) for the round. I don't want to have multiple answers!
What I have tried:
I have tried deleting the answer after storing it in one of the buttons so that it couldn't be chosen again but it ended up causing a crash since I was trying to delete a object in the array while using fast enumeration (for loop).
I have tried just detecting if the button title was equal to to the answer after setting the correct answer to a specific button but that didn't work for some odd reason (no crash). There will still multiple buttons with the same answer
What I need help with: How should I stop the answer from being in multiple buttons so that the quiz isn't showing the obvious answer?
What should I do instead?
Thanks!
Edit1: So I pretty much made a NSArray of my four UIButtons.
I put the answer into a random UIButton like this:
NSInteger chosen = (arc4random() % 4);
UIButton *randButton = (UIButton *)[buttonArray objectAtIndex:chosen];
[randButton setTitle:imageName forState:UIControlStateNormal];
Then I title the other buttons like so, my buttons that do not have the answer have a title of nothing so I do this:
- (void)titleButtons {
for (UIButton *buttons in buttonArray) {
if ([[buttons titleForState:UIControlStateNormal] == nil]) {
UIButton *button = buttons;
NSString *superheroString = (NSString*)[superheroArray objectAtIndex:(arc4random() % [superheroArray count])];
[button setTitle:superheroString forState:UIControlStateNormal];
[self checkTitles:button];
}
}
Then the checkTitle method looks like this, this is the method where I try to make sure where 2 buttons do not have the same answer as the imageName (answer):
- (void)checkTitles:(UIButton*)button {
if ([[button titleForState:UIControlStateNormal] isEqualToString:imageName]) {
//Duplicate Answer so re-title button
NSString *newTitle = [superheroArray objectAtIndex:(arc4random() % [superheroArray count])];
[button setTitle:newTitle forState:UIControlStateNormal];
//Call same method again to see if new title is still same answer as pic to avoid same answers
[self checkTitles:button];
}
}
}
If you have an NSArray with all your answers, and you want 1 correct answer and 3 different wrong answers, you can do the following:
Decide in which button do you want the right answer. (randomize it)
Get a random answer from your array and store the index of that answer in a temporary array.
Get another random answer and make sure you are not picking an answer with the same index as the one you have in your temp array, (and again, store the new index in the temporary array)
Don't compare strings using ==. This compares pointer addresses. Use isEqualToString: instead:
if (stringA == stringB)
Usually won't work (will for some constant strings)
if ([stringA isEqualToString:stringB])
Will always work.

Resources