简体   繁体   中英

UIView Draw Circle with Dotted Line Border

Is there a way to draw a UIView circle with a dotted line border? I want to have control over the spacing between the dots, and the size of the dots. I tried specifying my own pattern image, but when I make it into a circle it doesn't look good:

UIView *mainCircle = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
[mainCircle.layer setCornerRadius:100];
[mainCircle.layer setBorderWidth:5.0];
[mainCircle.layer setBorderColor:[[UIColor colorWithPatternImage:[UIImage imageNamed:@"dotted"]] CGColor]];
[self.view addSubview:mainCircle];
[mainCircle setCenter:self.view.center];

在此输入图像描述

Following on from aksh1t's answer and rob's answer , you should use a round line cap, along with a dash pattern to do this.

The only thing I would add is that with the current code, you can end up with results like this:

在此输入图像描述

Notice how at the top, you get an overlap of the dots. This is due to the fact that the circumference of the circle isn't entirely divisible by the number of dots.

You can fix this relatively easily by doing a simple bit of maths before. I wrote a few lines of code that'll allows you to provide an dot diameter value, along with an expected dot spacing - and it will try and approximate the nearest dot spacing that will result in an integral number of dots.

Also, I recommend you take an 100% layered approach, using CAShapeLayer to draw your circle. That way you can easily add animations to it without having to completely re-draw it for each frame.

Something like this should do the trick:

// your dot diameter.
CGFloat dotDiameter = 10.0;

// your 'expected' dot spacing. we'll try to get as closer value to this as possible.
CGFloat expDotSpacing = 20.0;

// the size of your view
CGSize s = self.view.frame.size;

// the radius of your circle, half the width or height (whichever is smaller) with the dot radius subtracted to account for stroking
CGFloat radius = (s.width < s.height) ? s.width*0.5-dotDiameter*0.5 : s.height*0.5-dotDiameter*0.5;

// the circumference of your circle
CGFloat circum = M_PI*radius*2.0;

// the number of dots to draw as given by the circumference divided by the diameter of the dot plus the expected dot spacing.
NSUInteger numberOfDots = round(circum/(dotDiameter+expDotSpacing));

// the calculated dot spacing, as given by the circumference divided by the number of dots, minus the dot diameter.
CGFloat dotSpacing = (circum/numberOfDots)-dotDiameter;

// your shape layer
CAShapeLayer* l = [CAShapeLayer layer];
l.frame = (CGRect){0, 0, s.width, s.height};

// set to the diameter of each dot
l.lineWidth = dotDiameter;

// your stroke color
l.strokeColor = [UIColor blackColor].CGColor;

// the circle path - given the center of the layer as the center and starting at the top of the arc.
UIBezierPath* p = [UIBezierPath bezierPathWithArcCenter:(CGPoint){s.width*0.5, s.height*0.5} radius:radius startAngle:-M_PI*0.5 endAngle:M_PI*1.5 clockwise:YES];
l.path = p.CGPath;

// prevent that layer from filling the area that the path occupies
l.fillColor = [UIColor clearColor].CGColor;

// round shape for your stroke
l.lineCap = kCALineCapRound;

// 0 length for the filled segment (radius calculated from the line width), dot diameter plus the dot spacing for the un-filled section
l.lineDashPattern = @[@(0), @(dotSpacing+dotDiameter)];

[self.view.layer addSublayer:l];

You'll now get the following output:

在此输入图像描述

If you want to use this in a UIView , I would suggest subclassing it and adding the CAShapeLayer as a sublayer. You'll also want to add a masking layer in order to mask the view's contents to inside the border.

I have added an example of this in the full project below.


Full Project: https://github.com/hamishknight/Dotted-Circle-View

The best way to do what you are trying would be to draw a circle UIBezierPath , and set the path to a dotted style. The dotted style path code was taken from this answer .

UIBezierPath * path = [[UIBezierPath alloc] init];
[path addArcWithCenter:center radius:50 startAngle:0 endAngle:2 * M_PI clockwise:YES];
[path setLineWidth:8.0];
CGFloat dashes[] = { path.lineWidth, path.lineWidth * 2 };
[path setLineDash:dashes count:2 phase:0];
[path setLineCapStyle:kCGLineCapRound];

// After you have the path itself, you can either make 
// an image and set it in a view or use the path directly
// in the layer of the view you want to.
// This is the code for the image option.

UIGraphicsBeginImageContextWithOptions(CGSizeMake(300, 20), false, 2);
[path stroke];
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM