I need to generate and save 320 images as PNGs when the game is first run. These images will then be loaded instead of being generated again. Here is the process:
load image template (black and white with alpha)
overlay non transparent pixels with specified colour
put on top the template at 0.3 opacity merging it to one final image
return back UIImage
store the UIImage, converted to NSData to PNG in Cache directory
This is done using UIGraphicsBeginImageContextWithOptions. This process needs to be done for 32 image templates in 10 colours on the background thread. The purpose is that these will be used as avatar/profile images in this game, scaled down at certain screens as appropriate. They cannot be generated every time though, because this causes too much lag.
The images are 400x400 each. They result being about 20/25 kB each when stored. When I try to use my current way of generating and storing, I get a memory warning and I see (using Instruments) that the number of alive CGImage and UIImage objects keeps increasing rapidly. This seems like they're being retained but I don't hold any references to them.
Here is my other question closer detailing the code I'm using: UIGraphicsBeginImageContext created image
What is the best way to create and store to secondary storage this many images? Thanks in advance.
Edit:
Here's the whole code I currently use to create and save the images:
//==========================================================
// Definitions and Macros
//==========================================================
//HEX color macro
#define UIColorFromRGB(rgbValue) [UIColor \
colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \
green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \
blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
//Colours
#define RED_COLOUR UIColorFromRGB(0xF65D58)
#define ORANGE_COLOUR UIColorFromRGB(0xFF8D16)
#define YELLOW_COLOUR UIColorFromRGB(0xFFD100)
#define LIGHT_GREEN_COLOUR UIColorFromRGB(0x82DE13)
#define DARK_GREEN_COLOUR UIColorFromRGB(0x67B74F)
#define TURQUOISE_COLOUR UIColorFromRGB(0x32ADA6)
#define LIGHT_BLUE_COLOUR UIColorFromRGB(0x11C9FF)
#define DARK_BLUE_COLOUR UIColorFromRGB(0x2E97F5)
#define PURPLE_COLOUR UIColorFromRGB(0x8F73FD)
#define PINK_COLOUR UIColorFromRGB(0xF35991)
#import "ViewController.h"
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//Generate the graphics
[self generateAndSaveGraphics];
}
//==========================================================
// Generating and Saving Graphics
//==========================================================
-(void)generateAndSaveGraphics {
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self createAvatarImages];
//Here create all other images that need to be saved to Cache directory
dispatch_async( dispatch_get_main_queue(), ^{ //Finished
NSLog(#"DONE"); //always runs out of memory before getting here
});
});
}
-(void)createAvatarImages {
//Create avatar images
NSArray *colours = [NSArray arrayWithObjects:RED_COLOUR, ORANGE_COLOUR, YELLOW_COLOUR, LIGHT_GREEN_COLOUR, DARK_GREEN_COLOUR, TURQUOISE_COLOUR, LIGHT_BLUE_COLOUR, DARK_BLUE_COLOUR, PURPLE_COLOUR, PINK_COLOUR, nil];
NSString *cacheDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
for(int i = 0; i < 32; i++) { //Avatar image templates are named m1 - m16 and f1 - f16
NSString *avatarImageName;
if(i < 16) { //female avatars
avatarImageName = [NSString stringWithFormat:#"f%i", i+1];
}
else { //male avatars
avatarImageName = [NSString stringWithFormat:#"m%i", i-15];
}
for(int j = 0; j < colours.count; j++) { //make avatar image for each colour
#autoreleasepool { //only helps very slightly
UIColor *colour = [colours objectAtIndex:j];
UIImage *avatarImage = [self tintedImageFromImage:[UIImage imageNamed:avatarImageName] colour:colour intensity:0.3];
NSString *fileName = [NSString stringWithFormat:#"%#_%i.png", avatarImageName, j];
NSString *filePath = [cacheDir stringByAppendingPathComponent:fileName];
NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation(avatarImage)];
[imageData writeToFile:filePath atomically:YES];
NSLog(#"AVATAR IMAGE CREATED");
}
}
}
}
//==========================================================
// Universal Image Tinting Code
//==========================================================
//Creates a tinted image based on the source greyscale image and tinting intensity
-(UIImage *)tintedImageFromImage:(UIImage *)sourceImage colour:(UIColor *)color intensity:(float)intensity {
if (UIGraphicsBeginImageContextWithOptions != NULL) {
UIGraphicsBeginImageContextWithOptions(sourceImage.size, NO, 0.0);
} else {
UIGraphicsBeginImageContext(sourceImage.size);
}
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect rect = CGRectMake(0, 0, sourceImage.size.width, sourceImage.size.height);
// draw alpha-mask
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextDrawImage(context, rect, sourceImage.CGImage);
// draw tint color, preserving alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeSourceIn);
[color setFill];
CGContextFillRect(context, rect);
//Set the original greyscale template as the overlay of the new image
sourceImage = [self verticallyFlipImage:sourceImage];
[sourceImage drawInRect:CGRectMake(0,0, sourceImage.size.width,sourceImage.size.height) blendMode:kCGBlendModeMultiply alpha:intensity];
UIImage *colouredImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
colouredImage = [self verticallyFlipImage:colouredImage];
return colouredImage;
}
//Vertically flips an image
-(UIImage *)verticallyFlipImage:(UIImage *)originalImage {
UIImageView *tempImageView = [[UIImageView alloc] initWithImage:originalImage];
UIGraphicsBeginImageContext(tempImageView.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, tempImageView.frame.size.height);
CGContextConcatCTM(context, flipVertical);
[tempImageView.layer renderInContext:context];
UIImage *flippedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return flippedImage;
}
#end
I've created a test project (in the zip) to illustrate the problem:
Project Files
For future reference, the solution is this one line of code:
tempImageView.image = nil;
Thanks to Matic.
It would seem that the issue is in method verticallyFlipImage. The graphics context seems to retain the temporary image view you create and with it the image you assign. This issue would probably be generally fixed by pushing each image through the process as its own dispatch call: Resample image -> callback -> resample next (or exit).
In the end of the whole resampling all the data is released and there is no memory leak. To make a quick fix you can simply call tempImageView.image = nil; before returning the image. The image view itself still produces a memory inflate but it is too small to have any impact.
This works for me and I hope it helps you.
EDIT: added the dispatch concept (comment reference)
dispatch_queue_t internalQueue;
- (void)createQueue {
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void) {
internalQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL); //we created a high priority queue
});
}
- (void)deleteQueue {
dispatch_release(internalQueue);
}
- (void)imageProcessingDone {
[self deleteQueue];
//all done here
}
- (void)processImagesInArray:(NSMutableArray *)imageArray {
//take out 1 of the objects (last in this case, you can do objectAtIndex:0 if you wish)
UIImage *img = [[imageArray lastObject] retain]; //note, image retained so the next line does not deallocate it (released at NOTE1)
[imageArray removeLastObject]; //remove from the array
dispatch_async(internalQueue, ^(void) { //dispach
//do all the image processing + saving
[img release];//NOTE1
//callback: In this case I push it the main thread. There should be little difference if you simply dispach it again on the internalQueue
if(imageArray.count > 0) {
[self performSelectorOnMainThread:#selector(processImagesInArray:) withObject:imageArray waitUntilDone:NO];
}
else {
[self performSelectorOnMainThread:#selector(imageProcessingDone) withObject:nil waitUntilDone:NO];
}
});
}
Related
I'm trying to loop through some images in a single UIImageView when I tap a button. The image must disappear 0.1 seconds after the button is pressed.
Here's the code:
int tapCount = 0;
UIImage *image0 = [UIImage imageNamed:#"0.jpg"];
UIImage *image1 = [UIImage imageNamed:#"1.jpg"];
UIImage *image2 = [UIImage imageNamed:#"2.jpg"];
imagesArray = [[NSArray alloc] initWithObjects:image0, image1, image2, nil];
-(IBAction)backgroundButton:(id)sender{
self.myImageView.image = [imagesArray objectAtIndex:tapCount%3];
tapCount++;
[self performSelector:#selector(eraseImage) withObject:self afterDelay:0.1];
}
-(void)eraseImage{
self.myImageView.image = nil;
}
The problem is that the images don't appear until I've completed one entire loop (at the 4th tap).
I'm guessing that somehow I must initialize the images in the UIImageView because it takes some time between the tapping and the image appearing, and since it disappears after 0.1 seconds...it doesn't show at all.
I've tried loading them inside viewDidLoad like this:
for(int i = 0; i<[imagesArray count]; i++){
self.myImageView.image = [imagesArray objectAtIndex : i];
}
But it only works with the last image that loads (image2 in this case).
Should I loop between different UIImageView instead of looping through different UIImage inside a single UIImageView?
Any other hints?
Creating a UIImage doesn't actually load the image data (you need to render it to a context for that to happen). So, if your images are large then you could be hiding them before they are actually rendered to the screen. You won't be able to hold many images in memory at the same time, but you can force the image data to be loaded by creating a context and drawing the image into it (which can be done in the background, using CGContextDrawImage).
There are a few 3rd party bits of code which do this, like this or check this discussion.
Use the animationImages and animationDuration property of the UIImageView
https://developer.apple.com/library/ios/documentation/uikit/reference/UIImageView_Class/Reference/Reference.html#//apple_ref/occ/instp/UIImageView/animationImages
I think there is a much simpler way to achieve that animation you are going for. Try the following code:
-(IBAction)backgroundButton:(id)sender{
[UIView animateWithDuration:0.2
delay:nil
options:UIViewAnimationCurveEaseIn
animations:^{
self.myImageView.image = [imagesArray objectAtIndex:tapCount%3];
self.myImageView.image = nil;
}
completion:nil
];
tapCount++;
if (tapCount == 2) {
tapCount = 0;
}
}
I finally managed to work this around using this solution:
First I preload all the images in the background thread
-(void)preload:(UIImage *)image{
CGImageRef ref = image.CGImage;
size_t width = CGImageGetWidth(ref);
size_t height = CGImageGetHeight(ref);
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, width * 4, space, kCGBitmapAlphaInfoMask & kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(space);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), ref);
CGContextRelease(context);
}
Then I execute the same action I had in the beginning:
-(IBAction)backgroundButton:(id)sender{
self.myImageView.image = [imagesArray objectAtIndex:tapCount%3];
tapCount++;
[self performSelector:#selector(eraseImage) withObject:self afterDelay:0.1];
}
-(void)eraseImage{
self.myImageView.image = nil;
}
tintColor is a life saver, it takes app theming to a whole new (easy) level.
//the life saving bit is the new UIImageRenderingModeAlwaysTemplate mode of UIImage
UIImage *templateImage = [[UIImage imageNamed:#"myTemplateImage"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
imageView.image = templateImage;
//set the desired tintColor
imageView.tintColor = color;
The above code will "paint" the image's non-transparent parts according to the UIImageview's tint color which is oh so cool.No need for core graphics for something simple like that.
The problem I face is with animations.
Continuing from the above code:
//The array with the names of the images we want to animate
NSArray *imageNames = #[#"1",#"2"#"3"#"4"#"5"];
//The array with the actual images
NSMutableArray *images = [NSMutableArray new];
for (int i = 0; i < imageNames.count; i++)
{
[images addObject:[[UIImage imageNamed:[imageNames objectAtIndex:i]] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]];
}
//We set the animation images of the UIImageView to the images in the array
imageView.animationImages = images;
//and start animating the animation
[imageView startAnimating];
The animation is performed correctly but the images use their original color (i.e. the color used in the gfx editing application) instead of the UIImageView's tintColor.
I am about to try to perform the animation myself (by doing something a little bit over the top like looping through the images and setting the UIImageView's image property with a NSTimer delay so that the human eye can catch it).
Before doing that I'd like to ask if the tintColor property of UIImageView is supposed to support what I'm trying to do with it i.e use it for animations.
Thanks.
Rather than animate the images myself, I decided to render the individual frames using a tint color and then let UIImage do the animation. I created a category on UIImage with the following methods:
+ (instancetype)animatedImageNamed:(NSString *)name tintColor:(UIColor *)tintColor duration:(NSTimeInterval)duration
{
NSMutableArray *images = [[NSMutableArray alloc] init];
short index = 0;
while ( index <= 1024 )
{
NSString *imageName = [NSString stringWithFormat:#"%#%d", name, index++];
UIImage *image = [UIImage imageNamed:imageName];
if ( image == nil ) break;
[images addObject:[image imageTintedWithColor:tintColor]];
}
return [self animatedImageWithImages:images duration:duration];
}
- (instancetype)imageTintedWithColor:(UIColor *)tintColor
{
CGRect imageBounds = CGRectMake( 0, 0, self.size.width, self.size.height );
UIGraphicsBeginImageContextWithOptions( self.size, NO, self.scale );
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM( context, 0, self.size.height );
CGContextScaleCTM( context, 1.0, -1.0 );
CGContextClipToMask( context, imageBounds, self.CGImage );
[tintColor setFill];
CGContextFillRect( context, imageBounds );
UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return tintedImage;
}
It works just like + [UIImage animatedImageNamed:duration:] (including looking for files named "image0", "image1", etc) except that it also takes a tint color.
Thanks to this answer for providing the image tinting code: https://stackoverflow.com/a/19152722/321527
.tintColor can probably handle it. I use NSTimers for UIButton's setTitleColor method all the time. Here's an example.
UPDATED: Tested and works on iPhone 5s iOS 7.1!
- (void)bringToMain:(UIImage *)imageNam {
timer = [NSTimer scheduledTimerWithTimeInterval:.002
target:self
selector:#selector(animateTint)
userInfo:nil
repeats:YES];
}
- (void)animateTint {
asd += 1.0f;
[imageView setTintColor:[UIColor colorWithRed:((asd/100.0f) green:0.0f blue:0.0f alpha:1.0f]];
if (asd == 100) {
asd = 0.0f
[timer invalidate];
}
}
My iOS app has high memory usage but no memory leaks. How do I reduce the memory usage.
Using Instruments, I discovered that my app maxes out at 90MB, before a memory warning occurs, and other memory is deallocated, and then it stays around 55-65MB for the rest of its usage.
I feel that 55-65MB is too high, right?
Since, Instruments did not catch any leaks. What can I do to reduce this memory usage?
I went through this year's WWDC video, but of the stuff I understood (this is my first iOS app), it mostly covered dealing with leaks.
Some possibly useful information:
VM: ImageIO_GIF_Data 30.35MB Live Bytes | 115 Living | 300 Transient |
136.12 MB Overall Bytes
VM: MappedFile 36.04 MB Live Bytes | 16 Living | 11 Transient | 36.09 MB Overall Bytes
All the other stuff is under 1MB
My app downloads around 30 GIF files from the internet, I use SDWebImage, and I just save the URLs of the images, and SDWebImage does the rest. :P
Thanks in advance,
From An iOS Memory Management First Timer
Thanks once again for you help
You say you are using a table view. Although cells are reused automatically, this makes it very easy to make mistakes and create too many objects.
1 common error is allocating objects (eg. UIImageView) in the cellForRowAtIndexPath method, as this means every time a cell is reused a new UIImageView is added to it as well as keeping the old ones. So double check what is going on in your cellForRowAtIndexPath method.
I decided to add full code for memory saving, if you are using GIF files, modify UIImage scale method (Found it here, an Stackoverflow). As said GangstaGraham in SD Image exist method sd_animatedImageByScalingAndCroppingToSize
#interface UIImage (Scaling)
-(UIImage *)imageByScalingProportionallyToSize:(CGSize)targetSize;
-(UIImage*) croppedImageWithRect: (CGRect) rect;
#end
#implementation UIImage (Scaling)
- (UIImage *)imageByScalingProportionallyToSize:(CGSize)targetSize {
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)]) {
if ([[UIScreen mainScreen] scale] == 2.0) {
targetSize.height *= 2.0f;
targetSize.width *= 2.0f;
}
}
NSUInteger width = targetSize.width;
NSUInteger height = targetSize.height;
UIImage *newImage = [self resizedImageWithMinimumSize: CGSizeMake (width, height)];
return [newImage croppedImageWithRect: CGRectMake ((newImage.size.width - width) / 2, (newImage.size.height - height) / 2, width, height)];
}
-(CGImageRef)CGImageWithCorrectOrientation
{
if (self.imageOrientation == UIImageOrientationDown) {
//retaining because caller expects to own the reference
CGImageRetain([self CGImage]);
return [self CGImage];
}
UIGraphicsBeginImageContext(self.size);
CGContextRef context = UIGraphicsGetCurrentContext();
if (self.imageOrientation == UIImageOrientationRight) {
CGContextRotateCTM (context, 90 * M_PI/180);
} else if (self.imageOrientation == UIImageOrientationLeft) {
CGContextRotateCTM (context, -90 * M_PI/180);
} else if (self.imageOrientation == UIImageOrientationUp) {
CGContextRotateCTM (context, 180 * M_PI/180);
}
[self drawAtPoint:CGPointMake(0, 0)];
CGImageRef cgImage = CGBitmapContextCreateImage(context);
UIGraphicsEndImageContext();
return cgImage;
}
-(UIImage*)resizedImageWithMinimumSize:(CGSize)size
{
CGImageRef imgRef = [self CGImageWithCorrectOrientation];
CGFloat original_width = CGImageGetWidth(imgRef);
CGFloat original_height = CGImageGetHeight(imgRef);
CGFloat width_ratio = size.width / original_width;
CGFloat height_ratio = size.height / original_height;
CGFloat scale_ratio = width_ratio > height_ratio ? width_ratio : height_ratio;
CGImageRelease(imgRef);
return [self drawImageInBounds: CGRectMake(0, 0, round(original_width * scale_ratio), round(original_height * scale_ratio))];
}
-(UIImage*)drawImageInBounds:(CGRect)bounds
{
UIGraphicsBeginImageContext(bounds.size);
[self drawInRect: bounds];
UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resizedImage;
}
-(UIImage*)croppedImageWithRect:(CGRect)rect
{
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect drawRect = CGRectMake(-rect.origin.x, -rect.origin.y, self.size.width, self.size.height);
CGContextClipToRect(context, CGRectMake(0, 0, rect.size.width, rect.size.height));
[self drawInRect:drawRect];
UIImage* subImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return subImage;
}
-(UIImage *) resizableImageWithCapInsets2: (UIEdgeInsets) inset
{
if ([self respondsToSelector:#selector(resizableImageWithCapInsets:resizingMode:)])
{
return [self resizableImageWithCapInsets:inset resizingMode:UIImageResizingModeStretch];
}
else
{
float left = (self.size.width-2)/2;//The middle points rarely vary anyway
float top = (self.size.height-2)/2;
return [self stretchableImageWithLeftCapWidth:left topCapHeight:top];
}
}
#end
And UIImageView:
#import <SDWebImage/SDImageCache.h>
#implementation UIImageView (Scaling)
-(void)setImageWithURL:(NSURL*)url scaleToSize:(BOOL)scale
{
if(url.absoluteString.length < 10) return;
if(!scale){
[self setImageWithURL:url];
return;
}
__block UIImageView* selfimg = self;
__block NSString* prevKey = SPRINTF(#"%#_%ix%i", url.absoluteString, (int)self.frame.size.width, (int)self.frame.size.height);
__block UIImage* prevImage = nil;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^ {
prevImage = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:prevKey];
if(prevImage){
dispatch_async(dispatch_get_main_queue(), ^ {
[self setImage:prevImage];
});
}else{
[[SDWebImageDownloader sharedDownloader] downloadImageWithURL:url options:SDWebImageDownloaderFILOQueueMode progress:nil completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
if(error){
[selfimg setImageWithURL:url scaleToSize:scale];
}else{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^ {
prevImage = [image imageByScalingProportionallyToSize:self.frame.size];
if(finished)
[[SDImageCache sharedImageCache] storeImage:prevImage forKey:prevKey];
dispatch_async(dispatch_get_main_queue(), ^ {
[self setImage:prevImage];
});
});
}
}];
}
});
return;
}
-(void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder scaleToSize:(BOOL)scale
{
[self setImage:placeholder];
[self setImageWithURL:url scaleToSize:scale];
}
#end
I would suggest, that you use Instruments and Heapshot Analysis. bbum wrote an article about it at his blog.
Here is a quick overview:
Start your App in Instruments and select the Allocations template
Wait some time after your App start to settle down
In Allocations, press Mark Heap; This is your baseline.
Use your app and return to the same screen as in #2. Press Mark Heap again.
Repeat that for some time.
If you see a steady growth of memory, you can drill down in the heapshots and see all objects allocated. That should give you a good start to reduce your memory footprint.
SDWebImage doesn't do the rest.
You need handle less images in memory as can:
erase UIImageView when it's not shown;
use reusable objects pattern;
and of course clear not visible (cached in memory) images when you've got memory warnings,
for this just use self.urImage = nil;
So, good look for app memory saving ;)
I have a UIImageView that is animated using the following code:
NSMutableArray *imageArray = [[NSMutableArray alloc] init];
for(int i = 1; i < 15; i++) {
NSString *str = [NSString stringWithFormat:#"marker_%i.png", i];
UIImage *img = [UIImage imageNamed:str];
if(img != nil) {
[imageArray addObject:img];
}
}
_imageContainer.animationImages = imageArray;
_imageContainer.animationDuration = 0.5f;
[_imageContainer startAnimating];
What I want now is to repeat the image to get a pattern. There is colorWithPatternImage, but that isn't made for animations.
I want the whole background filled with a animated pattern.
Instead of using a very large images (960x640) i could use a image of 64x64 for example and repeat that to fill the screen.
Is there any way?
Leave your code as it is, but instead using UIImageView use my subclass:
//
// AnimatedPatternView.h
//
#import <UIKit/UIKit.h>
#interface AnimatedPatternView : UIImageView;
#end
//
// AnimatedPatternView.m
//
#import "AnimatedPatternView.h"
#implementation AnimatedPatternView
-(void)setAnimationImages:(NSArray *)imageArray
{
NSMutableArray* array = [NSMutableArray arrayWithCapacity:imageArray.count];
for (UIImage* image in imageArray) {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, 0);
UIColor* patternColor = [UIColor colorWithPatternImage:image];
[patternColor setFill];
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextFillRect(ctx, self.bounds);
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[array addObject:img];
}
[super setAnimationImages:array];
}
#end
If you create the view using interface builder you only need to set the class of the image view in the identity inspector.
I'm currently developing an app that will reset an image iteratively, and the future images can not be stored or predicted. For simple tests, I used
UIImage * image = [UIImage imageNamed:#"mountain.jpg"];
image = [ /* function that changes image */ ];
self.imageView.image = image
and this worked fine. The imageView updated, just like I wanted. But when I do
for (i=0; i<10; i++){
image = [ /* function that changes image */ ];
self.imageView.image = image;
}
it doesn't work. So I tried this:
for (int i=0; i<10; i++) {
NSLog(#"%d", i);
[NSThread sleepForTimeInterval:1.0];
image = [self.brain recontructImage:image iterations:10];
self.imageView.image = image;
[self.imageView setNeedsDisplay];
[self.imageView setImage:image];
}
and flipped it so the [self.image setNeedsDisplay] and [self.image setImage:image] where before the image = [ /* function that changes image */ ].
I've only tried this in the simulator, and I'm beginning to wonder if it's broken, but I doubt it. Maybe it's an issue with timing, but I paused for a second. What am I doing wrong?