I got an NSArray with 5 pictures. I want to display them one after the other in a loop.
My code :
NSArray *tabImage = [[NSArray alloc] init];
tabImage = [[NSArray arrayWithObjects:
[UIImage imageNamed:#"picto_1.png"],
[UIImage imageNamed:#"picto_2.png"],
[UIImage imageNamed:#"picto_3.png"],
[UIImage imageNamed:#"picto_4.png"],
[UIImage imageNamed:#"picto_5.png"],
nil] retain];
int var = 0;
var = indexPath.row;
for (int var = 0; var < 20; var++) {
pictoImage.image = [tabImage objectAtIndex:(var % 5)];
}
I got the same picture all the time.
Thanks for help.
I’m guessing pictoImage is an UIImageView. In that case look at the class reference, specifically the animationImages properties.
pictoImage.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"picto_1.png"],
[UIImage imageNamed:#"picto_2.png"],
[UIImage imageNamed:#"picto_3.png"],
[UIImage imageNamed:#"picto_4.png"],
[UIImage imageNamed:#"picto_5.png"],
nil];
// How many seconds it should take to go through all images one time.
pictoImage.animationDuration = 5.0;
// How many times to repeat the animation (0 for indefinitely).
pictoImage.animationRepeatCount = 4;
[pictoImage startAnimating];
You are not giving the framework time to actually render the images to the window.
All your updates are being coalesced into one "setNeedsDisplay" call. On the next iteration of the run loop, the image view will be rendered using the last image that you set in the loop.
If pictoImage is an UIImageView, #Hagelin's answer is the best way to go. Now, if pictoImage is not an UIImageView object then you will have to take a different approach and that definitely is not a loop.
Set an NSTimer with a desired time interval and invoke a method that updates pictoImage iterating over the tabImages. It should be something like this :-
- (void)setup {
...
[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(updatePictoImage:)
userInfo:nil
repeats:YES];
...
}
- (void)updatePictoImage:(NSTimer*)theTimer {
static int i = 0;
if ( i > 20 ) {
pictoImage.image = [tabImages objectAtIndex:(i % 5)];
} else {
[theTimer invalidate];
}
}
This should update the pictoImage every 2 seconds with images in tabImages.
You don't print anything.
You are overriding the contents of your pictoImage 20 times in a row, leaving it with the last assignment when var is 19, so it should be picto_4.png.
Also, the whole var declaration doesn't make much sense at all... first you set it to 0, then you set it to indexPath.row, and in the loop it is completely redefined....
Related
I am teaching myself Objective C, and I am trying to progress with the idea of model-view-controller. My question is: what is the correct way of creating data in a model class? I have an example to illustrate what I mean, it's a bit long but a really simple question.
The following code is from a memory game I just finished. It works fine, I have separated the data from the view controller and so I have a separate class to hold the images for the cards. Here is that class. By the way, it all works, the only thing important for my question is that there is an array declared so feel free to skip most of the code that does the duplicating of the images and randomising. Anyway, here it is:
import "Cards.h"
#implementation Cards
- (NSMutableArray*) createCardImages{
NSMutableArray *Fruits = [[NSMutableArray alloc] initWithObjects:
[UIImage imageNamed:#"Apple.png"],
[UIImage imageNamed:#"Banana.png"],
[UIImage imageNamed:#"Pear.png"],
[UIImage imageNamed:#"Strawberry.png"],
[UIImage imageNamed:#"Lemon.png"],
[UIImage imageNamed:#"Orange.png"],
[UIImage imageNamed:#"Pineapple.png"],
[UIImage imageNamed:#"Kiwi.png"],
[UIImage imageNamed:#"Prune.png"],
[UIImage imageNamed:#"Peach.png"],
nil];
NSUInteger tileCount = [Fruits count];
for (NSUInteger i = 0; i < tileCount; ++i) {
NSInteger nElements = tileCount - i;
NSInteger n = (arc4random() % nElements) + i;
[FullList exchangeObjectAtIndex:i withObjectAtIndex:n];
}
NSArray *Shortlistprep1 = [Fruits subarrayWithRange:NSMakeRange(0, 10)];
NSArray *ShortListprep2=[Shortlistprep1 arrayByAddingObjectsFromArray:Shortlistprep1];
NSMutableArray *ShortList = [[NSMutableArray alloc]init];
ShortList = [NSMutableArray arrayWithArray:ShortListprep2];
return ShortList;
}
- (NSMutableArray*) shuffleCards{
NSUInteger cardCount = [self.cardImages count];
NSMutableArray *count = [[NSMutableArray alloc]init];
for (int tileID = 0; tileID < cardCount; tileID++){
[count addObject:[NSNumber
numberWithInt:tileID]];
}
return count;
}
Then I call this method from the view controller and it all works.
Next, I would like to add a different list of images so that different sets of images can be chosen when playing the game. Something like:
NSMutableArray *Clothes = [[NSMutableArray alloc] initWithObjects:
[UIImage imageNamed:#"Hat.png"],
[UIImage imageNamed:#"Coat.png"],
[UIImage imageNamed:#"Gloves.png"],
[UIImage imageNamed:#"Trousers.png"],
[UIImage imageNamed:#"Socks.png"],
[UIImage imageNamed:#"Sweater.png"],
[UIImage imageNamed:#"T-shirt.png"],
[UIImage imageNamed:#"Shirt.png"],
[UIImage imageNamed:#"Jumper.png"],
[UIImage imageNamed:#"Underwear.png"],
nil];
I know I could just duplicate the whole method for each new set, something like this:
- (NSMutableArray*) createCardImagesClothes{
NSMutableArray *Clothes = [[NSMutableArray alloc] initWithObjects:
[UIImage imageNamed:#"Hat.png"],
[UIImage imageNamed:#"Coat.png"],
[UIImage imageNamed:#"Gloves.png"],
[UIImage imageNamed:#"Trousers.png"],
[UIImage imageNamed:#"Socks.png"],
[UIImage imageNamed:#"Sweater.png"],
[UIImage imageNamed:#"T-shirt.png"],
[UIImage imageNamed:#"Shirt.png"],
[UIImage imageNamed:#"Jumper.png"],
[UIImage imageNamed:#"Underwear.png"],
nil];
NSUInteger tileCount = [FullList count];
for (NSUInteger i = 0; i < tileCount; ++i) {
NSInteger nElements = tileCount - i;
NSInteger n = (arc4random() % nElements) + i;
[FullList exchangeObjectAtIndex:i withObjectAtIndex:n];
}
NSArray *Shortlistprep1 = [FullList subarrayWithRange:NSMakeRange(0, 10)];
NSArray *ShortListprep2=[Shortlistprep1 arrayByAddingObjectsFromArray:Shortlistprep1];
NSMutableArray *ShortList = [[NSMutableArray alloc]init];
ShortList = [NSMutableArray arrayWithArray:ShortListprep2];
return ShortList;
}
And I am fine with doing this if this is the way forward, I just would like to learn best practices. I feel like there should be a way to just write one method and then have the properties declared on the h file as so:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface Cards : NSObject
// Create Cards
#property NSMutableArray *cardImages;
#property NSMutableArray *shuffledCards;
#propery NSMutableArray *Fruits;
#propery NSMutableArray *Clothes;
#propery NSMutableArray *SomeOtherSet;
#propery NSMutableArray *SomeOtherSet;
#propery NSMutableArray *SomeOtherSet;
- (NSMutableArray*) createCardImages;
- (NSMutableArray*) shuffleCards;
#end
and then all I would have to do is modify my method to accept an argument of the type NSMutableArray, like so:
- (NSMutableArray*) createCardImages:(NSMutableArray);
And just call the method with the argument which would represent whatever set I want to load.
My question, and it's a fairly basic one, is where would I actually load information into those arrays. There is no "main" or "viewDidLoad" method on the "m" file to actually load the data -in this case the images- into the array.
Does that make sense?
Thanks for reading.
Arguably you shouldn't be exposing the mutable array, your model should be a better container for the data and actually operate on the data. Shuffling by returning an array of numbers doesn't actually change the data model and it should.
Also, it would be cleaner if your data model dealt with data management, not the explicit creation. If you had a factory class which could be asked to generate a set of images and supply those images for your Cards class to deal with you would separate your concerns better. You could add class methods to Cards, or an instance method which took an enum to decide which images to use, but the images are really a separate issue to the management of those images as cards.
So, the view controller, when it's created to manage a game, asks the factory class for an instance of Cards for a specified image type. Once it has that Cards instance it can shuffle it and then request the card at a specified index, or grid location.
Index and grid location are interesting parts, because that could be different for different game types. Different game types can all use the same set of cards (though with a different Cards instance), but index those cards in a different way. To support this it's good to offer a simple index into the cards and allow the controller which knows the game logic to decide how to use that index system.
In all of the above the controller never accesses the cards as an array. The interface is similar, but all interaction is with Cards and that class hides its internal structure. Internally it would use an array, but it could use something else and the controller shouldn't care.
-(void)viewDidLoad {
NSMutableArray *coolImagesArray = [[NSMutableArray alloc] initWithObjects:#"active-1.jpg", #"active-2.jpg", #"active-3.jpg", #"active-4.jpg", #"active-5.jpg", nil];
}
-(void)someMethodWithData {
dispatch_async(dispatch_get_main_queue(), ^{
NSMutableArray *imagesArray = [[NSMutableArray alloc] init];
NSMutableString *inactiveString = [[NSMutableString alloc] init];
for (int i = 0; i < progress; i++) {
imageView.image = [UIImage imageNamed:[coolImagesArray objectAtIndex:i]];
inactiveString = [NSMutableString stringWithFormat:#"inactive-%d.png",i];
[imagesArray addObject:imageView];
}
UIImage *setImage = [UIImage imageNamed:#"inactive-1.png"];
for (int i = 0; i < [imagesArray count]; i++) {
[(UIImageView*)[imagesArray objectAtIndex:i] setImage:setImage];
}
});
}
This will always set the last or i(th) image correctly. It will not loop and change all the images I need. Is there another way to manage this?
imageView is pointing to one UIImageView object and in a loop you are changing the image of imageView not adding new UIImageView object to your array. All the objects inside the array pointing to 1 UIImageView object. So it is so normal that you can only change the last object's image only, because there is no other object in your array.
you should do it like:
dispatch_async(dispatch_get_main_queue(), ^{
NSMutableArray *imagesArray = [[NSMutableArray alloc] init];
NSMutableString *inactiveString = [[NSMutableString alloc] init];
for (int i = 0; i < progress; i++) {
UIImageView *temp = [[UIImageView alloc] initWithFrame:imageView.frame];
temp.image = [UIImage imageNamed:[coolImagesArray objectAtIndex:i]];
inactiveString = [NSMutableString stringWithFormat:#"inactive-%d.png",i];
[imagesArray addObject:temp];
}
UIImage *setImage = [UIImage imageNamed:#"inactive-1.png"];
for (int i = 0; i < [imagesArray count]; i++) {
[(UIImageView*)[imagesArray objectAtIndex:i] setImage:setImage];
}
});
For starters, I would create the image once so as to save memory.
I would also cast the object to a UIImageView (Not sure how that isn't getting flagged in xcode).
Lastly I would wrap it in a dispatch_async() to make sure it's happening on the main UI thread, because it could be getting called on a background thread and somehow the last image makes it to the main run loop, either way this is what it would look like:
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *setimage = [UIImage imageNamed:#"set-image.png"];
for (int i = 0; i < [imagesArray count]; i++) {
[(UIImageView *)[imagesArray objectAtIndex:i] setImage:setimage]];
}
});
I have an array of images. And I am trying to change the images randomly after 2 seconds.
-(void)loadImage
{
for(int i =0;i<=[imageArray count];i++)
{
imageView.image=[imageArray objecyAtIndex:i];
}
}
-(void)viewDidload
{
imageTimer = [NSTimer scheduledTimerWithTimeInterval: 2.0 target:self selector:#selector(loadImage) userInfo:nil repeats: YES];
}
But it is not working for me . It will be great appreciable if any one help to do this. Thanks .
modify your code in following way ..
-(void)viewDidload
{
index = 0; //index should be instance variable
imageTimer = [NSTimer scheduledTimerWithTimeInterval: 2.0 target:self selector:#selector(loadImage) userInfo:nil repeats: YES];
}
-(void)loadImage
{
if(index != imageArray.count-1)
{
imageView.image=[imageArray objecyAtIndex:index];
index++;
}
else
{
[imageTimer invalidate];
}
}
You have to make some changes in your loadImage method. You have to generate random number and set a range of the random numbers. I tried like this and it worked for me.
-(void)loadImage
{
NSString *max=#"3";
NSString *min=#"0";
int randNum = rand() % ([max intValue] - [min intValue]) + [min intValue];
_changeableImageView.image=[UIImage imageNamed:[imageArray objectAtIndex:randNum]];
}
- (void)viewDidLoad
{
[super viewDidLoad];
_changeableImageView.image=[UIImage imageNamed:#"program.jpg"];
imageArray=[[NSMutableArray alloc]initWithObjects:#"corporate.jpg",#"district.jpg",#"program.jpg", nil];
imageTimer = [NSTimer scheduledTimerWithTimeInterval: 2.0 target:self selector:#selector(loadImage) userInfo:nil repeats: YES];
}
I hope it will for for you.
-(void)loadImage
{
NSUInteger randomIndex = arc4random() % [imageArray count];
imageView.image = imageArray[randomIndex];
[self performSelector:#selector(loadImage) withObject:nil afterDelay:2.0];
}
-(void)viewDidload
{
[self performSelector:#selector(loadImage) withObject:nil afterDelay:2.0];
}
Your loadImage method does not select a random image from the array. What it actually does is iterate through every single member in the array and assign them to the image view one by one every time it is run.
To set a random image each time the method is called, your method should look like this:
-(void)loadImage
{
NSUInteger randomIndex = arc4random() % [imageArray count];
imageView.image = imageArray[randomIndex];
}
The problem with your code is that whenever the timer fires, the whole loop in loadImage is executed, so that your image is always the last image of the array.
You have to define a property that holds e.g. the index of the currently displayed image. In loadImage, you had to increment then this index (modulo the array size), and assign the next image to your imageView.
It's not working because every time loadImage is called you loop through and set every image in the array and therefore always end up with the last image in the array. Try something more like this:
-(void)loadImage{
int randNum = arc4random() % imageArray.count;
imageView.image=[imageArray objecyAtIndex:randNum-1];
}//end method
I have such code
arrayWithImages = [[NSMutableArray alloc] init];
NSEnumerator *enumForNames = [arrayWithNames objectEnumerator];
NSEnumerator *enumForURLs = [arrayWithURLs objectEnumerator];
id objName, objURL;
while(objName = [enumForNames nextObject]) {
objURL = [enumForURLs nextObject];
UIImageView *anImage = nil;
[anImage setImageWithURL:[NSURL URLWithString:objURL]];
(...)
[arrayWithImages addObject:anImage];
}
And each time I got SIGABRT in line with "[arrayWithImages addObject:anImage];"
What's here wrong?
I don’t see an setImageWithURL method on UIImageView. Where is this from?
Is there any output from the SIGABRT crash?
Try this code:
// Download the image
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:objURL]];
// Make an UIImage out of it
UIImage *anImage = [UIImage imageWithData:imageData];
// Create an image view with the image
UIImageView *imageView = [[UIImageView alloc] initWithImage:anImage];
// Check to make sure it exists
if (imageView != nil) {
// Add it to your array
[arrayWithImages addObject:imageView];
else {
NSLog(#"Image view is nil");
}
Note that you should download the images asynchronously to avoid hanging the loop. This blog post discussing asynchronous image downloading.
Also if you know that [enumForURLs nextObject]; will return an NSString (or even better, NSURL) you should assign objURL to type NSString (or NSURL).
I have an array of UIImage objects as follows:
frames = [[NSMutableArray alloc] initWithCapacity:[sortedArray count]];
for (int nDx = 0; nDx < [sortedArray count]; nDx++)
{
NSString * sImageName = [sortedArray objectAtIndex:nDx];
[frames addObject:[UIImage imageWithContentsOfFile:[NSString stringWithFormat:#"%#/%#", imageFolderPath, sImageName]]];
}
In my timer method I am doing the following to cycle through the images:
self.image = [frames objectAtIndex:currentFrame];
currentFrame++;
if (currentFrame >= [frames count]) currentFrame = 0;
However, when I start the timer the images cycle slow the first time, and then everything works as expected. I have also tried this without a timer.
How can I pre-load the images so they are ready to go when my timer starts?
Thanks for the help.
You can have a separate thread to load the images, using this code.
If it works I'll add it here.
Here img_array is UIImageView
img_array.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"img_1.png"],
[UIImage imageNamed:#"img_2.png"],
[UIImage imageNamed:#"img_3.png"],nil];
[img_array.animationImages setAnimationRepeatCount:HUGE_VAL];
img_array.animationImages .animationDuration = 4;
[img_array.animationImages startAnimating];
[self.view addSubview:img_array]
Try this: