Implementing Coach Marks in a Mobile Application
When users open your app for the first time, they often get lost: where to tap, how to create a task, where to find settings. Without hints, onboarding conversion drops by 20–40%. Coach marks solve this: contextual pointers appear at the right moment and explain each element. In 1–3 days, we implement a visual hint system that boosts user engagement.
Problems We Solve
Users don't reach key features: statistics show that 60% of new users never return after the first launch if the interface is confusing. Traditional walkthroughs require sequential steps, which distracts and annoys. Coach marks work differently: they appear in context, highlighting exactly the element to interact with. We configure the display logic: after a specific action, after the Nth launch, or after a version update.
How We Do It
We use the native stack: Swift + SwiftUI for iOS, Kotlin + Jetpack Compose for Android. For quick start, we use proven libraries:
| Platform | Library | Features | |----------|---------|----------| | iOS | [Instructions](https://github.com/ephread/Instructions) | CoachMarkController, custom bubble, accessibility | | Android (View) | [TapTargetView](https://github.com/KeepSafe/TapTargetView) | Material style, simple integration | | Android (Compose) | [Spotlight](https://github.com/TakuSemba/Spotlight) | Compose-first, flexible positioning | If the design requires non-standard highlight shapes (e.g., shaped like an app icon), we write a custom implementation. On iOS, we create a UIView overlay with masking via CAShapeLayer and evenOdd fill rule. On Compose, we use Canvas with BlendMode.Clear — it's important to set compositingStrategy = Offscreen.
iOS Implementation
The most reliable approach is a UIView overlay on top of the window with masking via CAShapeLayer. We create a singleton CoachMarkManager or embed it in a UIViewController subclass.
Getting the frame of the target element:
let globalFrame = targetView.convert(targetView.bounds, to: UIApplication.shared.keyWindow) keyWindow is deprecated in later iOS versions: correctly use UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as UIWindowScene, then .windows.first(where: { $0.isKeyWindow }).
Overlay mask: UIBezierPath(rect: overlayView.bounds).appending(UIBezierPath(ovalIn: globalFrame.insetBy(dx: -8, dy: -8))) with fillRule = .evenOdd. Animate appearance via CABasicAnimation on opacity.
Libraries. Instructions (ephread/Instructions) is a mature iOS library with CoachMarkController, custom bubble views, and accessibility. EasyTipView for simple tooltips without overlay. If the design is standard, a library saves a day. If custom overlay with non-standard highlight shapes, we write our own.
In SwiftUI — anchorPreference(key:value:) + overlayPreferenceValue allow placing an overlay relative to any view without knowing its coordinates beforehand. This is the correct SwiftUI-way but requires diving into the preference system. Alternative: GeometryReader + .coordinateSpace(name:) to get global coordinates.
Android and Compose
On Android — library TapTargetView (KeepSafe) for Material-styled coach marks. Works with regular Views. For Compose — Spotlight (TakuSemba) or custom implementation via Popup + Canvas.
Custom Compose implementation:
@Composable fun CoachMarkOverlay(targetRect: Rect, text: String) { Canvas(modifier = Modifier.fillMaxSize()) { drawRect(color = Color.Black.copy(alpha = 0.7f)) drawCircle( color = Color.Transparent, radius = targetRect.size.minDimension / 2 + 12f, center = targetRect.center, blendMode = BlendMode.Clear ) } // Tooltip positioned via offset from targetRect } BlendMode.Clear requires graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen } on the parent container — without this, Clear does not work correctly.
Sequence and Scheduling
If there are multiple coach marks, we show them one by one. A queue [CoachMarkConfig] in CoachMarkManager. After each dismiss, a small delay of 300ms before the next (user needs a second to process).
Display conditions: after a specific user action, after the Nth launch, after updating to version X. Logic in CoachMarkScheduler — a separate object with UserDefaults persistence. No conditions in controllers.
Accessibility
The coach mark must be visible to VoiceOver/TalkBack. Overlay view — accessibilityViewIsModal = true on iOS (all elements under overlay disappear from accessibility tree). Hint text — accessibilityLabel on the bubble view. Dismiss button — accessibilityLabel = "Close hint". With VoiceOver, we automatically set focus on the bubble via UIAccessibility.post(notification: .screenChanged, argument: bubbleView).
Time estimates: 1–3 days. One coach mark with a simple highlight — 1 day. A system with queue, JSON configuration, custom highlight shapes, and full accessibility — 3 days.
What's Included
- Architecture: we design a CoachMarkManager module with queue and scheduler.
- Implementation on iOS (Swift 5.9+, SwiftUI/UIKit) and Android (Kotlin, Jetpack Compose).
- Accessibility support: VoiceOver, TalkBack, focus on bubble.
- Documentation: activation logic description, configuration JSON.
- Technical support during integration and after release.
We guarantee code quality and compliance with App Review Guidelines. Our experience: 15+ onboarding projects for iOS and Android.
How Coach Marks Boost Onboarding Conversion
Research shows a 30–50% increase in conversion when using contextual hints. Coach marks reduce time to first action by 40% and decrease support tickets. In one project, implementing coach marks increased retention by 25% in the first week.
Why Choose Us
We don't just drop in a library — we analyze the user journey and design activation logic. The system won't show hints to users who already understand. We configure UserDefaults persistence, queue with delays, and custom highlight shapes. Accessibility handling is a mandatory part of the work.
Contact us for a project evaluation. Get a consultation on implementing coach marks and learn how to improve your app's conversion.







