14
votes

I would like to recreate the circular buttons found in the Clock app in iOS7. The buttons are basically circles with different appearance depending on the button states (green border, red border, grey fill).

I could of course achieve this using a simple UIButton with images for the different states.

However I am looking for a solution which draws the circle programmatically, so I can easily change radius, stroke width, etc.

As far as I can see UIButton only allows me to define an UIImage for each state, so I cannot modify the layers per state directly (e.g. provide a layer with cornerRadius). Is there another way?

8
Have you considered drawing a circular view and adding tap gestures to it? Just a thought. - Douglas
Yes that would probably work. However I thought to avoid recreating the UIButton functionality. So if there is a solution with a UIButton I would prefer that. - henning77

8 Answers

30
votes

Creating a custom button may be helpful.

in the .h file;

#import <UIKit/UIKit.h>
@interface CircleLineButton : UIButton

- (void)drawCircleButton:(UIColor *)color;

@end

in the .m file;

    #import "CircleLineButton.h"

    @interface CircleLineButton ()

    @property (nonatomic, strong) CAShapeLayer *circleLayer;
    @property (nonatomic, strong) UIColor *color;
    @end

    @implementation CircleLineButton



    - (void)drawCircleButton:(UIColor *)color
    {
        self.color = color;

        [self setTitleColor:color forState:UIControlStateNormal];

        self.circleLayer = [CAShapeLayer layer];

        [self.circleLayer setBounds:CGRectMake(0.0f, 0.0f, [self bounds].size.width,
                                      [self bounds].size.height)];
        [self.circleLayer setPosition:CGPointMake(CGRectGetMidX([self bounds]),CGRectGetMidY([self bounds]))];

        UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, CGRectGetWidth(self.frame), CGRectGetHeight(self.frame))];

        [self.circleLayer setPath:[path CGPath]];

        [self.circleLayer setStrokeColor:[color CGColor]];

        [self.circleLayer setLineWidth:2.0f];
        [self.circleLayer setFillColor:[[UIColor clearColor] CGColor]];

        [[self layer] addSublayer:self.circleLayer];
    }

    - (void)setHighlighted:(BOOL)highlighted
    {
        if (highlighted)
        {
            self.titleLabel.textColor = [UIColor whiteColor];
            [self.circleLayer setFillColor:self.color.CGColor];
        }
        else
        {
            [self.circleLayer setFillColor:[UIColor clearColor].CGColor];
            self.titleLabel.textColor = self.color;
        }
    }

@end

And in the view controller, call [self.myCircleButton drawCircleButton:[UIColor myColor]]

11
votes

There are lot of ways you could accomplish this, for example:

  • Use CAShapedLayer

  • Subclass UIView and use the drawRect: method to draw a circle

  • Just have a square UIView and use the layer.cornerRadius property.

Depending on your needs, something as simple as creating normal UIButton and calling

myButton.layer.cornerRadius = myButton.bounds.size.width / 2.0;

could work (you'll need to include the Quartz Framework)

4
votes

The pattern I've used to achieve this kind of thing is:

Subclass UIButton and implement drawRect to draw the button as you want, customising colours based on the selected and highlighted properties of the button.

Then override setSelected and setHighlighted to force redraws like so:

-(void)setHighlighted:(BOOL)highlighted {
    [super setHighlighted:highlighted];
    [self setNeedsDisplay];
}
4
votes

Add this code

imagePreview.layer.cornerRadius = (imagePreview.bounds.size.height/2);

where imagePreview is UIview/UIimageview/UIButton

don't forget to add

#import <QuartzCore/QuartzCore.h>
4
votes

Solution with a swift Extension :

extension UIView{
    func asCircle(){
        self.layer.cornerRadius = self.frame.size.width / 2;
        self.layer.masksToBounds = true
    }
}

Just call

myView.asCircle()

Can work with any type of view, not only a button

0
votes

You can use this control, it's subclass from UIButton. https://github.com/alvaromb/AMBCircularButton

0
votes

I think it work good.

override func viewDidLoad() {
    super.viewDidLoad()

    var button = UIButton.buttonWithType(.Custom) as UIButton
    button.frame = CGRectMake(160, 100, 50, 50)
    button.layer.cornerRadius = 0.5 * button.bounds.size.width
    button.setImage(UIImage(named:"thumbsUp.png"), forState: .Normal)
    button.addTarget(self, action: "thumbsUpButtonPressed", forControlEvents: .TouchUpInside)
    view.addSubview(button)
}

func thumbsUpButtonPressed() {
    println("thumbs up button pressed")
}
-1
votes
  1. In storyboard, Make square UIButton (eg. 100 * 100 )

  2. Link outlet (eg. @property (Strong, nonatomic) UIButton IBOutlet * myButton; )

  3. In your code write this: self.myButton.layer.cornerRadius = 50; // 50 is half of square's length of one side.