MapMarker takes a colour and a title and nothing else, so a map can show where things are but never who or what they are. Anything that needs a face, a logo, a number or a glyph on the map has to be left off it.
Tested on craft-native 0.0.293, both platforms.
What a marker can carry today
iOS, dist/ios/templates/CraftNativeScreen.swift:1558:
if type == "mapmarker" || type == "marker" {
guard let point = points.first else { continue }
nextMarkers[id] = (point, childProps["title"] as? String)
if let color = childProps["pinColor"] as? String { nextPinColors[id] = Self.color(color) }
if let bearing = Self.numeric(childProps["bearing"]), bearing.isFinite { … }
…
}geometry, title, pinColor, bearing, onPress. Android reads exactly the same set (dist/android/templates/CraftNativeMapGoogle.kt.template:131-166), so this is not a one-platform gap.
The view is then always built from a generated pin — CraftNativeScreen.swift:1869:
let color = (markerPinColors[id] ?? .systemRed).resolvedColor(with: traitCollection)
view.tintColor = color
view.image = Self.markerImage(color: color, direction: false)There is no branch that accepts a supplied image, so no amount of prop-passing reaches view.image.
Where it bit
A live board for a backyard ultra: every runner goes round the same loop from the same gun, and the screen draws the loop with each runner placed on it. The thing that makes it worth looking at is seeing who is where — the leader, your friend, the one about to miss the cutoff.
On the web the same feature draws each runner's avatar on the loop. On Craft the best available is a coloured pin, so four states had to be squeezed into four colours, with the identity pushed into the title where it is invisible until tapped:
| web | Craft | |
|---|---|---|
| on their own measured pace | avatar, solid ring | green pin |
| placed on the field's pace | avatar, dashed + dimmed | pale green pin |
| past their own lap time | avatar, red ring | red pin |
| resting in the corral | avatar + +N count | grey pin |
Four runners on a loop are four identical dots differing only in hue. The feature works; it just stops being the thing people open it for.
Precedent in Craft's own API
The chrome API already does exactly this. tabItems[].image accepts an https URL and is used for the account's photo — CraftApp.swift:774:
if item.keys.contains("image") {
next.image = (item["image"] as? String).flatMap { URL(string: $0)?.scheme == "https" ? $0 : nil }
}So the https-only-remote-image rule, the fetch and the null fallback all exist already; markers just do not get them.
Suggested shape
<MapMarker :geometry="[lat, lng]" image="https://…/avatar.jpg" :imageSize="30" imageShape="circle" title="Ada" />image: https URL only, same validation astabItems[].image; falls back to the current generated pin when absent, unreachable or non-https, so nothing regressesimageSize: points, clamped to something saneimageShape:circle|square, since a circular crop is what nearly every avatar-on-a-map wants and doing it caller-side is impossible
A smaller version that would still solve most of it: a glyph / symbol prop drawing an SF Symbol or one or two characters inside the existing pin. That would at least let markers be told apart without a tap.
Also worth knowing
Non-bearing markers are pinned at displayPriority = .required, collisionMode = .rectangle and zPriority = .max (CraftNativeScreen.swift:1871-1876). So markers at or near the same point all draw, all at the same z, and the caller cannot say which should be on top. For this screen that means the runner about to miss the cutoff can land underneath the group resting on the start line, with no way to order them. Happy to split that into its own issue if you would rather keep them separate.