ChatGPT解决这个技术问题 Extra ChatGPT

How to draw a custom UIView that is just a circle - iPhone app

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)


i
idmean

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];

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.
And it is. 100 is the height/width.
Yeah, sorry wasn't directing that at you, Kal. I was mentioning that to StanLe in case he wanted to use different sized views.
If your circleView size isn't 100X100, the cornerRadius should be the (new size)/2
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.
S
Steve

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.


How can I set custom colors, not only blue? I try to use white and blue like this: ([self.colorOfCircle CGColor]) but nothing happens :\
@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.
@gaussblurinc use CGContextSetFillColorWithColor(ctx, self.colorOfCircle.CGColor);, the method proposed in the solution CGColorGetComponents only works with some colors, see stackoverflow.com/questions/9238743/…
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! :)
j
jomafer

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;

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.
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 ??
You can subclass UIView and override the layerClass class method to make it a shape layer.
@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.
s
shim

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()


setting masksToBounds to true and using self are all optional in this answer, but it's still the shortest and best solution
M
Mobile Developer

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
    }

}

C
Cheok Yan Cheng

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()
    }           
}

s
shim

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:

https://i.stack.imgur.com/VOxz3.png


u
user8675

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:

https://i.stack.imgur.com/JHuRP.png

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.


s
shim

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        
}