-
Notifications
You must be signed in to change notification settings - Fork 118
Fix CarPlay map button icons on iOS 26+ #5705
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 6 commits
9980acf
c4316e4
b9e9694
64a3f2f
6f13229
f6a836a
72eacee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -88,6 +88,7 @@ @implementation OACarPlayDashboardInterfaceController | |
| int _calculationProgress; | ||
|
|
||
| CPMapButton *_3DModeMapButton; | ||
| BOOL _lastIs3DMode; | ||
| BOOL _wasIn3DBeforePreview; | ||
|
|
||
| OAAutoObserverProxy *_locationUpdateObserver; | ||
|
|
@@ -155,7 +156,9 @@ - (void) enterBrowsingState | |
| _mapTemplate.trailingNavigationBarButtons = @[panningButton]; | ||
| _mapTemplate.leadingNavigationBarButtons = @[[self createBarButton:EOACarPlayButtonTypeSettings], [self createBarButton:EOACarPlayButtonTypeDirections]]; | ||
|
|
||
| _3DModeMapButton = [self createMapButton:EOACarPlayButtonType3D]; | ||
| BOOL is3DMode = [OAMapViewTrackingUtilities.instance is3DMode]; | ||
| _lastIs3DMode = is3DMode; | ||
| _3DModeMapButton = [self createMapButton:EOACarPlayButtonType3D is3DMode:is3DMode]; | ||
| _mapTemplate.mapButtons = @[_3DModeMapButton, [self createMapButton:EOACarPlayButtonTypeCenterMap], [self createMapButton:EOACarPlayButtonTypeZoomIn], [self createMapButton:EOACarPlayButtonTypeZoomOut]]; | ||
| [self onMap3dModeUpdated]; | ||
| } | ||
|
|
@@ -351,6 +354,7 @@ - (void) enterRoutePreviewMode:(BOOL)shouldCheckConnectedMainScene | |
| destinationSubtitle = nil; | ||
| } | ||
|
|
||
| #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 260400 | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Xcode 26.2 Fix |
||
| if (@available(iOS 26.4, *)) { | ||
| CPLocationCoordinate3D startPoint = { | ||
| .latitude = startCoord.latitude, | ||
|
|
@@ -385,7 +389,10 @@ - (void) enterRoutePreviewMode:(BOOL)shouldCheckConnectedMainScene | |
| _currentTrip = [[CPTrip alloc] initWithOriginWaypoint:origin | ||
| destinationWaypoint:destination | ||
| routeChoices:@[routeChoice]]; | ||
| } else { | ||
| } | ||
| else | ||
| #endif | ||
| { | ||
|
|
||
| NSMutableDictionary<NSString *, NSString *> *addressDict = [NSMutableDictionary dictionary]; | ||
|
|
||
|
|
@@ -469,27 +476,102 @@ - (void)openNavigation | |
| [directionsGrid present]; | ||
| } | ||
|
|
||
| - (void)updateMapButton:(CPMapButton *)mapButton | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we move this into a common image helper and pass whether the image should use template rendering? This would avoid having separate [UIImage templateImageNamed:] and [UIImage imageNamed:] branches here. |
||
| forType:(EOACarPlayButtonType)type | ||
| is3DMode:(BOOL)is3DMode | ||
| { | ||
| UIImage *image = nil; | ||
| if (@available(iOS 26.0, *)) | ||
| { | ||
| switch (type) | ||
| { | ||
| case EOACarPlayButtonTypeZoomIn: | ||
| image = [UIImage templateImageNamed:ACImageNameIcCustomMapZoomIn]; | ||
| break; | ||
| case EOACarPlayButtonTypeZoomOut: | ||
| image = [UIImage templateImageNamed:ACImageNameIcCustomMapZoomOut]; | ||
| break; | ||
| case EOACarPlayButtonTypeCenterMap: | ||
| image = [UIImage templateImageNamed:ACImageNameIcCustomMapLocationPosition]; | ||
| break; | ||
| case EOACarPlayButtonType3D: | ||
| image = [UIImage templateImageNamed:is3DMode ? ACImageNameIcCustom2D : ACImageNameIcCustom3D]; | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| } | ||
| else | ||
| { | ||
| switch (type) | ||
| { | ||
| case EOACarPlayButtonTypeZoomIn: | ||
| image = [UIImage imageNamed:ACImageNameBtnMapZoomIn]; | ||
| break; | ||
| case EOACarPlayButtonTypeZoomOut: | ||
| image = [UIImage imageNamed:ACImageNameBtnMapZoomOut]; | ||
| break; | ||
| case EOACarPlayButtonTypeCenterMap: | ||
| image = [UIImage imageNamed:ACImageNameBtnMapCurrentLocation]; | ||
| break; | ||
| case EOACarPlayButtonType3D: | ||
| image = [UIImage imageNamed:is3DMode ? ACImageNameBtnMap2DMode : ACImageNameBtnMap3DMode]; | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| } | ||
| mapButton.image = image; | ||
| } | ||
|
|
||
| - (void)replace3DModeMapButton:(CPMapButton *)mapButton | ||
| { | ||
| NSUInteger buttonIndex = [_mapTemplate.mapButtons indexOfObjectIdenticalTo:_3DModeMapButton]; | ||
| if (buttonIndex == NSNotFound) | ||
| return; | ||
|
|
||
| NSMutableArray<CPMapButton *> *mapButtons = _mapTemplate.mapButtons.mutableCopy; | ||
| mapButtons[buttonIndex] = mapButton; | ||
| _3DModeMapButton = mapButton; | ||
| _mapTemplate.mapButtons = mapButtons; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. copy
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to recreate CPMapButton on iOS 26 when switching between 2D and 3D? If this is required because CarPlay caches the automatically generated focused image, could we document that workaround here? Otherwise, updating the existing button’s image would avoid replacing the entire mapButtons array |
||
| } | ||
|
|
||
| - (void)onMap3dModeUpdated | ||
| { | ||
| if (_3DModeMapButton) | ||
| { | ||
| dispatch_async(dispatch_get_main_queue(), ^{ | ||
| OAMapViewTrackingUtilities *mapViewTrackingUtilities = [OAMapViewTrackingUtilities instance]; | ||
| BOOL is3DMode = [mapViewTrackingUtilities is3DMode]; | ||
| Map3DModeVisibility map3DMode = [[[OAMapButtonsHelper sharedInstance] getMap3DButtonState] getVisibility]; | ||
| BOOL hideButton = map3DMode == Map3DModeVisibilityHidden | ||
| || (map3DMode == Map3DModeVisibilityVisibleIn3DMode && ![mapViewTrackingUtilities is3DMode]); | ||
| _3DModeMapButton.hidden = hideButton ? YES : NO; | ||
| if ([mapViewTrackingUtilities is3DMode]) | ||
| || (map3DMode == Map3DModeVisibilityVisibleIn3DMode && !is3DMode); | ||
| BOOL shouldRecreateButton = NO; | ||
| if (@available(iOS 26.0, *)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The button creation logic is also unclear |
||
| { | ||
| BOOL isButtonPresented = [_mapTemplate.mapButtons indexOfObjectIdenticalTo:_3DModeMapButton] != NSNotFound; | ||
| shouldRecreateButton = _lastIs3DMode != is3DMode && isButtonPresented; | ||
| } | ||
| _lastIs3DMode = is3DMode; | ||
|
|
||
| CPMapButton *mapButton = shouldRecreateButton | ||
| ? [self createMapButton:EOACarPlayButtonType3D is3DMode:is3DMode] | ||
| : _3DModeMapButton; | ||
| if (!shouldRecreateButton) | ||
| [self updateMapButton:mapButton forType:EOACarPlayButtonType3D is3DMode:is3DMode]; | ||
| mapButton.hidden = hideButton; | ||
| if (is3DMode) | ||
| { | ||
| _3DModeMapButton.image = [UIImage imageNamed:@"btn_map_2d_mode"]; | ||
| _3DModeMapButton.accessibilityLabel = OALocalizedString(@"map_3d_mode_action"); | ||
| mapButton.accessibilityLabel = OALocalizedString(@"map_3d_mode_action"); | ||
| } | ||
| else | ||
| { | ||
| _3DModeMapButton.image = [UIImage imageNamed:@"btn_map_3d_mode"]; | ||
| _3DModeMapButton.accessibilityLabel = OALocalizedString(@"map_2d_mode_action"); | ||
| mapButton.accessibilityLabel = OALocalizedString(@"map_2d_mode_action"); | ||
| } | ||
| _3DModeMapButton.accessibilityValue = [Map3DModeVisibilityWrapper getTitleForType:map3DMode]; | ||
| mapButton.accessibilityValue = [Map3DModeVisibilityWrapper getTitleForType:map3DMode]; | ||
|
|
||
| if (shouldRecreateButton) | ||
| [self replace3DModeMapButton:mapButton]; | ||
| }); | ||
| } | ||
| } | ||
|
|
@@ -607,7 +689,12 @@ - (void)openFindParkingWithCompletion:(dispatch_block_t)completion | |
| completion(); | ||
| } | ||
|
|
||
| - (CPMapButton *) createMapButton:(EOACarPlayButtonType)type | ||
| - (CPMapButton *)createMapButton:(EOACarPlayButtonType)type | ||
| { | ||
| return [self createMapButton:type is3DMode:[OAMapViewTrackingUtilities.instance is3DMode]]; | ||
| } | ||
|
|
||
| - (CPMapButton *)createMapButton:(EOACarPlayButtonType)type is3DMode:(BOOL)is3DMode | ||
| { | ||
| CPMapButton *mapButton = [[CPMapButton alloc] initWithHandler:^(CPMapButton * _Nonnull mapButton) { | ||
| switch (type) { | ||
|
|
@@ -636,14 +723,7 @@ - (CPMapButton *) createMapButton:(EOACarPlayButtonType)type | |
| } | ||
| }]; | ||
|
|
||
| if (type == EOACarPlayButtonTypeZoomIn) | ||
| mapButton.image = [UIImage imageNamed:@"btn_map_zoom_in"]; | ||
| else if (type == EOACarPlayButtonTypeZoomOut) | ||
| mapButton.image = [UIImage imageNamed:@"btn_map_zoom_out"]; | ||
| else if (type == EOACarPlayButtonTypeCenterMap) | ||
| mapButton.image = [UIImage imageNamed:@"btn_map_current_location"]; | ||
| else if (type == EOACarPlayButtonType3D) | ||
| mapButton.image = [UIImage imageNamed:[OAMapViewTrackingUtilities.instance is3DMode] ? @"btn_map_2d_mode" : @"btn_map_3d_mode"]; | ||
| [self updateMapButton:mapButton forType:type is3DMode:is3DMode]; | ||
|
|
||
| return mapButton; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to store is3DMode in _lastIs3DMode here? How will it behave if the 2D/3D mode is changed from the phone while CarPlay is connected?