From 76d14d653579e4ce9dd2b3026a8ef4e4ee190229 Mon Sep 17 00:00:00 2001 From: Jovanni Lo Date: Tue, 29 Sep 2026 04:17:23 +0800 Subject: [PATCH] fix: frame Apple static base map correctly with edge insets MKMapView fits the visible rect into its layout-margin viewport, so the inset warmup map rendered zoomed out and off-center from the overlay projection. Pass the viewport slice of mapRect instead. Example: fittedCamera takes edge insets and derives the Apple zoom from the full view; detail card follows the safe-area bottom inset. --- example/bare/src/screens/DetailScreen.tsx | 5 ++- example/expo/app/detail.tsx | 5 ++- .../shared/src/screens/MarkerDetailScreen.tsx | 35 ++++++++++++------- .../shared/src/screens/StaticMapsScreen.tsx | 21 +++++++---- ios/core/AppleStaticMapProvider.mm | 14 +++++++- .../references/advanced-patterns.md | 14 ++++---- 6 files changed, 64 insertions(+), 30 deletions(-) diff --git a/example/bare/src/screens/DetailScreen.tsx b/example/bare/src/screens/DetailScreen.tsx index 87bf71c..c8a7e29 100644 --- a/example/bare/src/screens/DetailScreen.tsx +++ b/example/bare/src/screens/DetailScreen.tsx @@ -1,4 +1,5 @@ import type { NativeStackScreenProps } from '@react-navigation/native-stack'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { MarkerDetailScreen } from '@lugg/shared-example'; import type { RootStackParamList } from '../App'; @@ -6,5 +7,7 @@ import type { RootStackParamList } from '../App'; type Props = NativeStackScreenProps; export function DetailScreen({ route }: Props) { - return ; + const { bottom } = useSafeAreaInsets(); + + return ; } diff --git a/example/expo/app/detail.tsx b/example/expo/app/detail.tsx index f4a3835..201acaf 100644 --- a/example/expo/app/detail.tsx +++ b/example/expo/app/detail.tsx @@ -1,7 +1,10 @@ import { useLocalSearchParams } from 'expo-router'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { MarkerDetailScreen } from '@lugg/shared-example'; export default function DetailScreen() { const { name } = useLocalSearchParams<{ name: string }>(); - return ; + const { bottom } = useSafeAreaInsets(); + + return ; } diff --git a/example/shared/src/screens/MarkerDetailScreen.tsx b/example/shared/src/screens/MarkerDetailScreen.tsx index a7840d4..7ec07fa 100644 --- a/example/shared/src/screens/MarkerDetailScreen.tsx +++ b/example/shared/src/screens/MarkerDetailScreen.tsx @@ -16,14 +16,17 @@ import { interface MarkerDetailScreenProps { name: string; + bottomInset?: number; } const CARD_HEIGHT = 200; -const CARD_BOTTOM = sizes.xl * 3; const formatCoordinate = (value: number) => value.toFixed(4); -export const MarkerDetailScreen = ({ name }: MarkerDetailScreenProps) => { +export const MarkerDetailScreen = ({ + name, + bottomInset = 0, +}: MarkerDetailScreenProps) => { const { colors } = useTheme(); const { width, height } = useWindowDimensions(); const apiKey = process.env.GOOGLE_MAPS_API_KEY; @@ -31,6 +34,14 @@ export const MarkerDetailScreen = ({ name }: MarkerDetailScreenProps) => { Platform.OS === 'ios' ? 'apple' : 'google' ); + const cardBottom = bottomInset + sizes.lg; + const edgeInsets = { + top: 0, + left: 0, + right: 0, + bottom: cardBottom + CARD_HEIGHT, + }; + const marker = INITIAL_MARKERS.find((m) => m.name === name); const place = PLACES.find((p) => p.name === name); @@ -42,8 +53,9 @@ export const MarkerDetailScreen = ({ name }: MarkerDetailScreenProps) => { ? fittedCamera( placeCoordinates(place), provider, - { width, height: height - CARD_BOTTOM - CARD_HEIGHT }, - sizes.xl + { width, height }, + sizes.xl, + edgeInsets ) : { coordinate, zoom: 15 }; @@ -58,12 +70,7 @@ export const MarkerDetailScreen = ({ name }: MarkerDetailScreenProps) => { staticKey={name} initialCoordinate={camera.coordinate} initialZoom={camera.zoom} - edgeInsets={{ - top: 0, - left: 0, - right: 0, - bottom: CARD_BOTTOM + CARD_HEIGHT, - }} + edgeInsets={edgeInsets} > {place ? ( <> @@ -76,7 +83,10 @@ export const MarkerDetailScreen = ({ name }: MarkerDetailScreenProps) => {