iOS Custom Annotation: A view below the annotation pin - ios

I need to replace the default annotation view with my custom annotation view.
I need the do following things:
Custom Annotation view with an image view embedded in it.
A view below it which contains a label in it.
For more clarification see the image:
In the above image I need to place an image view in the white space which you can see in the image in circular form, next I also need to add a view which contains a label on which I can set any text like me, friends, etc...
So, for this I searched number of questions on stack overflow but didn't got my answer. I don't want it on call out, I just want it simply as annotation when map is rendered. I have tried to make a custom class for this but not getting any idea how to deal with this.
Any help will be highly appreciated

You could just create your own annotation view:
#import MapKit;
#interface CustomAnnotationView : MKAnnotationView
#end
#interface CustomAnnotationView ()
#property (nonatomic) CGSize textSize;
#property (nonatomic) CGSize textBubbleSize;
#property (nonatomic, weak) UILabel *label;
#property (nonatomic) CGFloat lineWidth;
#property (nonatomic) CGFloat pinRadius;
#property (nonatomic) CGFloat pinHeight;
#property (nonatomic, strong) UIBezierPath *pinPath;
#property (nonatomic, strong) UIBezierPath *textBubblePath;
#end
#implementation CustomAnnotationView
- (instancetype)initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if (self) {
self.lineWidth = 1.0;
self.pinHeight = 40;
self.pinRadius = 15;
UILabel *label = [[UILabel alloc] init];
label.textAlignment = NSTextAlignmentCenter;
label.font = [UIFont preferredFontForTextStyle:UIFontTextStyleCallout];
label.textColor = [UIColor whiteColor];
[self addSubview:label];
self.label = label;
[self adjustLabelWidth:annotation];
self.opaque = false;
}
return self;
}
- (void)setAnnotation:(id<MKAnnotation>)annotation {
[super setAnnotation:annotation];
if (annotation) [self adjustLabelWidth:annotation];
}
- (void)adjustLabelWidth:(id<MKAnnotation>)annotation {
NSString *title = [annotation title];
NSDictionary *attributes = #{NSFontAttributeName : self.label.font};
self.textSize = [title sizeWithAttributes:attributes];
CGFloat delta = self.textSize.height * (1.0 - sinf(M_PI_4)) * 0.55;
self.textBubbleSize = CGSizeMake(self.textSize.width + delta * 2, self.textSize.height + delta * 2);
self.label.frame = CGRectMake(0, self.pinHeight, self.textBubbleSize.width, self.textBubbleSize.height);
self.label.text = title;
self.frame = CGRectMake(0, 0, self.textBubbleSize.width, self.pinHeight + self.textBubbleSize.height);
self.centerOffset = CGPointMake(0, self.frame.size.height / 2.0 - self.pinHeight);
}
- (void)drawRect:(CGRect)rect {
CGFloat radius = self.pinRadius - self.lineWidth / 2.0;
CGPoint startPoint = CGPointMake(self.textBubbleSize.width / 2.0, self.pinHeight);
CGPoint center = CGPointMake(self.textBubbleSize.width / 2, self.pinRadius);
CGPoint nextPoint;
// pin
self.pinPath = [UIBezierPath bezierPath];
[self.pinPath moveToPoint:startPoint];
nextPoint = CGPointMake(self.textBubbleSize.width / 2 - radius, self.pinRadius);
[self.pinPath addCurveToPoint:nextPoint
controlPoint1:CGPointMake(startPoint.x, startPoint.y - (startPoint.y - nextPoint.y) / 2.0)
controlPoint2:CGPointMake(nextPoint.x, nextPoint.y + (startPoint.y - nextPoint.y) / 2.0)];
[self.pinPath addArcWithCenter:center radius:radius startAngle:M_PI endAngle:0 clockwise:TRUE];
nextPoint = startPoint;
startPoint = self.pinPath.currentPoint;
[self.pinPath addCurveToPoint:nextPoint
controlPoint1:CGPointMake(startPoint.x, startPoint.y - (startPoint.y - nextPoint.y) / 2.0)
controlPoint2:CGPointMake(nextPoint.x, nextPoint.y + (startPoint.y - nextPoint.y) / 2.0)];
[[UIColor blackColor] setStroke];
[[UIColor colorWithRed:0.0 green:0.5 blue:1.0 alpha:0.8] setFill];
self.pinPath.lineWidth = self.lineWidth;
[self.pinPath fill];
[self.pinPath stroke];
[self.pinPath closePath];
// bubble around label
if ([self.annotation.title length] > 0) {
self.textBubblePath = [UIBezierPath bezierPath];
CGRect bubbleRect = CGRectInset(CGRectMake(0, self.pinHeight, self.textBubbleSize.width, self.textBubbleSize.height), self.lineWidth / 2, self.lineWidth / 2);
self.textBubblePath = [UIBezierPath bezierPathWithRoundedRect:bubbleRect
cornerRadius:bubbleRect.size.height / 2];
self.textBubblePath.lineWidth = self.lineWidth;
[self.textBubblePath fill];
[self.textBubblePath stroke];
} else {
self.textBubblePath = nil;
}
// center white dot
self.pinPath = [UIBezierPath bezierPathWithArcCenter:center radius:radius / 3.0 startAngle:0 endAngle:M_PI * 2.0 clockwise:TRUE];
self.pinPath.lineWidth = self.lineWidth;
[[UIColor whiteColor] setFill];
[self.pinPath fill];
}
- (UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event {
if ([self.pinPath containsPoint:point] || [self.textBubblePath containsPoint:point])
return self;
return nil;
}
#end
That yields something like:
Clearly, you can customize this to your heart's content, but it illustrates the basic idea: Write a MKAnnotationView subclass that overrides initWithAnnotation:reuseIdentifier: and implement your own drawRect.

Related

Zoom UIView inside UIScrollView with drawing

I have a scroll view (gray) with a zooming view inside (orange). The problem is if I zoom this view the red shape drawn on it gets zoomed too including lines width and blue squares size. What I want is to keep constant lines width and blue squares size (like on first picture) scaling just the area of the shape itself according to zoom level (drawn text is just for reference, I don't care about its size)
before zoom
after zoom
view controller
#import "ViewController.h"
#import "ZoomingView.h"
#interface ViewController ()
#property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
#end
#implementation ViewController
{
ZoomingView *_zoomingView;
}
- (void)viewDidLayoutSubviews
{
[self setup];
}
- (void)setup
{
CGFloat kViewSize = self.scrollView.frame.size.width - 40;
self.scrollView.minimumZoomScale = 1;
self.scrollView.maximumZoomScale = 10;
self.scrollView.delegate = self;
self.scrollView.contentSize = self.scrollView.bounds.size;
_zoomingView = [[ZoomingView alloc] initWithFrame:
CGRectMake((self.scrollView.frame.size.width - kViewSize) / 2,
(self.scrollView.frame.size.height - kViewSize) / 2,
kViewSize,
kViewSize)];
[self.scrollView addSubview:_zoomingView];
}
#pragma mark - UIScrollViewDelegate
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return _zoomingView;
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
// zooming view position fix
UIView *zoomView = [scrollView.delegate viewForZoomingInScrollView:scrollView];
CGRect zvf = zoomView.frame;
if (zvf.size.width < scrollView.bounds.size.width) {
zvf.origin.x = (scrollView.bounds.size.width - zvf.size.width) / 2.0f;
} else {
zvf.origin.x = 0.0;
}
if (zvf.size.height < scrollView.bounds.size.height) {
zvf.origin.y = (scrollView.bounds.size.height - zvf.size.height) / 2.0f;
} else {
zvf.origin.y = 0.0;
}
zoomView.frame = zvf;
[_zoomingView updateWithZoomScale:scrollView.zoomScale];
}
#end
zooming view
#import "ZoomingView.h"
#implementation ZoomingView
{
CGFloat _zoomScale;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setup];
}
return self;
}
- (void)setup
{
self.backgroundColor = [UIColor orangeColor];
_zoomScale = 1;
}
- (void)drawRect:(CGRect)rect
{
const CGFloat kPointSize = 10;
NSArray *points = #[[NSValue valueWithCGPoint:CGPointMake(30, 30)],
[NSValue valueWithCGPoint:CGPointMake(200, 40)],
[NSValue valueWithCGPoint:CGPointMake(180, 200)],
[NSValue valueWithCGPoint:CGPointMake(70, 180)]];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 1);
// points
[[UIColor blueColor] setStroke];
for (NSValue *value in points) {
CGPoint point = [value CGPointValue];
CGContextStrokeRect(context, CGRectMake(point.x - kPointSize / 2,
point.y - kPointSize / 2,
kPointSize,
kPointSize));
}
// lines
[[UIColor redColor] setStroke];
for (NSUInteger i = 0; i < points.count; i++) {
CGPoint point = [points[i] CGPointValue];
if (i == 0) {
CGContextMoveToPoint(context, point.x, point.y);
} else {
CGContextAddLineToPoint(context, point.x, point.y);
}
}
CGContextClosePath(context);
CGContextStrokePath(context);
// text
NSAttributedString *str = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:#"%f", _zoomScale] attributes:#{NSFontAttributeName : [UIFont systemFontOfSize:12]}];
[str drawAtPoint:CGPointMake(5, 5)];
}
- (void)updateWithZoomScale:(CGFloat)zoomScale
{
_zoomScale = zoomScale;
[self setNeedsDisplay];
}
#end
EDIT
Based on proposed solution (which works for sure) I was interested if I could make it work using my drawRect routine and Core Graphics methods.
So I changed my code this way, applying proposed scaling and contentsScale approach from this answer. As a result, without contentsScale drawing looks very blurry and with it much better, but a light blurriness persists anyway.
So the approach with layers gives the best result, although I don't get why.
- (void)drawRect:(CGRect)rect
{
const CGFloat kPointSize = 10;
NSArray *points = #[[NSValue valueWithCGPoint:CGPointMake(30, 30)],
[NSValue valueWithCGPoint:CGPointMake(200, 40)],
[NSValue valueWithCGPoint:CGPointMake(180, 200)],
[NSValue valueWithCGPoint:CGPointMake(70, 180)]];
CGFloat scaledPointSize = kPointSize * (1.0 / _zoomScale);
CGFloat lineWidth = 1.0 / _zoomScale;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, lineWidth);
// points
[[UIColor blueColor] setStroke];
for (NSValue *value in points) {
CGPoint point = [value CGPointValue];
CGContextStrokeRect(context, CGRectMake(point.x - scaledPointSize / 2,
point.y - scaledPointSize / 2,
scaledPointSize,
scaledPointSize));
}
// lines
[[UIColor redColor] setStroke];
for (NSUInteger i = 0; i < points.count; i++) {
CGPoint point = [points[i] CGPointValue];
if (i == 0) {
CGContextMoveToPoint(context, point.x, point.y);
} else {
CGContextAddLineToPoint(context, point.x, point.y);
}
}
CGContextClosePath(context);
CGContextStrokePath(context);
// text
NSAttributedString *str = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:#"%f", _zoomScale] attributes:#{NSFontAttributeName : [UIFont systemFontOfSize:12]}];
[str drawAtPoint:CGPointMake(5, 5)];
}
- (void)updateWithZoomScale:(CGFloat)zoomScale
{
_zoomScale = zoomScale;
[self setNeedsDisplay];
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithBool:YES]
forKey:kCATransactionDisableActions];
self.layer.contentsScale = zoomScale;
[CATransaction commit];
}
You may be better off putting your boxes and line-shape on CAShapeLayers, where you can update the line-width based on the zoom scale.
You only need to create and define your line-shape once. For your boxes, though, you'll need to re-create the path when you change the zoom (to keep the width/height of the boxes at a constant non-zoomed point size.
Give this a try. You should be able to simply replace your current ZoomingView.m class - no changes to the view controller necessary.
//
// ZoomingView.m
//
// modified by Don Mag
//
#import "ZoomingView.h"
#interface ZoomingView()
#property (strong, nonatomic) CAShapeLayer *shapeLayer;
#property (strong, nonatomic) CAShapeLayer *boxesLayer;
#property (strong, nonatomic) NSArray *points;
#end
#implementation ZoomingView
{
CGFloat _zoomScale;
CGFloat _kPointSize;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setup];
}
return self;
}
- (void)setup
{
self.backgroundColor = [UIColor orangeColor];
_points = #[[NSValue valueWithCGPoint:CGPointMake(30, 30)],
[NSValue valueWithCGPoint:CGPointMake(200, 40)],
[NSValue valueWithCGPoint:CGPointMake(180, 200)],
[NSValue valueWithCGPoint:CGPointMake(70, 180)]];
_zoomScale = 1;
_kPointSize = 10.0;
// create and setup boxes layer
_boxesLayer = [CAShapeLayer new];
[self.layer addSublayer:_boxesLayer];
_boxesLayer.strokeColor = [UIColor redColor].CGColor;
_boxesLayer.fillColor = [UIColor clearColor].CGColor;
_boxesLayer.lineWidth = 1.0;
_boxesLayer.frame = self.bounds;
// create and setup shape layer
_shapeLayer = [CAShapeLayer new];
[self.layer addSublayer:_shapeLayer];
_shapeLayer.strokeColor = [UIColor greenColor].CGColor;
_shapeLayer.fillColor = [UIColor clearColor].CGColor;
_shapeLayer.lineWidth = 1.0;
_shapeLayer.frame = self.bounds;
// new path for shape
UIBezierPath *thePath = [UIBezierPath new];
for (NSValue *value in _points) {
CGPoint point = [value CGPointValue];
if ([value isEqualToValue:_points.firstObject]) {
[thePath moveToPoint:point];
} else {
[thePath addLineToPoint:point];
}
}
[thePath closePath];
[_shapeLayer setPath:thePath.CGPath];
// trigger the boxes update
[self updateWithZoomScale:_zoomScale];
}
- (void)drawRect:(CGRect)rect
{
// text
NSAttributedString *str = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:#"%f", _zoomScale] attributes:#{NSFontAttributeName : [UIFont systemFontOfSize:12]}];
[str drawAtPoint:CGPointMake(5, 5)];
}
- (void)updateWithZoomScale:(CGFloat)zoomScale
{
_zoomScale = zoomScale;
CGFloat scaledPointSize = _kPointSize * (1.0 / zoomScale);
// create a path for the boxes
// needs to be done here, because the width/height of the boxes
// must change with the scale
UIBezierPath *thePath = [UIBezierPath new];
for (NSValue *value in _points) {
CGPoint point = [value CGPointValue];
CGRect r = CGRectMake(point.x - scaledPointSize / 2.0,
point.y - scaledPointSize / 2.0,
scaledPointSize,
scaledPointSize);
[thePath appendPath:[UIBezierPath bezierPathWithRect:r]];
}
[_boxesLayer setPath:thePath.CGPath];
_boxesLayer.lineWidth = 1.0 / zoomScale;
_shapeLayer.lineWidth = 1.0 / zoomScale;
[self setNeedsDisplay];
}
#end
Results:
Note: Should go without saying, but... This is intended to be a starting point for you to work with, not "production code."

