UIImageView - How to get the file name of the image assigned? - ios

Is it possible to read the name of an UIImageView's UIImage
that's presently stored in the UIImageView?
I was hoping you could do something kind of like this, but haven't figured it out.
NSString *currentImageName = [MyIImageView getFileName];

you can use setAccessibilityIdentifier method for any subclass of UIView
UIImageView *image ;
[image setAccessibilityIdentifier:#"file name"] ;
NSString *file_name = [image accessibilityIdentifier] ;

Nope. You can't do that.
The reason is that a UIImageView instance does not store an image file. It stores a displays a UIImage instance. When you make an image from a file, you do something like this:
UIImage *picture = [UIImage imageNamed:#"myFile.png"];
Once this is done, there is no longer any reference to the filename. The UIImage instance contains the data, regardless of where it got it. Thus, the UIImageView couldn't possibly know the filename.
Also, even if you could, you would never get filename info from a view. That breaks MVC.

No no no… in general these things are possible. It'll just make you feel like a dirty person. If you absolutely must, do this:
Create a category with your own implementation of +imageNamed:(NSString*)imageName that calls through to the existing implementation and uses the technique identified here (How do I use objc_setAssociatedObject/objc_getAssociatedObject inside an object?) to permanently associate imageName with the UIImage object that is returned.
Use Method Swizzling to swap the provided implementation of imageNamed: for your implementation in the method lookup table of the Objective-C runtime.
Access the name you associated with the UIImage instance (using objc_getAssociatedObject) anytime you want it.
I can verify that this works, with the caveat that you can't get the names of UIImage's loaded in NIBs. It appears that images loaded from NIBs are not created through any standard function calls, so it's really a mystery to me.
I'm leaving the implementation up to you. Copy-pasting code that screws with the Objective-C runtime is a very bad idea, so think carefully about your project's needs and implement this only if you must.

There is no native way to do this; however, you could easily create this behavior yourself.
You can subclass UIImageView and add a new instance variable:
NSString* imageFileName;
Then you could override setImage, first setting imageFileName to the filename of the image you're setting, and then calling [super setImage:imageFileName]. Something like this:
-(void) setImage:(NSString*)fileName
{
imageFileName = fileName;
[super setImage:fileName];
}
Just because it can't be done natively doesn't mean it isn't possible :)

if ([imageForCheckMark.image isEqual:[UIImage imageNamed:#"crossCheckMark.png"]]||[imageForCheckMark.image isEqual:[UIImage imageNamed:#"checkMark.png"]])
{
}

Nope. No way to do that natively.
You're going to have to subclass UIImageView, and add an imageFileName property (which you set when you set the image).

Neither UIImageView not UIImage holds on to the filename of the image loaded.
You can either
1: (as suggested by Kenny Winker above) subclass UIImageView to have a fileName property or
2: name the image files with numbers (image1.jpg, image2.jpg etc) and tag those images with the corresponding number (tag=1 for image1.jpg, tag=2 for image2.jpg etc) or
3: Have a class level variable (eg. NSString *currentFileName) which updates whenever you update the UIImageView's image

Or you can use the restoration identifier, like this:
let myImageView = UIImageView()
myImageView.image = UIImage(named: "anyImage")
myImageView.restorationIdentifier = "anyImage" // Same name as image's name!
// Later, in UI Tests:
print(myImageView.restorationIdentifier!) // Prints "anyImage"
Basically in this solution you're using the restoration identifier to hold the image's name, so you can use it later anywhere. If you update the image, you must also update the restoration identifier, like this:
myImageView.restorationIdentifier = "newImageName"
I hope that helps you, good luck!

This code will help you out:-
- (NSString *)getFileName:(UIImageView *)imgView{
NSString *imgName = [imgView image].accessibilityIdentifier;
NSLog(#"%#",imgName);
return imgName;
}
Use this as:-
NSString *currentImageName = [self getFileName:MyIImageView];

In short:
uiImageView.image?.imageAsset?.value(forKey: "assetName")
UIImage has an imageAsset property (since iOS 8.0) that references the UIImageAsset it was created from (if any).
UIImageAsset has an assetName property that has the information you want. Unfortunately it is not public, hence the need to use value(forKey: "assetName"). Use at your own risk, as it's officially out of bounds for the App Store.

Yes you can compare with the help of data like below code
UITableViewCell *cell = (UITableViewCell*)[self.view viewWithTag:indexPath.row + 100];
UIImage *secondImage = [UIImage imageNamed:#"boxhover.png"];
NSData *imgData1 = UIImagePNGRepresentation(cell.imageView.image);
NSData *imgData2 = UIImagePNGRepresentation(secondImage);
BOOL isCompare = [imgData1 isEqual:imgData2];
if(isCompare)
{
//contain same image
cell.imageView.image = [UIImage imageNamed:#"box.png"];
}
else
{
//does not contain same image
cell.imageView.image = secondImage;
}

You can use objective c Runtime feature for associating imagename with the UImageView.
First import #import <objc/runtime.h> in your class
then implement your code as below :
NSString *filename = #"exampleImage";
UIImage *image = [UIImage imagedName:filename];
objc_setAssociatedObject(image, "imageFilename", filename, OBJC_ASSOCIATION_COPY);
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
//You can then get the image later:
NSString *filename = objc_getAssociatedObject(imageView, "imageFilename");
Hope it helps you.

Get image name Swift 4.2
There is a way if you want to compare button image names that you have in assets.
#IBOutlet weak var extraShotCheckbox: UIButton!
#IBAction func extraShotCheckBoxAction(_ sender: Any) {
extraShotCheckbox.setImage(changeCheckBoxImage(button: extraShotCheckbox), for: .normal)
}
func changeCheckBoxImage(button: UIButton) -> UIImage {
if let imageView = button.imageView, let image = imageView.image {
if image == UIImage(named: "checkboxGrayOn") {
return UIImage(named: "checkbox")!
} else {
return UIImage(named: "checkboxGrayOn")!
}
}
return UIImage()
}

Swift 3
First set the accessibilityIdentifier as imageName
myImageView.image?.accessibilityIdentifier = "add-image"
Then Use the following code.
extension UIImageView {
func getFileName() -> String? {
// First set accessibilityIdentifier of image before calling.
let imgName = self.image?.accessibilityIdentifier
return imgName
}
}
Finally, The calling way of method to identify
myImageView.getFileName()

I have deal with this problem, I have been solved it by MVC design pattern, I created Card class:
#interface Card : NSObject
#property (strong,nonatomic) UIImage* img;
#property (strong,nonatomic) NSString* url;
#end
//then in the UIViewController in the DidLoad Method to Do :
// init Cards
Card* card10= [[Card alloc]init];
card10.url=#"image.jpg";
card10.img = [UIImage imageNamed:[card10 url]];
// for Example
UIImageView * myImageView = [[UIImageView alloc]initWithImage:card10.img];
[self.view addSubview:myImageView];
//may you want to check the image name , so you can do this:
//for example
NSString * str = #"image.jpg";
if([str isEqualToString: [card10 url]]){
// your code here
}

use below
UIImageView *imageView = ((UIImageView *)(barButtonItem.customView.subviews.lastObject));
file_name = imageView.accessibilityLabel;

The code is work in swift3 - write code inside didFinishPickingMediaWithInfo delegate method:
if let referenceUrl = info[UIImagePickerControllerReferenceURL] as? NSURL {
ALAssetsLibrary().asset(for: referenceUrl as URL!, resultBlock: { asset in
let fileName = asset?.defaultRepresentation().filename()
print(fileName!)
//do whatever with your file name
}, failureBlock: nil)
}

Related

Inability to create my UIImageView classes from string

I am creating subclasses of UIImageView class, something like LittleImageView, RedImageView, etc.
These subclasses have this convenience method for the creation of specific images:
+ (UIImageView *)novo {
UIImageView *newImage = [[super alloc] initWithImage:...
// do stuff
return newImage;
}
When I try to create such classes using this new command by string
id newObject = [NSClassFromString(nameOfClass) novo];
I get a crash with "unrecognized selector sent to class". Apparently objective-c is trying to do a [UIImageView novo] instead of doing a [RedImageView novo], for instance.
Any ideas how to solve this?
I can't reproduce your experience exactly, but there are a few changes you should consider: (1) declare the constructor as returning the derived type, (2) declare the local variable as the derived type, (3) used the derived class to alloc (self, not super)...
// in MyImageView.h
+ (instancetype)myImageView:(UIImage *)image;
// in MyImageView.m
+ (instancetype)myImageView:(UIImage *)image {
MyImageView *miv = [[self alloc] initWithImage:image];
// ...
return miv;
}
Now you can use elsewhere without the sketchy use of id in your local variable declaration. It looks like this, and in my test, generates instances of the correct type...
MyImageView *firstTry = [MyImageView myImageView:nil];
MyImageView *secondTry = [NSClassFromString(#"MyImageView") myImageView:nil];
NSLog(#"%#, %#", firstTry, secondTry);

Unable to set UIImage, always returns nil

This is probably a ridiculous question but I just can't figure it out for the life of me. I have a class which subclasses NSObject which has a property called image setup, like this:
#property (strong, nonatomic) UIImage *image;
Somewhere else I initialize the class and attempt to set the image in the class, like this:
UIImage *image = [UIImage imageNamed:#"Test"];
Class *item = [[Class alloc] init];
item.image = image;
But when I try to call item.image it is always nil.
The image called Test is in the project and is used elsewhere so I know it has access to the image, but for some reason it isn't being copied to the instance of my class. I have put NSLog messages and breakpoints directly after setting the image which have shown it to be nil as well as using commands via the debugger which have also shown the image in the class to be nil while the UIImage I create to copy is not nil.
I know I am doing something wrong but I have searched and can't figure out what it is.
Thanks for looking, any help is much appreciated.
edit:
I am trying to access the image from a method inside of the class.
- (void)save {
if (self.image != nil) {
// do some stuff
} else {
NSLog(#"image is nil");
}
}
And it is always coming up nil.
edit 2:
Thanks again everyone. I figured out what the problem was. I was overriding the setters/getters which was causing the issue.
Is it a .png file? Try doing it like this:
UIImage *image = [UIImage imageNamed:#"Test.png"];

How to check NSCache instance value set or not

I have an instance of NSCache, like: NSCache *imageCache; It is basically used for holding some remote "image" values with different "#keys". I alloc & init NSCache in a Global class at the beginning and set a if else condition, like this:
if (self.imageCache == nil)
{
self.imageCache = [[NSCache alloc] init];
}
else
{
NSLog(#"cache set");
}
I #import that "Global Class" in all of ViewControllers, so that I don't have to parse images every time. But the problem is when I go to other ViewControllers It seems like, NSCache alloc & init every time. Because It takes same time to load the images as 1stVC. I think the if else condition didn't working perfectly or it's not the appropriate way to check either NSCache set or not.
Can anyone tell me whats wrong with it? One thing more, The imageCache is used from a global variable.
Thanks in advance.
Have a good day.
ADDITION:
This is the method where I load the UIButtons in UIScrollView as subView. This is a UIViewClass which I add in my "EveryViewController" as a subView Just take have a look on the if (cachedImage) line. It works fine. But when I want to check either the NSCache (iADImageCache) set or not, it shows me it's not set. But which should be set. In this situation how can I check all several iADImageCache with their different #"Key" name?
Thanks again.
-(void) loadUIButton
{
[self loadScrollView];
for (int i = 0; i < [iADDisplayArray count]; i++)
{
adButtonOutLet = [[UIButton alloc] initWithFrame:CGRectMake(i*320, 0, ButtonWidth, ButtonHight)];
currentAd = [iADDisplayArray objectAtIndex:i];
NSString *path = currentAd.bannerIconURL;
NSURL *url = [NSURL URLWithString:path];
NSMutableURLRequest *requestWithBodyParams = [NSMutableURLRequest requestWithURL:url];
NSData *imageData = [NSURLConnection sendSynchronousRequest:requestWithBodyParams returningResponse:nil error:nil];
UIImage *originalImage = [UIImage imageWithData:imageData];
UIImage *cachedImage = [self.iADImageCache objectForKey:currentAd.bannerIconURL];
if (cachedImage)
{
[adButtonOutLet setImage:cachedImage forState:UIControlStateNormal];
//NSLog(#"OnecachedImage %#", cachedImage);
}
else
{
[self.iADImageCache setObject:originalImage forKey:currentAd.bannerIconURL];
[adButtonOutLet setImage:originalImage forState:UIControlStateNormal];
NSLog(#"OneimageCache %#", self.iADImageCache);
}
adButtonOutLet.userInteractionEnabled= YES;
[adButtonOutLet setTag:i];
[adButtonOutLet addTarget:self action:#selector(goToURL:) forControlEvents:UIControlEventTouchUpInside];
[self.iADScrollView addSubview:adButtonOutLet];
}
}
You could make a singleton class, instance at application did finish launching and use it wherever you are, through all the view controllers. In the singleton class put a property
#property(nonatomic,strong) NSCache* imageCache;
then instance it just once in the singleton class init method. In this way you don't have to care about it, and you can just add images to that cache. Of course you have to check if the image is cached or not based on the existance of a key inside that cache.
NSCache* globalCache = [SingletonClass sharedInstanceMethod].imageCache;
UIImage *imageX = [globalCache objectForKey: #"keyX"];
if (!imageX) {
// download your image
imageX = <Download method>;
[globalCache setObject: imageX forKey: #"keyX"];
}
// Do your stuff (like showing the image)
....
...
..
.
Hope it helps
Unless actually needed across the whole program, I would suggest another approach that would restrict the caching to the class that actually needs to use it by adding the class method:
+ (NSCache *)staticCacheForClass
{
static NSCache *staticCache;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
staticCache = [NSCache new];
staticCache.name = #"myCache";
});
return staticCache;
}
Using this - you restrict the changes to the cache to be internal to the class that uses it and avoid creating another singleton. The cache is also preserved across creating and destroying instances of this class.
That said, a singleton could be a viable solution if you need to access the cache from different classes, since a cache singleton is not really a global state.
Cheers.

CUICatalog: Invalid asset name supplied: , or invalid scale factor: 2.000000

I have a UiVIewController in which i have dragged a table view and put all the needed connections like delegate and data source and it works just fine, everything is great. I tried to set a background to this table view , and the i got this weird error
CUICatalog: Invalid asset name supplied: , or invalid scale factor: 2.000000
I tried to set the background using this method :
UIImageView *tempImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"mypredictions_bg.png"]];
[tempImageView setFrame:self.tableView.frame];
self.tableView.backgroundView = tempImageView;
What am i missing ? I checked the name of the picture is correct
In My case I made category for UIImageView and UITextfied,
In That, sometimes I don't need an image, that time I supplied #"" (i.e. Null string), it occurs problem, after some R&D I supply just "nil" instead of #"", it solves my warning, so maybe this type of warning occurs while not get proper image.
I made a debug class which swizzles imageNamed so that you can get a debug trace on where this is happening.
You need to install JRSwizzle to use it.
https://github.com/rentzsch/jrswizzle
#import "UIImageDebugger.h"
#import "JRSwizzle.h"
#implementation UIImageDebugger
+ (void)startDebugging
{
static dispatch_once_t once;
dispatch_once(&once, ^{
NSError *error=NULL;
[UIImage jr_swizzleClassMethod:#selector(imageNamed:)
withClassMethod:#selector(hs_xxz_imageNamed:)
error:&error];
if (error)
{
NSLog(#"error setting up UIImageDebugger : %#",error);
}
else
{
NSLog(#"!!!!!!!!!!!!!!!!!!!! UIImage swizzle in effect - take this out for release!!");
}
});
}
#end
#interface UIImage (UIViewDebugger)
+ (UIImage*)hs_xxz_imageNamed:(NSString *)name;
#end
#implementation UIImage (UIViewDebugger)
+ (UIImage*)hs_xxz_imageNamed:(NSString *)name
{
if (!name)
{
NSLog(#"null image name at \n%#",[NSThread callStackSymbols]);
}
UIImage *image=[self hs_xxz_imageNamed:name];
if (!image)
{
NSLog(#"failed to make image at \n%#",[NSThread callStackSymbols]);
}
return image;
}
#end
If your image type is png, you don't have to replace it end of the image filename because default XCode image type is already png. Try this;
UIImageView *tempImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:#"mypredictions_bg"]]];
In my case, was directing to the wrong viewController.
I was directing to the viewController that handled the content of a page based ViewController, instead of linking to the viewController in which the pageContentViewController as well as the PageViewController were linked. Hope this helps.

How to get a UIImage's image as an NSString?

I need to get the value of a UIImage's image as a string.
Previously I was doing:
if(images[imageNo].image == [UIImage imageNamed:#"image1.png"])
{
But now I have a lot more conditions so I would like to use a switch, doing:
switch(images[imageNo].image)
{
case #"image1.png":
break;
}
Is there a way to achieve this? Thanks!
Mmm I am not sure what you want to accomplish here but using [UIImage imageNamed:#"image1.png"], is definitely a very bad way to do the comparison as you are creating a new UIImage object on the fly for no reason at all, furthermore you are using the == operator when you should be using isEqual since they are objects.
I believe what you want to do is convert it to a base 64 string perhaps?. If so you an use this:
NSData *imageData = UIImageJPEGRepresentation(myImage, 1.0);
NSString *encodedString = [imageData base64Encoding];
The answer provided by H2C03 is appropriate. Use a dictionary to associate the names.
However, an alternative is using associative objects. Note that there is a cost in both space and time using associative objects, but they are convenient for just this type of case (adding a category-property).
#interface UIImage (MyImageLabel)
#property (nonatomic, copy) NSString *label;
#end
#import <objc/runtime.h>
#implementation UIImage (MyImageLabel)
static char const kImageLabelKey[1];
- (NSString*)label {
return objc_getAssociatedObject(self, kImageLabelKey);
}
- (void)setLabel:(NSString *)label {
objc_setAssociatedObject(self, kImageLabelKey, label, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
#end
Now, you can have a label property on your UIImage instances, like so:
myImage.label = someLabelStringCouldEvenBeFilename;
and
if ([myImage.label isEqualToString:someString]) {
}
The typical caveats apply regarding anything in a category. Most will encourage you to use a unique prefix or postfix to differentiate your category methods from potential future Apple names.
Note, that you could add another method to the category, like this...
+ (UIImage)myImageNamed:(NSString*)name {
id result = [self imageNamed:name];
[result setLabel:name];
return result;
}
and now you automatically set the label.
Of course, you could do this as a subclass, if you will always be creating your own image, and avoid the "nastiness" associated with associative objects (though all your images need to be MyUIImages).
#interface MyUIImage : UIImage
#property (nonatomic, copy) NSString *label;
#end
#implementation MyUIImage
// Now, you override imageNamed:
+ (UIImage*)imageNamed:(NSString*)name {
UIImage *image = [super imageNamed:name];
self.label = name;
return image;
}
#end
Both of your approaches are incorrect. The first one relies on caching (which is not documented), meanwhile the second is a syntax error - the 'case' keywords expect a compile-time constant expression.
As far as I know, there's no such a method in the iOS SDK that would return an image's filename - simply because it's not a property of the image (what would it return for a programmatically created image?). You should instead try to operate on an NSDictionary, storing the UIImage objects associated with the filenames as keys, and comparing the keys using isEqualToString:.
So you should create your images only once, and store them in a dictionary:
// assuming `images` is an already created instance variable of your class
[images setObject:[UIImage imageNamed:#"ImgOne.png"] forKey:#"ImgOne.png"];
// and so on, with every image you need
// then once you have to check against a file name, use:
UIImage *img = [images objectForKey:#"FileName.png"];
if ([someImage isEqual:img]) {
// you can now be sure that the image set to the object was once named "FileName"
}

Resources