UIImageView comparing images in game - iphone

I am working on a rock paper scissor shoe game, and I have no idea on how to do this. I have two UIImageView(s), and within each one is an image. I want to be able to compare the two images. I have looked up online, and cannot find anything. I want to be able to say
if([imageView1.image == rock] && [imageView2.image == scissor]) {
textLabel.text = #"You won!";
}
I know that this syntax is wrong of course, but I am trying to show the english part of what I am looking for. Any help is appreciated.
I do not have any kind of source code to show you as I do not know what I am doing with this. I am not looking for pixel to pixel comparison, or anything complex, I am only looking for a way to determine if the image is the same or not.

OK, Here is how I would solve this problem using enums. Firstly, declare your enum. You can call it whatever you like, i cam calling it RPS_STATE
enum RPS_STATE {
RPS_ROCK,
RPS_SCISSOR,
RPS_PAPER,
RPS_UNDEFINED
};
it's always useful to include an undefined state for init purposes. Now, these things defined in the enum are actually just integers from 0-3.
So you can use the following when you are setting your images.
-(void) setToRock:(UIImageView *) view {
view.image = rockImage;
view.tag = RPS_ROCK;
}
-(void) setToScissor:(UIImageView *) view {
view.image = scissorImage;
view.tag = RPS_SCISSOR;
}
-(void) setToPaper:(UIImageView *) view {
view.image = paperImage;
view.tag = RPS_PAPER;
}
then you can set and compare them nice and easy.
[self setToPaper:imageView1];
[self setToPaper:imageView2];
if(imageView1.tag == imageView2.tag){
}
and so on. You can also use enums as a type. eg
enum RPS_STATE variableHoldingOnlyRPSState;
Hope that helps :)

Related

Foreach statement on subviews doesn't seem to filter out what needs filtering out

