What is the best way to animate images of a button?
Say I want to cycle through like 6 frames and have the user still be able to click the button?
Should I animate the images and just have an invisible button on top of it in interface builder?
Is there a way to animate them within defining the UIButton?
Or is it better to animate images and find the users touch point and act on that?
You can do this using the imageView property of UIButton. This will allow the button to behave as it normally would with your images animating on it. Here's an example:
NSMutableArray *imageArray = [NSMutableArray new];
for (int i = 1; i < 4; i ++) {
[imageArray addObject:[UIImage imageNamed:[NSString stringWithFormat:#"%d.png",i]]];
}
[myButton setImage:[UIImage imageNamed:#"1.png"] forState:UIControlStateNormal];
[myButton.imageView setAnimationImages:[imageArray copy]];
[myButton.imageView setAnimationDuration:0.5];
[myButton.imageView startAnimating];
Related
I have a UIToolbar with audio controls. Sometime around iOS 7 upgrade the color of the bar changed and iOS started shading my buttons differently. That introduced a bug where the play and pause buttons which I change programatically don't match the new look of the toolbar:
Toolbar starts out looking fine:
Now I pressed play so code inserted pause button but wrong color:
Now I pressed pause and code inserted play button, again with wrong color:
I want the play and pause buttons to have that same dark look as the other buttons. I assume I have to do something differently when building the replacement buttons. The raw icon images are all that lighter color, but iOS toolbar seems to be automatically recoloring to darker color from the storyboard, as seen in first screenshot.
Here's the code I use to build the buttons:
- (UIBarButtonItem *) makeBarButton: (NSString *) imageName action:(SEL)targetAction
{
UIImage* image = [UIImage imageNamed:imageName];
CGRect frame = CGRectMake(0 - 20, 0 - 20, image.size.width + 20, image.size.height + 20);
UIButton *button = [[UIButton alloc] initWithFrame:frame];
[button addTarget:self action:targetAction forControlEvents:UIControlEventTouchUpInside];
[button setImage:image forState:UIControlStateNormal];
[button setShowsTouchWhenHighlighted:YES];
return [[UIBarButtonItem alloc] initWithCustomView:button];
}
- (void) setAsPlaying:(BOOL)isPlaying
{
self.rootViewController.playing = isPlaying;
// we need to change which of play/pause buttons are showing, if the one to
// reverse current action isn't showing
if ((isPlaying && !self.pauseButton) || (!isPlaying && !self.playButton))
{
UIBarButtonItem *buttonToRemove = nil;
UIBarButtonItem *buttonToAdd = nil;
if (isPlaying)
{
buttonToRemove = self.playButton;
self.playButton = nil;
self.pauseButton = [self makeBarButton:#"icon_pause.png" action:#selector(pauseAudio:)];
buttonToAdd = self.pauseButton;
}
else
{
buttonToRemove = self.pauseButton;
self.pauseButton = nil;
self.playButton = [self makeBarButton:#"icon_play.png" action:#selector(playAudio:)];
buttonToAdd = self.playButton;
}
// Get the reference to the current toolbar buttons
NSMutableArray *toolbarButtons = [[self.toolbar items] mutableCopy];
// Remove a button from the toolbar and add the other one
if (buttonToRemove)
[toolbarButtons removeObject:buttonToRemove];
if (![toolbarButtons containsObject:buttonToAdd])
[toolbarButtons insertObject:buttonToAdd atIndex:4];
[self.toolbar setItems:toolbarButtons];
}
}
Appreciate your help with this.
Check the following:
If the images are stored inside your image assets, make sure they are template images.
Then you can either A) set their tint colour in Interface Builder to the grey colour you prefer or B) do it programmatically in your makeBarButton method as such;
button.tintColor = [UIColor grayColor];
I'm making a custom view to display a simple star rating system in my app. Each star is a UIButton that has a specific star image-file set as its image. Everything works fine in storyboard but when I run the app on a device each star image is slightly transparent.
When I set the background color of the buttons themselves, they are 100% opaque yet the images are still slightly transparent. Any reason why that same button's image wouldn't be opaque? I just want the stars to always be opaque and not dependent on background.
I just joined SO so I don't have enough reputation to post images yet but here's a link to an image that might explain the issue a bit better: photo
Basic code for the custom UIView:
- (void)layoutSubviews
{
[super layoutSubviews];
self.opaque = YES;
[self drawStars];
}
- (void)drawStars {
float imagePadding = self.padding;
CGFloat starWidth = self.selectedStarImage.size.width;
for (NSInteger counter = 0; counter < self.totalStars; counter++)
{
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(counter * (imagePadding + starWidth), 0, starWidth, starWidth)];
button.opaque = YES;
if (counter >= self.rating) {
[button setImage:self.starImage forState:UIControlStateNormal];
}
else {
[button setImage:self.selectedStarImage forState:UIControlStateNormal];
}
[self addSubview:button];
[button addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
button.tag=counter;
}
}
- (void)updateStars
{
for (UIView *subview in self.subviews) {
[subview removeFromSuperview];
}
[self drawStars];
}
Any help would be much appreciated.
It could happen that the alpha of view on which you are adding the buttons as subviews could be lesser than one.
i think you have to first change the image of star because its background it transparent that is the problem and check the background color of button.
Or
you can create whole transparent image and set proper background color which you want.
These images are not proper. or check it by using another images.
I'm trying to create a level select screen for a game I'm making which consists of a grid of circular buttons with level numbers in them. At any one time one button should be selected and displayed with a filled background instead of just an outline.
My initial implementation was to create a view which looked something like this:
#implementation LevelView
- (void)drawRect:(CGRect)rect {
int i = 1;
for (int row = 0; row < NUM_ROWS; row++) {
for (int col = 0; col < NUM_COLS; col++) {
// Calculate frame of the circle for this level.
if (i == selected) {
// Use CoreGraphics to draw filled circle with text.
}
else {
// Use CoreGraphics to draw outlined circle with text.
}
i++;
}
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
for (UITouch *touch in touches) {
// Get the level that the touch is in the circle of.
int level = [self levelForPoint:[touch locationInView:self]];
selected = level;
[self setNeedsDisplay];
}
}
#end
The problem with this is that the selecting and unselecting of the various buttons are not animated (fade in, fade out) like the circular buttons in the iOS7 lock screen or Phone app.
Is there a better/easier way that I can accomplish this?
In response to your request to fade button state: we subclassed UIButton and overrode the setHighlighted: method like so:
- (void) setHighlighted:(BOOL)highlighted {
[super setHighlighted:highlighted];
if (highlighted) {
[self setBackgroundColor:[UIColor whiteColor]];
} else {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:.4f];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[self setBackgroundColor:nil];
[UIView commitAnimations];
}
}
As promised here is my way of tackling the problem. Let's first create a grid of UIButtons in viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
for (NSUInteger i = 0; i < 10; ++i)
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.tag = i;
[button setImage:[UIImage imageNamed:#"normal_image"] forState:UIControlStateNormal];
[button setImage:[UIImage imageNamed:#"selected_image"] forState:UIControlStateSelected];
[button addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
//You can assign a button frame here or in viewWillLayoutSubviews or viewDidLayoutSubviews
button.frame = CGRectMake(your...frame);
[self.view addSubview:button];
[buttons addObject:button]; //NSMutableArray ivar containing our buttons
}
And that takes care of placing our buttons. You can use 2 for loops to make a grid of your choice. As for the image I'm assuming that you're using pre-baked images (you can pre-bake for retina and non retina separately). If you want to render it programatically let me know and I will edit the post.
As for the selection logic we implement the selector buttonTapped:
- (void)buttonTapped:(UIButton *)sender
{
for (UIButton *button in buttons) //Resets all selected states if there were any
button.state = UIControlStateNormal;
sender.state = UIControlStateSelected;
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Hey" message:[NSString stringWithFormat:#"You touches the %ldth button.", (long)sender.tag + 1] delegate:nil cancelButtonTitle:#"Cool" otherButtonTitles:nil]
[alert show];
}
EDIT as for the animation purposes you can take a look at the link here or you can use an array of imageViews with attached UITapGestureRecognizers.
EDIT 2 A basic way on how to draw images for your buttons:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(buttonWidth, buttonHeight), NO, 0.0f); //Makes a graphics context that is not opaque and uses the screen scale (so you do not need to worry about retina or non retina)
[[UIColor redColor] set];
UIRectFill(CGRectMake(0.0f, 0.0f, buttonWidth, buttonHeight);
NSDictionary *drawingAttributes = #{NSFontAttributeName: [UIFont systemFontOfSize:15.0f], NSForegroundColorAttributeName: [UIColor blueColor]};
[#"Normal state" drawAtPoint:CGPointZero withAttributes:drawingAttributes];
UIImage *normalStateImate = UIGraphicsGetImageFromCurrentImageContext();
[[UIColor greenColor] set];
UIRectFill(CGRectMake(0.0f, 0.0f, buttonWidth, buttonHeight);
[#"Selected state" drawAtPoint:CGPointZero withAttributes:drawingAttributes];
UIImage *selectedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
And then you would apply the images in the same way as I have written above. How to draw pretty images and whatnot is another topic and if you want help on that I suggest googling or asking another question. If you ever feel like changing the images you would redraw them and apply them again to your buttons.
I make a app which include a image view within scroll view.
I have 3 classes and each class add images into image view.
When i press button1, it call class1 and it fill Class1images(from image001 to image200).
When i press button2, it call class2 and it fill Class2images(from image201 to image400)
The problem is when i call class1, image view show class1images(from image001 to image200).
After calling class1, i call class2.
When i call class2, my image view can't show class2images(from image201 to image400)
But, class1Images (from image001 to image200) are remaining in app.
I think class1 images are still in memory, so that class2 images can't add to memory.
I want to remove class1images when i call class2.
When i call next class, it will remove old images in memory and replace with new images.
But, my app is develop with storyboard and ARC.
So, i can't release memory manually in ARC mode.
Is there any ways to remove old images in memory?
- (void)viewDidLoad{
[super loadView];
self.view.backgroundColor = [UIColor grayColor];
ScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width,self.view.frame.size.height)];
ScrollView.pagingEnabled = YES;
NSInteger numberOfViews = 200;
for (int i = 0; i < numberOfViews; i++) {
CGFloat xOrigin = i * self.view.frame.size.width;
// Create a UIImage to hold Info.png
UIImage *image1 = [UIImage imageNamed:#"Image001.jpg"];
UIImage *image2 = [UIImage imageNamed:#"Image002.jpg"];
:
:
UIImage *image200 = [UIImage imageNamed:#"Image200.jpg"];
NSArray *images = [[NSArray alloc] initWithObjects:image1,image2, . . . ,image200,nil];
ImageView = [[UIImageView alloc] initWithFrame:CGRectMake(xOrigin, 0,self.view.frame.size.width, self.view.frame.size.height)];
[ImageView setImage:[images objectAtIndex:i]];
[ScrollView addSubview:ImageView];}
ScrollView.contentSize = CGSizeMake(self.view.frame.size.width*numberOfViews,self.view.frame.size.height);
[self.view addSubview:ScrollView];}
setting an imageview's animationImages property to a new array will remove the olf array and animate the new one.
to be 110% safe call stopAnimation, set the images, call startAnimation
I think your scrollview is not getting cleaned up. You need to remove previous images from scrollView and after that add images from next class.
try to implement something like this for removing images from scrollview and after this add other images on button click
for(UIView *subView in self.ScrollView.subviews)
{
if([subView isKindOfClass:[UIImageView class]])
[subView removeFromSuperview];
}
Hopefully can help you. Just give it a try.
I have code that creates, adds and tags unbuttons in a uiview which is itself in a uiscrollview. At a certain point I try to change (background color and image) some of the unbuttons with certain tags . The problem is if I choose the first button with tag 0 the for loop bombs out for the image change because either uiscrollview or the uiview do not have that method available. But I am trying to only target the unbuttons within the view (all synched up). If I pick any other buttons it works as expected. I could offset the tag from 0 to one instead but I want to know why my for loop does not work.
for (int i=0; i<[devicesArray count]; i++) {
NSLog(#"red %i", i);
for (UIView *subview in [uiv_ButtonsView subviews]) {
if([subview isKindOfClass:[UIButton class]]) {
int number = [[devicesArray objectAtIndex:i] intValue];
subview.alpha=1.0;
[[subview viewWithTag:number] setBackgroundColor:[UIColor redColor]];
UIButton *btttn = (UIButton *)[subview viewWithTag:number];
[btttn setBackgroundImage:nil forState:UIControlStateNormal];
}
}
}
Thanks - this is now working code:
for (int i=0; i<[devicesArray count]; i++) {
int number = [[devicesArray objectAtIndex:i] intValue];
[[uiv_Quilt viewWithTag:number] setBackgroundColor:[UIColor redColor]];
[[uiv_Quilt viewWithTag:number] setBackgroundImage:nil forState:UIControlStateNormal];
}
for (UIView *subview in [uiv_ButtonsView subviews]) {
subView is a subview of uiv_ButtonsView
if([subview isKindOfClass:[UIButton class]]) {
subView is a UIButton
[[subview viewWithTag:number] setBackgroundColor:[UIColor redColor]];
Hmmm. Now you're getting a subview of the UIButton, with tag 0 - this is the default tag for all views. You're in the private view hierarchy of the UIButton here, we've no idea what this is.
UIButton *btttn = (UIButton *)[subview viewWithTag:number];
Same again - you've told the compiler that you are getting a button back from this call, but you won't be. subView is already a button, buttons don't have other buttons as subviews.
[btttn setBackgroundImage:nil forState:UIControlStateNormal];
So this line of code won't work.
I don't see why you either do all your operations on subView, or use [uiv_ButtonsView viewWithTag:xx] to get your button. In the latter case, you will need to start your tags at 1, since all views have a default tag of 0.
The [subview viewWithTag:number] doesn't make sense to me (your button, subview, presumably doesn't have further subviews). And the iteration through your devicesArray with a nested iteration through the uiv_ButtonsView doesn't makes sense either. If you're using tags, you shouldn't need to iterate through your uiv_ButtonsView's subviews, but rather you could have used [uiv_ButtonsView viewWithTag:number].