105 lines
3.7 KiB
Objective-C
105 lines
3.7 KiB
Objective-C
#import "MGLUserLocationHeadingBeamLayer.h"
|
|
|
|
#import "MGLFaux3DUserLocationAnnotationView.h"
|
|
#import "MGLGeometry.h"
|
|
|
|
@implementation MGLUserLocationHeadingBeamLayer
|
|
{
|
|
CAShapeLayer *_maskLayer;
|
|
}
|
|
|
|
- (instancetype)initWithUserLocationAnnotationView:(MGLUserLocationAnnotationView *)userLocationView
|
|
{
|
|
CGFloat size = MGLUserLocationAnnotationHaloSize;
|
|
|
|
self = [super init];
|
|
self.bounds = CGRectMake(0, 0, size, size);
|
|
self.position = CGPointMake(CGRectGetMidX(userLocationView.bounds), CGRectGetMidY(userLocationView.bounds));
|
|
self.contents = (__bridge id)[self gradientImageWithTintColor:userLocationView.tintColor.CGColor];
|
|
self.contentsGravity = kCAGravityBottom;
|
|
self.contentsScale = UIScreen.mainScreen.scale;
|
|
self.opacity = 0.4;
|
|
self.shouldRasterize = YES;
|
|
self.rasterizationScale = UIScreen.mainScreen.scale;
|
|
self.drawsAsynchronously = YES;
|
|
|
|
_maskLayer = [CAShapeLayer layer];
|
|
_maskLayer.frame = self.bounds;
|
|
_maskLayer.path = [self clippingMaskForAccuracy:0];
|
|
self.mask = _maskLayer;
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)updateHeadingAccuracy:(CLLocationDirection)accuracy
|
|
{
|
|
// recalculate the clipping mask based on updated accuracy
|
|
_maskLayer.path = [self clippingMaskForAccuracy:accuracy];
|
|
}
|
|
|
|
- (void)updateTintColor:(CGColorRef)color
|
|
{
|
|
// redraw the raw tinted gradient
|
|
self.contents = (__bridge id)[self gradientImageWithTintColor:color];
|
|
}
|
|
|
|
- (CGImageRef)gradientImageWithTintColor:(CGColorRef)tintColor
|
|
{
|
|
UIImage *image;
|
|
|
|
CGFloat haloRadius = MGLUserLocationAnnotationHaloSize / 2.0;
|
|
|
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(MGLUserLocationAnnotationHaloSize, haloRadius), NO, 0);
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
|
|
|
// gradient from the tint color to no-alpha tint color
|
|
CGFloat gradientLocations[] = {0.0, 1.0};
|
|
CGGradientRef gradient = CGGradientCreateWithColors(
|
|
colorSpace,
|
|
(__bridge CFArrayRef)@[(__bridge id)tintColor,
|
|
(id)CFBridgingRelease(CGColorCreateCopyWithAlpha(tintColor, 0))],
|
|
gradientLocations);
|
|
|
|
// draw the gradient from the center point to the edge (full halo radius)
|
|
CGPoint centerPoint = CGPointMake(haloRadius, haloRadius);
|
|
CGContextDrawRadialGradient(context, gradient,
|
|
centerPoint, 0.0,
|
|
centerPoint, haloRadius,
|
|
kNilOptions);
|
|
|
|
image = UIGraphicsGetImageFromCurrentImageContext();
|
|
UIGraphicsEndImageContext();
|
|
|
|
CGGradientRelease(gradient);
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
return image.CGImage;
|
|
}
|
|
|
|
- (CGPathRef)clippingMaskForAccuracy:(CGFloat)accuracy
|
|
{
|
|
// size the mask using accuracy, but keep within a good display range
|
|
CGFloat clippingDegrees = 90 - accuracy;
|
|
clippingDegrees = fmin(clippingDegrees, 70); // most accurate
|
|
clippingDegrees = fmax(clippingDegrees, 10); // least accurate
|
|
|
|
CGRect ovalRect = CGRectMake(0, 0, MGLUserLocationAnnotationHaloSize, MGLUserLocationAnnotationHaloSize);
|
|
UIBezierPath *ovalPath = UIBezierPath.bezierPath;
|
|
|
|
// clip the oval to ± incoming accuracy degrees (converted to radians), from the top
|
|
[ovalPath addArcWithCenter:CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect))
|
|
radius:CGRectGetWidth(ovalRect) / 2.0
|
|
startAngle:MGLRadiansFromDegrees(-180 + clippingDegrees)
|
|
endAngle:MGLRadiansFromDegrees(-clippingDegrees)
|
|
clockwise:YES];
|
|
|
|
[ovalPath addLineToPoint:CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect))];
|
|
[ovalPath closePath];
|
|
|
|
return ovalPath.CGPath;
|
|
}
|
|
|
|
@end
|