My for statement seems to be returning more than it should be, and causing an error. Here is the code...
for (BookCustomCell *bcc in [self.tableView subviews])
{
if (bcc.rowIDTag == [bookID intValue])
{
//Do something here.
}
}
As I step through this everything seems to be fine. It goes through the For statement four times, and each time bcc.rowIDTag exists and has a value. It then goes through it a fifth time. bcc.rowIDTag does not exist and as a result it crashes (SIGABRT).
Why would it be going through a fifth time?
Conceptually, what am I missing? (The way I understand things it finds all the
BookCustomCells in the tableView subviews and then iterates through each of them. Is that correct?)
Is there a better way to accomplish my goal? (I have a tableview with multiple rows. Each
row has multiple buttons which are in a custom cell. When I click a button I need to act upon all of the other buttons within that one row/cell.)
(None of this programming stuff is coming easy for me, but I'm bound and determined to learn it. Thanks for your help.)
Your code is wrong, UITableView contains some subviews that are not UITableViewCell's, that's why you're getting SIGABRT crashes.
for (BookCustomCell *bcc in [self.tableView visibleCells])
{
if (bcc.rowIDTag == [bookID intValue])
{
//Do something here.
}
}
This should do the trick.
You trying to apply methods to a class that isn't BookCustomCell, this should be better for you.
for (UIView*tmp in [self.tableView subviews])
{
if ([tmp isKindOfClass:BookCustomCell])
{
BookCustomCell *bcc = (BookCustomCell*) tmp;
if (bcc.rowIDTag == [bookID intValue])
{
//Do something here.
}
}
}
Here we take all the views, check to see if it is a BookCustomCell, if it is, apply your code.
From your description, it sounds like you may be modifying subviews during the loop. Is that correct? You cannot modify the array during fast enumeration. SIGABRT means an exception is thrown. If you look in your console log, I suspect you will see something like "mutation during enumeration."
If you need to modify the array, you need to make a copy first:
NSArray *subviews = [[self.tableView subviews] copy];
for (BookCustomCell *bcc in subviews) {
...
}
[subviews release];
I do believe that SIGABRT arises because there is either no fifth bcc, either the fifth bcc doesn't have the rowIDTag property. So try adding a check if rowIDTag exists or isn't equal to nil.
I've never done it the way you did, but if I'd have to iterate through all the cells I'd fo the following:
for (int i = 0; i < [self.tableView numberOfRowsInSection:yourSectionHere]; i++) {
BookCustomCell *bcc = (BookCustomCell *)[self.tableView cellForRowAtIndexPath:yourIndexPathHere];
if (bcc.rowIDTag && bcc.rowIDTag == [bookID intValue] {
//do your stuff here
}
}
I don't have a Mac nearby, so there might be an error or typing mistake or...
Hope it helps

Adding object to multiple views

Im have a subclass of UIView, called PinView, which contains an image. Basically PinView gets added multiple times to my app, and then I perform a transform on PinView objects. The issue is that when I add a lot of PinViews, my app gets sluggish because I am transforming each PinView.
Ideally, I want to have one 'static' PinView that gets added to a UIView multiple times but i only have to transform it once. Currently this doesn't seem to work. Every time I add the static PinView to my UIView, it will only ever appear in the UIView once (due to it only being able to have one superview I'm guessing).
Im struggle to find out the best way to go about solving this problem - how do I use a single pointer to a PinView, add it multiple times to a UIView and be able to perform a transform on the PinView which gets passed on to PinViews displayed in my UIView? (by the way, the transform is always the same for all the PinViews).
Im assuming this will be the best way to get a performance improvement, if this is not the case please let me know.
UPDATE:
- (void)layoutSubviews
{
CGAffineTransform transform = CGAffineTransformMakeScale(1.0/self.zoomValue, 1.0/self.zoomValue);
NSMutableArray *mut = nil;
PinView *pinView = nil;
CallOutView *callOut = nil;
//get all dictionary entries
for(NSString *identifier in self.annotationsDict.allKeys){
//get the array from dictionary
mut = [(NSArray *)([self.annotationsDict objectForKey:identifier]) mutableCopy];
//change layout if not nil
if([[mut objectAtIndex:PIN] isKindOfClass:[PinView class]]){
pinView = ((PinView *)[mut objectAtIndex:PIN]);
pinView.transform = transform;
[mut replaceObjectAtIndex:PIN withObject:pinView];
}
if([[mut objectAtIndex:CALL_OUT] isKindOfClass:[CallOutView class]]){
callOut = ((CallOutView *)[mut objectAtIndex:CALL_OUT]);
callOut.transform = transform;
[mut replaceObjectAtIndex:CALL_OUT withObject:callOut];
if(pinView !=nil)callOut.center = CGPointMake(pinView.center.x, pinView.center.y - pinView.frame.size.height);
}
[self updateAnnotationsKey:identifier forObject:[NSArray arrayWithArray:mut]];
mut = nil;
pinView = nil;
callOut = nil;
}
}
UPDATE:
Removed the above and now just have:
- (void)layoutSubviews
{
CGAffineTransform transform = CGAffineTransformMakeScale(1.0/self.zoomValue, 1.0/self.zoomValue);
for(UIView *view in self.subviews){
view.transform = transform;
}
}
This can't be done I'm afraid. Each UIView instance can only be added to the screen once.
If all your views have similar transforms, you might have more luck using something like CAReplicatorLayer, which is a system for automatically creating duplicates of CALayers with different transforms.
That will only works if your views are all arranged in a grid or circle or something though. If they are just dotted randomly, it won't help.
If you are trying to draw more than 100 views, you're probably just bumping up against the fundamental performance ceiling of Core Animation on iOS.
The next approach to try would be to use OpenGL to draw your pins, perhaps using a library like Sparrow or Cocos2D to simplify drawing multiple transformed images with OpenGL (I'd recommend Sparrow as it integrates better with other UIKit controls - Cocos is more appropriate for games).
UPDATE:
This code is unnecessary:
mut = [(NSArray *)([self.annotationsDict objectForKey:identifier]) mutableCopy];
if([[mut objectAtIndex:PIN] isKindOfClass:[PinView class]]){
pinView = ((PinView *)[mut objectAtIndex:PIN]);
pinView.transform = transform;
[mut replaceObjectAtIndex:PIN withObject:pinView];
}
The code below is sufficient, because setting the transform doesn't modify the pointer to the object, so it will update the object in the array even if the array isn't mutable, and array objects are declared as 'id' so they don't need to be cast if you assign them to a variable of a known type.
mut = [self.annotationsDict objectForKey:identifier];
if([[mut objectAtIndex:PIN] isKindOfClass:[PinView class]]){
pinView = [mut objectAtIndex:PIN];
pinView.transform = transform;
}
I would also think you can remove the isKindOfClass: check if you only ever use those array indices for those object types. It may seem like a good precaution, but it carries a performance penalty if you're doing it over and over in a loop.
But for 10 views, I just wouldn't expect this to be that slow at all. Have you tried it without moving the callout centres. Does that perform better? If so, can you limit that to just the callouts that are currently visible, or move them using CGAffineTransformTranslate instead of setting the centre (which may be a bit quicker).

A better way than this, to find a UIImageView that may be in multiple arrays?

I need to find which array a certain imageView is in, then when found do stuff with the array. Here is my first approach:
BOOL pieceMatchInLeft = NO;
BOOL pieceMatchInRight = NO;
BOOL pieceMatchInNotMoved = NO;
for (UIImageView *pieceMatch in firstWordMoveLeftArrayCopy)
{
pieceMatchInLeft = piece == pieceMatch;
if (pieceMatchInLeft) break;
}
for (UIImageView *pieceMatch in firstWordMoveRightArrayCopy)
{
pieceMatchInRight = piece == pieceMatch;
if (pieceMatchInRight) break;
}
for (UIImageView *pieceMatch in firstWordLettersNotMovedArray)
{
pieceMatchInNotMoved = piece == pieceMatch;
if (pieceMatchInNotMoved) break;
}
if (pieceMatchInNotMoved)
{
NSLog(#"Piece is in Not Moved.");
}
if (pieceMatchInRight)
{
NSLog(#"Piece is in Right.");
}
if (pieceMatchInLeft)
{
NSLog(#"Piece is in Left.");
}
I'm sure there is a much better way to do what I'm trying to do here. Doing it this way, I'm going to have to apply the exact same logic to all three if statements, rather than just the array that contains the "piece" (UIImageView)
Thanks guys!
You can use the containsObject function, which would still require three if statements but would eliminate the for loops. If you use else if it only evaluates the if's till it enters one. Which helps.
So it would end up looking like:
if([firstWordMoveLeftArrayCopy containsObject:piece])
//do something
else if([firstWordMoveRightArrayCopy containsObject:piece])
//do something
(else //if it has to be in one of the arrays or)
else if([firstWordLettersNotMovedArray containsObject:piece])
//something else
Another option is create a custom object or subclass UIImageView that contains a enum of where it is at.
Can't you extend UIImageView and add a status property that will tell you in what array the piece is?

UISlider how to set the initial value

I'm pretty new at this iphone dev stuff and i'm working on some existing code at a company. The uislider i'm trying to set the initial value on is actually in a UITableViewCell and is a custom control. I was thinking in the cell init
cell = (QuantitiesCell *)[self loadCellWithNibName:#"QuantitiesCell" forTableView:ltableView withStyle:UITableViewCellStyleDefault];
i could just call something like
((QuantitiesCell *)cell).value = 5;
The actual QuantitiesCell class has the member value and the following functions
-(void)initCell;{
if (listOfValues == nil) {
[self initListOfValues];
}
quantitiesSLider.maximumValue = [listOfValues count]-1;
quantitiesSLider.minimumValue = 0;
quantitiesSLider.value = self.value;
}
-(void)initListOfValues;{
listOfValues = [[NSMutableArray alloc] initWithCapacity:10];
int j =0;
for (float i = minValue; i <= maxValue+increment; i=i+increment) {
[listOfValues addObject:[NSNumber numberWithFloat: i]];
if (i == value) {
quantitiesSLider.value = j;
}
j++;
}
}
like i said, i'm pretty new at this so if i need to post more of the code to show whats going to get help, let me know,
This slider always is defaulting to 1, the slider ranges usually from 1-10, and i want to set it to a specific value of the item i'm trying to edit.
Setting slider.value is the correct way if your linkage is correct (you have made the connections in Interface Builder). If you have created a UISlider in code, all you have to do is set the value property. If that is not working then be sure firstly that the object is "live" (correctly allocated, not released, not out of scope etc.) and secondly that the functions in which you set slider.value are actually being called.
If you are using Interface Builder and are not sure of how to connect your slider as an IBOutlet, you should check out iTunes University - search for "CS193P" to find some excellent videos from Stanford University. The first couple will take you through making those connections. If using IB and you have not made the connection - nothing will happen.
PS I had the same issuer - you need to add the UISlide view first then you can change the value.

Using the value of a string to determine the instance of a class

I have a switch statement similar to this one:
switch (number)
{
case 1:
if (imageView1.hidden == NO)
{
imageView1.hidden = YES;
} else
{
imageView1.hidden = NO;
}
break;
case 2:
if (imageView2.hidden == NO)
{
imageView2.hidden = YES;
} else
{
imageView2.hidden = NO;
}
break;
And so forth and so on.
My question is how do I use a string with a value say "imageView1" and use that to access the instance of my imageView class instead of having a different case for each instance of imageView? I know it muse be similar to creating an NSPath from a string or something like that, but I'm just not sure where to look or what it would be called.
Thanks in advance for any help!
I don't disagree with those who are concerned about the design, if this is actually the code. I will assume, however, that you are only posting a generalized version of your question. And since this is an important concept in Objective-C, so we should talk about it.
You can access an object's properties by name using Key Value coding, and the routine -valueWithKey:.
NSString *nameOfView = #"imageView1";
[[self valueForKey:nameOfView] setHidden:YES];
This will, in order, look for a method called -imageView1, an ivar named imageView1 and finally an ivar named _imageView1. This technique is very heavily used in Cocoa, and is important to understand. This is one of the many reasons we name things carefully, and yet another reason that we make accessors that handle memory management for us. Search the docs for "Key-Value Compliance" for more information.
Now for this specific case, I would tend towards something more like JimG's solution, using an NSArray of views, so I can loop through them and turn on or off the ones I want based on their index. But I can imagine a lot of cases where that wouldn't be appropriate, and KVC may be.
Why not put the instances in an NSArray and index into that?
NSArray *views = [NSArray arrayWithObjects: imageView1, imageView2, nil];
NSImageView *iview = [views objectAtIndex: number];
Also, you could consider something like:
iview.hidden = ! iview.hidden;
[Edit: missing asterisks, oops]