I am facing memory issue when adding a UIButton to a UITableView. Below is my code for setting the UIButton's image:
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setImage:image forState:UIControlStateNormal];
But when I use imageName: method instead of imageWithContentsOfFile: method it's working perfectly. Does anybody have good solution for this issue?
imageWithContentsOfFile: vs imageNamed:
As per my knowledge,
imageNamed loads the image in a special system cache, and then future calls with that image path will return the image in the cache instead of reloading it from disk.
imageWithContentsOfFile simply loads the image at the path you specify, but does no caching. Multiple calls to imageWithContentsOfFile for the same image will result in multiple copies in memory.
AND about memory leaks i am not sure which one is better from both to use while using large number of images in programming...
Related
How to make a distance between the image and the text closer?
In ios 6 the same code displays normally, but in the new version of the distance increased.
I do everything by standard methods
[cell.textLabel setText: .....
// cell.indentationLevel = 0; // this is for test
UIImage * img = [UIImage imageNamed: # "image10.png"];
NSData * imageData = UIImagePNGRepresentation (img);
cell.imageView.image = [UIImage imageWithData: imageData];
attached image
https://www.dropbox.com/s/lt119xgozvzzstd/image01.png
If I remember correctly, the indentation level only affects the text. So when you're changing the indentation level/width you are in fact changing the distance between the imageView and the textLabel.
I tested this an unfortunately you can't make the indentationWidth negative. Thus you will need to subclass the UITableViewCell and set it up with your own views (I would suggest doing that in the Storyboard, with autolayout and make the indentation into a NSLayoutConstraint so that you have a property on your cell indentationConstant, for example which you can change). I have done this for a slightly different purposes, I needed to indent the whole contentView. No way around subclassing and creating custom views in Storyboard, sorry. (I am not even going to suggest some terrible hacks of accessing the subviews and modifying their frame, everything is autolayout today).
I have a program that fetches an image from the library, but I'm using code I found online to resize that image so that it can fit on the screen (basically making it 640x960), but then it would still be too big to display, so in another UIImage I'm copying the first resized image and re-resizing this one to make it about 1/4 of the screen (or 160x240). The code is this:
for ViewController.h:
UIImage *img;
UIImage *thumb;
-(UIImage*) scaleImage: (UIImage*)image toSize:(CGSize)newSize;
(this of course, is only the code related to my problem)
for ViewController.m
-(UIImage*) scaleImage: (UIImage*)image toSize:(CGSize)newSize {
UIGraphicsBeginImageContext(newSize);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
and in the same m file on another function, the scaleImage function is called when pressing a button with these lines:
[self scaleImage:img toSize:CGSizeMake(640, 960)];
thumb = img;
[self scaleImage:thumb toSize:CGSizeMake(160, 240)];
In the project I've previously been able to successfully provide an image for img using [info objectForKey:UIImagePickerControllerOriginalImage]; which would be the image chosen from the library. I've already "file owned" everything so that this function takes place (and it does because I create an UIAlert within it and it shows, and a NSLog to print out when scaleImage starts and it does, twice), but the image is never re-sized!! Does anyone know why?? Please let me know, thank you for anyone who comments with help or suggestions!!
Your scaleImage method returns the scaled image, for example
thumb = [self scaleImage:img toSize:CGSizeMake(640, 960)];
I have an image with the following naming convention.
It always shows the non 2x version on retina device.
I had removed cache images from derived data but still not showing.
It works if i explicitly set the imageNamed to "Back"
These are the images.
Back#2x.png
Back.png
UIImage *backImage = [UIImage imageNamed:#"Back"];
NSLog(#"back image height %f",backImage.size.height);
NSLog(#"back image width %f",backImage.size.width);
UIButton *btnBack = [UIButton buttonWithType:UIButtonTypeCustom];
[btnBack setImage:backImage forState:UIControlStateNormal];
btnBack.frame = CGRectMake(0, 0, backImage.size.width, backImage.size.height);
[btnBack addTarget:self action:#selector(Click_On_Btn_Back) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *backBarButton = [[UIBarButtonItem alloc] initWithCustomView:btnBack];
self.navigationItem.leftBarButtonItem = backBarButton;
Try
[UIImage imageNamed:#"Back.png"];
Also, make sure both versions of the image are listed under Target->Build Phases->Copy Bundle Resources. If the #2x version is not, make sure when you import it you check the box "Copy into destination group's folder (if needed)."
You should not specify .png in imageNamed: parameter.
Example: files should be names Back.png and Back#2x.png; Your code should be
UIImage *backImage = [UIImage imageNamed:#"Back"];
This is just one approach, but you can get the path from the bundle:
// in this example, we load test.png from the bundle
NSString *pathToResource = [[NSBundle mainBundle] pathForResource:#"test" ofType:#"png"];
What makes this so convenient is that it not only automatically determines whether to use the #2x version of your image, but also when you have localized files, this provides the path the the file for the current user locale. This is handy since localized files are not in the main directory, but are rather in their own subfolders (for example, English localized files are in the #"en.lproj" subfolder), and calling them by name is a hassle because you need the full path. This method gets the path for you.
#Keller's answer should work. are you sure that you are using png images.
I have a 400 pattern images at 400x300 bundled within my app. I would like to make some kind of factory method to take a portion of that image and load them into UIImageViews. I've had some success with using content mode and clipping to bounds, but when I load a ton of these into a view it can take upwards of 5 seconds for the view to load. Here is an example of my current method.
UIImageView *tinyImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed#"400x300testImage.png"];
[tinyImageView setFrame:CGRectMake(0, 0, 10, 200)];
[tinyImageView setContentMode:UIViewContentModeTopLeft];
[tinyImageView setClipsToBounds:YES];
[self.tinyImagesView addSubview:tinyImageView];
I've been reading the ImageIO class files and I think my answer is in there but I'm having a hard time putting together workable code. In another stackoverflow question I came across this code
CFDictionaryRef options = (__bridge CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys:
(id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailWithTransform,
(id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageIfAbsent,
(id)[NSNumber numberWithFloat:200.0f], (id)kCGImageSourceThumbnailMaxPixelSize,
nil];
CGImageRef imgRef = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options);
UIImage *scaled = [UIImage imageWithCGImage:imgRef];
CGImageRelease(imgRef);
CFRelease(imageSource);
return scaled;
This has a similar load time to loading the full images and clipping.
Is it possible to read in only a 10x200 strip of an image file and load that into a UIImageView that is as fast as creating that 10x200 png and loading that using imageNamed?
I'm pretty sure what you really want is a CATiledLayer, where you can point it at the set of images and have it automatically pull up what it needs.
You can just add a CATiledLayer to any UIView.
I am loading an image in a UIImageView which I then add to a UIScrollView.
The image is a local image and is about 5000 pixels in height.
The problem is that when I add the UIImageView to the UIScrollView the thread is blocked.
It is obvious because when I do this I can not scroll the UIScrollView till the image is displayed.
Here's the example.
UIScrollView *myscrollview = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 768, 1004)];
myscrollview.contentSize = CGSizeMake(7680, 1004);
myscrollview.pagingEnabled = TRUE;
[self.view addSubview:myscrollview];
NSString* str = [[NSBundle mainBundle] pathForResource:#"APPS.jpg" ofType:nil inDirectory:#""];
NSData *imageData = [NSData dataWithContentsOfFile:str];
UIImageView *singleImageView = [[UIImageView alloc] initWithImage:[UIImage imageWithData:imageData]];
//the line below is the blocking line
[scrollView addSubview:singleImageView];
It is the last line in the script that blocks the scroller. When I leave it out everything works perfect, except for the fact the image is not showing of course.
I seem to recall that using multithreading does not work on UIView operations so I guess that's out of the question ass well.
Thanks for your kind help.
If you're providing these large images, you should maybe check out CATiledLayer; there's a video of a good presentation on how to use this from WWDC 2010.
If these aren't your images, and you can't downsample them or break them into tiles, you can draw the image on a background thread. You may not draw to the screen graphics context on any main thread but the main thread, but that doesn't prevent you from drawing to a non-screen graphics context. On your background thread you can
create a drawing context with CGBitmapContextCreate
draw your image on it just as you would draw onto the screen in drawRect:
when you're done loading and drawing the image invoke your view's drawRect: method on the main thread using performSelectorOnMainThread:withObject:waitUntilDone:
In your view's drawRect: method, once you've fully drawn your image on the in-memory context, copy it to the screen using CGBitmapContextCreateImage and CGContextDrawImage.
This isn't trivial, you'll need to start your background thread at the right time, synchronize access to your images, etc. The CATiledLayer approach is almost certainly the better one if you can find a way to manipulate the images to make that work.
Why you want to load such huge image to memory at one time? split it into many small images and load/free it dynamically.
Try allocating the UIImageView without a UIImage, and add it as a sub-view to the UIScrollView.
Load the UIImage in a separate thread, and get the method running on the other thread to set the image property of the UIImageView when the image is loaded into memory. Also you'll probably encounter some memory problems as an image of this size loaded into a UIImage will probably be 30MB+
UIScrollView *myscrollview = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 768, 1004)];
myscrollview.contentSize = CGSizeMake(7680, 1004);
myscrollview.pagingEnabled = TRUE;
[self.view addSubview:myscrollview];
NSString* str = [[NSBundle mainBundle] pathForResource:#"APPS.jpg" ofType:nil inDirectory:#""];
UIImageView *singleImageView = [[UIImageView alloc] init];
[scrollView addSubview:singleImageView];
//Then fire off a method on another thread to load the UIImage and set the image
//property of the UIImageView.
Just keep an eye on memory and beware of using convenience constructors with UIImage (or any object that could end up being huge)
Also where are you currently running this code?