iOS / Apple Maps: custom Marker is mispositioned when its content changes size (New Architecture)
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 45/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Cần làm rõ
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- ios, objective-c, react-native
- Lĩnh vực
- mobile
Hướng nghiên cứu
Start with ios/AirMaps/AIRMapMarker.m, especially layoutSubviews and reactSetFrame:, then compare the Fabric path in RNMapsMarkerView.mm. Reproduce the shrinking-child case on Apple Maps under Fabric and trace how marker sizing is owned. Done means a changing-size marker remains centered without disappearing or drifting across layout passes.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Summary
On Apple Maps (PROVIDER_DEFAULT) with the New Architecture, a custom <Marker>
whose child changes size ends up mispositioned — it stays placed as though it
still had a previous, larger size. Markers whose child never changes size are
placed correctly, which is why this is easy to miss.
The cause is that AIRMapMarker's bounds are derived from the first subview and
only ever grow:
- (void)layoutSubviews
{
[super layoutSubviews];
CGRect reactFrame = self.frame;
UIView *firstSubView = self.subviews.firstObject;
if (firstSubView && (CGRectGetWidth(firstSubView.frame) > CGRectGetWidth(reactFrame) ||
CGRectGetHeight(firstSubView.frame) > CGRectGetHeight(reactFrame))) {
reactFrame = firstSubView.frame; // <- taken ONLY when larger
}
[self reactSetFrame:reactFrame];
}
reactSetFrame: publishes that size as self.bounds, and MapKit centres an
annotation view on its bounds. So once a marker has been wide, it keeps the wide
bounds, and its visible content is drawn off-centre by half the difference —
permanently, since the size never comes back down.
Environment
| react-native-maps | 1.27.2 |
| react-native | 0.85.3 (New Architecture / Fabric) |
| Expo SDK | 56 |
| Platform | iOS Simulator, PROVIDER_DEFAULT (Apple Maps) |
Google Maps on iOS and Android are not affected — they honour the fractional
anchor prop, which Apple Maps ignores.
Reproduction
Render a <Marker> whose child's width depends on state, and change that state:
<Marker coordinate={coord} tracksViewChanges={tracks}>
<View style={{ backgroundColor: '#000', paddingHorizontal: 10, height: 28 }}>
<Text style={{ color: '#fff' }}>{label}</Text>
</View>
</Marker>
Switch label from a long string to a short one. The marker keeps the wider
bounds and the visible pill sits off to one side of the coordinate. Switching
back to a long label appears to "fix" it, which makes it read like an
intermittent bug.
Measurements
Two labels of the same design, on the same coordinate:
- content ≈148pt wide → coordinate fell ≈66pt from the content's left edge
- content ≈272pt wide → coordinate fell ≈64pt from the content's left edge
A near-constant absolute offset across very different content widths — the
signature of bounds that are inherited rather than measured.
Two fixes that do NOT work (both tested on device)
1. Deleting -layoutSubviews and -setFrame: — i.e. matching what
v2.0.0-beta.15 already does — results in no marker rendering at all on the
New Architecture.
The reason matters for 2.x: RNMapsMarkerView.mm (the Fabric component view) has
no updateLayoutMetrics, no layoutSubviews and no setFrame override, and
never assigns _view.frame; it only mounts children onto the legacy view via
insertReactSubview:. So under Fabric, -layoutSubviews is the only code that
ever sizes the marker. v2.0.0-beta.15 can delete it because its marker is
Paper-only (ios/Maps/ contains RNMMapMarker + manager and no Fabric component
view), where the UIManager calls reactSetFrame: directly.
2. Relaxing the condition to take the subview's frame unconditionally makes
the marker drift upward off the screen — reactSetFrame: then runs on every
layout pass, and each pass with a non-zero dy subtracts from self.center.y.
So the fix is not simply "delete it" or "always take it"; the interaction between
-layoutSubviews, reactSetFrame:'s center rewrite, and Fabric's lack of a
layout override needs to be resolved together. I did not want to guess at that in
a PR without maintainer input on the intended ownership of layout.
Workaround for users
Give the marker's child a constant size, and vary only what is drawn inside
it. A fixed-size <Svg> works well, because it is a single native view whose
dimensions never change:
// canvas is CONSTANT for every label; only the pill drawn inside it varies
<Marker coordinate={coord} tracksViewChanges={tracks}>
<Svg width={456} height={72} viewBox="0 0 456 72">
<Rect x={236} y={22} width={pillWidth} height={28} rx={14} fill="rgba(0,0,0,0.72)" />
<SvgText x={248} y={36} fontSize={14} fill="#fff" alignmentBaseline="central">
{label}
</SvgText>
</Svg>
</Marker>
Because the canvas never changes size, reactSetFrame: computes dy = 0, bounds
never ratchet, and MapKit's centring is stable. Any offset you need (e.g. placing
the content beside the coordinate rather than on it) can be baked into where you
draw inside the canvas — no anchor or centerOffset required, which is
convenient given anchor is a no-op on Apple Maps.
This is what the library's own MKMarkerAnnotationView-style pins do implicitly:
fixed-size children have never exhibited the problem.
Note for 2.x
reactSetFrame:'s center rewrite is byte-identical in 1.27.2, 1.28.0, 1.29.0
and 2.0.0-beta.15. The two methods above are gone in 2.0, which likely resolves
the ratchet for the Paper marker — but if a Fabric marker component lands in 2.x,
whatever sizes the annotation will need to be re-established, or the same
"no marker at all" result will follow. I have not been able to test 2.0 directly.
Happy to open a PR if maintainers can say where layout should be owned under
Fabric — the fix looks small once that is decided.
- Ngôn ngữ chính
- TypeScript
- Star
- 16k
- Fork
- 5k
- Merge trung bình
- 17 giờ 45 phút
- Pull request đã merge (30 ngày)
- 2
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của react-native-maps/react-native-maps
-
Độ khó 1/5 1-3 giờ Mức phù hợp với người mới 88/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 88/100
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 90/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
react-native-maps/react-native-maps#5987 · 1 bình luận ·
Tất cả issue của react-native-maps/react-native-maps
Issue tương tự
-
blocklist removal
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 65/100
MetaMask/eth-phishing-detect#296544 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
pastelsky/bundlephobia#1122 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
-
category/development priority/P2 scope/file-operations scope/testing type/enhancement
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
-
Enatega Customer and Rider app: Add-ons price is not visible to customer after order is placed. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100