Tüm Yazılar
KategoriiOS
Okuma Süresi
21 dk
Yayın Tarihi
...
Kelime Sayısı
1.684kelime

Kahveni hazırla - bu içerikli bir makale!

iOS erişilebilirlik: VoiceOver, Dynamic Type, renk kontrastı, reduce motion, accessibility audit ve WCAG uyumluluğu ile kapsayıcı uygulamalar geliştirin.

iOS Accessibility: VoiceOver'dan Dynamic Type'a Tam Rehber

Dünya nüfusunun %15'i bir engele sahip. iOS'un erişilebilirlik özellikleri, uygulamanı tüm kullanıcılar için kullanılabilir kılar. Apple, erişilebilirlik konusunda endüstrinin lideri — ve App Store'da erişilebilirlik standartlarını karşılamayan uygulamalar reddedilebilir.

💡 Hızlı Not: Bu rehber Apple'ın Human Interface Guidelines Accessibility bölümü ve WCAG 2.1 AA standartlarından derlendi.

İçindekiler

  1. Neden Erişilebilirlik?
  2. VoiceOver Temelleri
  3. SwiftUI Accessibility Modifiers
  4. Dynamic Type
  5. Renk Kontrastı ve Color Blindness
  6. Reduce Motion
  7. Custom Accessibility Actions
  8. Accessibility Audit (Xcode)
  9. WCAG Compliance
  10. Testing Checklist

