loop Nsmutable arrray of uiimages from end to beginning - objective-c

I have an NSMUtable array of images where a different image is displayed with a previous and a next button, but when I get to the end of the array the simulator crashes. I want to loop the end of the array to the beginning so that when I get to the end of the array of images when I hit the next button again it loops back to the first image, also when Im at the first image if I hit the previous button it loops to the last image with no crashes

What you want is a circular array, which is easy to implement using a standard NSMutableArray. For instance, say you store your images in an array called imageArray and use a simple variable to keep track of the index of your current image, like:
int currentImageIndex = 0;
...then you might implement nextImage and previousImage like:
- (UIImage*) nextImage {
currentImageIndex = (currentImageIndex + 1) % [imageArray count];
return [imageArray objectAtIndex:currentImageIndex];
}
- (UIImage*) previousImage {
currentImageIndex--;
if (currentImageIndex < 0) {
currentImageIndex = [imageArray count] - 1;
}
return [imageArray objectAtIndex:currentImageIndex];
}
Then just use nextImage and previousImage whenever you want to step through the array, and problem solved.

Simple enough to do. all you need to do is create a check to see if you're on the last element, and if so, set your tracker (like count or i etc) to 0 again,
Heres the psudo code
//set index
if ( array [ index ] == len(array) - 1) //at end
{
index = 0
}
if(array [index] == -1)//at beginning
{
index = len(array) -1
}
// do something with array[index]

I want to propose this solution:
A property for the selected UIImage, a NSInteger property to hold the current index.
-(IBAction) nextButtonTapped:(id)sender
{
self.currentIndex = self.currentIndex++ % [self.images count];
self.selectedImage= [self.images objectAtIndex:self.currentIndex];
[self reloadImageView];
}
-(IBAction) previousButtonTapped:(id)sender
{
self.currentIndex--;
if (self.currentIndex < 0)
self.currentIndex += [self.images count];
self.selectedImage= [self.images objectAtIndex:self.currentIndex];
[self reloadImageView];
}
-(void)reloadImageView
{
//do, what is necessary to display new image. Animation?
}

Related

For loop in Sprite Kit seems to be emptying an array?

