138

How would I go about drawing a custom UIView that is literally just a ball (a 2D circle)? Would I just override the drawRect method? And can someone show me the code for drawing a blue circle?

Also, would it be okay to change the frame of that view within the class itself? Or do I need to change the frame from a different class?

(just trying to set up a ball bouncing around)

StanLe
  • 5,037
  • 9
  • 38
  • 41

9 Answers9

215

You could use QuartzCore and do something this --

self.circleView = [[UIView alloc] initWithFrame:CGRectMake(10,20,100,100)];
self.circleView.alpha = 0.5;
self.circleView.layer.cornerRadius = 50;  // half the width/height
self.circleView.backgroundColor = [UIColor blueColor];
idmean
  • 14,540
  • 9
  • 54
  • 83
Kal
  • 24,724
  • 7
  • 65
  • 65
  • 105
    FYI, in order for your view to be a circle using QuartCore, your corner radius needs to be half your frame height/width. This is the easiest way of making a circle, but is not necessarily the most efficient. If performance is vital, drawRect will probably yield better results. – Benjamin Mayo Jul 05 '11 at 22:28
  • 4
    And it is. 100 is the height/width. – Kal Jul 05 '11 at 22:31
  • 3
    Yeah, sorry wasn't directing that at you, Kal. I was mentioning that to StanLe in case he wanted to use different sized views. – Benjamin Mayo Jul 06 '11 at 17:24
  • If your circleView size isn't 100X100, the cornerRadius should be the (new size)/2 – gran33 Sep 02 '14 at 08:53
  • But I noticed that circle with cornerradius has sometimes slightly "flat"/clipped edges. At least when used with a border. Any idea why? The radius is exactly half of view's size. I thought it may be clipping issue, but doesn't seem like that, tried even with a smaller sublayer - still have the same effect. – User May 22 '15 at 15:12
  • @Ixx maybe you have to override `- (void)drawRect` if you want to make it look better... – Nik May 27 '15 at 22:20
  • @dig I was aware that that is possible... ended doing that and yes, that looks correct. Was just curious if there was a fix using corner radius. Set clip bounds to false also didn't help. – User May 31 '15 at 18:02
137

Would I just override the drawRect method?

Yes:

- (void)drawRect:(CGRect)rect
{
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextAddEllipseInRect(ctx, rect);
    CGContextSetFillColor(ctx, CGColorGetComponents([[UIColor blueColor] CGColor]));
    CGContextFillPath(ctx);
}

Also, would it be okay to change the frame of that view within the class itself?

Ideally not, but you could.

Or do I need to change the frame from a different class?

I'd let the parent control that.

Steve
  • 31,144
  • 19
  • 99
  • 122
  • 1
    How can I set custom colors, not only blue? I try to use white and blue like this: ([self.colorOfCircle CGColor]) but nothing happens :\ – gaussblurinc Jul 17 '13 at 09:40
  • @loldop is self.colorOfCircle a UIColor? Is it set? If you put a breakpoint on that line and look at the value, is it what you're expecting? IF you set that after the fact, you'll have to invalidate the portion of your view that contains the circle. – Steve Jul 17 '13 at 16:00
  • 10
    @gaussblurinc use `CGContextSetFillColorWithColor(ctx, self.colorOfCircle.CGColor);`, the method proposed in the solution `CGColorGetComponents` only works with some colors, see http://stackoverflow.com/questions/9238743/is-there-an-issue-with-cgcolorgetcomponents – yonilevy Nov 12 '13 at 16:44
  • Note to anyone who gets themselves stuck on this. Rather than `rect`, I accidentally used `self.frame` for the ellipse. The correct value is `self.bounds`. D'oh! :) – Olie Jul 17 '14 at 03:32
31

Here is another way by using UIBezierPath (maybe it's too late ^^) Create a circle and mask UIView with it, as follows:

UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
view.backgroundColor = [UIColor blueColor];

CAShapeLayer *shape = [CAShapeLayer layer];
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:view.center radius:(view.bounds.size.width / 2) startAngle:0 endAngle:(2 * M_PI) clockwise:YES];
shape.path = path.CGPath;
view.layer.mask = shape;
jomafer
  • 2,655
  • 1
  • 32
  • 47
