Files
MapLibreParticleLab/platform/ios/MGLUserLocationAnnotationView.m
Minh Nguyễn 2c436444d7 Made user dot accessible
Added an accessibility label to the user dot.
2015-04-24 13:06:54 -07:00

201 lines
8.5 KiB
Objective-C

#import "MGLUserLocationAnnotationView.h"
#import "MGLUserLocation.h"
#import "MGLUserLocation_Private.h"
#import "MGLAnnotation.h"
#import "MGLMapView.h"
const CGFloat MGLTrackingDotRingWidth = 24.0;
@interface MGLUserLocationAnnotationView ()
@property (nonatomic, readwrite) CALayer *haloLayer;
@end
#pragma mark -
@implementation MGLUserLocationAnnotationView
{
CALayer *_accuracyRingLayer;
CALayer *_dotBorderLayer;
CALayer *_dotLayer;
}
- (instancetype)initInMapView:(MGLMapView *)mapView
{
if (self = [super init])
{
self.annotation = [[MGLUserLocation alloc] init];
self.annotation.mapView = mapView;
_mapView = mapView;
[self setupLayers];
self.accessibilityLabel = @"User location";
}
return self;
}
- (void)setTintColor:(UIColor *)tintColor
{
UIImage *trackingDotHaloImage = [self trackingDotHaloImage];
_haloLayer.bounds = CGRectMake(0, 0, trackingDotHaloImage.size.width, trackingDotHaloImage.size.height);
_haloLayer.contents = (__bridge id)[trackingDotHaloImage CGImage];
UIImage *dotImage = [self dotImage];
_dotLayer.bounds = CGRectMake(0, 0, dotImage.size.width, dotImage.size.height);
_dotLayer.contents = (__bridge id)[dotImage CGImage];
}
- (void)setupLayers
{
if (CLLocationCoordinate2DIsValid(self.annotation.coordinate))
{
if ( ! _accuracyRingLayer && self.annotation.location.horizontalAccuracy)
{
UIImage *accuracyRingImage = [self accuracyRingImage];
_accuracyRingLayer = [CALayer layer];
_haloLayer.bounds = CGRectMake(0, 0, accuracyRingImage.size.width, accuracyRingImage.size.height);
_haloLayer.contents = (__bridge id)[accuracyRingImage CGImage];
_haloLayer.position = CGPointMake(super.layer.bounds.size.width / 2.0, super.layer.bounds.size.height / 2.0);
[self.layer addSublayer:_accuracyRingLayer];
}
if ( ! _haloLayer)
{
UIImage *haloImage = [self trackingDotHaloImage];
_haloLayer = [CALayer layer];
_haloLayer.bounds = CGRectMake(0, 0, haloImage.size.width, haloImage.size.height);
_haloLayer.contents = (__bridge id)[haloImage CGImage];
_haloLayer.position = CGPointMake(super.layer.bounds.size.width / 2.0, super.layer.bounds.size.height / 2.0);
[CATransaction begin];
[CATransaction setAnimationDuration:3.5];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
// scale out radially
//
CABasicAnimation *boundsAnimation = [CABasicAnimation animationWithKeyPath:@"transform"];
boundsAnimation.repeatCount = MAXFLOAT;
boundsAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.1, 0.1, 1.0)];
boundsAnimation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(2.0, 2.0, 1.0)];
boundsAnimation.removedOnCompletion = NO;
[_haloLayer addAnimation:boundsAnimation forKey:@"animateScale"];
// go transparent as scaled out
//
CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
opacityAnimation.repeatCount = MAXFLOAT;
opacityAnimation.fromValue = [NSNumber numberWithFloat:1.0];
opacityAnimation.toValue = [NSNumber numberWithFloat:-1.0];
opacityAnimation.removedOnCompletion = NO;
[_haloLayer addAnimation:opacityAnimation forKey:@"animateOpacity"];
[CATransaction commit];
[self.layer addSublayer:_haloLayer];
}
// white dot background with shadow
//
if ( ! _dotBorderLayer)
{
CGRect rect = CGRectMake(0, 0, MGLTrackingDotRingWidth * 1.5, MGLTrackingDotRingWidth * 1.5);
UIGraphicsBeginImageContextWithOptions(rect.size, NO, [[UIScreen mainScreen] scale]);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetShadow(context, CGSizeMake(0, 0), MGLTrackingDotRingWidth / 4.0);
CGContextSetFillColorWithColor(context, [[UIColor whiteColor] CGColor]);
CGContextFillEllipseInRect(context, CGRectMake((rect.size.width - MGLTrackingDotRingWidth) / 2.0, (rect.size.height - MGLTrackingDotRingWidth) / 2.0, MGLTrackingDotRingWidth, MGLTrackingDotRingWidth));
UIImage *whiteBackground = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
_dotBorderLayer = [CALayer layer];
_dotBorderLayer.bounds = CGRectMake(0, 0, whiteBackground.size.width, whiteBackground.size.height);
_dotBorderLayer.contents = (__bridge id)[whiteBackground CGImage];
_dotBorderLayer.position = CGPointMake(super.layer.bounds.size.width / 2.0, super.layer.bounds.size.height / 2.0);
[self.layer addSublayer:_dotBorderLayer];
}
// pulsing, tinted dot sublayer
//
if ( ! _dotLayer)
{
UIImage *dotImage = [self dotImage];
_dotLayer = [CALayer layer];
_dotLayer.bounds = CGRectMake(0, 0, dotImage.size.width, dotImage.size.height);
_dotLayer.contents = (__bridge id)[dotImage CGImage];
_dotLayer.position = CGPointMake(super.layer.bounds.size.width / 2.0, super.layer.bounds.size.height / 2.0);
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"];
animation.repeatCount = MAXFLOAT;
animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.0, 1.0, 1.0)];
animation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.8, 0.8, 1.0)];
animation.removedOnCompletion = NO;
animation.autoreverses = YES;
animation.duration = 1.5;
animation.beginTime = CACurrentMediaTime() + 1.0;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[_dotLayer addAnimation:animation forKey:@"animateTransform"];
[self.layer addSublayer:_dotLayer];
}
}
}
- (UIImage *)accuracyRingImage
{
CGFloat latRadians = self.annotation.coordinate.latitude * M_PI / 180.0f;
CGFloat pixelRadius = self.annotation.location.horizontalAccuracy / cos(latRadians) / [self.mapView metersPerPixelAtLatitude:self.annotation.coordinate.latitude];
UIGraphicsBeginImageContextWithOptions(CGSizeMake(pixelRadius * 2, pixelRadius * 2), NO, [[UIScreen mainScreen] scale]);
CGContextSetStrokeColorWithColor(UIGraphicsGetCurrentContext(), [[UIColor colorWithRed:0.378 green:0.552 blue:0.827 alpha:0.7] CGColor]);
CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), [[UIColor colorWithRed:0.378 green:0.552 blue:0.827 alpha:0.15] CGColor]);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 2.0);
CGContextStrokeEllipseInRect(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, pixelRadius * 2, pixelRadius * 2));
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return finalImage;
}
- (UIImage *)trackingDotHaloImage
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 100), NO, [[UIScreen mainScreen] scale]);
CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), [[_mapView.tintColor colorWithAlphaComponent:0.75] CGColor]);
CGContextFillEllipseInRect(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, 100, 100));
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return finalImage;
}
- (UIImage *)dotImage
{
CGFloat tintedWidth = MGLTrackingDotRingWidth * 0.7;
CGRect rect = CGRectMake(0, 0, tintedWidth, tintedWidth);
UIGraphicsBeginImageContextWithOptions(rect.size, NO, [[UIScreen mainScreen] scale]);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [_mapView.tintColor CGColor]);
CGContextFillEllipseInRect(context, CGRectMake((rect.size.width - tintedWidth) / 2.0, (rect.size.height - tintedWidth) / 2.0, tintedWidth, tintedWidth));
UIImage *tintedForeground = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return tintedForeground;
}
@end