image comparison is not working after application did become active - ios

I am using following for comparing button's background image.
if([[button currentBackgroundImage] isEqual:[UIImage imageNamed:#"image1.png"]]){
// do something
}
The code works fine when application is in active state. But when app gets back from idle state the above code doesn't work.
Any idea why this happens?
Thanks

The images don't compare after you come back from background because you're creating a new instance of that image for your comparison by using [UIImage imageNamed:#"image1.png"] (the images are compared by looking at their hash values, not by looking at the actual image contents). If you create a property for your image, when you first use imageNamed:, and use that in the comparison, it should work properly. So, I tested with this code, and it returned true when I checked after coming back from the background (I set the button's background image in IB).
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UIButton *greenButton;
#property (strong,nonatomic) UIImage *greenPng;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.greenPng = [UIImage imageNamed:#"Green.png"];
}
- (IBAction)checkImages:(id)sender {
BOOL isTheSame = [self.greenButton.currentBackgroundImage isEqual:self.greenPng];
NSLog(#"The images are %#",isTheSame? #"the same" : #"different");
NSLog(#" button image hash is %d",self.greenButton.currentBackgroundImage.hash);
NSLog(#" imageNamed image hash is %d",self.greenPng.hash);
}
After Edit: I'm not sure my explanation is quite correct -- in one run of the app, you can make multiple calls to imageNamed:, and all the images that are returned will have the same hash (including the image you pick in IB, if you do it that way). I think this is due to cashing. In any case, when you come back from the background and call imageNamed: again, it returns an image with a different hash.

Related

Custom ImageView doesn't display image

I have created on custom imageView class. And am trying to override the image setter method. But for some reason the imageView is not displaying anything, though the image is getting assigned onto it.
The code is as follows:
#implementation CustomImageView
#synthesize image = _image;
- (void)setImage:(UIImage *)correctedImage
{
_image = correctedImage;
}
#end
I have tried removing the setter method. i.e Without overriding any method at all. Still it is not displaying anything. :(
The problem was that I had to set the image by passing it to the super.
[super setImage: correctedImage];
Simply, setting it from the base class doesn't seen to work :/

UIImage caching with Xcode Asset Catalogs

We all know about the mysterious behind-the-scenes caching mechanism of UIImage's imageNamed: method. In Apple's UIImage Class Reference it says:
In low-memory situations, image data may be purged from a UIImage object to free up memory on the system. This purging behavior affects only the image data stored internally by the UIImage object and not the object itself. When you attempt to draw an image whose data has been purged, the image object automatically reloads the data from its original file. This extra load step, however, may incur a small performance penalty.
In fact, image data will not be "purged from a UIImage object to free up memory on the system" as the documentation suggests, however. Instead, the app receives memory warnings until it quits "due to memory pressure".
EDIT: When using the conventional image file references in your Xcode project, the UIImage caching works fine. It's just when you transition to Asset Catalogs that the memory is never released.
I implemented a UIScrollView with a couple of UIImageViews to scroll through a long list of images. When scrolling, the next images are being loaded and assigned to the UIImageView's image property, removing the strong link to the UIImage it has been holding previously.
Because of imageNamed:'s caching mechanism, I quickly run out of memory, though, and the app terminates with around 170 MB memory allocated.
Of course there are plenty of interesting solutions around to implement custom caching mechanisms, including overriding the imageNamed: class method in a category. Often, the class method imageWithContentOfFile: that does not cache the image data is used instead, as even suggested by Apple developers at the WWDC 2011.
These solutions work fine for regular image files, although you have to get the path and file extension which is not quite as elegant as I would like it to be.
I am using the new Asset Catalogs introduced in Xcode 5, though, to make use of the mechanisms of conditionally loading images depending on the device and the efficient image file storage. As of now, there seems to be no straight forward way to load an image from an Asset Catalog without using imageNamed:, unless I am missing an obvious solution.
Do you guys have figured out a UIImage caching mechanism with Asset Catalogs?
I would like to implement a category on UIImage similar to the following:
static NSCache *_cache = nil;
#implementation UIImage (Caching)
+ (UIImage *)cachedImageNamed:(NSString *)name {
if (!_cache) _cache = [[NSCache alloc] init];
if (![_cache objectForKey:name]) {
UIImage *image = ???; // load image from Asset Catalog without internal caching mechanism
[_cache setObject:image forKey:name];
}
return [_cache objectForKey:name];
}
+ (void)emptyCache {
[_cache removeAllObjects];
}
#end
Even better would of course be a way to have more control over UIImage's internal cache and the possibility to purge image data on low memory conditions as described in the documentation when using Asset Catalogs.
Thank you for reading and I look forward to your ideas!
UPDATE: Cache eviction works fines (at least since iOS 8.3).
I am running into the same issue (iOS 7.1.1) and I kind of though that #Lukas might be right
There is a high probability that the mistake is not inside Apple's ... caching but in your .. code.
Therefore I have written a very simple Test App (view full source below) where I still see the issue. If you see anything wrong with it, please let the me know about it. I know that it really depends on the image sizes. I only see the issue on an iPad Retina.
#interface ViewController ()
#property (nonatomic, strong) UIImageView *imageView;
#property (nonatomic, strong) NSArray *imageArray;
#property (nonatomic) NSUInteger counter;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.imageArray = #[#"img1", ... , #"img568"];
self.counter = 0;
UIImage *image = [UIImage imageNamed:[self.imageArray objectAtIndex:self.counter]];
self.imageView = [[UIImageView alloc] initWithImage: image];
[self.view addSubview: self.imageView];
[self performSelector:#selector(loadNextImage) withObject:nil afterDelay:1];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
NSLog(#"WARN: %s", __PRETTY_FUNCTION__);
}
- (void)loadNextImage{
self.counter++;
if (self.counter < [self.imageArray count])
{
NSLog(#"INFO: %s - %lu - %#",
__PRETTY_FUNCTION__,
(unsigned long)self.counter,
[self.imageArray objectAtIndex:self.counter]);
UIImage *image = [UIImage imageNamed:[self.imageArray objectAtIndex:self.counter]];
self.imageView.frame = CGRectMake(0, 0, image.size.width, image.size.height);
[self.imageView setImage:image];
[self performSelector:#selector(loadNextImage) withObject:nil afterDelay:0.2];
} else
{
NSLog(#"INFO: %s %#", __PRETTY_FUNCTION__, #"finished");
[self.imageView removeFromSuperview];
}
}
#end
Inplace Implementation
I wrote some code to keep the image asset but load it with imageWithData: or imageWithContentsOfFile: use xcassets without imageNamed to prevent memory problems?

UIButton imageForState is not correct after the app returns from being in the background

I have a UIButton declared in the .h file as
#property (retain, nonatomic) IBOutlet UIButton *btnQuarter;
When I have
if ([self.btnQuarter imageForState:UIControlStateNormal] == [UIImage imageNamed:#"match.png"])
as soon as the app loads it works perfectly. However when the app returns from being locked or being on the home screen it doesn't work.
try this code:
- (IBAction)nextQtr:(id)sender
{
if ([[sender imageForState:UIControlStateNormal] isEqual:[UIImage imageNamed:#"match.png"]])
}
It doesn't work, because you're comparing pointers to objects. In your case they point to different UIImages, even though the images themselves contain the same data. [UIImage imageNamed:] caches the image, which is why on app startup it works properly, however when you enter background the cache is released (I think) in order to free memory. When you return from the background to foreground, the [UIImage imageNamed:] call creates the same UIImage, but at another memory address.

Adding an image to a NSObject - Possible?

I'm going to have objects, read from a .csv (Comma seperated file) which contains file names, later. Thats why i'm trying to do it like this.
What I would like to accomplish is to have the button get the image from the filename in the csv.
What i've tried so far:
The Object:
#interface question : NSObject
{
#private
UIImage *imageName;
}
#property (nonatomic, retain)UIImage *imageName;
(I have tried with NSString aswell as UIImage, neither works like I want them)
ViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
question *question1 = [[question alloc]init];
[question1.imageName initWithContentsOfFile:#"Default.png"];//Will be read from a csv later on, just typing it in manually for a test
testImage = question1.imageName;
}
-(void)imageshow
{
//(UIImage *)testImage;(NSString *)imageNameString; Didnt work
//[self.testImage initWithContentsOfFile:#"%#",questiont.imageName]; - Doesnt work either
[testButton setImage:testImage forState:UIControlStateNormal];
}
-(IBAction)test:(id)sender
{
[self imageshow];
}
Where this code fails is when I press the test button, nothing happens (not crash)
How can I do this properly?
(Add an image with a text filename to a NSObject and then show it in a button/image view)
question1.img = [UIImage imageNamed:#"Default.png"];
[testButton setImage:question1.img forState:UIControlStateNormal];
and yes you can try the following for dynamic images
question1.img = [UIImage imageNamed:[NSString stringWithFormat:"%#",imgName]];
imgName => is the name of the image not the image
example: "abc.png" or "123.png" ,etc
if your property is named imageName the accessor variable is automaticly named _imageName.
You are using two different variables in your code.
#interface question : NSObject
{
#private
UIImage *_imageName;
}
#property (nonatomic, retain)UIImage *imageName;
imageName is not a great name for an property that contains an image.. it would better fit to a string.
There are some things to change.
For mere conventional reasons write all class names with an upper case at the beginning. It eases reading, exchange in forums as SO and avoids mistakes.
Second name variables either speaking or abstract, preferrably speaking. But to not choose a mislieading name such imageName. There should be a name in that variable, not an image, or maybe the graphic that displays a name. :)
imageWithContentsOfFile expects a fully qualified file name. If "Default.png" is part of your boundle, as I guess by its name, then imageNamed:#"Default.png" would do.
Your setImage looks nearly good. It cannot work of course while your image is (null). However, it is not good business practis accessing the iVar direct at this point. You shoudl use its setter/getter. Use instead:
[self.testButton setImage:self.testImage forState:UIControlStateNormal];
In your case it does not make much of a difference. But similar to the other conventions it helps avoiding errors when ever you mix your code with some code that you found on the web or when you work in a team.

How to get filesystem path to image cached with SDWebImage (iOS)

I'm using SDWebImage for image caching at UICollectionView in my iOS app.
Everything is fine, however, when user scrolls fast over the collection view there is always little pause before placeholder is replaced with the cached image. I believe this is due to cache checks. For better user experience I would like cells to show the proper image instead of placeholder once an image is actually cached. This would be easy if I could get local (device) filesystem's path of the cached image and store it at the Show instance, and use it (if exists) instead of my placeholder.
There is the possibility to pass success block to the setImageWithURL method However, the only argument it gets is UIImage instance (actually in master branch there is also BOOL variable called cached being passed too). So - is there a possibility to get image's filesystem path straight from the UIImage instance? Or should I modify SDWebImage so it pass that information along to cached instance? Or is there any better way achieve a goal I described earlier?
My Show class has imageURL and here is how show cell use SDWebImage:
#import "ShowCell.h"
#import <SDWebImage/UIImageView+WebCache.h>
#implementation ShowCell
#synthesize imageView = _imageView;
#synthesize label = _label;
#synthesize show = _show;
- (void)setShow:(Show *)show
{
if (_show != show) {
self.label.text = show.title;
if (show.imageURL) {
[self.imageView setImageWithURL:[NSURL URLWithString:show.imageURL]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
}
_show = show;
}
}
#end
There are private SDImageCache methods to get filesystem path to cached image. We can make these methods public with a category. Just put the code below into the, say, SDImageCache+Private.h and add it to your project:
#import "SDImageCache.h"
#interface SDImageCache (PrivateMethods)
- (NSString *)defaultCachePathForKey:(NSString *)key;
- (NSString *)cachedFileNameForKey:(NSString *)key;
#end
I had a similar problem, see: SDWebImage showing placeholder for images in cache
Basically I forked the project to resolve this.
Update:
You can just do this to avoid the flickering (this works with the master branch of the project):
[imageView setImageWithURL:url placeholderImage:imageView.image options:0
progress:^(NSUInteger receivedSize, long long expectedSize) {
imageView.image = [UIImage imageNamed:#"QuestionMarkFace.png"];
} completed:nil];

Resources