how Draw a percentage of a circle objective c

i can need layout like same as image
but i can not draw like this so any idea about this,
The easiest approach would be to have a view with two subviews, one for the green "percent filled" level, and one for the label for the text. Then you can update the frame for the "percent filled" based upon, obviously, what percent filled you want it. And then apply a circular mask to the whole thing.
For example:
// CircleLevelView.h
//
// Created by Robert Ryan on 10/28/17.
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface CircleLevelView : UIView
/// Percent filled
///
/// Value between 0.0 and 1.0.
#property (nonatomic) IBInspectable CGFloat percent;
/// Text to show up in center of view
///
/// Value between 0.0 and 1.0.
#property (nonatomic, strong) IBInspectable NSString *text;
#end
And
// CircleLevelView.m
//
// Created by Robert Ryan on 10/28/17.
#import "CircleLevelView.h"
#interface CircleLevelView()
#property (nonatomic, weak) CAShapeLayer *circleMask;
#property (nonatomic, weak) UILabel *label;
#property (nonatomic, weak) UIView *fillView;
#end
#implementation CircleLevelView
#synthesize percent = _percent;
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
[self configure];
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
[self configure];
return self;
}
- (instancetype)init {
return [self initWithFrame:CGRectZero];
}
- (void)configure {
self.clipsToBounds = true;
UILabel *fillView = [[UILabel alloc] init];
fillView.translatesAutoresizingMaskIntoConstraints = false;
fillView.backgroundColor = [UIColor colorWithRed:169.0 / 255.0
green:208.0 / 255.0
blue:66.0 / 255.0
alpha:1.0];
[self addSubview:fillView];
self.fillView = fillView;
UILabel *label = [[UILabel alloc] init];
label.translatesAutoresizingMaskIntoConstraints = false;
label.backgroundColor = [UIColor clearColor];
label.textColor = [UIColor blackColor];
label.textAlignment = NSTextAlignmentCenter;
[self addSubview:label];
self.label = label;
[NSLayoutConstraint activateConstraints:#[
[label.topAnchor constraintEqualToAnchor:self.topAnchor],
[label.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
[label.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
[label.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],
[fillView.topAnchor constraintEqualToAnchor:self.topAnchor],
[fillView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
[fillView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
[fillView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor]
]];
CAShapeLayer *circleMask = [CAShapeLayer layer];
circleMask.fillColor = [UIColor whiteColor].CGColor;
circleMask.strokeColor = [UIColor blackColor].CGColor;
circleMask.lineWidth = 0;
self.layer.mask = circleMask;
self.circleMask = circleMask;
}
- (void)layoutSubviews {
[super layoutSubviews];
CGPoint center = CGPointMake(self.bounds.origin.x + self.bounds.size.width / 2.0, self.bounds.origin.y + self.bounds.size.height / 2.0);
CGFloat radius = MIN(self.bounds.size.width, self.bounds.size.height) / 2.0;
self.circleMask.path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:0 endAngle:M_PI * 2.0 clockwise:true].CGPath;
[self updatePercentFill];
}
- (void)updatePercentFill {
CGFloat circleHeight = MIN(self.bounds.size.width, self.bounds.size.height);
CGFloat percentHeight = circleHeight * self.percent;
self.fillView.frame = CGRectMake(0,
(self.bounds.size.height - circleHeight) / 2 + (circleHeight - percentHeight),
self.bounds.size.width,
circleHeight);
}
// MARK: - Custom Accessor Methods
- (CGFloat)percent {
return _percent;
}
- (void)setPercent:(CGFloat)percent {
_percent = percent;
[self updatePercentFill];
}
- (NSString *)text {
return self.label.text;
}
- (void)setText:(NSString *)text {
self.label.text = text;
}
#end
That yields:
If you know the chord position you can fill to the chord by setting the clip region and then filling the circle.
To work out the position of the chord to give an area of x% you'll need to do some geometry/trigonometry. Every chord which does not pass through the centre forms and isosceles triangle with the centre, and the two sides of that triangle which are radii delimit a segment of the circle. So the area on one side of a chord which does not pass through the centre is the difference of the area of a triangle and a segment, and you can work out how the chord divides the area or where the chord needs to be to divide the area in a particular ratio.
If that all sounds like gobbledegook try looking up chord geometry and you'll undoubtedly find books/sites with helpful diagrams and formulas.
HTH

Animation with UIBezierPath

I searched a lot for this topic, all answers said that i have to use CAShapeLayer or Layers and ... . the problem is i don't really know how to use them, and i know a solution to my problem; i'd just like to know if there's any way to do this without CAShapeLayer.
I'm trying to make a Star shaped rating view. there's a star, and a rectangle inside it from bottom to top indicating percentage of the rated star.
here is my .h file:
IB_DESIGNABLE
#interface FSStarRatingView : UIView
#property (assign, nonatomic) IBInspectable BOOL transparentInside;
#property (assign, nonatomic) IBInspectable CGFloat innerRadius; // between 0-1
#property (assign, nonatomic) IBInspectable CGFloat percentage; // between 0-1
#property (strong, nonatomic) IBInspectable UIColor *fillColor;
#property (strong, nonatomic) IBInspectable UIColor *unfilledColor; // works if transparentInside is YES
#property (strong, nonatomic) IBInspectable UIColor *strokeColor;
- (void)setPercentage:(CGFloat)percentage withAnimationDuration:(CGFloat)duration;
and here is my .m file
- (UIBezierPath *)starShapedBezierPath {
CGPoint points[10];
// points[0] = CGPointMake(self.center.x, 0);
CGFloat outterRadius = self.frame.size.width / 2;
CGFloat startDegree = 3.0 * M_PI_2;
CGPoint center = CGPointMake(outterRadius, outterRadius * 2.0/1.85);
for (int i = 0; i < 10; i+=2) {
points[i] = CGPointMake(center.x + outterRadius * cos(startDegree),
center.y + outterRadius * sin(startDegree));
startDegree += 0.4 * M_PI;
}
CGFloat innerRadius = self.innerRadius * outterRadius;
startDegree = M_PI_2;
for (int i = 5; i < 15; i+=2) {
points[i%10] = CGPointMake(center.x + innerRadius * cos(startDegree),
center.y + innerRadius * sin (startDegree));
startDegree += 0.4 * M_PI;
}
UIBezierPath *star = [UIBezierPath bezierPath];
[star moveToPoint:points[0]];
for (int i = 1; i < 10; i++)
[star addLineToPoint:points[i]];
[star closePath];
return star;
}
- (void)setPercentage:(CGFloat)percentage {
_percentage = percentage;
[self setNeedsDisplay];
}
- (void)setPercentage:(CGFloat)percentage withAnimationDuration:(CGFloat)duration{
[UIView
animateWithDuration:duration
animations:^{
self.percentage = percentage;
}];
}
- (void)drawRect:(CGRect)rect {
UIBezierPath *star = [self starShapedBezierPath];
star.lineWidth = 1.0;
[self.strokeColor setStroke];
[star stroke];
[star addClip];
CGFloat topBottomOffset = 0.15 * self.frame.size.width / 4.0;
CGFloat availableHeight = self.frame.size.height - 2.0 * topBottomOffset;
CGFloat usedHeight = availableHeight * self.percentage;
if (! self.transparentInside) {
UIBezierPath *unusedPercentageRect = [UIBezierPath bezierPathWithRect:CGRectMake(0, topBottomOffset, self.frame.size.width, availableHeight - usedHeight)];
[self.unfilledColor setFill];
[unusedPercentageRect fill];
}
UIBezierPath *usedPercentageRect = [UIBezierPath bezierPathWithRect:CGRectMake(0, topBottomOffset + availableHeight - usedHeight, self.frame.size.width, usedHeight)];
[self.fillColor setFill];
[usedPercentageRect fill];
[star addClip];
}
I know it is possible to do it with 2 subviews and animating their frame with animation, just wondering if there's a way to do it directly with Bezierpaths...
thanks

IB_DESIGNABLE tintColor issues

I'm getting into IB_DESIGNABLE, and I've stumbled over an issue.
When I set the tintColor of my custom view with IB, it is rendered in the right way in the IB.
But when I run it on the device it is displayed with default tintColor.
#pragma mark - UIView
- (void)drawRect:(CGRect)rect {
[self drawCircleRadius:MIN(rect.size.width / 2, rect.size.height / 2) - self.lineWidth / 2.f
rect:rect
startAngle:self.startAngleRadians
endAngle:self.endAngleRadians
lineWidth:self.lineWidth];
}
#pragma mark - private methods
- (void)drawCircleRadius:(CGFloat)radius
rect:(CGRect)rect
startAngle:(CGFloat)startAngle
endAngle:(CGFloat)endAngel
lineWidth:(CGFloat)lineWidth {
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[self.tintColor setStroke];
[bezierPath addArcWithCenter:CGPointMake(rect.size.width / 2, rect.size.height / 2)
radius:radius
startAngle:startAngle
endAngle:endAngel
clockwise:YES];
bezierPath.lineWidth = lineWidth;
[bezierPath stroke];
}
What the difference? Why is it displayed with the default tint color in the device, and correctly displayed in IB?
UPDATE:
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface PKCircleView : UIView
#property (nonatomic, assign) IBInspectable CGFloat startAngleRadians;
#property (nonatomic, assign) IBInspectable CGFloat endAngleRadians;
#property (nonatomic, assign) IBInspectable CGFloat lineWidth;
#end
The issue was on that line
self.tintColor = [UIColor defaultDwonloadButtonBlueColor];
:
static PKCircleView *CommonInit(PKCircleView *self) {
if (self != nil) {
self.backgroundColor = [UIColor clearColor];
self.startAngleRadians = M_PI * 1.5;
self.endAngleRadians = self.startAngleRadians + (M_PI * 2);
self.lineWidth = 1.f;
self.tintColor = [UIColor defaultDwonloadButtonBlueColor];
}
return self;
}
#implementation PKCircleView
#pragma mark - initialization
- (id)initWithCoder:(NSCoder *)decoder {
return CommonInit([super initWithCoder:decoder]);
}
- (instancetype)initWithFrame:(CGRect)frame {
return CommonInit([super initWithFrame:frame]);
}
it seems like setTintColor from IB is called before init... methods.

How can I Achieve Semi Circle progress bar?

Can Anyone help me? Give me some ideas to achieve this:)
make an UIView class declare in .h file
CGFloat startAngle;
CGFloat endAngle;
#property(assign) int percent;
Replace initWithFrame and drawRect method in .m class
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor whiteColor];
// Determine our start and stop angles for the arc (in radians)
startAngle = M_PI * 1;
endAngle = startAngle + (M_PI * 2); }
return self;
}
- (void)drawRect:(CGRect)rect
{
NSString* textContent = [NSString stringWithFormat:#"%d", percent];
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
// Create our arc, with the correct angles
[bezierPath addArcWithCenter:CGPointMake(rect.size.width / 2, rect.size.height / 2)
radius:130
startAngle:startAngle
endAngle:(endAngle - startAngle) * (percent / 100.0) + startAngle
clockwise:YES];
// Set the display for the path, and stroke it
bezierPath.lineWidth = 20;
[[UIColor redColor] setStroke];
[bezierPath stroke];
// Text Drawing
CGRect textRect = CGRectMake((rect.size.width / 2.0) - 71/2.0, (rect.size.height / 2.0) - 45/2.0, 71, 45);
[[UIColor blackColor] setFill];
[textContent drawInRect: textRect withFont: [UIFont fontWithName: #"Helvetica-Bold" size: 42.5] lineBreakMode: NSLineBreakByWordWrapping alignment: NSTextAlignmentCenter];
}
In your Controller .h import CornerRadious and declare
NSTimer *m_timer;
CornerRadious *cr;
In .m class in ViewDidLoadMethod
cr = [[CornerRadious alloc] initWithFrame:self.view.bounds];
cr.percent = 50;
[self.view addSubview:cr];
aslo make add in these method
- (void)viewDidAppear:(BOOL)animated
{
// Kick off a timer to count it down
m_timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(decrementSpin) userInfo:nil repeats:YES];
}
- (void)decrementSpin
{
// If we can decrement our percentage, do so, and redraw the view
if (cr.percent > 0) {
cr.percent = cr.percent - 1;
[cr setNeedsDisplay];
}
else {
[m_timer invalidate];
m_timer = nil;
}
}
Hope it work

Resources