How to display animated GIF in Objective C on top of the layered View? - objective-c

I am trying to draw animated gif on my screen in mac OSX app .
I used this code to insert the gif: I can see the Gif as 1 picture it doesn't animates
only static picture :( what should I add to make it animated ?
#import <Cocoa/Cocoa.h>
#import <Quartz/Quartz.h>//for drawing circle
#import "sharedPrefferences.h"
#interface GenericFanSubView : NSView
{
NSColor * _backgroundColor;
NSImageView* imageView;
}
- (void)setBackgroundColor :(NSColor*)color;
- (void)insertGif1;
- (void)insertGif2;
- (void)insertGif3;
#end
#import "GenericFanSubView.h"
#define PI 3.14285714285714
#implementation GenericFanSubView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
imageView = [[NSImageView alloc]initWithFrame:CGRectMake(0, 0,self.frame.size.width,self.frame.size.height)];
[imageView setAnimates: YES];
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
// Drawing code here.
[self drawCircleInRect];
_backgroundColor = [NSColor whiteColor];
[self insertGif1];
}
-(void)drawCircleInRect
{
//draw colored circle here
CGContextRef context = [[NSGraphicsContext // 1
currentContext] graphicsPort];
// ********** Your drawing code here ********** // 2
CGContextSetFillColorWithColor(context,[self NSColorToCGColor:(_backgroundColor)]);
float radius1 = self.frame.size.height/2;
float startAngle = 0;
float endAngle = endAngle = PI*2;
CGPoint position = CGPointMake(self.frame.size.height/2,self.frame.size.height/2);//center of the view
CGContextBeginPath(context);
CGContextAddArc(context, position.x, position.y, radius1, startAngle, endAngle, 1);
CGContextDrawPath(context, kCGPathFill); // Or kCGPathFill
}
- (void)setBackgroundColor :(NSColor*)color
{
_backgroundColor = color;
[self setNeedsDisplay:YES];
}
- (CGColorRef)NSColorToCGColor:(NSColor *)color
{
NSInteger numberOfComponents = [color numberOfComponents];
CGFloat components[numberOfComponents];
CGColorSpaceRef colorSpace = [[color colorSpace] CGColorSpace];
[color getComponents:(CGFloat *)&components];
CGColorRef cgColor = CGColorCreate(colorSpace, components);
return cgColor;
}
//curentlly calling only this 1
- (void)insertGif1
{
[imageView removeFromSuperview];
[imageView setImageScaling:NSImageScaleNone];
[imageView setAnimates: YES];
imageView.image = [NSImage imageNamed:#"FanBlades11.gif"];
[self addSubview:imageView];
}
#end
Edit: I discovered the source of the problem:
I was adding my class (that represents gif inside the circle) on top of RMBlurredView
and the animations doesn't work when I adding it as subview ,However it works on all the other views I added.
Any ideas what could be the reason inside the RMBlurredView to stop my NSImageView from animating ?
Edit:
I think [self setWantsLayer:YES]; is the reason I am not getting animations
how can I still get the animation with this feature enabled?
Edit:
Here is a simple sample with my problem
http://snk.to/f-cdk3wmfn
my gif:This is my gif it is invisible on white background color

"You must disable the autoscaling feature of the NSImageView for the
animation playback to function. After you've done that, no extra
programming required. It works like a charm!"
--http://www.cocoabuilder.com/archive/cocoa/108530-nsimageview-and-animated-gifs.html
imageView.imageScaling = NSImageScaleNone;
imageView.animates = YES;
needed for layer backed views:
if the image view is in a layer backed view or is layer backed itself:
imageView.canDrawSubviewsIntoLayer = YES;
working example using the question's own gif:
NSImageView *view = [[NSImageView alloc] initWithFrame:CGRectMake(10, 10, 50, 50)];
view.imageScaling = NSImageScaleNone;
view.animates = YES;
view.image = [NSImage imageNamed:#"FanBlades2_42x42.gif"];
view.canDrawSubviewsIntoLayer = YES;
NSView *layerview = [[NSView alloc] initWithFrame:CGRectMake(0, 0, 60, 60)];
layerview.wantsLayer = YES;
[layerview addSubview:view];
[self.window.contentView addSubview:layerview];

Related

Fill Rectangle with Color Percentage - Objective C

I created a box dynamically with rounded corners.
As i know width and height of the rectangle which is 300, what i am trying to achieve is to pass a variable which fills up the color as per the variable value.
So if the variable value is 10 the color should filled up untill 10ml from Top.
Here is the updated code.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIView *myBox = [[UIView alloc] initWithFrame:CGRectMake(20,20, 300, 300)];
myBox.layer.cornerRadius = 10;
myBox.clipsToBounds = YES;
myBox.backgroundColor = [UIColor blackColor];
// [myBox addSubview:<#(UIView *)#>];
[self drawRect:CGRectMake(0, 100, 300, 150)];
[self.view addSubview:myBox];
}
- (void)drawRect:(CGRect)rect {
self.view.backgroundColor = [UIColor cyanColor];
float percentage = 0.5;
float roundedOffPercentage = MIN(MAX(percentage, 0.0f), 1.0f);
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(CGRectGetMinX(rect), CGRectGetMaxY(rect), CGRectGetWidth(rect), CGRectGetHeight(rect)*roundedOffPercentage) cornerRadius:0];
// draw the bezier path now
}
You can subclass UIView and override it's - (void)drawRect:(CGRect)rect
And do like the following. I hope it may help you.
For Example , Create One class TestView by overriding UIView Class.
TestView.h
#import <UIKit/UIKit.h>
#interface TestView : UIView
#end
TestView.m
#import "TestView.h"
#implementation TestView
- (void)drawRect:(CGRect)rect
{
CGRect topRect = CGRectMake(0, 0, rect.size.width, rect.size.height/2.0);
// Fill the rectangle with grey
[[UIColor grayColor] setFill];
UIRectFill( topRect );
CGRect bottomRect = CGRectMake(0, rect.size.height/2.0, rect.size.width, rect.size.height/2.0);
[[UIColor redColor] setFill];
UIRectFill( bottomRect );
}
ViewController.m
#import "ViewController.h"
#import "TestView.h"
- (void)viewDidLoad {
[super viewDidLoad];
TestView *testView = [[TestView alloc] initWithFrame:CGRectMake(10, 10, 60, 60)];
testView.backgroundColor = [UIColor greenColor];
[self.view addSubview:testView];
}
If you're using Interface builder, make sure to change the view's class to YOUR_CUSTOM_VIEW_CLASS. You can do that by selecting in the view in Interface Builder and selecting the "Identity" pane in the inspector (its the one on the far right the the "i" icon).
Subclass UIView and do the drawing yourself according to the percentage you need.
- (void)drawRect:(CGRect)rect {
self.backgroundColor = [UIColor cyanColor];
float percentage = 0.5;
float roundedOffPercentage = MIN(MAX(percentage, 0.0f), 1.0f);
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(CGRectGetMinX(rect), CGRectGetMaxY(rect), CGRectGetWidth(rect), CGRectGetHeight(rect)*roundedOffPercentage) cornerRadius:0];
// draw the bezier path now
}

