Changing the random image - objective-c

With the help of a few threads here I created a slideshow that starts as soon as my view loads
However I am trying to make a slight modification where, I want the images to be random Not showing in the order in the array or in any particular order. This is my codde
-(void)viewDidAppear:(BOOL)animated
{
NSArray *myImages=[NSArray arrayWithObjects:
[UIImage imageNamed:#"Pitt Bull"],
[UIImage imageNamed:#"German Sherpard"],
[UIImage imageNamed:#"Pincer"],
nil];
int indexed=arc4random()%[myImages count];
UIImage *image=[myImages objectAtIndex:indexed];
NSArray *imageAr=[NSArray arrayWithObject:image];
[NSTimer scheduledTimerWithTimeInterval:12.0
target:self
selector:#selector(viewDidAppear:)
userInfo:Nil
repeats:YES];
[self.kenView animageWithImages:imageAr
transitionDuration:12
loop:YES
isLandscape:YES];
}
However it only works once. it loads a random picture at the start, but after that it keeps loading the same image. I can see that is because indexed is only assigned an integer when the view "did appear" . I was wondering if there was another way too select a new random number since I am not using a button/action as I saw many of other members doing. OR a better way of accomplishing this.
Thank you

You may want to try this function, + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats from NSTimer.
For example, in ViewDidLoad
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(showImage:) userInfo:nil repeats:YES];
and declare a function
- (void)showImage:(NSTimer*)timer
{
// your code to generate random index and animate image transition
}
if for some reason, you want to stop the timer
[timer invalidate];
Update
To avoid the timer messing up issue, trigger timer inside showImage, in ViewDidLoad, do [self showImage]
- (void)showImage
{
// your code to generate random index and animate image transition
// trigger the timer when the transition is finished
// You can leverage the method animateWithDuration:animations:completion:
// from UIView
[UIView animateWithDuration:12
animations:^(){
// image transition
}
completion:^(BOOL finished){
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(showImage) userInfo:nil repeats:NO];
}]
}
This should provide you the idea.

Consider using this method to randomly shuffle the elements of your array at startup.

Related

CGRectMake Does not move image (Sometimes)

When click on the image in my app and the image is at the top, the image will always move down perfectly. But when the image is at the bottom sometimes the image does not move up. Even though it does not move the process is still run and it logs as if it was at the top. I tried everything I know how to do but do you see anything wrong? Thanks!!
- (void )imageTapped:(UITapGestureRecognizer *) gestureRecognizer
{
NSLog(#"imageTapped");
[UIView beginAnimations:#"move" context:nil];
[UIView setAnimationDuration:0.3];
if (self.imgV.frame.origin.y == 20) {
NSLog(#"Top");
upOrDown = #"1";
self.invalidateTheTimer = #"";
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateLabel) userInfo:nil repeats:YES];
self.imgV.frame=CGRectMake(0, self.view.frame.size.height-self.imgV.frame.size.height, self.imgV.frame.size.width, self.imgV.frame.size.height);
self.vBottom.frame=CGRectMake(0, self.imgV.frame.origin.y+self.imgV.frame.size.height, self.vBottom.frame.size.width, self.vBottom.frame.size.height);
} else if (self.imgV.frame.origin.y >= 507) {
NSLog(#"Bottom");
self.imgV.frame=CGRectMake(0, 20, self.imgV.frame.size.width, self.imgV.frame.size.height);
self.vBottom.frame=CGRectMake(0, self.imgV.frame.origin.y+self.imgV.frame.size.height, self.vBottom.frame.size.width, self.vBottom.frame.size.height);
[hour setText:#""];
[minute setText:#""];
[second setText:#""];
[timer invalidate];
self.invalidateTheTimer = #"INVALIDATE";
}
NSLog(#"%f", self.imgV.frame.origin.y);
[UIView commitAnimations];
}
Considering you haven't posted your entire code, it's hard to figure out what's wrong exactly, but looking at the mess you've made, I suggest you take a look into block based animations which should simplify what you're trying to accomplish.
Additionally, if I had to guess, I'd say the issue is in your timer and whatever method it's invoking is probably messing with your animations.

Multiple NSTimers issue

I have sub-classed a component (it looks like circle and there is a picture inside it (NSView subclass)).
I want to change the picture every X time (to make it look like an animation).
When in my main view controller I drawing 1 sub-class like this everything works Fine, but when I adding more the picture changes much faster with each.
(I am firing the picture change using an NSTimer)
I am assuming that the issue happens because I have multiple NSTimers on the same queue
However I tried to use
NSTimer *uiTimer = [NSTimer timerWithTimeInterval:(1.0 / 5.0) target:self selector:#selector(changePicture) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:uiTimer forMode:NSRunLoopCommonModes];
It didn't solve the issue (I assume because it is still on the main thread)
So I came up with NSThread Solution
[NSThread detachNewThreadSelector:#selector(updateModel) toTarget:self withObject:nil];
- (void) updateModel
{
[NSTimer scheduledTimerWithTimeInterval:secondsBetweenUpdates
target:self
selector:#selector(changePicture)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] run];
}
It has a bit different behaviour (but still no luck as more sub-classes I have as faster picture changes).
My last shoot was this solution:
// Update the UI 5 times per second on the main queue
// Keep a strong reference to _timer in ARC
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, (1.0 / 5.0) * NSEC_PER_SEC, 0.25 * NSEC_PER_SEC);
dispatch_source_set_event_handler(_timer, ^{
[self changePicture];
});
// Start the timer
dispatch_resume(_timer);
I am not usually giving up , but I am trying to resolve it for 3 days already... And I think I need and advice of how to do this so it will work as intended.
If you are using GCD I would recommend you use dispatch_after, for example:
float delayTime = 0.2f;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(delayTime * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
[self changePicture];
});

Detect collisions with UIAnimation

Im using this code to try to detect collisions between two images, one of which is in an animation, but it doesn't work.
[UIView animateWithDuration:5 animations:^{
bird.center = CGPointMake(bird.center.x, 600);
fallTimer = [NSTimer scheduledTimerWithTimeInterval:.001 target:self selector:#selector(check) userInfo:nil repeats:YES];
}];
-(void)check {
if (CGRectIntersectsRect(bird.frame, cat.frame)) {
NSLog(#"YES");
}
}
How can i detect the collision?
You can't use the frame of a view while it's being animated, the returned value will not be accurate. Instead, you should be able to get the presentationLayer from the views layer and check its frame.

how to stop repeat timer?

I am a beginner when it comes to iOS App development. I want to move a label from left to right until it reaches half the screen width - i.e. the label should move by 240px (the label moves left to right like a marquee).
I have used NSTimer and I want to stop the timer when the label reaches half the view's width.
I have used the following code but it moves the label out of the view:
- (void)viewDidLoad {
[super viewDidLoad];
timer = [[NSTimer scheduledTimerWithTimeInterval:0.09 target:self selector:#selector(time:) userInfo:nil repeats:YES] retain];
}
- (void)time:(NSTimer *)theTimer {
label.center = CGPointMake(label.center.x+3.5, label.center.y);
NSLog(#"point:%#", label);
if (label.center.x < - (label.bounds.size.width/2)) {
label.center = CGPointMake(320+(label.bounds.size.width/2), label.center.y);
}
}
How can I solve this, please?
If you want to stop the repeating timer, you can use
if (/*You label is in position*/)
[myTimer invalidate];
But that's not the normal way to do animation in iOS, try this instead:
CGRect endFrame = /*The frame of your label in end position*/
[UIView animateWithDuration:0.5 animations:^{ myLabel.frame = endFrame;}];
To stop a timer, do [timer invalidate].
You can't "pause" a timer, so once you do this you'll need to call another timer.
correct way to invalidate your timer is
[myTimer invalidate];
myTimer = nil;

Cocoa Image Gallery Window

I have a HUD panel in an app and I want to be able to take a set of images and show each image on the panel for a few seconds before displaying the next image. I'm very new to Cocoa and am having trouble implementing this so some pointers would be welcomed. Here's what I'm currently trying:
[newShots enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL *stop)
{
//get url
NSURL *imageUrl = [[NSURL alloc] initWithString:[obj objectForKey:#"image_url"]];;
//get image from url
NSImage *image = [[NSImage alloc] initWithContentsOfURL:imageUrl];
//set it to shot panel
[shotPanelImageView setImage:image];
//clean up
[imageUrl release];
[image release];
//set needs refresh
[shotPanelImageView setNeedsDisplay:YES];
//sleep a few before moving to next
[NSThread sleepForTimeInterval:3.0];
}];
As you can see I'm just looping the info for each image, grabbing it via URL, setting it to the view, and then calling thread sleep for a few seconds before moving on. The issue is that the view will not redraw with the new image when it is assigned. I thought that setNeedsDisplay:YES would force a redraw but only the first image in the collection is ever displayed. I've put in NSLog()'s and debugged and I know for sure the enumeration is working correctly as I can see the new image information being set as it should.
Is there something I'm missing or is this a completely wrong way of going about solving this problem?
Thanks,
Craig
You are sleeping the main thread, which I'm pretty sure is not a good idea. I suggest that the Cocoa way to do what you want is to use a timer. In place of your code above:
[NSTimer scheduledTimerWithTimeInterval:3.0
target:self
selector:#selector(showNextShot:)
userInfo:nil
repeats:YES];
(The userInfo parameter allows you to pass along an arbitrary object that you want to use when the timer fires, so you could potentially use this to keep track of the current index as an NSNumber, but it would have to be wrapped in a mutable container object, because you can't set it later.)
Then put the code from your block into the method called by the timer. You'll need to make an instance variable for the current index.
- (void)showNextShot:(NSTimer *)timer {
if( currentShotIdx >= [newShots count] ){
[timer invalidate]; // Stop the timer
return; // Also call another cleanup method if needed
}
NSDictionary * obj = [newShots objectAtIndex:currentShotIdx];
// Your code...
currentShotIdx++;
}
To avoid the initial 3 sec delay caused by using a timer, you can call the same method your timer uses, right before you set it up:
[self showNextShot:nil]
[NSTimer scheduled...
Or could also schedule a non-repeating timer to fire as soon as possible (if you really wanted to use userInfo):
[NSTimer scheduledTimerWithTimeInterval:0.0
...
repeats:NO];
EDIT: I forgot about -initWithFireDate:interval:target:selector:userInfo:repeats:!
NSTimer *tim = [[NSTimer alloc] initWithFireDate:[NSDate date]
interval:3.0
target:self
selector:#selector(showNextShot:)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:tim
forMode:NSDefaultRunLoopMode];
[tim release];