Ana Sayfa / Blog / Dark Mode implementation: tasarımdan kod’a disiplin

Dark Mode implementation: tasarımdan kod’a disiplin

iOS 13'ten beri Dark Mode var. Kötü implement edilmiş app'ler hâlâ her yerde. Doğru yaklaşım, design'dan kod'a.

Dark Mode iOS 13’te (2019) geldi. 6 yıl sonra hâlâ çoğu app’te implementation problemi var: kontrast yetersiz, bazı ekranlar light mode’da kalmış, hard-coded color’lar.

12 iOS uygulamamın hepsi Dark Mode destekliyor ve kullanıcı feedback’i olumlu. Bu yazıda design’dan implementation’a kadar yaklaşımımı paylaşacağım.

Neden Dark Mode?

Dark Mode sadece aesthetic değil:

  1. Battery saving. OLED screen’de dark pixels enerji tasarrufu (~%30-60).
  2. Eye comfort. Gece kullanımında göz yorgunluğu azalıyor.
  3. System consistency. iOS system-wide dark mode, uyumsuz app jarring.
  4. Accessibility. Some visual impairment’lerde dark mode daha rahat.

User base’nin %60-70’i dark mode preference’ı aktif. Support etmemek modern app için seçenek değil.

Design-first approach

Dark mode color palette’i designer’ın ana sorumluluğu. Developer palette’i alıp implement ediyor.

Kötü yaklaşım: “Light mode renklerimi invert edelim”. White → Black, Black → White. Çalışmıyor.

Gerçek dark mode palette’i:

Backgrounds:
– Pure black (#000000) değil. Too harsh.
– Dark grays: #121212 (Google Material), #1A1A1A, #1F1F1F
– Multiple levels: primary background, secondary (card), tertiary (sub-card)

Text:
– Pure white (#FFFFFF) rarely. Eye strain.
– Off-white: #FAFAFA, #E0E0E0
– Muted: #8A8A8A for secondary info

Accents:
– Desaturate brand colors. Neon on dark eye-burning.
– Example: Brand blue #007AFF → Dark blue #4DA3FF

Color semantic system

Color’ları literal name ile kullanma. Semantic name ver.

Kötü:

Color(hex: "#333333")  // Ne bu rengi?
Color.darkGray  // Hangi context?

İyi:

Color("Background")  // Asset catalog'da light/dark variants
Color("TextPrimary")
Color("TextSecondary")
Color("Accent")
Color("CardBackground")

Asset catalog’da her color’ın Any Appearance + Dark Appearance.

// Asset Catalog > New Color Set
Background:
  Any Appearance: #FFFFFF
  Dark Appearance: #1A1A1A

SwiftUI kullanımında otomatik switch.

System colors vs custom

iOS sistem color’ları built-in dark mode desteği:

Color(.label)        // text (dark gray veya light gray)
Color(.secondaryLabel)
Color(.systemBackground)  // app background
Color(.secondarySystemBackground)
Color(.systemFill)

System color’lar platform consistency sağlıyor. Custom color’lar brand identity için.

Benim pattern: primary action/accent custom, structural elements system.

SwiftUI implementation

SwiftUI dark mode default support:

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello")
                .foregroundColor(Color("TextPrimary"))
                .padding()
                .background(Color("CardBackground"))
        }
        .background(Color("Background"))
    }
}

No special code. System color scheme’i detect ediyor.

Explicit color scheme (override):

.preferredColorScheme(.dark)  // Force dark

Rarely kullanılıyor. Respect system preference.

UIKit implementation

UIKit’te biraz daha manual:

view.backgroundColor = UIColor(named: "Background")
label.textColor = UIColor(named: "TextPrimary")

Dynamic colors:

let adaptiveColor = UIColor { traits in
    traits.userInterfaceStyle == .dark ? .systemGray : .black
}

Trait collection change detect:

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)
    if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
        // Color scheme değişti, redraw
    }
}

UIKit bazı custom drawing’lerde explicit trait check gerekiyor.

Image assets

Image’lar da dark variant gerektiriyor.

Asset Catalog image:

