diff --git a/android/src/main/java/com/reactnativenavigation/options/BackButton.java b/android/src/main/java/com/reactnativenavigation/options/BackButton.java index f766ea78549..8be3bcbf489 100644 --- a/android/src/main/java/com/reactnativenavigation/options/BackButton.java +++ b/android/src/main/java/com/reactnativenavigation/options/BackButton.java @@ -27,6 +27,7 @@ public static BackButton parse(Context context, JSONObject json) { result.disableIconTint = BoolParser.parse(json, "disableIconTint"); result.color = ThemeColour.parse(context, json.optJSONObject( "color")); result.disabledColor = ThemeColour.parse(context, json.optJSONObject( "disabledColor")); + result.iconBackground = IconBackgroundOptions.parse(context, json.optJSONObject("iconBackground")); result.testId = TextParser.parse(json, "testID"); result.popStackOnPress = BoolParser.parse(json, "popStackOnPress"); @@ -54,6 +55,7 @@ public void mergeWith(BackButton other) { if (other.disabledColor.hasValue()) disabledColor = other.disabledColor; if (other.disableIconTint.hasValue()) disableIconTint = other.disableIconTint; if (other.enabled.hasValue()) enabled = other.enabled; + if (other.iconBackground.hasValue()) iconBackground = other.iconBackground; if (other.testId.hasValue()) testId = other.testId; if (other.popStackOnPress.hasValue()) popStackOnPress = other.popStackOnPress; } @@ -67,6 +69,7 @@ void mergeWithDefault(final BackButton defaultOptions) { if (!disabledColor.hasValue()) disabledColor = defaultOptions.disabledColor; if (!disableIconTint.hasValue()) disableIconTint = defaultOptions.disableIconTint; if (!enabled.hasValue()) enabled = defaultOptions.enabled; + if (!iconBackground.hasValue()) iconBackground = defaultOptions.iconBackground; if (!testId.hasValue()) testId = defaultOptions.testId; if (!popStackOnPress.hasValue()) popStackOnPress = defaultOptions.popStackOnPress; } diff --git a/android/src/main/java/com/reactnativenavigation/viewcontrollers/stack/topbar/button/ButtonPresenter.kt b/android/src/main/java/com/reactnativenavigation/viewcontrollers/stack/topbar/button/ButtonPresenter.kt index dbe077468fd..ff16ed3d10b 100644 --- a/android/src/main/java/com/reactnativenavigation/viewcontrollers/stack/topbar/button/ButtonPresenter.kt +++ b/android/src/main/java/com/reactnativenavigation/viewcontrollers/stack/topbar/button/ButtonPresenter.kt @@ -163,9 +163,10 @@ open class ButtonPresenter(private val context: Context, private val button: But fun applyNavigationIcon(toolbar: Toolbar, onPress: (ButtonOptions) -> Unit) { iconResolver.resolve(button) { icon: Drawable -> setIconColor(icon) + val iconWithBackground = applyIconBackgroundDrawable(icon) toolbar.setNavigationOnClickListener { onPress(button) } toolbar.navigationIcon = null - toolbar.navigationIcon = icon + toolbar.navigationIcon = iconWithBackground setLeftButtonTestId(toolbar) if (button.accessibilityLabel.hasValue()) toolbar.navigationContentDescription = button.accessibilityLabel.get() } diff --git a/ios/RNNBackButtonOptions.h b/ios/RNNBackButtonOptions.h index e9a0324d62c..47a72954596 100644 --- a/ios/RNNBackButtonOptions.h +++ b/ios/RNNBackButtonOptions.h @@ -1,4 +1,5 @@ #import "RNNOptions.h" +#import "RNNIconBackgroundOptions.h" @interface RNNBackButtonOptions : RNNOptions @@ -16,6 +17,7 @@ @property(nonatomic, strong) Text *displayMode; @property(nonatomic, strong) Text *identifier; @property(nonatomic, strong) Bool *popStackOnPress; +@property(nonatomic, strong) RNNIconBackgroundOptions *iconBackground; - (BOOL)hasValue; diff --git a/ios/RNNBackButtonOptions.mm b/ios/RNNBackButtonOptions.mm index 5fd739a66b5..252d119149d 100644 --- a/ios/RNNBackButtonOptions.mm +++ b/ios/RNNBackButtonOptions.mm @@ -19,11 +19,13 @@ - (instancetype)initWithDict:(NSDictionary *)dict { self.enableMenu = [BoolParser parse:dict key:@"enableMenu"]; self.displayMode = [TextParser parse:dict key:@"displayMode"]; self.popStackOnPress = [BoolParser parse:dict key:@"popStackOnPress"]; + self.iconBackground = [[RNNIconBackgroundOptions alloc] initWithDict:dict[@"iconBackground"] enabled:nil]; return self; } - (void)mergeOptions:(RNNBackButtonOptions *)options { + [self.iconBackground mergeOptions:options.iconBackground]; if (options.identifier.hasValue) self.identifier = options.identifier; if (options.icon.hasValue) @@ -57,7 +59,8 @@ - (void)mergeOptions:(RNNBackButtonOptions *)options { - (BOOL)hasValue { return self.icon.hasValue || self.showTitle.hasValue || self.color.hasValue || self.fontFamily.hasValue || self.fontSize.hasValue || self.title.hasValue || - self.enableMenu.hasValue || self.displayMode.hasValue || self.sfSymbol.hasValue; + self.enableMenu.hasValue || self.displayMode.hasValue || self.sfSymbol.hasValue || + self.iconBackground.hasValue; } @end diff --git a/ios/RNNIconDrawer.mm b/ios/RNNIconDrawer.mm index a787d3e1b4a..ccc99582d35 100644 --- a/ios/RNNIconDrawer.mm +++ b/ios/RNNIconDrawer.mm @@ -29,6 +29,10 @@ - (UIImage *)draw:(UIImage *)image UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); + if (color) { + return [newImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; + } + return newImage; } diff --git a/ios/TopBarPresenter.mm b/ios/TopBarPresenter.mm index 1115bddfa07..cc6531e3fa3 100644 --- a/ios/TopBarPresenter.mm +++ b/ios/TopBarPresenter.mm @@ -1,6 +1,7 @@ #import "TopBarPresenter.h" #import "RNNFontAttributesCreator.h" #import "RNNUIBarBackButtonItem.h" +#import "RNNIconDrawer.h" #import "UIColor+RNNUtils.h" #import "UIImage+utils.h" #import "UINavigationController+RNNOptions.h" @@ -179,6 +180,21 @@ - (void)setBackButtonOptions:(RNNBackButtonOptions *)backButtonOptions { : icon; } + // Apply iconBackground if present + if (backButtonOptions.iconBackground.hasValue && icon) { + UIColor *backgroundColor = [backButtonOptions.iconBackground.color withDefault:UIColor.clearColor]; + CGFloat cornerRadius = [backButtonOptions.iconBackground.cornerRadius withDefault:@(0)].floatValue; + CGFloat width = [backButtonOptions.iconBackground.width withDefault:@(icon.size.width)].floatValue; + CGFloat height = [backButtonOptions.iconBackground.height withDefault:@(icon.size.height)].floatValue; + + RNNIconDrawer *iconDrawer = [[RNNIconDrawer alloc] init]; + icon = [iconDrawer draw:icon + imageColor:color + backgroundColor:backgroundColor + size:CGSizeMake(width, height) + cornerRadius:cornerRadius]; + } + [self setBackIndicatorImage:icon withColor:color]; title = title ? title : (previousNavigationItem.title ? previousNavigationItem.title : @""); diff --git a/playground/e2e/BackButton.test.js b/playground/e2e/BackButton.test.js index 6a6ab464d35..4706b498d4b 100644 --- a/playground/e2e/BackButton.test.js +++ b/playground/e2e/BackButton.test.js @@ -2,10 +2,11 @@ import { default as TestIDs, default as testIDs } from '../src/testIDs'; import Android from './AndroidUtils'; import Utils from './Utils'; -const { elementByLabel, elementById } = Utils; +const { elementByLabel, elementById, elementTopBar, expectImagesToBeEqual } = Utils; describe('Back Button', () => { beforeEach(async () => { + // eslint-disable-next-line no-undef await device.launchApp({ newInstance: true }); await elementById(TestIDs.NAVIGATION_TAB).tap(); await elementById(TestIDs.BACK_BUTTON_SCREEN_BTN).tap(); @@ -63,4 +64,12 @@ describe('Back Button', () => { await expect(elementByLabel('Modal')).toBeVisible(); await expect(elementByLabel('navigationButtonPressed | RNN.hardwareBackButton')).toBeVisible(); }); + + it.e2e('should render back button with iconBackground', async () => { + await elementById(TestIDs.PUSH_BACK_BUTTON_ICON_BACKGROUND).tap(); + // eslint-disable-next-line no-undef + const snapshottedImagePath = `./e2e/assets/back_button_icon_background.${device.getPlatform()}.png`; + const actual = await elementTopBar().takeScreenshot('back_button_icon_background'); + expectImagesToBeEqual(actual, snapshottedImagePath); + }); }); diff --git a/playground/e2e/assets/back_button_icon_background.ios.png b/playground/e2e/assets/back_button_icon_background.ios.png new file mode 100644 index 00000000000..a30866ef23e Binary files /dev/null and b/playground/e2e/assets/back_button_icon_background.ios.png differ diff --git a/playground/ios/playground.xcodeproj/project.pbxproj b/playground/ios/playground.xcodeproj/project.pbxproj index 430b05e2bb0..29fcfc6ffae 100644 --- a/playground/ios/playground.xcodeproj/project.pbxproj +++ b/playground/ios/playground.xcodeproj/project.pbxproj @@ -681,11 +681,11 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-playground/Pods-playground-frameworks.sh", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermesvm.framework/hermesvm", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermesvm.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -769,11 +769,11 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-NavigationIOS12Tests/Pods-NavigationIOS12Tests-frameworks.sh", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermesvm.framework/hermesvm", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermesvm.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -813,11 +813,11 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-NavigationTests/Pods-NavigationTests-frameworks.sh", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermesvm.framework/hermesvm", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermesvm.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -879,11 +879,11 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-SnapshotTests/Pods-SnapshotTests-frameworks.sh", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermesvm.framework/hermesvm", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermesvm.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; diff --git a/playground/src/screens/BackButtonScreen.tsx b/playground/src/screens/BackButtonScreen.tsx index eb8630df121..19bb86b1056 100644 --- a/playground/src/screens/BackButtonScreen.tsx +++ b/playground/src/screens/BackButtonScreen.tsx @@ -15,6 +15,7 @@ const { MODAL_DISABLED_BACK_BTN, TOGGLE_BACK_BUTTON_VISIBILITY, BACK_BUTTON, + PUSH_BACK_BUTTON_ICON_BACKGROUND, } = testIDs; export default class BackButtonScreen extends React.Component { @@ -57,6 +58,11 @@ export default class BackButtonScreen extends React.Component { testID={TOGGLE_BACK_BUTTON_VISIBILITY} onPress={this.toggleVisibility} /> +