UILabel inside UIView produces rounded corner with "square corner"

I have an UIView which I created and set background color to white. This view contains UILabel, which is a class called BubbleView. (Sorry I cannot add a picture because you need reputation 10+ :(
PROBLEM:
1. The following code produces a gray Label with rounded corner with gray-border square corner tips. This is because the UIView produces the square corner tips. The UILabel is rounded. Please note that I already set the background of UIView to white.
2. My text string of the UILabel is hiding behind UIView, so it is not displayed.
I'd love to show you pictures, but I am new and I cannot add pictures until I get to 10+ reputations.
http://i.stack.imgur.com/CdRjy.png
http://i.stack.imgur.com/zCdCV.png
Here is my code for setting the text and the view:
BubbleView:
- (void)drawRect:(CGRect)rect
{
const CGFloat boxWidth = self.bubbleWidth;
const CGFloat boxHeight = self.bubbleHeight;
NSLog(#"text, width, height: %#, %f, %f", self.text, self.bubbleWidth, self.bubbleHeight);
CGRect boxRect = CGRectMake(
roundf(self.bounds.size.width - boxWidth) / 2.0f,
roundf(self.bounds.size.height - boxHeight) / 2.0f,
boxWidth,
boxHeight);
UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:boxRect cornerRadius:14.0f];
[[UIColor colorWithWhite:0.3f alpha:0.8f] setFill];
[roundedRect fill];
NSDictionary *attributes = #{
NSFontAttributeName : [UIFont systemFontOfSize:16.0f],
NSForegroundColorAttributeName : [UIColor whiteColor]
};
CGPoint textPoint = CGPointMake(
self.center.x+boxWidth/2,
self.center.y+boxHeight/2);
NSLog(#"text point origin: %f, %f", textPoint.x, textPoint.y);
[self.text drawAtPoint:textPoint withAttributes:attributes];
}
Main View Controller:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor whiteColor];
[self setText];
}
-(void) viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[self setText];
}
- (void) setText
{
NSString *textR = #"I need this text to show up on autolayout so that i could continue working";
UIFont* font = [UIFont fontWithName:#"HelveticaNeue" size:14.0f];
CGSize constraint = CGSizeMake(250,9999);
CGRect textRect = [textR boundingRectWithSize:constraint
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:font}
context:nil];
BubbleView *hostView = [[BubbleView alloc] initWithFrame:CGRectMake(20.0f, 160.0f, textRect.size.width+20, textRect.size.height+20)];
hostView.bubbleWidth = textRect.size.width+20;
hostView.bubbleHeight = textRect.size.height+20;
hostView.text = textR;
hostView.layer.cornerRadius = 10.0f;
self.view.layer.masksToBounds = TRUE;
[hostView drawRect:textRect];
hostView.backgroundColor = [UIColor whiteColor];
self.detailsView = hostView;
//self.detailsView.backgroundColor = [UIColor whiteColor];
NSLog(#"size: %f, %f", textRect.size.width, textRect.size.height);
NSLog(#"origin: %f, %f - size: %f, %f, backgroundColor: #%#", self.detailsView.frame.origin.x, self.detailsView.frame.origin.y, self.detailsView.frame.size.width, self.detailsView.frame.size.height, self.detailsView.backgroundColor);
[self.view addSubview:self.detailsView];
self.hostSays.text = textR;
self.hostSays.textColor = [UIColor blueColor];
[self.view layoutSubviews];
}
SOLUTION (ONLY 1 PART):
OK so I managed to solve half of my problems. I had to add the following code in my BubbleView class (inside - (id)initWithFrame:(CGRect)frame). This got rid of the square angles! (I think Wain was the one who suggested this but I might've misunderstood him)...
[self setOpaque:NO];
[self setBackgroundColor:[UIColor clearColor]];
So...still have the other part 2 of problem to solve and I'm hoping someone has run into this issue before!
Set the background colour of the view and add the label as a subview. Set the frame to get your required padding. Do not implement drawRect.
Now, the view will draw the background colour and the label automatically and the label will draw the text (with its background colour and border settings).
I know when I create custom buttons I need to setMasksToBounds
+ (void) addBorderToButtons:(UIButton *) btn
{
// Round button corners
CALayer *btnLayer = [btn layer];
[btnLayer setMasksToBounds:YES];
[btnLayer setCornerRadius:15.0f];
// Apply a 1 pixel, black border around Buy Button
[btnLayer setBorderWidth:1.5f];
[btnLayer setBorderColor:[[UIColor blackColor] CGColor]];
}
Setting this changes
To this
If you want to save your coding approach you strongly need to add [super drawRect:rect] in your drawRect: method
- (void)drawRect:(CGRect)rect{
[super drawRect:rect];
YOUR CODE
}
In this case you will see your text in UILabel.
Also you should not call drawRect: directly. It will be called automatically in runtime:
BubbleView *hostView =
[[BubbleView alloc] initWithFrame:CGRectMake(20.0f,
160.0f,
textRect.size.width+20,
textRect.size.height+20)];
hostView.bubbleWidth = textRect.size.width+20;
hostView.bubbleHeight = textRect.size.height+20;
hostView.text = textR;
// hostView.layer.cornerRadius = 10.0f;
// self.view.layer.masksToBounds = TRUE;
// [hostView drawRect:textRect];
// hostView.backgroundColor = [UIColor whiteColor];
self.detailsView = hostView;

UIPageControl doesn't properly react to taps

In my TestApp, I created a class "Carousel" which should let me create a swipe menu with a UIPageControl in an easy way (by simply creating an instance of the class Carousel).
Carousel is a subclass of UIView
At init, it creates an UIView, containing UIScrollView, UIPageControl
I can add further UIViews to the scroll view
I don't know if this is the proper way to do it, but my example worked quite well in my TestApp. Swiping between pages works perfectly and the display of the current page in the UIPageControl is correct.
If there were not one single problem: The UIPageControl sometimes reacts to clicks/taps (I only tested in Simulator so far!), sometimes it doesn't. Let's say most of the time it doesn't. I couldn't find out yet when it does, for me it's just random...
As you can see below, I added
[pageControl addTarget:self action:#selector(pageChange:) forControlEvents:UIControlEventValueChanged];
to my code. I thought this would do the proper handling of taps? But unfortunately, pageChange doesn't get always called (so the value of the UIPageControl doesn't change every time I click).
I would appreciate any input on this because I couldn't find any solution on this yet.
This is what I have so far:
Carousel.h
#import <UIKit/UIKit.h>
#interface Carousel : UIView {
UIScrollView *scrollView;
UIPageControl *pageControl;
BOOL pageControlBeingUsed;
}
- (void)addView:(UIView *)view;
- (void)setTotalPages:(NSUInteger)pages;
- (void)setCurrentPage:(NSUInteger)current;
- (void)createPageControlAt:(CGRect)cg;
- (void)createScrollViewAt:(CGRect)cg;
#end
Carousel.m
#import "Carousel.h"
#implementation Carousel
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Create a scroll view
scrollView = [[UIScrollView alloc] init];
[self addSubview:scrollView];
scrollView.delegate = (id) self;
// Init Page Control
pageControl = [[UIPageControl alloc] init];
[pageControl addTarget:self action:#selector(pageChange:) forControlEvents:UIControlEventValueChanged];
[self addSubview:pageControl];
}
return self;
}
- (IBAction)pageChange:(id)sender {
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * pageControl.currentPage;
frame.origin.y = 0;
[scrollView scrollRectToVisible:frame animated:TRUE];
NSLog(#"%i", pageControl.currentPage);
}
- (void)addView:(UIView *)view {
[scrollView addSubview:view];
}
- (void)createPageControlAt:(CGRect)cg {
pageControl.frame = cg;
}
- (void)setTotalPages:(NSUInteger)pages {
pageControl.numberOfPages = pages;
}
- (void)setCurrentPage:(NSUInteger)current {
pageControl.currentPage = current;
}
- (void)createScrollViewAt:(CGRect)cg {
[scrollView setPagingEnabled:TRUE];
scrollView.frame = cg;
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width*pageControl.numberOfPages, scrollView.frame.size.height);
[scrollView setShowsHorizontalScrollIndicator:FALSE];
[scrollView setShowsVerticalScrollIndicator:FALSE];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
float frac = scrollView.contentOffset.x / scrollView.frame.size.width;
NSUInteger page = lround(frac);
pageControl.currentPage = page;
}
#end
ViewController.m (somewhere in viewDidLoad)
Carousel *carousel = [[Carousel alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
for (int i=0; i<5; i++) {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(i * 320, 0, 320, 420)];
UIColor *color;
if(i%3==0) color = [UIColor blueColor];
else if(i%3==1) color = [UIColor redColor];
else color = [UIColor purpleColor];
view.backgroundColor = color;
[carousel addView:view];
view = nil;
}
[carousel setTotalPages:5];
[carousel setCurrentPage:0];
[carousel createPageControlAt:CGRectMake(0,420,320,40)];
[carousel createScrollViewAt:CGRectMake(0,0,320,420)];
Your code is correct. Most likely the frame of your pageControl is pretty small, so theres not a lot of area to look for touch events. You would need to increase the size of the height of pageControl in order to make sure taps are recognized all of the time.

loading screen not centered on second launch

I have a UIView which is my loading view. All it does is display the circular loading circle(lol to much "circle" for one sentence).
It works fine the first time but after that the circle is not centered. It moves to the left and down some. How can I get it to always be centered, take in mind I have limited the app to only display in the landscape modes (landscape left, landscape right) in all views so the issue is not coming from the device being rotated.
call to load the view:
loadingViewController = [LoadingViewController loadSpinnerIntoView:self.view];
LoadingViewController.h:
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import "CrestronClient.h"
#interface LoadingViewController : UIView
{
CrestronClient *cClient;
}
+(LoadingViewController *)loadSpinnerIntoView:(UIView *)superView;
-(void)removeLoadingView;
- (UIImage *)addBackground;
#end
LoadingView.m:
#import "LoadingViewController.h"
#import "RootViewController.h"
#implementation LoadingViewController
CGRect priorFrameSettings;
UIView *parentView;
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||interfaceOrientation == UIInterfaceOrientationLandscapeRight ) {
return YES;
}else{
return NO;
}
}
-(void)removeLoadingView
{
// [parentView setFrame:priorFrameSettings];
CATransition *animation = [CATransition animation];
[animation setType:kCATransitionFade];
[[[self superview] layer] addAnimation:animation forKey:#"layerAnimation"];
[self removeFromSuperview];
}
+(LoadingViewController *)loadSpinnerIntoView:(UIView *)superView
{
priorFrameSettings = superView.frame;
parentView = superView;
// [superView setFrame:CGRectMake(0, 0, 1024, 1024)];
// Create a new view with the same frame size as the superView
LoadingViewController *loadingViewController = [[LoadingViewController alloc] initWithFrame:superView.frame];
loadingViewController.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
// If something's gone wrong, abort!
if(!loadingViewController){ return nil; }
[superView addSubview:loadingViewController];
if(!loadingViewController){ return nil; }
// This is the new stuff here ;)
UIActivityIndicatorView *indicator =
[[[UIActivityIndicatorView alloc]
initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleWhiteLarge] autorelease];
// Set the resizing mask so it's not stretched
UIImageView *background = [[UIImageView alloc] initWithImage:[loadingViewController addBackground]];
// Make a little bit of the superView show through
background.alpha = 0.7;
[loadingViewController addSubview:background];
indicator.autoresizingMask =
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleBottomMargin |
UIViewAutoresizingFlexibleLeftMargin;
// Place it in the middle of the view
indicator.center = superView.center;
// Add it into the spinnerView
[loadingViewController addSubview:indicator];
// Start it spinning! Don't miss this step
[indicator startAnimating];
// Create a new animation
CATransition *animation = [CATransition animation];
// Set the type to a nice wee fade
[animation setType:kCATransitionFade];
// Add it to the superView
[[superView layer] addAnimation:animation forKey:#"layerAnimation"];
return loadingViewController;
}
- (UIImage *)addBackground{
cClient = [CrestronClient sharedManager];
if (cClient.isConnected == FALSE) {
[cClient connect];
}
// Create an image context (think of this as a canvas for our masterpiece) the same size as the view
UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, 1);
// Our gradient only has two locations - start and finish. More complex gradients might have more colours
size_t num_locations = 2;
// The location of the colors is at the start and end
CGFloat locations[2] = { 0.0, 1.0 };
// These are the colors! That's two RBGA values
CGFloat components[8] = {
0.4,0.4,0.4, 0.8,
0.1,0.1,0.1, 0.5 };
// Create a color space
CGColorSpaceRef myColorspace = CGColorSpaceCreateDeviceRGB();
// Create a gradient with the values we've set up
CGGradientRef myGradient = CGGradientCreateWithColorComponents (myColorspace, components, locations, num_locations);
// Set the radius to a nice size, 80% of the width. You can adjust this
float myRadius = (self.bounds.size.width*.8)/2;
// Now we draw the gradient into the context. Think painting onto the canvas
CGContextDrawRadialGradient (UIGraphicsGetCurrentContext(), myGradient, self.center, 0, self.center, myRadius, kCGGradientDrawsAfterEndLocation);
// Rip the 'canvas' into a UIImage object
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// And release memory
CGColorSpaceRelease(myColorspace);
CGGradientRelease(myGradient);
UIGraphicsEndImageContext();
// … obvious.
return image;
}
- (void)dealloc {
[super dealloc];
}
#end
Make sure the loading view is set to its parents frame and has the proper autoresizingMask set. This would likely by UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight.
fixed the background by adding
[background setFrame:CGRectMake(0, 0, 1024, 768 )];
and fixed the centering of the circle with:
indicator.center = background.center;

Objective C - Help with changing background color when UIButton is pressed

I am new to programing and any help is appreciated. I am trying to change the background color of a button once it has been pressed. I have tried setBackgroundColor without success. I am not sure that it is compatible with UIButton. Is there any way to programatically accomplish such a task? All thoughts and suggestions are appreciated.
I woudl suggest creating a simple image that contains the background color you want and setting that via the existing methods in the UIButton. (check Wrights Answer for the doc link).
UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];
NSString* fileLocation = [[NSBundle mainBundle] pathForResource:#"buttonBG" ofType:#"png"];
UIImage* bgImage = [UIImage imageWithContentsOfFile:fileLocation];
if (bgImage != nil) { // check if the image was actually set
[button setBackgroundImage:bgImage forState:UIControlStateHighlighted];
} else {
NSLog(#"Error trying to read the background image");
}
That should do the trick. There might be an even better way to create the necessary image on the fly, but that's stuff I'm not firm in.
[edit: a bit more verbose code ]
Assuming you have an unadorned custom button with a title of "On" for the normal state:
- (IBAction) toggleButtonState {
if ([toggleButton titleForState:UIControlStateNormal] == #"On") {
[toggleButton setTitle: #"Off" forState:UIControlStateNormal];
[toggleButton setBackgroundColor:[UIColor redColor]];
}
else {
[toggleButton setTitle: #"On" forState:UIControlStateNormal];
[toggleButton setBackgroundColor:[UIColor greenColor]];
}
}
All the other buttons have an image placed in front of the view, so at most you'll see the corners change if the image doesn't completely fill the space.
I'd also suggest using an image, but for learning purposes, this will work.
Ive just been having the same issue and ended up using a UIButton subclass to tackle the issue. I used gradients simply because it looked a bit better if you have no need for them you can simply remove them. I have explained the process I used and included the full code at the bottom of the post.
Firstly add properties for the layers.I created two layers one for the base gradient and one for a gloss to add a little bit of style.
#interface gradientButton()
#property (nonatomic, strong) CAGradientLayer* gradientLayer;
#property (nonatomic, strong) CAGradientLayer* glossyLayer;
#end
Then either in -(void)awakeFromNib or in -(id)initWithFrame:(CGRect)frame
,depending on if you will load from storyboard or code respectively, configure the gradients and add the layers, round your corners off and customize the font highlight color.
-(void)awakeFromNib
{
_gradientLayer = [[CAGradientLayer alloc] init];
_gradientLayer.bounds = self.bounds;
_gradientLayer.position = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
[self.layer insertSublayer:_gradientLayer atIndex:0];
self.layer.cornerRadius = 5.0f;
self.layer.masksToBounds = YES;
self.layer.borderWidth = 1.0f;
_glossyLayer = [[CAGradientLayer alloc] init];
_glossyLayer.bounds = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height/2);
_glossyLayer.position = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/4);
[self.layer addSublayer:_glossyLayer];
[self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[self setTitleColor:[UIColor yellowColor] forState:UIControlStateHighlighted];
}
Next, override - (void)drawRect:(CGRect)rect to apply your layers and define your colors.
#define GRADIENT_TOP [UIColor colorWithRed:38.0/255.0 green:78.0/255.0 blue:54.0/255.0 alpha:1]
#define GRADIENT_BOTTOM [UIColor colorWithRed:44.0/255.0 green:71.0/255.0 blue:56.0/255.0 alpha:1]
#define GLOSS_TOP [UIColor colorWithRed:0.70f green:0.70f blue:0.70f alpha:0.95f]
#define GLOSS_BOTTOM [UIColor colorWithRed:0.70f green:0.70f blue:0.70f alpha:0.35f]
#define GRADIENT_SELECTED_TOP [UIColor colorWithRed:138.0/255.0 green:178.0/255.0 blue:154.0/255.0 alpha:1]
#define GRADIENT_SELECTED_BOTTOM [UIColor colorWithRed:114.0/255.0 green:171.0/255.0 blue:156.0/255.0 alpha:1]
- (void)drawRect:(CGRect)rect
{
[_gradientLayer setColors:#[(id)[GRADIENT_TOP CGColor],(id)[GRADIENT_BOTTOM CGColor]]];
[_glossyLayer setColors:#[(id)[GLOSS_TOP CGColor], (id)[GLOSS_BOTTOM CGColor]]];
[super drawRect:rect];
}
Finally, and the bit we've all been waiting for, override -(void)setHighlighted:(BOOL)highlighted{
so we can apply the highlight effect were looking for.
-(void)setHighlighted:(BOOL)highlighted{
[super setHighlighted:highlighted];
if(highlighted)
[_gradientLayer setColors:#[(id)[GRADIENT_SELECTED_TOP CGColor],(id)[GRADIENT_SELECTED_BOTTOM CGColor]]];
else
[_gradientLayer setColors:#[(id)[GRADIENT_TOP CGColor],(id)[GRADIENT_BOTTOM CGColor]]];
}
There we have it, now just drag out a UIButton modify the class and your all good. Heres the full Implementation so you can copy it straight out. http://pastebin.com/nUVeujyp
Check out the UIButton Class Reference.
Regular UIButtons do not have the backgroundColor option.
My suggestion would to use the UISegmentedControl, which has the tinColor option.
I have created a subclass which get background color and creates an UIImage for each state.
For me it's more useful a subclass instead a category, so that's up to you.
#implementation ROCRoundColorButton
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
UIColor *darkColor;
darkColor = [self darkColorFromBackgroundColor];
[self setBackgroundImage:[self imageWithColor:self.backgroundColor withSize:self.frame.size] forState:UIControlStateNormal];
[self setBackgroundImage:[self imageWithColor:darkColor withSize:self.frame.size] forState:UIControlStateSelected];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
UIColor *darkColor;
darkColor = [self darkColorFromBackgroundColor];
[self setBackgroundImage:[self imageWithColor:self.backgroundColor withSize:self.frame.size] forState:UIControlStateNormal];
[self setBackgroundImage:[self imageWithColor:darkColor withSize:self.frame.size] forState:UIControlStateSelected];
}
return self;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#pragma mark -
#pragma mark Private methods
- (UIColor *)darkColorFromBackgroundColor
{
const float* components = CGColorGetComponents( self.backgroundColor.CGColor );
CGFloat red = components[0];
CGFloat green = components[1];
CGFloat blue = components[2];
CGFloat alpha = components[3];
if (red > 0) {
red -= 0.1;
}
if (green > 0) {
green -= 0.1;
}
if (blue > 0) {
blue -= 0.1;
}
UIColor *darkColor = [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
return darkColor;
}
- (UIImage *)imageWithColor:(UIColor *)color withSize:(CGSize)size
{
UIGraphicsBeginImageContext(size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [color CGColor]);
//CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height));
UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, size.width, size.height) cornerRadius:5];
[roundedRect fillWithBlendMode: kCGBlendModeNormal alpha:1.0f];
[color setFill];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
In fact, you can use it in the storyboard, changing the class and setting de background color in the view.