Tapping button to go through each frame of animation - iphone

I am trying to create an app where when you tap a button it will go to the next frame in an animation.
I have 8 image files, and when I press the button I want the 1st image to display, and when i press the button again, I want the 2nd image to replace the 1st image and so on.
I was thinking something like:
-(IBAction)buttonPressDoStuff:(id)sender {
imageThing.image = [UIImage imageNamed:#"image1.png"];
imageThing.image = [UIImage imageNamed:#"image2.png"];
imageThing.image = [UIImage imageNamed:#"image3.png"];
imageThing.image = [UIImage imageNamed:#"image4.png"];
}
and somehow making this all work consecutively with each press.
I am fairly new at objective c, so any help would be much appreciated.
Can anyone throw up some sample code to do this?

Let's think about this. If you want to do something sequentially, that sounds like the job of an array. So what do you think about this:
In your .h file, add these instance variables:
NSMutableArray* picturesArray;
NSInteger counter;
And now in your .m file, in your class' init method:
//this loop will fill your array with the pictures
for(int idx = 0; idx < NUMBER_OF_PICTURES; idx++) {
//IMPORTANT: this assumes that your pictures' names start with
//'image0.png` for the first image, then 'image1.png`, and so on
//if your images' names start with 'image1.png' and then go up, then you
//should change the 'int idx = 0' declaration in the for loop to 'int idx = 1'
//so the loop will start at 0. You will then need to change the condition
//to 'idx < (NUMBER_OF_PICTURES + 1)' to accomodate the last image
NSString* temp = [NSString stringWithFormat:#"image%i.png", idx];
UIImage* tempImage = [UIImage imageNamed:temp];
[picturesArray addObject:tempImage];
}
and in your buttonPressDoStuff: method:
//this method will move to the next picture in the array each time it is pressed
-(IBAction)buttonPressDoStuff:(id)sender {
if(counter < [pictureArray count]) {
imageThing.image = [picturesArray objectAtIndex:counter];
counter++;
}
}
Your init method should look something like this:
- (id)initWithNibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if(self) {
//do setup here
for(int idx = 0; idx < NUMBER_OF_PICTURES; idx++) {
NSString* temp = [NSString stringWithFormat:#"image%i.png", idx];
UIImage* tempImage = [UIImage imageNamed:temp];
[picturesArray addObject:tempImage];
}
}
//it is important that you return 'self' no matter what- if you don't,
//you will get the 'control reached end of non-void method' warning
return self;
}

Related

Why isn't my ImageView doing anything, at least redrawing?

I made a function that will cycle through images (1.png, 2.png, 3.png, 4.png, 5.png) and display them to a UIImageView. The idea is that I am loading a webpage and I want this imageview to run through this function, basically as a loading icon. However I dont understand why it isn't working, as far as the imageview goes, i dont see anything. Please help! My function's code is below. I know it is something wrong i'm doing here because everything else i test including the webview delegate works just fine.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for(int c = 1; !webviewLoaded; c++)
{
c = c % 6;
if(c == 6)
c = 1;
NSString *numberString = [NSString stringWithFormat:#"%d.png", c];
UIImage *img = [UIImage imageNamed:numberString];
[self.loadingImage setImage:img];
sleep(1);
}
});
Because you are doing UI stuff on a background thread, you should't change UI components from a background thread.
You can change your code as following:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
for(int c = 1; !webviewLoaded; c++)
{
c = c % 6;
if(c == 6)
c = 1;
NSString *numberString = [NSString stringWithFormat:#"%d.png", c];
UIImage *img = [UIImage imageNamed:numberString];
[self.loadingImage setImage:img];
sleep(1);
}
}
});
This piece of code will work, but is kind of useless and it will give you results not as good as you're expecting because what you are doing is create a separate thread (operation) just to call the UI thread from it and this is not efficient.
What you should do is entirely remove the dispatch like this:
for(int c = 1; !webviewLoaded; c++)
{
c = c % 6;
if(c == 6)
c = 1;
NSString *numberString = [NSString stringWithFormat:#"%d.png", c];
UIImage *img = [UIImage imageNamed:numberString];
[self.loadingImage setImage:img];
sleep(1);
}
This will also work, but make sure you have those files #"%d.png added to your project.
And because you are not familiar with GCD here is a good tutorial

iOS: Find all controls with the same tag

I have created a storyboard with 12 buttons, 12 smaller buttons and 12 labels.
It is like that:
btnBig1.tag = 1
btnSmall1.tag = 1
lbl1.tag = 1
btnBig2.tag = 2
btnSmall2.tag = 2
lbl2.tag = 2
etc...
Now when a procedure is called
- (IBAction)processButtonUpInside:(id)sender
{
UIButton *nButton = (UIButton*)sender;
int nInt = nButton.tag;
}
... I would like to do something with all 3 controls (big button, small button and label).
It should look like this (pseudo-code):
- (IBAction)processButtonUpInside:(id)sender
{
UIButton *nButton = (UIButton*)sender;
int nInt = nButton.tag;
UIButton *nButtonBig (UIButton*)CastFromTagID(nInt)
//do something with the big button
UIButton *nButtonSmall (UIButton*)CastFromTagID(nInt)
//do something with the small button
UILabel *nLabel (UILabel*)CastFromTagID(nInt)
//do something with the label
}
As you can see, the CastFromTagID is my "own invention". I don't know how I should actually do this.
Can somebody help?
Thank you very much.
You can use 3 a different starting point for each button family:
enum {
kTagFirstBigButton = 1000,
kTagFirstSmallButton = 2000,
kTagFirstLabel = 3000,
}
Assign the tags using them:
btnBig1.tag = kTagFirstBigButton + 1;
btnSmall1.tag = kTagFirstSmallButton + 1;
lbl1.tag = kTagFirstLabel + 1;
btnBig2.tag = kTagFirstBigButton + 2;
btnSmall2.tag = kTagFirstSmallButton + 2;
lbl2.tag = kTagFirstLabel + 2;
...
Now it's easy to find anything:
- (IBAction)processButtonUpInside:(id)sender
{
UIButton *nButton = (UIButton*)sender;
/* I'm not sure what button is `sender` here
If it's a big or small one you can guess
comparing its tag with the first tag
*/
int offset = nButton.tag;
UIButton *nButtonBig = (UIButton*)[view viewWithTag:kTagFirstBigButton + offset];
//do something with the big button
UIButton *nButtonSmall = (UIButton*)[view viewWithTag:kTagFirstSmallButton + offset];
//do something with the small button
UILabel *nLabel = (UILabel*)[view viewWithTag:kTagFirstLabel + offset];
//do something with the label
}
You shouldn't assign the same tag id to different views.
Indeed, do something like this:
btnBig1.tag = 11 btnSmall1.tag = 12 lbl1.tag = 13;
btnBig2.tag = 21 btnSmall2.tag = 22 lbl2.tag = 23;
Then consider the last digit of the tag id:
UIView *nView = (UIView *)sender;
if (lastDigit(sender.tag) == 3)
// this is a label
{
UIButton *nButtonBig = [nView.superview viewWithTag:nInt-2];
UIButton *nButtonSmall = [nView.superview viewWithTag:nInt-1];
UILabel *nLabel = (UILabel *)sender;
}
else if (lastDigit(sender.tag) == 2)
.....
where lastDigit(sender.tag) is a function that returns the last digit of a given integer.
In my case where I don't have a reference to the subviews that I want to edit under the same tag I just grab all of the subviews for a given view then loop through all of them and check the tag, if the tag match I execute some code. For example..
#define kSameViewTag 9500001
for (int i = 0; i < 10; i++) // add a bunch of same tag views //
{
UIView *someview = [UIView new];
someview.tag = kSameViewTag;
[YourParent addSubview:someview];
}
Then later on or whenever you need to loop through your views you can do.
NSArray *subviews = [[YourParent subviews] copy];
for (UIView *v in subviews)
{
if (v.tag == kSameViewTag)
{
// Do some code //
}
}
Now this may become an performance issue if you have a lot of subviews so you can always run it on the background thread then jump into the main thread to do UI updates. For Example:
NSArray *subviews = [[YourParent subviews] copy];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
for (UIView *v in subviews)
{
if (v.tag == kSameViewTag)
{
dispatch_async(dispatch_get_main_queue(), ^{
// Do Some UI Stuff Here //
});
}
}
});

Animating a button to look like a spinner on click

So basically I have a button. When it is clicked the first time I want it to display a spinner animation (kind of like the game twister, the spinner thing). I want this animation on the button itself. Preferably as the background. I have the other part of the button working correctly where when it is pressed again it stops pointing in a direction at random. Any ideas on how to get this working?
-(IBAction)spinnerButton:(id)sender{
NSString *display;
if (pressCount%2 == 0) {
NSString *spinning;
spinning = #"Press again to stop spinner.";
display = [[NSString alloc] initWithFormat:#"%#",spinning];
sender.animationImages = [NSArray arrayWithArray: imageArray];
sender.animationDuration = 1.0;
sender.animationRepeatCount = 0;
sender.startAnimating;
}
The imageArray is put together here.
- (void)viewDidLoad
{
[super viewDidLoad];
imageArray = [[NSMutableArray alloc] initWithCapacity:8];
for (int i = 0; i < 8; i++) {
[imageArray addObject:[UIImage imageNamed:[NSString stringWithFormat:#"piece%d.png", i]]];
}
}
My thought is that it needs to look something like this
[sender setBackgroundImage:[UIImage imageNamed:#"piece1.png"] forState:UIControlStateNormal];
but I'm not exactly sure how to place the array in it and make set the duration and all that stuffs. Thanks any help is welcomed and appreciated.
I got it to work fine. I placed an Image View over the top of the button. It still allowed me to click the button and it allowed me to tie the images and animation in correctly.

Assigning random image with arc4random?

Ok, I'm going to try to make this more clear because my last question was extremely confusing. I've included a picture this time. Each one of these circles is a UIImageView and they are each assigned a random image which is one of 7 colors. So each circle could be one of 7 colors. I want to make it so that the user has to hit the circles in order of a pre-determined order according to color. For example, blue, green, yellow, pink, purple, orange, red. My HUGE problem is that I can't seem to figure out how to determine whether or not a color that isn't supposed to be hit got hit. Is there a way to assign multiple image views the same value and then somehow have an if statement that says....
if(a blue circle is hit && ANY ORANGE CIRCLE IS STILL IN VIEW){
do something
}
The only way I know how to code this would be an insane amount of code, because of all the random images being assigned.
bluebubble = [UIImage imageNamed:#"bluebubble.png"];
greenbubble = [UIImage imageNamed:#"greenbubble.png"];
redbubble = [UIImage imageNamed:#"redbubble.png"];
yellowbubble = [UIImage imageNamed:#"yellowbubble.png"];
orangebubble = [UIImage imageNamed:#"orangebubble.png"];
pinkbubble = [UIImage imageNamed:#"pinkbubble.png"];
purplebubble = [UIImage imageNamed:#"purplebubble.png"];
image1 = arc4random()%7;
if(image1 == 0){
[test setImage:bluebubble];
}
else if(image1 == 1){
[test setImage:greenbubble];
}
else if(image1 == 2){
[test setImage:redbubble];
}
else if(image1 == 3){
[test setImage:yellowbubble];
}
else if(image1 == 4){
[test setImage:orangebubble];
}
else if(image1 == 5){
[test setImage:pinkbubble];
}
else if(image1 == 6){
[test setImage:purplebubble];
}
image2 = arc4random()%7;
if(image2 == 0){
[test2 setImage:bluebubble];
}
else if(image2 == 1){
[test2 setImage:greenbubble];
}
else if(image2 == 2){
[test2 setImage:redbubble];
}
else if(image2 == 3){
[test2 setImage:yellowbubble];
}
else if(image2 == 4){
[test2 setImage:orangebubble];
}
else if(image2 == 5){
[test2 setImage:pinkbubble];
}
else if(image2 == 6){
[test2 setImage:purplebubble];
}
I think it's also wise to create a more reasonable way to assign the different color images to your image views, so this solution does both.
These Should be declared in the header of the class
NSArray *allCircleImagesViews; // These are suppose to be the onscreen UIImagesViews
NSArray *circlesByColor;
NSMutableArray *correctCircles; // The current circles the user is allowed to click
NSArray *colorOrder; // The order of the colors to click
int currentColorIndex; // The current index in the order of colors
Now for the functions:
The first one creates assign the different color images, sets the correct order of the colors, and sets the mechanism to determine if the correct color was clicked
- (void) populateImages
{
NSArray *images = [NSArray arrayWithObjects:
[UIImage imageNamed:#"bluebubble.png"],
[UIImage imageNamed:#"redbubble.png"],
[UIImage imageNamed:#"yellowbubble.png"],
[UIImage imageNamed:#"greenbubble.png"],
[UIImage imageNamed:#"orangebubble.png"],
[UIImage imageNamed:#"pinkbubble.png"],
[UIImage imageNamed:#"purplebubble.png"],
nil];
NSArray *circlesByColor=[NSArray arrayWithObjects:
[NSMutableArray array],
[NSMutableArray array],
[NSMutableArray array],
[NSMutableArray array],
[NSMutableArray array],
[NSMutableArray array],
[NSMutableArray array],
nil];
[circlesByColor retain];
// Assign images to circles and set the
for (UIImageView *currCircle in allCircleImagesViews)
{
int nextIndex = arc4random()%7;
currCircle.image = [images objectAtIndex:0];
[(NSMutableArray *)[circlesByColor objectAtIndex:nextIndex] addObject:currCircle];
}
// Set the correct order
NSArray *colorOrder = [NSArray arrayWithObjects:
[NSNumber numberWithInt:5], // Pink
[NSNumber numberWithInt:0], // Blue
[NSNumber numberWithInt:1], // etc.
[NSNumber numberWithInt:4],
[NSNumber numberWithInt:2],
[NSNumber numberWithInt:3],
[NSNumber numberWithInt:6],nil];
[colorOrder retain];
currentColorIndex = 0;
correctCircles = [circlesByColor objectAtIndex:[[colorOrder objectAtIndex:currentColorIndex] intValue]];
}
The following function takes care of checking if the correct circle was clicked
- (void) checkCircle:(UIImageView *)clickedImageView
{
BOOL result;
if ([correctCircles containsObject:clickedImageView])
{
[correctCircles removeObject:clickedImageView];
result = YES;
}
else {
result = NO;
}
if ([correctCircles count] == 0)
{
currentColorIndex++;
if (currentColorIndex < 7)
correctCircles = [circlesByColor objectAtIndex:[[colorOrder objectAtIndex:currentColorIndex] intValue]];
else
correctCircles = nil;
}
if (!result)
{
// Wrong circle clicked logic
}
else {
if (!correctCircles)
{
// Game won logic
}
else {
// Correct circle clicked logic
}
}
}
Yes, you can set the image property of UIImageView to the same UIImage. In fact, you are already doing this!
The imageNamed: class method will return a reference to the same object when called again with the same image file name.
However, you should get the four UIImage references once and then use those. You should also use an array and loop or at least a function to make this code shorter and simpler.
Actually, it sounds like you will want your four UIImage references to be instance variables so that you can compare the image property of tapped UIImageView objects against them later.
If your purpose is simply the if clause, then what I would do is to define two dictionaries with the key as the color and the values as the arrays of burbles with the value be the location of the burble.
NSDictionary *hitDictionaryOfBurbles;
NSDictionary *notCurrentlyBeingHitDictionaryOfBurbles;
With this dictionary defined, you have with you all the items of all colors currently not being hit. You can then easily check this dictionary the value of the array of Orange color to see how many items there are.
Offcourse, once the burble is being hit, you should change the value of these two dictionaries to reflect the change.
Es.
hitDictioanryOfBurbles:
"blue", [0,7,13]
"green", [2,6,16]
etc...
notCurrentlyBeingHitDictioanryOfBurbles:
"blue", [25,33,37]
"green", [19,27,29]
etc...
If you have other logic to support, then you should define more data structures.
Are you always going to be hitting these in a certain order, one after the other, where only one color is valid at a time? If so, you can create a stack of UIImages (I don't think there's a built-in stack data type, but you can simulate it easily with NSMutableArray) consisting of the colors in order.
You also keep track of all the color-circle subviews on the screen in a separate NSMutableArray. Then, whenever a user hits a circle, execute the following code:
if(hitImageView.image == [imageStack peek]) {
//"hit the right one" logic
//you hit it, so remove it from the screen
[allImageViews removeObject:hitImageView];
[hitImageView removeFromSuperView];
//if it was the last imageview of the "correct" color...
BOOL wasLast = YES;
for(UIImageView *imageView in allImageViews) {
if(imageView.image == [imageStack peek]) {
wasLast = NO;
break;
}
}
//...then set the correct color to whatever is next
if(wasLast)
[imageStack pop];
if(![imageStack hasItems])
;//"won the game" logic
}
else {
//"hit the wrong one" logic
}
(This is O(N) for determining remaining circles, but if you're dealing with such a small set then it really doesn't matter. If you needed to optimize it, you could of course keep track of the remaining counts of each color.)
On another note, you'll be a much happier person if, instead of using that gigantic if-else block to identify the correct color, you just name the files color1.png, color2.png, etc. and say [UIImage imageNamed:[NSString stringWithFormat:#"color%i.png", randNum]];

Unable to assign images to an imageView from an Array, but can assign images to an imageView from a UIImage variable

I have a problem with arrays and passing images between views that I would like some help on!
So the background is that I have:
• RootViewController (which handles my table view)
• CommunicationViewController which handles the detail of the selected element from the table
• SelectSlideViewController which displays an image clicked on from the CommunicationViewController and allows the user to select a different one from the camera roll
So the problem:
• In the CommunicationViewConroller, I have the following code if the user clicks on a button:
- (IBAction) selectSlide:(id) sender
{
if(self.selectSlideView == nil)
{
SelectSlideViewController *viewController = [[SelectSlideViewController alloc]
initWithNibName:#"SelectSlideViewController" bundle:[NSBundle mainBundle]];
self.selectSlideView = viewController;
[viewController release];
}
[self.navigationController pushViewController:self.selectSlideView animated:YES];
self.selectSlideView.cmn = cmn;
self.selectSlideView.title = cmn.name;
self.selectSlideView.imageView.image = self.myImage5;
}
And the above code works, if for, example, I click on button 5, as it sets image5 to the view in the “SelectSlideViewController”.
However, I would like to have multiple buttons using the same “selectSlide” action – and to do that, I need to figure out which button was pressed, and then assign the correct image to the “SelectSlideViewController” from an array of images (or a series of if-else statements which is clunky).
• So my revised code is as follows, but it doesn’t work with the array – any thoughts?:
- (IBAction) selectSlide:(id) sender
{
if(self.selectSlideView == nil)
{
SelectSlideViewController *viewController = [[SelectSlideViewController alloc]
initWithNibName:#"SelectSlideViewController" bundle:[NSBundle mainBundle]];
self.selectSlideView = viewController;
[viewController release];
}
NSUInteger tmpInt = -1;
tmpInt = [buttonArray indexOfObject:sender];
[self.navigationController pushViewController:self.selectSlideView animated:YES];
self.selectSlideView.cmn = cmn;
self.selectSlideView.title = cmn.name;
NSLog(#"The int was %d",tmpInt);
NSLog(#"This is the image array size %d ",[imageArray count]);
If(tmpInt >-1 && tmpInt <9)
{
self.selectSlideView.imageView.image = [imageArray objectAtIndex:tmpInt];
}
/** this code works, but is a bit clunky:
if(tmpInt == 0)
self.selectSlideView.imageView.image = self.myImage1;
else if (tmpInt == 1)
self.selectSlideView.imageView.image = self.myImage2;
else if (tmpInt == 2)
self.selectSlideView.imageView.image = self.myImage3;
else if (tmpInt == 3)
self.selectSlideView.imageView.image = self.myImage4;
else if (tmpInt == 4)
self.selectSlideView.imageView.image = self.myImage5;
else if (tmpInt == 5)
self.selectSlideView.imageView.image = self.myImage6;
else if (tmpInt == 6)
self.selectSlideView.imageView.image = self.myImage7;
else if (tmpInt == 7)
self.selectSlideView.imageView.image = self.myImage8;
else if (tmpInt == 8)
self.selectSlideView.imageView.image = self.myImage9;
**/
}
I have definitely confirmed that the imageArray is of size 9, and that it is picking the correct index from the array, however the SelectSlideViewController doesn't display the image, if accessed from the array. It displays it if accessed using the clunky if-else statements.
You could try to load the images directly the first time, then cache afterwards. (I've excluded the caching code, and the scaling code, but you can add them after you get the image).
If(tmpInt >-1 && tmpInt <9)
{
// Check if already cached
self.selectSlideView.imageView.image = [self getButtonImages:tmpInt];
// add scaling code here for smaller image
}
In #implementation you can add:
- (UIImage) getButtonImages:(NSInteger)index
{
UIImage* myImage = [UIImage imageNamed:#"image%d.jpg", index];
return myImage;
}
Here is the solution to the above problem (credited to "JSD" from www.iphonedevsdk.com):
Since selectSlideView.imageView was loaded from a nib the view wasn't loaded the first time through.
I had to include the following line of code before I assigned images from my Array to the view:
[selectSlideView view];