How to Build a Status Bar Animator in Android and iOS

How to Build a Status Bar Animator in Android and iOS

A status bar animator smoothly updates the system status bar (colors, icons visibility, translucency) in response to UI changes, improving perceived polish. This guide shows simple, cross-platform approaches for Android and iOS with code examples, performance notes, and accessibility considerations.

Why animate the status bar

  • Polish: Smooth transitions feel professional.
  • Context: Visually links app states (e.g., fullscreen media vs. content lists).
  • Accessibility: Gentle changes avoid jarring contrasts for users with visual sensitivities.

Design decisions (assumed defaults)

  • Animate status bar background color and icon brightness (light/dark).
  • Triggered by view scroll or screen state changes.
  • Maintain 60 FPS where possible; avoid blocking main thread.

Android: approach and implementation

Overview

On Android, status bar appearance is controlled via WindowInsets, system UI visibility flags, and Window.setStatusBarColor (API 21+). For light/dark icons use View.SYSTEM_UI_FLAG_LIGHT_STATUSBAR (API 23+) or WindowInsetsController (API 30+). We’ll provide a backward-compatible implementation using windowInsetsController when available, falling back to flags.

Key components

  • ValueAnimator for interpolating colors.
  • Helper to set icon brightness.
  • Lifecycle-aware coroutine/scroll listener to trigger animations.

Example (Kotlin)

kotlin

// StatusBarAnimator.kt import android.animation.ArgbEvaluator import android.animation.ValueAnimator import android.app.Activity import android.os.Build import android.view.View import android.view.WindowInsetsController import androidx.core.content.ContextCompat class StatusBarAnimator(private val activity: Activity) { private var colorAnimator: ValueAnimator? = null fun animateToColor(targetColor: Int, lightIcons: Boolean, duration: Long = 300L) { val window = activity.window val startColor = window.statusBarColor colorAnimator?.cancel() colorAnimator = ValueAnimator.ofObject(ArgbEvaluator(), startColor, targetColor).apply { this.duration = duration addUpdateListener { anim -> window.statusBarColor = anim.animatedValue as Int } start() } setIconBrightness(lightIcons) } private fun setIconBrightness(lightIcons: Boolean) { val window = activity.window if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { val controller = window.insetsController controller?.setSystemBarsAppearance( if (lightIcons) 0 else WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS, WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS ) } else { @Suppress(“DEPRECATION”) val decor = window.decorView var flags = decor.systemUiVisibility flags = if (lightIcons) flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv() else flags or View.SYSTEM_UI_FLAG_LIGHT_STATUSBAR decor.systemUiVisibility = flags } } fun cancel() { colorAnimator?.cancel() } }

Usage

  • On scroll: map scroll fraction (0..1) to desired color using interpolate and call animateToColor with duration 0–300ms depending on change size.
  • On navigation: call animateToColor in activity/fragment transitions.

Performance tips

  • Reuse ValueAnimator; cancel before starting new.
  • Avoid expensive color calculations on main thread — precompute where possible.
  • Keep durations short (150–350ms) to match material motion.

iOS: approach and implementation

Overview

iOS status bar style (light/dark content) is controlled per-ViewController via preferredStatusBarStyle and setNeedsStatusBarAppearanceUpdate. To animate background color you typically place a view under the status bar (or make navigation bar transparent) and animate that view’s backgroundColor.

Key components

  • UIViewPropertyAnimator or UIView.animate for smooth color transitions.
  • Child view pinned to top safe area acting as background.
  • Override preferredStatusBarStyle and call setNeedsStatusBarAppearanceUpdate inside an animation block for smooth icon style transition.

Example (Swift)

swift

// StatusBarAnimator.swift import UIKit class StatusBarAnimator { private weak var viewController: UIViewController? private var backgroundView: UIView? init(viewController: UIViewController) { self.viewController = viewController ensureBackgroundView() } private func ensureBackgroundView() { guard let vc = viewController, backgroundView == nil else { return } let bg = UIView() bg.translatesAutoresizingMaskIntoConstraints = false vc.view.addSubview(bg) let guide = vc.view.safeAreaLayoutGuide NSLayoutConstraint.activate([ bg.topAnchor.constraint(equalTo: vc.view.topAnchor), bg.leadingAnchor.constraint(equalTo: vc.view.leadingAnchor), bg.trailingAnchor.constraint(equalTo: vc.view.trailingAnchor), bg.bottomAnchor.constraint(equalTo: guide.topAnchor) // covers status bar area ]) backgroundView = bg } func animate(to color: UIColor, lightContent: Bool, duration: TimeInterval = 0.25) { guard let vc = viewController else { return } UIView.animate(withDuration: duration) { self.backgroundView?.backgroundColor = color // update status bar style by telling VC which style to use objc_setAssociatedObject(vc, &AssociatedKeys.lightStatusBar, lightContent, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) vc.setNeedsStatusBarAppearanceUpdate() } } } // Usage: in your UIViewController subclass add: private struct AssociatedKeys { static var lightStatusBar = “lightStatusBar” } extension UIViewController { open override var preferredStatusBarStyle: UIStatusBarStyle { let light = (objc_getAssociatedObject(self, &AssociatedKeys.lightStatusBar) as? Bool) ?? false return light ? .lightContent : .darkContent } }

Notes

  • On iOS 13+ prefer .darkContent/.lightContent; for older iOS use .default/.lightContent.
  • If using UINavigationBar, consider animating its barTintColor or using setBackgroundImage to make it transparent.

Cross-platform UX patterns

  • Use short, consistent durations (200–300ms).
  • Animate only what’s

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *