summaryrefslogtreecommitdiff
path: root/platform/ios/src/MGLCompassButton.mm
blob: 94a6820a744e89e45b09791bc1e5a60d9b2038f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#import "MGLCompassButton_Private.h"
#import "MGLCompassDirectionFormatter.h"

#import <Mapbox/MGLGeometry.h>

#import "MGLMapView_Private.h"
#import "UIImage+MGLAdditions.h"
#import "NSBundle+MGLAdditions.h"

#include <mbgl/math/wrap.hpp>

@interface MGLCompassButton ()

@property (nonatomic, weak) MGLMapView *mapView;
@property (nonatomic) MGLCompassDirectionFormatter *accessibilityCompassFormatter;

@end

@implementation MGLCompassButton

+ (instancetype)compassButtonWithMapView:(MGLMapView *)mapView {
    return [[MGLCompassButton alloc] initWithMapView:mapView];
}

- (instancetype)initWithMapView:(MGLMapView *)mapView {
    if (self = [super init]) {
        self.mapView = mapView;
        [self commonInit];
    }
    return self;
}

- (void)commonInit {
    self.image = self.compassImage;

    self.compassVisibility = MGLOrnamentVisibilityAdaptive;

    self.alpha = 0;
    self.userInteractionEnabled = YES;
    self.translatesAutoresizingMaskIntoConstraints = NO;

    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
    [self addGestureRecognizer:tapGesture];

    self.accessibilityTraits = UIAccessibilityTraitButton;
    self.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"COMPASS_A11Y_LABEL", nil, nil, @"Compass", @"Accessibility label");
    self.accessibilityHint = NSLocalizedStringWithDefaultValue(@"COMPASS_A11Y_HINT", nil, nil, @"Rotates the map to face due north", @"Accessibility hint");

    self.accessibilityCompassFormatter = [[MGLCompassDirectionFormatter alloc] init];
    self.accessibilityCompassFormatter.unitStyle = NSFormattingUnitStyleLong;

    [self sizeToFit];
}

- (void)setCompassVisibility:(MGLOrnamentVisibility)compassVisibility {
    if (_compassVisibility == compassVisibility) { return; }
    _compassVisibility = compassVisibility;

    [self updateCompassAnimated:NO];
}

- (UIImage *)compassImage {
    UIImage *scaleImage = [UIImage mgl_resourceImageNamed:@"Compass"];
    UIGraphicsBeginImageContextWithOptions(scaleImage.size, NO, UIScreen.mainScreen.scale);
    [scaleImage drawInRect:{CGPointZero, scaleImage.size}];

    NSAttributedString *north = [[NSAttributedString alloc] initWithString:NSLocalizedStringWithDefaultValue(@"COMPASS_NORTH", nil, nil, @"N", @"Compass abbreviation for north") attributes:@{
        NSFontAttributeName: [UIFont systemFontOfSize:11 weight:UIFontWeightUltraLight],
        NSForegroundColorAttributeName: [UIColor whiteColor],
    }];
    CGRect stringRect = CGRectMake((scaleImage.size.width - north.size.width) / 2,
                                   scaleImage.size.height * 0.435,
                                   north.size.width, north.size.height);
    [north drawInRect:stringRect];

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

- (void)handleTapGesture:(__unused UITapGestureRecognizer *)sender {
    [self.mapView resetNorth];
}

- (void)updateCompass {
    [self updateCompassAnimated:YES];
}

- (void)updateCompassAnimated:(BOOL)animated {
    CLLocationDirection direction = self.mapView.direction;
    CLLocationDirection plateDirection = mbgl::util::wrap(-direction, 0., 360.);
    self.transform = CGAffineTransformMakeRotation(MGLRadiansFromDegrees(plateDirection));

    self.isAccessibilityElement = direction > 0;
    self.accessibilityValue = [self.accessibilityCompassFormatter stringFromDirection:direction];

    switch (self.compassVisibility) {
        case MGLOrnamentVisibilityAdaptive:
            if (direction > 0 && self.alpha < 1) {
                [self showCompass:animated];
            } else if (direction == 0 && self.alpha > 0) {
                [self hideCompass:animated];
            }
            break;
        case MGLOrnamentVisibilityVisible:
            [self showCompass:animated];
            break;
        case MGLOrnamentVisibilityHidden:
            [self hideCompass:animated];
            break;
    }
}

- (void)showCompass:(BOOL)animated {
    animated ? [self animateToAlpha:1] : [self setAlpha:1];
}

- (void)hideCompass:(BOOL)animated {
    animated ? [self animateToAlpha:0] : [self setAlpha:0];
}

- (void)animateToAlpha:(CGFloat)alpha {
    [UIView animateWithDuration:MGLAnimationDuration delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
         self.alpha = alpha;
     } completion:nil];
}

@end