Execute selector to find UIImageView using concatenated NSString - objective-c

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.

Related

Checking whether two objects being used in two arrays are the same

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;
}

Inspecting the string created by stringWithFormat:

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.

How to choose a method based on an element of an NSArray (Objective-C)

I'm writing a sort of calculator app. I have a UIPickerView (1 column) loading data from an NSArray of strings. The user will select one of these (it's selecting which type of calculator to use -- each uses a different method to calculate). The user inputs some things into some UITextFields and then presses a UIButton to do the calculations.
My NSArray is this:
calcNames = [NSArray alloc] initWithObjects:#"first", #"second", #"third", nil];
And my methods are called firstCalc(input1, input2, input3), secondCalc(input1, input2, input3), and so on. (The inputs are coming from the UITextFields.)
When I press the button, I would like to tell it to look at what the selection in the UIPickerView is and run the corresponding method without just typing an if-then statement for each one (it's very inconvenient to do this for reasons specific to my app, which are beyond the scope of this discussion).
So I have already defined a way to determine what the selected calc is:
selectedCalc = [[NSString alloc] initWithString:[calcNames objectAtIndex:row]]
where 'row' is the current selection in the UIPickerView.
Now I have a doCalculations method for when someone presses the UIButton:
-(IBAction)doCalculations:(id)sender {
// save the data input
double input1 = [input1Field.text doubleValue];
double input2 = [input2Field.text doubleValue];
double input3 = [input3Field.text doubleValue];
// do the calculations
int i;
for (i = 0; i < [calcNames count]; i++) {
if (selectedCalc == [calcNames objectAtIndex:i]) {
// do calculations here
double numResult = ??????
// if selectedCalc is "first", I want it to do firstCalc(input 1, input 2, input 3)
// if selectedCalc is "second", I want it to do secondCalc(input 1, input 2, input 3), and so on
// the rest is just for displaying the result
NSString* result = [NSString stringWithFormat:#"The answer is %f", numResult];
[resultLabel setText:result];
}
}
}
So basically, it runs a for loop until it finds which calculator is selected from the UIPickerView and when it finds it, runs the calculations and displays them.
I've been trying to understand if maybe function pointers or selectors (NSSelectorFromString?) are the right things to use here and how to use them, but I'm really struggling to understand where to go after a couple days of reading Apple's documentation, Stack Overflow questions, playing with sample code, and tinkering with my own code.
Sorry if the question is too lengthy, I thought it may be more helpful to others looking for assistance in the future to see the full idea. (At least I know sometimes I'm lost with these question pages.)
I would be very grateful for any assistance,
Ryan
You can dynamically invoke a method using a selector. You could for example have a secondary array to calcNames with selector called calcSelectors:
SEL calcSelectors[] = (SEL[3]){
#selector(first:arg:),
#selector(second:arg:),
#selector(third:arg:)};
Calling the right method would then be as simple as:
[self performSelector:calcSelectors[calcIndex] withObject:arg1 withObject:arg2];
If you need more then 2 arguments, then you also need to mess a bit with a NSInvocation instance to setup the call.
Example 1:
NSString *method=[calcNames objectAtIndex:0];//here play with objectatindex
SEL s=NSSelectorFromString(method);
[self performSelector:s];
which will call this method
-(void)first{
NSLog(#"first");
}
-----------------------------------------
Example 2:
NSString *totalMethodName;
totalMethodName=#"vijay";
totalMethodName=[totalMethodName stringByAppendingString:#"With"];
totalMethodName=[totalMethodName stringByAppendingString:#"Apple"];
SEL s=NSSelectorFromString(totalMethodName);
[self performSelector:s];
will call
-(void)vijayWithApple{
NSLog(#"vijayWithApple called");
}
You can make use of NSInvocation to dynamically bind multiple arguments to a selector. Follow this post to learn it.
If you are going to use NSInvocation you have to define your methods in the objective-C way something like the following.
- (double)firstCalcWithInput1:(double)input1 input2:(double)input2 andInput3:(double)input3;
- (double)secondCalcWithInput1:(double)input1 input2:(double)input2 andInput3:(double)input3;

How do I receive the current value of a slider in code?

I am (attempting) developing my first application in Xcode using cocoa framework.
I have a slider which min value is 10 and max is 50. This is to select max search results.
I have linked a label on my user interface to display the value of the slider and when it is moved, it updates the label on the user interface.
However, I am trying to join around 4 strings to create my final URL one of them is the value of said label.
I am trying to read the value of the label on the interface for use in creating the finished URL
NSString *startofURL = #"http://starturl.com/?q=";
NSString *searchTerm = whatToSearch;
NSString *middleofURL = "&max-results=";
NSString *resultsStr = labelMaxResults.stringValue; //Problem here ??
I have 2 questions; firstly, How do I go about retrieving the value of my slider via code instead of trying to get it from the linked label, as I think this is my problem.
secondly, I have read up on joining and appending strings, however I am a little confused on which is the best method to use in order to join up the 4 strings into one long URL.
NSSlider * slider = [[NSSlider alloc] init];
[slider setMinValue:50];
[slider setMaxValue:150];
int sliderValue = [slider intValue];
this doesn't put your slider on screen, but assume you made it in IB, ignore the first line, you can set your min max and get the value.
you can make an action like
-(IBAction)sliderMoved:(id)sender
then bind that to the slider, if you set the slider to continuous you will get updates every time that it moves other wise just when you let go of the slider
-(IBAction)sliderMoved:(id)sender
{
sliderValue = [slider intValue];
[self doSomethingElseNow];
}
For the first part of your question, it depends which OS you are using as to which process to use.
If you are using OS X, the best way to get a value for your slider is to create a binding in IB for your slider. To do this, click on the slider in IB and go to the bindings section. In the Value section, click on Value and select the class you wish to use the value in as your Bind To class (I'm going to use MyClass for this example). Then for the model key path, assign it to some value of your choice. For the purpose of this, I'll just call it sliderValue.
Then in MyClass.h, you must set up the following:
#interface MyClass : <your class type> {
int sliderValue;
}
#property (readwrite, assign) int sliderValue;
In MyClass.m, you'll need to synthesize the value.
#synthesize sliderValue;
At this point, you should be able to get the value of your slider at any point in your code by calling [self sliderValue].
If you are, however, using iOS, then all you have to do is call the value property from your slider. So if you have the a UISlider *mySlider, all you have to do is call mySlider.value to get the current value of your slider.
For your second question, you can go about this two ways. If you want to append the strings, simply follow the format:
NSString *appendedString = #"";
appendedString = [appendedString stringByAppendingString:string1];
and so on until you have all your strings into your URL.
In your case, I would personally set up the entire URL string as a stringWithFormat:
NSString *urlString = [NSString stringWithFormat:#"http://www.starturl.com/%#%#%#", whatToSearch, middleOfURL, resultsStr];
Insert the values you want into the URL that way by setting up whatToSearch, etc., to the values you want. This way, you don't have to worry about appending everything together
Your code should look like this:
NSString *urlString = [NSString stringWithFormat:#"http://starturl.com/?q=%#&max-results=%f", whatToSearch, [slider floatValue]];
EDIT: Corrected my answer. For UISlider it should be value property. For NSSlider you should use - floatValue method

Use an If statement with the button name in Objective-C (Cocoa/iPhone SDK)

I have to implement a small feature in an iPhone app and was wondering if there was a way to do use an if statement where the condition is the string of a button.
Here’s a sample of the code in question:
- (IBAction)someMethod:(id)sender{
UIButton *button = (UIButton *)sender;
if ( button.titleLabel.text == “SomeText” )
{
//do something
}
else
{
// some other thing
}
Now I can’t make it work, because I think I’m using the wrong code in button.titleLabel.text. I’ve even tried #“SomeText”), but I always end up in //some other thing.
What am I doing wrong?
Thanks.
What you're currently doing is comparing two pointers to objects, the objects button.titleLabel.text and #"SomeText". As both point to different places in the memory, the comparison will return NO.
If you want to compare the values of both NSString objects, however, you can use [button.titleLabel.text isEqualToString:#"SomeText"].
Also note that "SomeText" is not the same as #"SomeText"! The first is a regular C string, where the last one is a Cocoa NSString object.