MyIcon.imageset/
  - MyIcon.png (Any Appearance)
  - MyIcon-dark.png (Dark Appearance)
  - Contents.json

Xcode GUI’den her image için dark variant assign. SwiftUI otomatik switch.

Alternative: SF Symbols. Her zaman adaptive. Preferred.

Alternative: Single image + blend mode:

Image("icon")
    .renderingMode(.template)
    .foregroundColor(Color("IconTint"))

Icon monochrome ise tint color ile adapt.

Common mistakes

1. Hard-coded colors. Color(.red), Color(hex: "#FFF"). Dark mode’da yanlış görünüyor.

Fix: Her zaman asset catalog color.

2. Shadow opacity. Light mode’da black shadow güzel. Dark mode’da invisible.

Fix:

.shadow(color: Color("ShadowColor"), radius: 4)
// ShadowColor asset: light #00000020, dark #00000080

3. Image’lerde white background. PNG’lar white background’la ihraç edilmiş. Dark mode’da çirkin white patch.

Fix: Transparent background. Ya da dark variant image.

4. Accent color uyumsuzluğu. Brand color her iki mode’da farklı tonlar gerektirebilir. Light’ta saturated OK, dark’ta desature.

5. Status bar. Dark mode’da light content status bar. Otomatik handle edilir ama custom navigation controller’larda eksik.

Testing strategy

1. Simulator dark mode toggle:

Cmd+Shift+A (simulator shortcut)

Instantly switch. Her ekranı hem light hem dark’ta test.

2. Xcode preview:

#Preview("Light") {
    ContentView()
}

#Preview("Dark") {
    ContentView()
        .preferredColorScheme(.dark)
}

Side-by-side preview. Development sırasında instant feedback.

3. Real device test. Simulator’da OLED black perfect görünüyor, gerçek cihazda farklı. Periodic real device check.

4. Dynamic switch test. App open, Control Center’dan dark mode toggle, app’in davranışını izle. Smooth transition mı, glitch mı?

Accessibility ile ilişki

Dark mode yalnız değil, accessibility ile ilgili:

  • Reduce Motion: Dark mode transition animation’ları respect.
  • Increase Contrast: Dark mode’da daha fazla contrast.
  • Reduce Transparency: Dark mode’da transparent background’lar.

Trait collection’da hepsini check:

if traitCollection.userInterfaceStyle == .dark {
    if traitCollection.accessibilityContrast == .high {
        // High contrast dark mode
    }
}

Custom theme support

Some apps beyond light/dark multiple themes offer: “Sepia”, “Ocean Blue”, “Auto”.

Implementation:

enum AppTheme {
    case system, light, dark, sepia, ocean
}

@AppStorage("userTheme") var theme: AppTheme = .system

var preferredColorScheme: ColorScheme? {
    switch theme {
    case .system: return nil
    case .light: return .light
    case .dark: return .dark
    case .sepia, .ocean: return nil  // Custom color scheme applied via colors
    }
}

Sepia için tamamen farklı color asset set. Complex ama certain apps için değerli.

Design review checklist

Dark mode implementation PR review’unda:

  • [ ] Her ekran hem light hem dark’ta test edildi mi?
  • [ ] Hard-coded color var mı?
  • [ ] Image’ların dark variant’ı var mı (veya SF Symbol)?
  • [ ] Shadow’lar her iki mode’da visible mı?
  • [ ] Text contrast WCAG AA geçiyor mu (4.5:1)?
  • [ ] Accent color dark mode’da saturated görünüyor mu?
  • [ ] Border/separator’lar her iki mode’da seen mı?

Bu checklist %90 dark mode issue’larını yakalıyor.

Sonuç

Dark mode implementation discipline gerektiriyor. Asset catalog color system, semantic naming, thorough testing.

İlk kez implement ediyorsan hafta içi feature. Sonraki app’lerde pattern established, quick setup.

Designer ile collaboration kritik. Developer palette değil, designer’ın tasarladığı palette’i implement. Quality collaboration’dan geliyor.

Bu konuda bir projeniz mi var?

Kısa bir özet bırakın, 24 saat içinde size dönüş yapayım.

İletişime Geç