I have two arrays that include 5 pictures of a background with a solid color. This is the code used to put the images into the arrays:
self.colorArray = #[
[UIImage imageNamed:#"orange_square"],
[UIImage imageNamed:#"purple_square"],
[UIImage imageNamed:#"red_square"],
[UIImage imageNamed:#"blue_square"],
[UIImage imageNamed:#"green_square"],
];
self.iconColorArray = #[
[UIImage imageNamed:#"orange_icon"],
[UIImage imageNamed:#"purple_icon"],
[UIImage imageNamed:#"red_icon"],
[UIImage imageNamed:#"blue_icon"],
[UIImage imageNamed:#"green_icon"],
];
As you can see the images are respectively put in order in terms of the color of their background. (I use different images for the arrays because iconColorArray is being used for a smaller UIImageView)
The app randomly changes the images of the two UIImageViews.
What I want is a if-else statement to compare the two arrays to see if the same objectAtIndex is being used. Exp: If orange_square is used for one UIImageView and orange_icon is being used for the other, the condition in the if-else statement will return true.
Basically to answer my question, just tell me how I would get the index of an object being used by a UIImageView in a array.
Edit:
Using Matt's advice, I changed the code to:
NSUInteger d = [self.colorArray indexOfObject:self.squareOne.image];
NSUInteger e = [self.iconColorArray indexOfObject:self.icon.image];
Now, I can compare them.
The way you get the index of an object is with (wait for it) indexOfObject:.
The problem is that the use of this method presupposes that this object can be compared for equality. I don't know whether UIImage does; I rather doubt it, but you can try.
Another possibility is indexOfObjectIdenticalTo:; this might work if the image is not copied by the image view but is an actual reference to one and the same image as the image in the array.
Having said all that, which would I do? None of them. I wouldn't even keep arrays of images; it's terribly wasteful of memory, and your app is likely to crash before it gets off the ground. What I would do is keep an array of the names of the images; strings are tiny, images are huge. And in order to make the comparison, I would subclass UIImageView to have a name property so that I could assign the name as well as the image, and now the problem is trivial.
Or you could even display the images through a view controller of their own, and give the view controller the name property. As a matter of fact I happen to have an example of doing that:
https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/bk2ch06p311pageController/ch19p626pageController/Pep.swift
And in fact if you explore the rest of that project you will see that I the proceed to do exactly what you are asking to do: to learn what pep boy is being displayed, I ask the Pep object for its boy string and I look up its index in a list of Pep boys:
https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/bk2ch06p311pageController/ch19p626pageController/AppDelegate.swift
It's in Swift so I use find instead of indexOfObject: but it amounts to the same thing.
You could create a third object as a dictionary, where the image square names names are the keys are the keys. And then you colorArray and iconColorArray could derive from this dictionary.
self.imageMap = { #"orange_square" : #"orange_icon",
#"purple_square" : #"purple_icon" }
- (BOOL)squareUsed:(UIImage *)square isSameAsIcon:(UIImage *)iconUsed {
for (NSString *key in self.imageMap) {
if ([[UIImage imageNamed:key] isEqual:square]) {
if ([UIImage imageNamed:self.imageMap[key]] isEqual:iconUsed]) {
return YES;
}
// We matched the square and the icon didn't match.
return NO;
}
}
// We never matched the square. Assert?
return NO;
}
Related
I'm new to Objective-C so sorry if this is a newbie question.
I've searched for a couple of hours and can't seem to find an answer to my question.
So I'm trying to access a UIImageView so I can hide/unhide it by concatenating strings together to get the name of the UIImageView which should hide/unhide.
I have it working by doing:
self.faceItemEyesFrightened.hidden = false;
However the Frightened part of the name could be different each time a button is clicked so, trying to refactor my code I run a function which returns the type of UIImageView should be affected.
So I have the following:
NSString *fullEmotionString = [#"faceItemEyes" stringByAppendingString:emotionIs];
where emotionIs would be Frightened, therefore forming
faceItemEyesFrightened
So my problem comes when I wish to do something like this:
self.fullEmotionString.hidden = false;
Obviously that's not the right way of doing it but I'm not sure how it should be done, any advice greatly appreciated.
Cheers!
You could use NSSelectorFromString like this:
SEL selector = NSSelectorFromString(fullEmotionString);
UIImageView *imageView = [self performSelector:selector];
imageView.hidden = NO;
Note, that this requires a getter called faceItemEyesFrightened to be defined, which is usually this case if you're using properties and didn't change the name of the accessors.
That being said, I think this is not an optimal solution to your problem.
You could for instance subclass UIImageView and add an enum MyImageViewEmotion that describes the emotion in the image. Then, instead of using lots of variables, like faceItemEyesFrightened or faceItemEyesHappy, you could put all of them in a simple array and then get one of them like this:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"self.emotion == %ld",
MyImageViewEmotionFrightened];
MyImageView *imageView = [eyeImageViews filteredArrayUsingPredicate:predicate][0]
Of use an NSDictionary, where you put the emotion string as the key and the image views as the value. Then you could access them very easily:
UIImageView *imageView = emotionViewDictionary[#"Frightened"];
By the way, boolean values in Objective-C are called YES and NO and not true and false.
I have a (retained) UIImage property that is being used to hold a user selected image.
This is the code I have at present when the user makes a selection:
- (IBAction) selectImage1 {
UIImage *image = [UIImage imageNamed: #"image1-big.png"];
self.bigImage = image;
}
but I'm wondering if it is possible to omit the use of the temporary variable convenience method and just do this:
- (IBAction) selectImage1 {
self.bigImage = [UIImage imageNamed: #"image1-big.png"];
}
If there are problems with this second method (I'm guessing something to do with memory management), could someone please explain?
Thank you!
The second way is perfectly fine. The line UIImage *image = [UIImage imageNamed: #"image1-big.png"]; gives you a variable image that is auto-released. Assigning it to your ivar via the self.bigImage = image calls bigImage's setter method which retains the value. Thus the line self.bigImage = [UIImage imageNamed: #"image1-big.png"]; is equivalent to the more verbose way.
There is no difference in terms of memory management between the two snippets you posted; unless you get really specific about retain counts in between the two lines in the first snippet.
In an ARC environment, the local variable will be a 'strong' pointer, however it is released when the method leaves scope. In the second snippet, there is no intermediate retain/release'd pointer, and so may actually be slightly more efficient.
The places I have seen the first snippet's technique be necessary are when you have a weak pointer (i.e. a weak #property) where setting self.foo = [UIView ... would immediately allow it to be released. In these cases it is better to use a local variable to keep it around while you work with it:
UIView *someFoo = [UIView...
[self addSubview:someFoo];
self.someWeakProperty = someFoo;
compare with:
self.someWeakProperty = [UIView...
[self addSubview:self.someWeakProperty]; // it's already nil!!
When setting the image for a button, I use stringWithFormat: like so:
[buttonImage setImage:[ImgUtil image:[NSString stringWithFormat:#"myImage_%d.png", selectNum + 1 ]] ];
I want to inspect that string. I thought maybe I could get the name back from the button:
if (buttonImage.image == [UIImage imageNamed:#"myImage_2.png"]) {
NSLog(#"the name of the buttonImage is %#", buttonImage.image);
}
but that doesn't work. How can I look at that string?
You could use associated references to attach a string the key "name" at load time. You create the UIImage from a file, and attach the name using the objective-c associated references API: here.
You can also sub-class UIImage to store an extra name.
You can even add a category to provide an easy API.
If what you want is to "test what the "myImage_%d.png" ends up being" in the following line:
[buttonImage setImage:[ImgUtil image:[NSString stringWithFormat:#"myImage_%d.png", selectNum + 1 ]] ];
Then I would suggest that you reformat and simplify your code. It will give you the additional advantage of making it easier to read:
NSString* imageName = [NSString stringWithFormat:#"myImage_%d.png", selectNum + 1 ];
NSLog(#"imageName is %#", imageName);
[buttonImage setImage:[ImgUtil image:imageName]];
No, you can't.
buttonImage.image is a UIImage stored in memory inside the button.
[UIImage imageNamed:#"myImage_2.png"] creates an entirely different UIImage. Both UIImages could very well have been created from the same file--in this case, #"myImage_2.png"--but they are two separate UIImages in memory.
The == check in your line:
if(buttonImage.image == [UIImage imageNamed:#"myImage_2.png"])
Does not check if the UIImages were created from the same file; it checks if they are pointing to the same location in memory. Which they are not, because they are two separately created and stored UIImage instances.
--
So, no--you cannot do this. Something that might solve your problem another way, though, is to subclass UIButton and add a properly NSString* imageFilename. (If you're setting different images for each control state, you'd need more than one variable to store those image file names in). Then override the setImage:forControlState method of the UIButton subclass and store the filename there every time the image is changed. Then you can perform the following check:
if([imageFileName isEqualToString:[[NSString stringWithFormat:#"myImage_%d.png", selectNum + 1 ]])
And that would get you the answer you want!
You can store the UIImage as instance of the class, and compare it. You won't be using more memory than a pointer.
selectNum stands for the selected image, right?If so, try to get selectNum when picking the picture.
I've been looking at the Stanford University iphone videos on iTunes U. And saw the teacher trying to do something similar to this code but he realised and said it didn't work though I didn't get why:
- (IBAction)flashPressed{
if (window.backgroundColor == [UIColor magentaColor]){
window.backgroundColor = [UIColor redColor];
}else {
window.backgroundColor = [UIColor magentaColor];
}
}
Objective-C, windows based application. Not sure what else you need to know.
The reason it doesn't work is that UIView's backgroundColor is a copy property. It's declared like this:
#property(nonatomic, copy) UIColor *backgroundColor;
That means that when the color object that you get from [UIColor redColor] is set as the backgroundColor, the whole object is copied, and the copy retained by the UIView will be on a different memory address than the one retained by the UIColor class object.
== checks if the pointers are the same, which means that it will succeed only if the two pointers point to the very same object. This is what you want to do sometimes. For example:
if ([aView superView] == self)
[aView removeFromSuperview];
Here, you want to be sure that aView's super view is actually this very object, not just one that is "the same" according to some criteria.
But when you compare two strings, you are (almost always) interested in whether they contain the same characters, and it doesn't matter whether they are on different memory addresses. Thus you use:
if ([aString isEqualToString:anotherString]) // faster than isEqual:
And in our example with the colors, it's the same: we want to know whether the two objects both represent a red color, not whether the two pointers point to the exact same object.
If the backgroundColor property was declared as retain you could have used ==, and it would have worked until UIColor for some reason reallocated its redColor object. That's not likely to happen, but it's to underscore that the object represents a unique thing that objects like strings and colors are usually copied rather than ´retained´. There can be only one color red, and there can be only one string that containing the characters "Hello world!". So it comes down to a metaphysical argument in the end.
To check if two UIColors are equal, use the isEqual: message instead of the == operator.
if ([window.backgroundColor isEqual:[UIColor redColor]]) {
NSLog(#"Yup, it's red");
} else {
NSLog(#"OMG, it's not red!");
}
// result --> Yup, it's red
That's a general pattern for comparing objects rather than using == like you do for primitives like ints or floats. NSString works the same way.
Too much information section:
The pattern for objects that have a defined order is to give them a compare: method that returns a NSSortDescriptor.
How can I find out what the image name (usually file name?) loaded into a UIImage instance is? In this case, they were all initWithContentsOfFile:.
Once it's loaded using that, you can't. The file name is only used to load the data into memory. After that, it becomes irrelevant.
If you need to maintain the file name, then perhaps you need to make a class with the filename (as an NSString or NSURL, perhaps) and the image data (as an NSImage or NSData object) as ivars. I'm not sure what your application is, so I can't give you any architectural advice at this point.
If you already know what the image names are you can compare your image to another image using image named:
if (bg.image == [UIImage imageNamed:#"field.jpeg"]) {
bg.image = [UIImage imageNamed:#"cave.jpeg"];
} else {
bg.image = [UIImage imageNamed:#"field.jpeg"];
}
Here I use one button to switch between two images.
It's also something you can't just add a category on UIImage to, unfortunately, because it requires state.