Neden Erişilebilirlik? {#neden-a11y}

İstatistik
Değer
Dünya engelli nüfusu
1.3 milyar (%15)
iOS VoiceOver kullanıcısı
Milyonlarca
Renk körlüğü oranı
Erkeklerin %8'i
Yaşla gelen görme sorunu
50+ yaş %50+
App Store erişilebilirlik reddi
Artan trend

VoiceOver Temelleri {#voiceover}

VoiceOver, iOS'un ekran okuyucusudur. Görme engelli kullanıcılar uygulamanı parmak hareketleriyle yönetir ve VoiceOver ekrandakileri sesli okur.

swift
1// Temel accessibility label
2Image(systemName: "heart.fill")
3 .accessibilityLabel("Favori")
4 
5// Daha detaylı
6Button(action: addToCart) {
7 Image(systemName: "cart.badge.plus")
8}
9.accessibilityLabel("Sepete ekle")
10.accessibilityHint("Ürünü alışveriş sepetinize ekler")
11.accessibilityAddTraits(.isButton)

SwiftUI Accessibility Modifiers {#swiftui-a11y}

swift
1// Accessibility modifier'lar
2struct ProductCard: View {
3 let product: Product
4 
5 var body: some View {
6 VStack {
7 AsyncImage(url: product.imageURL)
8 .accessibilityHidden(true) // Dekoratif image
9 
10 Text(product.name)
11 .font(.headline)
12 
13 Text("\(product.price, format: .currency(code: "TRY"))")
14 .font(.subheadline)
15 
16 HStack {
17 ForEach(0..<5) { index in
18 Image(systemName: index < product.rating ? "star.fill" : "star")
19 }
20 }
21 .accessibilityElement(children: .ignore)
22 .accessibilityLabel("\(product.rating) üzerinden 5 yıldız")
23 }
24 // Tüm card tek element olarak okunur
25 .accessibilityElement(children: .combine)
26 .accessibilityLabel("\(product.name), \(product.price) TL, \(product.rating) yıldız")
27 .accessibilityAddTraits(.isButton)
28 }
29}
30 
31// Rotor actions - VoiceOver kullanıcıları için hızlı eylemler
32Text(article.title)
33 .accessibilityAction(named: "Paylaş") { shareArticle(article) }
34 .accessibilityAction(named: "Yer İmi") { bookmarkArticle(article) }
35 
36// Custom accessibility value
37Slider(value: $volume, in: 0...100)
38 .accessibilityValue("Ses seviyesi yüzde \(Int(volume))")

Dynamic Type {#dynamic-type}

swift
1// SwiftUI - otomatik Dynamic Type desteği
2Text("Başlık")
3 .font(.title) // Otomatik ölçeklenir
4 
5// Custom font ile Dynamic Type
6Text("Custom")
7 .font(.custom("Avenir", size: 16, relativeTo: .body))
8 
9// Minimum/maksimum boyut
10Text("Sınırlı")
11 .dynamicTypeSize(.small ... .xxxLarge) // Aralık belirle
12 
13// Layout adaptasyonu
14@Environment(\.dynamicTypeSize) var dynamicTypeSize
15 
16var body: some View {
17 if dynamicTypeSize >= .accessibility1 {
18 // Büyük text için dikey layout
19 VStack { content }
20 } else {
21 // Normal text için yatay layout
22 HStack { content }
23 }
24}

Renk Kontrastı ve Color Blindness {#renk-kontrast}

swift
1// Minimum kontrast oranı: 4.5:1 (normal text), 3:1 (büyük text)
2 
3// ✅ İyi kontrast
4Text("Okunabilir")
5 .foregroundStyle(.primary) // Otomatik kontrast
6 
7// Renk körlüğü desteği
8// Sadece renge bağımlı olma - ikon/şekil de kullan
9HStack {
10 Image(systemName: "checkmark.circle.fill")
11 .foregroundStyle(.green)
12 Text("Başarılı")
13}
14// ❌ Sadece renk ile durum belirtme
15Circle().fill(.red) // Renk körü kullanıcı anlayamaz
16 
17// differentiateWithoutColor
18@Environment(\.accessibilityDifferentiateWithoutColor) var differentiateWithoutColor
19 
20var statusView: some View {
21 if differentiateWithoutColor {
22 Label("Hata", systemImage: "xmark.circle")
23 } else {
24 Circle().fill(.red)
25 }
26}

Reduce Motion {#reduce-motion}

swift
1@Environment(\.accessibilityReduceMotion) var reduceMotion
2 
3var body: some View {
4 content
5 .animation(reduceMotion ? nil : .spring(), value: isExpanded)
6 .transition(reduceMotion ? .opacity : .slide)
7}
8 
9// Otomatik hareket kontrolü
10withAnimation(reduceMotion ? nil : .easeInOut(duration: 0.3)) {
11 isVisible.toggle()
12}

Custom Accessibility Actions {#custom-actions}

swift
1// Swipe actions alternatifi - VoiceOver kullanıcıları için
2struct MessageRow: View {
3 var body: some View {
4 Text(message.text)
5 .accessibilityAction(named: "Yanıtla") { reply() }
6 .accessibilityAction(named: "Sil") { delete() }
7 .accessibilityAction(named: "İlet") { forward() }
8 }
9}
10 
11// Custom adjustable - artır/azalt
12struct QuantityPicker: View {
13 @Binding var quantity: Int
14 
15 var body: some View {
16 HStack {
17 Button("-") { quantity -= 1 }
18 Text("\(quantity)")
19 Button("+") { quantity += 1 }
20 }
21 .accessibilityElement(children: .ignore)
22 .accessibilityLabel("Adet")
23 .accessibilityValue("\(quantity)")
24 .accessibilityAdjustableAction { direction in
25 switch direction {
26 case .increment: quantity += 1
27 case .decrement: quantity = max(0, quantity - 1)
28 @unknown default: break
29 }
30 }
31 }
32}

Accessibility Audit (Xcode) {#audit}

  1. Xcode Accessibility Inspector: Developer Tools'dan aç
  2. Audit button: Mevcut ekranın erişilebilirlik sorunlarını tara
  3. Inspection: Her element'in label, value, traits bilgisi
  4. Simulator: Settings > Accessibility > VoiceOver ile test

WCAG Compliance {#wcag}

Level
Gereksinim
iOS Karşılığı
A
Temel erişilebilirlik
accessibilityLabel, traits
AA
Yeterli erişilebilirlik
4.5:1 kontrast, Dynamic Type
AAA
İleri erişilebilirlik
Reduce motion, full VoiceOver

Testing Checklist {#testing}

  1. ✅ VoiceOver ile tüm ekranları test et
  2. ✅ Dynamic Type XXL ile layout kontrol et
  3. ✅ Bold Text açıkken kontrol et
  4. ✅ Renk kontrastı 4.5:1 minimum
  5. ✅ Touch target 44x44pt minimum
  6. ✅ Reduce Motion açıkken animasyonlar
  7. ✅ Keyboard navigation (iPad)
  8. ✅ Switch Control uyumluluğu

Accessibility Notifications {#a11y-notifications}

VoiceOver kullanıcılarına dinamik değişiklikleri bildirmek için accessibility notification'lar kullan:

swift
1// Ekran değişikliği bildirimi - yeni ekran yüklendiğinde
2UIAccessibility.post(notification: .screenChanged, argument: headerView)
3 
4// Layout değişikliği - ekranın bir bölümü güncellendiğinde
5UIAccessibility.post(notification: .layoutChanged, argument: updatedLabel)
6 
7// Announcement - önemli bilgi duyurusu
8UIAccessibility.post(notification: .announcement, argument: "Sipariş başarıyla oluşturuldu")
9 
10// SwiftUI'da announcement
11struct OrderView: View {
12 @State private var orderStatus = ""
13 
14 var body: some View {
15 VStack {
16 Text(orderStatus)
17 .accessibilityLabel("Sipariş durumu: \(orderStatus)")
18 }
19 .onChange(of: orderStatus) { _, newValue in
20 // VoiceOver kullanıcısına otomatik duyuru
21 AccessibilityNotification.Announcement(newValue).post()
22 }
23 }
24}
25 
26// Priority announcement (iOS 17+) - diğer duyuruları kesebilir
27AccessibilityNotification.Announcement("Acil uyarı: Oturum süresi dolmak üzere")
28 .post()

Notification Tipleri

Notification
Ne Zaman Kullanılır
VoiceOver Davranışı
.screenChanged
Yeni ekran/modal açıldığında
Focus ilk elemente atlar
.layoutChanged
Kısmi UI güncellemesinde
Focus belirtilen elemente gider
.announcement
Durum bildirimi
Mevcut okumayı kesmeden duyurur
.pageScrolled
Sayfa scroll edildiğinde
"Sayfa 2/5" gibi bilgi verir

Switch Control ve Keyboard Navigation {#switch-control}

Switch Control, motor engelli kullanıcıların tek veya birkaç butonla cihazı kullanmasını sağlar. Keyboard navigation ise iPad ve Mac Catalyst uygulamalarında kritik:

swift
1// Focus sistemi - keyboard ve switch control için
2struct NavigableList: View {
3 @FocusState private var focusedItem: String?
4 let items: [MenuItem]
5 
6 var body: some View {
7 VStack {
8 ForEach(items) { item in
9 Button(action: { select(item) }) {
10 MenuItemRow(item: item)
11 }
12 .focused($focusedItem, equals: item.id)
13 .focusEffectDisabled(false) // Focus ring göster
14 .accessibilityFocused($focusedItem, equals: item.id)
15 }
16 }
17 .focusSection() // Tab ile bu gruba gelinebilir
18 .onKeyPress(.return) {
19 if let focused = focusedItem { activate(focused) }
20 return .handled
21 }
22 .onKeyPress(.escape) {
23 focusedItem = nil
24 return .handled
25 }
26 }
27}
28 
29// Accessibility focus management
30struct DetailView: View {
31 @AccessibilityFocusState var isHeaderFocused: Bool
32 
33 var body: some View {
34 VStack {
35 Text("Detay Başlığı")
36 .font(.title)
37 .accessibilityFocused($isHeaderFocused)
38 
39 // ... içerik
40 }
41 .onAppear {
42 // Ekran açıldığında VoiceOver focus'unu başlığa taşı
43 DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
44 isHeaderFocused = true
45 }
46 }
47 }
48}

Accessibility Containers ve Grouping {#containers}

Karmaşık UI'larda element'leri mantıklı gruplara ayırmak VoiceOver deneyimini dramatik şekilde iyileştirir:

swift
1// Karmaşık card'ı tek anlamlı element yap
2struct TransactionCard: View {
3 let transaction: Transaction
4 
5 var body: some View {
6 HStack {
7 VStack(alignment: .leading) {
8 Text(transaction.merchant)
9 .font(.headline)
10 Text(transaction.date.formatted(date: .abbreviated, time: .shortened))
11 .font(.caption)
12 .foregroundStyle(.secondary)
13 }
14 Spacer()
15 Text(transaction.amount.formatted(.currency(code: "TRY")))
16 .font(.title3)
17 .foregroundStyle(transaction.amount < 0 ? .red : .green)
18 }
19 .padding()
20 // Tüm child'ları birleştir - VoiceOver tek element olarak okur
21 .accessibilityElement(children: .combine)
22 // Özel okuma sırası ve formatı
23 .accessibilityLabel(
24 "\(transaction.merchant), " +
25 "\(transaction.amount < 0 ? "harcama" : "gelir") " +
26 "\(abs(transaction.amount).formatted(.currency(code: "TRY"))), " +
27 "\(transaction.date.formatted(date: .abbreviated, time: .shortened))"
28 )
29 // Swipe action'lar
30 .accessibilityAction(named: "Detay Görüntüle") { showDetail(transaction) }
31 .accessibilityAction(named: "Tekrarla") { repeatTransaction(transaction) }
32 }
33}
34 
35// Accessibility scroll - büyük listeler için
36struct InfiniteScrollList: View {
37 @State private var page = 1
38 
39 var body: some View {
40 ScrollView {
41 LazyVStack {
42 ForEach(items) { item in
43 ItemRow(item: item)
44 }
45 }
46 }
47 .accessibilityScrollAction { edge in
48 if edge == .bottom {
49 page += 1
50 loadMore()
51 // Scroll sonucunu VoiceOver'a bildir
52 UIAccessibility.post(
53 notification: .pageScrolled,
54 argument: "Sayfa \(page) yüklendi, \(items.count) sonuç"
55 )
56 }
57 }
58 }
59}

Erişilebilirlik Environment Değerleri

Environment Key
Tip
Açıklama
\.accessibilityReduceMotion
Bool
Hareket azaltma
\.accessibilityReduceTransparency
Bool
Şeffaflık azaltma
\.accessibilityDifferentiateWithoutColor
Bool
Renksiz ayırt etme
\.accessibilityShowButtonShapes
Bool
Buton şekillerini göster
\.accessibilityInvertColors
Bool
Renk tersine çevirme
\.accessibilityVoiceOverEnabled
Bool
VoiceOver aktif mi
\.dynamicTypeSize
DynamicTypeSize
Metin boyutu tercihi
\.legibilityWeight
LegibilityWeight
Bold Text tercihi

Easter Egg

Gizli bir bilgi buldun!

Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?

ALTIN İPUCU

Bu yazının en değerli bilgisi

Bu ipucu, yazının en önemli çıkarımını içeriyor.

Okuyucu Ödülü

Tebrikler! Bu yazıyı sonuna kadar okuduğun için sana özel bir hediyem var: **Kaynaklar:** - [Apple: Accessibility](https://developer.apple.com/accessibility/) - [WWDC23: Build accessible apps](https://developer.apple.com/videos/play/wwdc2023/10034/) - [WCAG 2.1 Guidelines](https://www.w3.org/TR/WCAG21/)

Etiketler

#accessibility#voiceover#dynamic-type#a11y#inclusive-design#ios#swiftui
Muhittin Çamdalı

Muhittin Çamdalı

Senior iOS Developer

12+ yıllık deneyime sahip iOS Developer. Swift, SwiftUI ve modern iOS mimarileri konusunda uzman. Apple platformlarında performanslı ve kullanıcı dostu uygulamalar geliştiriyorum.

iOS Geliştirme Haberleri

Haftalık Swift tips, SwiftUI tricks ve iOS best practices. Spam yok, sadece değerli içerik.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.

Paylaş

Bunu da begenebilirsiniz