Gintama
  • 1,152
  • 20
  • 35
  • 1
    There's not really a need to make that shape layer a mask; you could just use the shape layer directly as the view's layer and give it a fill color. The mask is probably significantly more expensive. – Jesse Rusak Jul 13 '14 at 15:42
  • UIView's layer is CALayer so how can we draw a shape on this layer. I suppose that we add shape layer to view's layer without masking it ?? – Gintama Jul 13 '14 at 17:49
  • 1
    You can subclass UIView and override the `layerClass` class method to make it a shape layer. – Jesse Rusak Jul 13 '14 at 17:51
  • @JesseRusak, the problem I'm having with your suggestion (to set the shape on the UIView's layer itself) is that you have to can't properly use the backgroundColor. You would need to apply a fillColor and set the backgroundColor to clearColor, which means a user of the UIView would not be able to naturally set the backgroundColor. – Richard Venable Jun 07 '16 at 16:56
29

My contribution with a Swift extension:

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

Just call myView.asCircle()

shim
  • 9,289
  • 12
  • 69
  • 108
Kevin ABRIOUX
  • 16,507
  • 12
  • 93
  • 99
  • setting `masksToBounds` to true and using `self` are all optional in this answer, but it's still the shortest and best solution – Max Desiatov Apr 13 '17 at 20:12
22

Swift 3 - custom class, easy to reuse. It uses backgroundColor set in UI builder

import UIKit

@IBDesignable
class CircleBackgroundView: UIView {

    override func layoutSubviews() {
        super.layoutSubviews()
        layer.cornerRadius = bounds.size.width / 2
        layer.masksToBounds = true
    }

}
Mobile Developer
  • 5,730
  • 1
  • 39
  • 45
15

Swift 3 class:

import UIKit

class CircleView: UIView {

    override func draw(_ rect: CGRect) {
        guard let context = UIGraphicsGetCurrentContext() else {return}
        
        context.addEllipse(in: rect)
        context.setFillColor(UIColor.blue.cgColor)
        context.fillPath()
    }           
}
Cheok Yan Cheng
  • 47,586
  • 132
  • 466
  • 875
Vyacheslav
  • 26,359
  • 19
  • 112
  • 194
6

Another way of approaching circle (and other shapes) drawing is by using masks. You draw circles or other shapes by, first, making masks of the shapes you need, second, provide squares of your color and, third, apply masks to those squares of color. You can change either mask or color to get a new custom circle or other shape.

#import <QuartzCore/QuartzCore.h>

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *area1;
@property (weak, nonatomic) IBOutlet UIView *area2;
@property (weak, nonatomic) IBOutlet UIView *area3;
@property (weak, nonatomic) IBOutlet UIView *area4;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.area1.backgroundColor = [UIColor blueColor];
    [self useMaskFor: self.area1];

    self.area2.backgroundColor = [UIColor orangeColor];
    [self useMaskFor: self.area2];

    self.area3.backgroundColor = [UIColor colorWithRed: 1.0 green: 0.0 blue: 0.5 alpha:1.0];
    [self useMaskFor: self.area3];

    self.area4.backgroundColor = [UIColor colorWithRed: 1.0 green: 0.0 blue: 0.5 alpha:0.5];
    [self useMaskFor: self.area4];        
}

- (void)useMaskFor: (UIView *)colorArea {        
    CALayer *maskLayer = [CALayer layer];
    maskLayer.frame = colorArea.bounds;
    UIImage *maskImage = [UIImage imageNamed:@"cirMask.png"];
    maskLayer.contents = (__bridge id)maskImage.CGImage;
    colorArea.layer.mask = maskLayer;
}

@end

Here is the output of the code above:

four circles

shim
  • 9,289
  • 12
  • 69
  • 108
matrix3003
  • 386
  • 3
  • 9
2

There's another alternative for lazy people. You can set the layer.cornerRadius key path for your view in the Interface Builder. For example, if your view has a width = height of 48, set layer.cornerRadius = 24:

enter image description here

However, this only works if you have a static size of the view (width/height is fixed) and it's not showing the circle in the interface builder.

user8675
  • 657
  • 6
  • 13
1

Swift 3 - Xcode 8.1

@IBOutlet weak var myView: UIView!

override func viewDidLoad() {
    super.viewDidLoad() 

    let size:CGFloat = 35.0
    myView.bounds = CGRect(x: 0, y: 0, width: size, height: size)
    myView.layer.cornerRadius = size / 2
    myView.layer.borderWidth = 1
    myView.layer.borderColor = UIColor.Gray.cgColor        
}
shim
  • 9,289
  • 12
  • 69
  • 108
Mohammad Razipour
  • 3,643
  • 3
  • 29
  • 49