I'm just starting to wrap my brain around Sprite Kit and I am encountering a very strange error when attempting to change the property of a node in a for loop im using.
I have two SKSpriteNode objects, one is the child of a SKScene (BLATheBugs) and the other is a child of the first (BLAEmptySpaces). I have a grid laid out with BLAEmptySpaces, and BLATheBugs on top of those empty spaces which are supposed to take UITouch, and move to an empty space if its bool isOccpupied property == False. When the scene is set up, the SKScene triggers a method in TheBugs:
-(void) spawnEmptySpacesInitialize
{
[self addChild:[self spawnEmptySpaces]];
}
which in turn triggers:
-(BLAEmptySpaces *) spawnEmptySpaces
{
emptySpace = [[BLAEmptySpaces alloc] init];
emptySpace.numberOfEmptySpacesNeeded = 12;
[emptySpace spawnEmptySpaces];
[emptySpace positionTheEmptySpaces];
return emptySpace;
}
which finally triggers a method in the EmptySpaces object:
-(BLAEmptySpaces *) spawnEmptySpaces
{
_emptySpacesArray = [NSMutableArray new];
for (int x = 0; x < _numberOfEmptySpacesNeeded; x++)
{
_anEmptySpace = [[BLAEmptySpaces alloc] initWithImageNamed:#"BlueLight.png"];
_anEmptySpace.zPosition = 50;
[_emptySpacesArray addObject:_anEmptySpace];
[self addChild: _anEmptySpace];
}
return self;
}
everything seems fine, (except for needing the additional "addChild" in the EmptySpaces object to get them to be drawn on the screen which i have also been trying to fix) but when i call the method to move TheBugs:
-(void) moveLeftOneSpace
{
NSLog(#"%d", emptySpace.emptySpacesArray.count);
for (emptySpace in emptySpace.emptySpacesArray)
{
NSLog(#"cycle");
if (emptySpace.isOccupied == NO)
{
for (_yellowBug in yellowBugArray)
{
if (_positionOfFingerTouchX > _yellowBug.position.x - variableOne && _positionOfFingerTouchX < _yellowBug.position.x + variableTwo && _positionOfFingerTouchY > _yellowBug.position.y - variableOne && _positionOfFingerTouchY < _yellowBug.position.y + variableTwo && emptySpace.position.x == _yellowBug.position.x - 80 && emptySpace.position.y == _yellowBug.position.y)
{
_yellowBug.position = CGPointMake(_yellowBug.position.x - spaceBetweenBugs, _yellowBug.position.y);
emptySpace.isOccupied = YES;
NSLog(#"execute");
}
}
}
}
}
It at first tells me there are 12 objects in the array and runs the operation. if I try to move any piece again, it tells me there are now NO objects in the array (yellowBugArray). It is also probably worth noting that it will not let me access emptySpace.anEmptySpace. Throws me an error.
Sorry for the long post, but hopefully somewhere in here is the cause of my problem.
Thank you very much guys!

Getting two images to appear in random sequence iOS

I am new to the community, so let me know if my question is unclear. I am trying to make a choice reaction exercise on the iPAD. There are two images that should appear in random sequence on the left and right of the screen, and the user will respond by tapping a button that corresponds to the position of the appeared image. Here's the problem, I tried to get the two images to appear at random order using the following way:
- (void) viewDidAppear:(BOOL)animated
{
for(int n = 1; n <= 20; n = n + 1)
{
int r = arc4random() % 2;
NSLog(#"%i", r);
if(r==1)
{
[self greenCircleAppear:nil finished:nil context: nil];
}
else
{
[self redCircleAppear:nil finished:nil context: nil];
}
}
}
However, 20 random numbers get generated while only 1 set of animation is run. Is there a way to let the animation finish running in each loop before the next loop begins? Any help is appreciated, thanks in advance!
When you say "only one set of animation is run" I'm assuming that means greenCircleAppear and redCircleAppear begin the sequence of images appearing and the user pressing a button. If that's the case, I'd recommend not using a for loop in viewDidAppear but instead have viewDidAppear initialize the current state and call a method that presents the next animation. When the animation is finished, have it call the method that presents the next animation. Something along these lines:
Add this to the interface:
#interface ViewController ()
#property NSInteger currentIteration;
#end
This is in the implementation:
- (void)viewDidAppear:(BOOL)animated {
self.currentIteration = 0;
[self showNextAnimation];
}
- (void)greenCircleAppear:(id)arg1 finished:(id)arg2 context:(id)arg3 {
//perform animation
NSLog(#"green");
[self showNextAnimation];
}
- (void)redCircleAppear:(id)arg1 finished:(id)arg2 context:(id)arg3 {
//perform animation
NSLog(#"red");
[self showNextAnimation];
}
- (void)showNextAnimation {
self.currentIteration = self.currentIteration + 1;
if (self.currentIteration <= 20) { //you should replace '20' with a constant
int r = arc4random() % 2;
NSLog(#"%i", r);
if(r==1)
{
[self greenCircleAppear:nil finished:nil context: nil];
}
else
{
[self redCircleAppear:nil finished:nil context: nil];
}
}
else {
//do what needs to be done after the last animation
}
}

fast enumeration for removing item in NSMutableArray crash

i have a strange issue , if i remove my item at forin enumeration , it would crash , so like this:
for (Obstacle *obstacleToTrack in _obstaclesToAnimate) {
//this if else not so important for happening crash
if(obstacleToTrack.distance > 0){
obstacleToTrack.distance -= _playerSpeed * _elapsed;
}else{
if (obstacleToTrack.watchOut) {
obstacleToTrack.watchOut = NO;
}
obstacleToTrack.x -= (_playerSpeed + obstacleToTrack.speed) * _elapsed;
}
if (obstacleToTrack.x < -obstacleToTrack.width || _gameState == GS_OVER) {
[self removeChild:obstacleToTrack];
//this line makes crash happen , if remove this line code work fine
[_obstaclesToAnimate removeObject:obstacleToTrack];
}
}
if i change my code to
NSMutableArray *forRemoving = [[NSMutableArray alloc]init];
for (Obstacle *obstacleToTrack in _obstaclesToAnimate) {
//this if else not so important for happening crash
if(obstacleToTrack.distance > 0){
obstacleToTrack.distance -= _playerSpeed * _elapsed;
}else{
if (obstacleToTrack.watchOut) {
obstacleToTrack.watchOut = NO;
}
obstacleToTrack.x -= (_playerSpeed + obstacleToTrack.speed) * _elapsed;
}
if (obstacleToTrack.x < -obstacleToTrack.width || _gameState == GS_OVER) {
// code change here
[self removeChild:obstacleToTrack];
[forRemoving addObject:obstacleToTrack];
}
}
for(Obstacle *obstacleToTrack in forRemoving){
[_obstaclesToAnimate removeObject:obstacleToTrack];
[forRemoving removeObject:obstacleToTrack];
}
[forRemoving release];
this would work perfect , could someone tell me why?
The answer is that if you remove an object the other objects in that array move postion in the array since an item is removed.
For example we have an array with 4 items, if we remove the first item (item 0) the item that used to be at index 1 is now at index 0 and the item at 2 is now at 1.
Thus the enumeration breaks.
You could solve this by looping thru the array from the count down to 0:
for (int i = [array count]-1; i >= 0; i--) {
id object = [array objectAtIndex:i];
if (some check) {
[array removeObjectAtIndex:i];
}
}
Like rckoenes said, you break the enumaration by removing stuff in the array while iterating through it.
What you can do is to have a second array where you insert the objects that you want to remove. Then, after your enumeration is finished you can remove all the objects that are found in the second array, from your first array.
You must not modify a collection while iterating through its items.
If you iterate index based (i.e. the classical for loop), you can remove things, but be careful about adjusting your index.
Check this link
https://developer.apple.com/library/mac/documentation/cocoa/conceptual/Collections/Articles/Enumerators.html
Enumeration is “safe”—the enumerator has a mutation guard so that if
you attempt to modify the collection during enumeration, an exception
is raised.

Label display not instant with iPhone app

I am developing an application for the iPhone. The question I have is how to display a new label with a different text every .5 seconds. For example, it would display Blue, Red, Green, Orange and Purple; one right after one another. Right now I am doing this:
results = aDictionary;
NSArray *myKeys = [results allKeys];
NSArray *sortedKeys = [myKey sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
int keyCount = [sortedKeys count];
while (flag == NO) {
NSTimeInterval timeMS = [startDate timeIntervalSinceNow] * -10000.0;
if (timeMS >= i) {
ii++;
i += 1000;
NSLog(#"endDate = %f", timeMS);
int randomNumber = rand() % keyCount + 1;
lblResult.text = [results valueForKey:[sortedKeys objectAtIndex:(randomNumber - 1)]];
result = [results valueForKey:[sortedKeys objectAtIndex:(randomNumber - 1)]];
lblResult.text = result;
}
if (ii > 25) {
flag = YES;
}
}
lblResult.text = [results valueForKey:[sortedKeys objectAtIndex:(sortedKeys.count - 1)]];
this function is called at the viewDidAppear Function and currently isn't displaying the new labels. It only displays the one at the end. Am I doing anything wrong? What would be the best method to approach this?
The problem is that you're not giving the run loop a chance to run (and therefore, drawing to happen). You'll want to use an NSTimer that fires periodically and sets the next text (you could remember in an instance variable where you currently are).
Or use something like this (assuming that items is an NSArray holding your strings):
- (void)updateText:(NSNumber *)num
{
NSUInteger index = [num unsignedInteger];
[label setText:[items objectAtIndex:index]];
index++;
// to loop, add
// if (index == [items count]) { index = 0; }
if (index < [items count]) {
[self performSelector:#selector(updateText:) withObject:[NSNumber numberWithUnsignedInteger:index] afterDelay:0.5];
}
}
At the beginning (e.g. in viewDidAppear:), you could then call
[self updateText:[NSNumber numberWithUnsignedInteger:0]];
to trigger the initial update.
You'd of course need to ensure that the performs are not continuing when your view disappears, you could do this by canceling the performSelector, or if you're using a timer, by simply invalidating it, or using a boolean, or ...
And if you want to get really fancy, use GCD :)

IKImageBrowserView Moving items doesn't animate

I have an IKImageBrowser setup which appears to be working well. I have set it up to allow reordering and also set animation to YES (in my awakeFromNib), however whenever I select and try and reorder the images I get strange behaviour:
1) They don't reorder most of the time
2) If they do they don't animate
3) Sometimes the images land on each other, if I scroll away and back they are back where they started.
If I highlight an image and delete it, it animates and disappears as expected...
Is this a problem with Core Animation? Do I need to add a core animation layer to the object in interface builder? I followed this tutorial from Apple to get these results: http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Conceptual/ImageKitProgrammingGuide/ImageBrowser/ImageBrowser.html#//apple_ref/doc/uid/TP40004907-CH5-SW1
Here's the code in question:
- (BOOL) imageBrowser:(IKImageBrowserView *) aBrowser moveItemsAtIndexes: (NSIndexSet *)indexes toIndex:(NSUInteger)destinationIndex{
int index;
NSMutableArray *temporaryArray;
temporaryArray = [[[NSMutableArray alloc] init] autorelease];
for(index=[indexes lastIndex]; index != NSNotFound;
index = [indexes indexLessThanIndex:index])
{
if (index < destinationIndex)
destinationIndex --;
id obj = [mImages objectAtIndex:index];
[temporaryArray addObject:obj];
[mImages removeObjectAtIndex:index];
}
// Insert at the new destination
int n = [temporaryArray count];
for(index=0; index < n; index++){
[mImages insertObject:[temporaryArray objectAtIndex:index]
atIndex:destinationIndex];
}
return YES;
}
Interestingly, this line throws a warning
for(index=[indexes lastIndex]; index != NSNotFound;
comparison is always true due to
limited range of data type
As mentioned above by NSGod, changing int index to NSUInteger index solved the problem