# 2026'da iOS Erişilebilirlik Mülakat Soruları: VoiceOver ve Dynamic Type > iOS mülakatlarına hazırlık için kritik erişilebilirlik soruları: VoiceOver, Dynamic Type, semantik trait'ler ve denetimler. - Published: 2026-03-12 - Updated: 2026-05-01 - Author: SharpSkill - Tags: ios, accessibility, voiceover, dynamic-type, swift - Reading time: 14 min --- Erişilebilirlik, iOS geliştirmede kritik bir beceri haline geldi. İşe alımcılar artık VoiceOver, Dynamic Type ve erişilebilirlik API'lerine hâkimiyeti sistematik olarak ölçüyor. Bu sorular, teknik mülakatlarda başarılı olmak için temel kavramları kapsıyor. > **Erişilebilirlik mülakatlarda neden önemlidir** > > 1 milyardan fazla insan engelli olarak yaşıyor. Apple sıkı erişilebilirlik standartlarını uyguluyor ve birçok şirket erişilemeyen uygulamaları yayınlamayı reddediyor. Bu yetkinlik, kıdemli adayları diğerlerinden ayırır. ## iOS Erişilebilirliğinin Temelleri ### 1. iOS'un başlıca erişilebilirlik araçları nelerdir? iOS, farklı engellilik türlerini hedefleyen kapsamlı bir erişilebilirlik araç ekosistemi sunar. VoiceOver, görme engelli kullanıcılar için ekran okumayı sağlar. Dynamic Type metin boyutunu ayarlar. Switch Control harici anahtarlarla kontrole olanak tanır. Voice Control ise tam sesli komutları sunar. ```swift // AccessibilityTools.swift // Check if VoiceOver is running if UIAccessibility.isVoiceOverRunning { // Adapt the interface for VoiceOver showSimplifiedInterface() } // Check if Reduce Motion is enabled if UIAccessibility.isReduceMotionEnabled { // Disable complex animations animationDuration = 0 } // Check if Bold Text is enabled if UIAccessibility.isBoldTextEnabled { // Use heavier fonts applyBoldFonts() } // Observe settings changes NotificationCenter.default.addObserver( forName: UIAccessibility.voiceOverStatusDidChangeNotification, object: nil, queue: .main ) { _ in // React to the change updateAccessibilityLayout() } ``` Bu API'ler, kullanıcı tercihlerine ve aktif yardımcı teknolojilere göre arayüzü dinamik olarak uyarlamayı mümkün kılar. ### 2. VoiceOver teknik olarak nasıl çalışır? VoiceOver, arayüzü gezerek erişilebilir öğeleri tanımlar. Her öğe label, value, traits ve eylemler gibi özellikleri açığa çıkarır. Ekran okuyucu, kullanıcı hareketlerle gezinirken bu bilgileri sesli olarak sentezler. ```swift // VoiceOverBasics.swift // In UIKit: configure an accessible element class CustomButton: UIButton { override var accessibilityLabel: String? { get { "Validation button" } // What VoiceOver reads set { } } override var accessibilityHint: String? { get { "Double-tap to confirm the order" } // Possible action set { } } override var accessibilityTraits: UIAccessibilityTraits { get { .button } // Element type set { } } override var isAccessibilityElement: Bool { get { true } // Element focusable by VoiceOver set { } } } // In SwiftUI: equivalent with modifiers struct ValidateButton: View { var body: some View { Button("Validate") { confirmOrder() } .accessibilityLabel("Validation button") .accessibilityHint("Double-tap to confirm the order") } } ``` Erişilebilirlik hiyerarşisi görsel hiyerarşiden farklılaşabilir; bu da karmaşık düzenlerde bile mantıklı bir gezinmeyi olanaklı kılar. ### 3. accessibilityLabel ile accessibilityValue arasındaki fark nedir? `accessibilityLabel`, öğeyi kalıcı olarak tanımlar; `accessibilityValue` ise değişebilen mevcut durumunu temsil eder. Bu ayrım, slider veya switch gibi dinamik kontrol öğeleri için kritiktir. ```swift // LabelVsValue.swift // Example with a volume slider class VolumeSlider: UISlider { override var accessibilityLabel: String? { get { "Volume" } // Fixed element identity set { } } override var accessibilityValue: String? { get { "\(Int(value * 100)) percent" } // Current state that changes set { } } } // VoiceOver reads: "Volume, 75 percent" // In SwiftUI with a Toggle struct NotificationToggle: View { @Binding var isEnabled: Bool var body: some View { Toggle(isOn: $isEnabled) { Text("Notifications") } .accessibilityLabel("Push notifications") // accessibilityValue is automatic for Toggle: "on" or "off" } } // For a custom stepper struct QuantityStepper: View { @State private var quantity = 1 var body: some View { Stepper("Quantity", value: $quantity, in: 1...99) .accessibilityLabel("Item quantity") .accessibilityValue("\(quantity) item\(quantity > 1 ? "s" : "")") } } ``` VoiceOver her zaman label'ı ardından value'yu seslendirir; böylece kullanıcı hem öğenin ne olduğunu hem de mevcut durumunu kavrar. ### 4. accessibilityTraits'i ve önemini açıklayın Traits, VoiceOver'a bir öğenin türü ve davranışı hakkında bilgi verir. Öğenin nasıl seslendirileceğini ve hangi eylemlerin uygulanabileceğini belirler. `.button` trait'i olmayan bir buton, buton olarak duyurulmaz. ```swift // AccessibilityTraits.swift // Common traits in UIKit class CustomCell: UITableViewCell { override var accessibilityTraits: UIAccessibilityTraits { get { var traits: UIAccessibilityTraits = [] // Element type traits.insert(.button) // Interactive element // Current state if isSelected { traits.insert(.selected) // Currently selected } // Special characteristics if !isEnabled { traits.insert(.notEnabled) // Disabled } return traits } set { } } } // Available traits // .button - Clickable element // .link - Hyperlink // .header - Section header // .image - Image // .selected - Selected state // .notEnabled - Disabled // .adjustable - Adjustable value (slider) // .staticText - Non-interactive text // .searchField - Search field // .playsSound - Plays a sound // .startsMediaSession - Starts media playback // In SwiftUI struct SectionHeader: View { let title: String var body: some View { Text(title) .font(.headline) .accessibilityAddTraits(.isHeader) // Announced as header } } struct SelectableRow: View { let isSelected: Bool var body: some View { HStack { Text("Option") if isSelected { Image(systemName: "checkmark") } } .accessibilityElement(children: .combine) .accessibilityAddTraits(isSelected ? .isSelected : []) } } ``` Traits, yardımcı teknolojilerin kullanıcılara tutarlı ve öngörülebilir bir deneyim sunmasını sağlar. ## Dynamic Type ve Uyarlanabilir Metin Boyutları ### 5. Dynamic Type doğru biçimde nasıl uygulanır? Dynamic Type, kullanıcının sistem metin boyutunu ayarlamasına olanak tanır. Doğru bir uygulama, semantik metin stillerini ve esnek kısıtlamaları kullanır; böylece arayüz otomatik olarak uyum sağlar. ```swift // DynamicType.swift // In UIKit: use preferred text styles class ArticleCell: UITableViewCell { let titleLabel = UILabel() let bodyLabel = UILabel() func configure() { // Use semantic text styles titleLabel.font = UIFont.preferredFont(forTextStyle: .headline) bodyLabel.font = UIFont.preferredFont(forTextStyle: .body) // CRUCIAL: enable automatic adjustment titleLabel.adjustsFontForContentSizeCategory = true bodyLabel.adjustsFontForContentSizeCategory = true // Allow multiline for large sizes titleLabel.numberOfLines = 0 bodyLabel.numberOfLines = 0 } } // In SwiftUI: it's automatic with styles struct ArticleView: View { var body: some View { VStack(alignment: .leading) { Text("Article Title") .font(.headline) // Adapts automatically Text("Article content...") .font(.body) } } } // Custom font with Dynamic Type extension UIFont { static func customFont( size: CGFloat, style: TextStyle ) -> UIFont { let customFont = UIFont(name: "CustomFont-Regular", size: size)! // Adapt to current size category return UIFontMetrics(forTextStyle: style) .scaledFont(for: customFont) } } ``` `adjustsFontForContentSizeCategory` olmadan, kullanıcı tercihlerini değiştirse bile etiketler ilk boyutlarını korur. ### 6. Aşırı erişilebilirlik boyutları nasıl ele alınır? Erişilebilirlik boyutları (AX1'den AX5'e) metin boyutunu üç katına çıkarabilir. Arayüz, alternatif düzenlerle uyum sağlamalıdır: yatay yerine dikey istiflemek, dekoratif öğeleri gizlemek ve aralıkları ayarlamak. ```swift // LargeContentSize.swift // Detect accessibility sizes struct AdaptiveLayout: View { @Environment(\.dynamicTypeSize) var dynamicTypeSize var body: some View { // Different layout for large sizes if dynamicTypeSize.isAccessibilitySize { // Vertical layout for large sizes VStack(alignment: .leading, spacing: 12) { iconAndTitle actionButtons } } else { // Standard horizontal layout HStack { iconAndTitle Spacer() actionButtons } } } var iconAndTitle: some View { HStack { Image(systemName: "bell.fill") .accessibilityHidden(true) // Decorative, ignore Text("Notifications") } } var actionButtons: some View { HStack(spacing: 16) { Button("Enable") { } Button("Settings") { } } } } // In UIKit: observe changes class AdaptiveViewController: UIViewController { override func traitCollectionDidChange( _ previousTraitCollection: UITraitCollection? ) { super.traitCollectionDidChange(previousTraitCollection) // Check if size category changed if traitCollection.preferredContentSizeCategory != previousTraitCollection?.preferredContentSizeCategory { updateLayoutForContentSize() } } func updateLayoutForContentSize() { let category = traitCollection.preferredContentSizeCategory if category.isAccessibilityCategory { // Enable accessible layout stackView.axis = .vertical decorativeView.isHidden = true } else { // Standard layout stackView.axis = .horizontal decorativeView.isHidden = false } } } ``` Erişilebilirlik kategorileri `.accessibilityMedium` (AX1) ile başlar. Tasarım bu durumları en başından öngörmelidir. ### 7. Görseller nasıl erişilebilir kılınır? Görseller rollerine göre farklı muamele gerektirir: bilgilendirici olanlar açıklama ister, dekoratif olanlar yok sayılmalıdır, etkileşimli olanlar ise eylem etiketleri gerektirir. ```swift // AccessibleImages.swift // Informative image: describe the content struct ProductImage: View { let product: Product var body: some View { AsyncImage(url: product.imageURL) { image in image .resizable() .accessibilityLabel(product.imageDescription) // "Product photo: iPhone 15 Pro, titanium color" } placeholder: { ProgressView() .accessibilityLabel("Loading image") } } } // Decorative image: hide from VoiceOver struct DecorationView: View { var body: some View { Image(systemName: "sparkles") .accessibilityHidden(true) // Ignored by VoiceOver } } // Image button: describe the action, not the image struct FavoriteButton: View { @Binding var isFavorite: Bool var body: some View { Button { isFavorite.toggle() } label: { Image(systemName: isFavorite ? "heart.fill" : "heart") } // Don't describe the icon, describe the action .accessibilityLabel(isFavorite ? "Remove from favorites" : "Add to favorites") } } // In UIKit class ProductImageView: UIImageView { override var isAccessibilityElement: Bool { get { true } set { } } func configure(with product: Product) { image = product.image accessibilityLabel = "Photo: \(product.name), \(product.color)" } } // Image with embedded text (infographic) class InfographicView: UIImageView { override var accessibilityLabel: String? { get { // Transcribe the textual content of the infographic """ Statistics infographic 2026. Growth: 45%. Active users: 2.3 million. Satisfaction: 4.8 out of 5. """ } set { } } } ``` Kötü açıklanmış bir görsel ya da gizlenmemiş dekoratif bir görsel, VoiceOver deneyimini önemli ölçüde bozar. ## Gruplama ve Gelişmiş Gezinme ### 8. VoiceOver için öğeler nasıl gruplanır? Gruplama, birden fazla görsel öğeyi tek bir erişilebilir öğeye birleştirir. Bu, gezinmeyi hızlandırır ve tek bir duyuruda tam bağlam sağlar. ```swift // AccessibilityGrouping.swift // In SwiftUI: combine children struct ProductCard: View { let product: Product var body: some View { VStack(alignment: .leading) { Text(product.name) .font(.headline) Text(product.price) .font(.subheadline) HStack { Image(systemName: "star.fill") Text(product.rating) } } // Combines all text into a single element .accessibilityElement(children: .combine) // VoiceOver reads: "iPhone 15 Pro, $1199, 4.8 stars" } } // In SwiftUI: ignore children and define manually struct OrderSummary: View { let order: Order var body: some View { HStack { Image(systemName: "bag") VStack(alignment: .leading) { Text(order.itemCount) Text(order.total) } Image(systemName: "chevron.right") } // Ignore children hierarchy .accessibilityElement(children: .ignore) // Define custom label .accessibilityLabel("Order of \(order.itemCount) items for \(order.total)") .accessibilityHint("Double-tap to view details") .accessibilityAddTraits(.isButton) } } // In UIKit: accessible container class ProductCardView: UIView { let nameLabel = UILabel() let priceLabel = UILabel() let ratingLabel = UILabel() override var isAccessibilityElement: Bool { get { true } // The container is the accessible element set { } } override var accessibilityLabel: String? { get { // Combine information "\(nameLabel.text ?? ""), \(priceLabel.text ?? ""), \(ratingLabel.text ?? "")" } set { } } } ``` Gruplama olmadan VoiceOver her bir Text üzerinde tek tek durarak gezinmeyi belirgin biçimde yavaşlatır. ### 9. Özel erişilebilirlik eylemleri nasıl uygulanır? Özel eylemler, görsel arayüzü kalabalıklaştırmadan erişilebilir işlevler ekler. VoiceOver kullanıcıları bunlara dikey kaydırma ile erişir. ```swift // CustomAccessibilityActions.swift // In SwiftUI: custom actions struct EmailRow: View { let email: Email let onArchive: () -> Void let onDelete: () -> Void let onFlag: () -> Void var body: some View { HStack { VStack(alignment: .leading) { Text(email.sender) .font(.headline) Text(email.subject) .font(.subheadline) } Spacer() if email.isFlagged { Image(systemName: "flag.fill") } } .accessibilityElement(children: .combine) .accessibilityLabel("\(email.sender), \(email.subject)") // Actions accessible via vertical swipe .accessibilityAction(named: "Archive") { onArchive() } .accessibilityAction(named: "Delete") { onDelete() } .accessibilityAction(named: email.isFlagged ? "Remove flag" : "Flag") { onFlag() } } } // In UIKit: override accessibilityCustomActions class EmailCell: UITableViewCell { var email: Email! var onArchive: (() -> Void)? var onDelete: (() -> Void)? override var accessibilityCustomActions: [UIAccessibilityCustomAction]? { get { [ UIAccessibilityCustomAction( name: "Archive", target: self, selector: #selector(archiveAction) ), UIAccessibilityCustomAction( name: "Delete", target: self, selector: #selector(deleteAction) ) ] } set { } } @objc private func archiveAction() -> Bool { onArchive?() return true // true = action succeeded } @objc private func deleteAction() -> Bool { onDelete?() return true } } ``` Özel eylemler, VoiceOver ile keşfedilmesi güç olan kaydırma hareketlerinin yerine etkili biçimde geçer. ### 10. VoiceOver odağı programlı olarak nasıl yönetilir? Arayüzdeki değişikliklerden sonra odağı kontrol etmek kritik öneme sahiptir: modal görünmesi, içerik yüklenmesi, gezinme. Odak, kullanıcıyı ilgili bilgiye yönlendirmelidir. ```swift // AccessibilityFocus.swift // In SwiftUI with @AccessibilityFocusState (iOS 15+) struct SearchView: View { @State private var searchText = "" @State private var results: [Result] = [] @AccessibilityFocusState private var focusedResult: Result? var body: some View { VStack { TextField("Search", text: $searchText) .onSubmit { performSearch() } List(results) { result in ResultRow(result: result) .accessibilityFocused($focusedResult, equals: result) } } } func performSearch() { Task { results = await api.search(searchText) // Move focus to first result if let first = results.first { focusedResult = first } } } } // In UIKit: post a notification class ModalViewController: UIViewController { @IBOutlet weak var titleLabel: UILabel! override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) // Announce and move focus UIAccessibility.post( notification: .screenChanged, // New screen argument: titleLabel // Element to focus ) } func showError(_ message: String) { errorLabel.text = message errorLabel.isHidden = false // Announce layout change UIAccessibility.post( notification: .layoutChanged, // Change in current screen argument: errorLabel ) } func showLoadingComplete() { // Announce without moving focus UIAccessibility.post( notification: .announcement, argument: "Loading complete" ) } } ``` `.screenChanged` ve `.layoutChanged` bildirimleri, VoiceOver'a arayüzdeki önemli değişiklikleri haber verir. ## Ayarlanabilir Öğeler ve Karmaşık Kontroller ### 11. Özel bir slider nasıl erişilebilir hale getirilir? Özel bir slider, `.adjustable` trait'ini uygulamalı ve artırma/azaltma hareketlerine yanıt vermelidir. VoiceOver, değeri değiştirmek için dikey kaydırma kullanır. ```swift // AccessibleSlider.swift // In SwiftUI: Slider is accessible by default struct VolumeControl: View { @Binding var volume: Double var body: some View { Slider(value: $volume, in: 0...100, step: 5) .accessibilityLabel("Volume") .accessibilityValue("\(Int(volume)) percent") } } // Custom accessible slider struct CustomRatingSlider: View { @Binding var rating: Int var body: some View { HStack { ForEach(1...5, id: \.self) { star in Image(systemName: star <= rating ? "star.fill" : "star") .onTapGesture { rating = star } } } .accessibilityElement(children: .ignore) .accessibilityLabel("Rating") .accessibilityValue("\(rating) star\(rating > 1 ? "s" : "") out of 5") .accessibilityAdjustableAction { direction in switch direction { case .increment: rating = min(5, rating + 1) case .decrement: rating = max(1, rating - 1) @unknown default: break } } } } // In UIKit: adjustable trait class StarRatingView: UIView { var rating: Int = 3 { didSet { updateStars() // Announce the new value UIAccessibility.post( notification: .announcement, argument: accessibilityValue ) } } override var accessibilityTraits: UIAccessibilityTraits { get { .adjustable } // Enables adjustment gestures set { } } override var accessibilityLabel: String? { get { "Rating" } set { } } override var accessibilityValue: String? { get { "\(rating) stars out of 5" } set { } } // Called on swipe up override func accessibilityIncrement() { rating = min(5, rating + 1) } // Called on swipe down override func accessibilityDecrement() { rating = max(1, rating - 1) } } ``` `.adjustable` trait'i olmadan özel bir slider, kullanıcının her yıldıza ayrı ayrı dokunmasını gerektirirdi. ### 12. Erişilebilir bir karusel nasıl oluşturulur? Karuseller erişilebilirlik açısından zorluklar barındırır: doğrusal olmayan gezinme, ekran dışı içerik ve birden çok durum. Çözüm, `.adjustable` trait'i ile bağlamsal duyuruları birleştirmektir. ```swift // AccessibleCarousel.swift struct ImageCarousel: View { let images: [CarouselImage] @State private var currentIndex = 0 var body: some View { TabView(selection: $currentIndex) { ForEach(images.indices, id: \.self) { index in CarouselItem(image: images[index]) .tag(index) } } .tabViewStyle(.page) .accessibilityElement(children: .contain) .accessibilityLabel("Image carousel") .accessibilityValue("Image \(currentIndex + 1) of \(images.count)") .accessibilityHint("Swipe to change image") .accessibilityAdjustableAction { direction in withAnimation { switch direction { case .increment: currentIndex = min(images.count - 1, currentIndex + 1) case .decrement: currentIndex = max(0, currentIndex - 1) @unknown default: break } } } } } // In UIKit with UIPageViewController class CarouselAccessibilityContainer: UIView { var currentPage = 0 var totalPages = 5 override var isAccessibilityElement: Bool { get { true } set { } } override var accessibilityTraits: UIAccessibilityTraits { get { .adjustable } set { } } override var accessibilityLabel: String? { get { "Promotional carousel" } set { } } override var accessibilityValue: String? { get { "Page \(currentPage + 1) of \(totalPages)" } set { } } override func accessibilityIncrement() { guard currentPage < totalPages - 1 else { return } currentPage += 1 delegate?.scrollToPage(currentPage) } override func accessibilityDecrement() { guard currentPage > 0 else { return } currentPage -= 1 delegate?.scrollToPage(currentPage) } } ``` Bu yaklaşım, standart kaydırma hareketlerine bağlı kalmadan karusel içinde gezinmeyi sağlar. ### 13. Form erişilebilirliği nasıl yönetilir? Erişilebilir formlar her alanı etiketiyle ilişkilendirir, hataları net biçimde belirtir ve alanlar arasında akıcı gezinmeye olanak tanır. ```swift // AccessibleForms.swift struct RegistrationForm: View { @State private var email = "" @State private var password = "" @State private var emailError: String? @State private var passwordError: String? var body: some View { Form { Section { TextField("Email", text: $email) .textContentType(.emailAddress) .keyboardType(.emailAddress) .accessibilityLabel("Email address") .accessibilityHint(emailError != nil ? "Error: \(emailError!)" : nil) if let error = emailError { Text(error) .foregroundColor(.red) .font(.caption) .accessibilityLabel("Email error: \(error)") } } Section { SecureField("Password", text: $password) .textContentType(.newPassword) .accessibilityLabel("Password") .accessibilityHint(passwordHint) PasswordStrengthIndicator(password: password) } Button("Create account") { submitForm() } .disabled(!isFormValid) .accessibilityHint(isFormValid ? "Double-tap to create your account" : "Form incomplete") } } var passwordHint: String { if let error = passwordError { return "Error: \(error)" } return "Minimum 8 characters with uppercase and numbers" } } // Accessible password strength indicator struct PasswordStrengthIndicator: View { let password: String var strength: PasswordStrength { // Calculation logic PasswordStrength.calculate(password) } var body: some View { HStack(spacing: 4) { ForEach(0..<4) { index in Rectangle() .fill(index < strength.level ? strength.color : Color.gray.opacity(0.3)) .frame(height: 4) } } .accessibilityElement(children: .ignore) .accessibilityLabel("Password strength") .accessibilityValue(strength.description) // "Weak", "Medium", "Strong", "Very strong" } } ``` Erişilebilirlik ipuçları, görsel arayüzü kalabalıklaştırmadan hata ve talimatları iletir. ## Erişilebilirlik Denetimleri ve Testler ### 14. iOS uygulamasının erişilebilirliği nasıl denetlenir? Denetim, otomatik araçları ve manuel testleri birleştirir. Xcode'un Accessibility Inspector'ı öğeleri analiz eder, VoiceOver gerçek deneyimi doğrular ve birim testleri erişilebilirlik özelliklerini kontrol eder. ```swift // AccessibilityAudit.swift // Unit test accessibility properties import XCTest @testable import MyApp class AccessibilityTests: XCTestCase { func testProductCardAccessibility() { let product = Product(name: "iPhone", price: "$999", rating: "4.5") let view = ProductCardView(product: product) // Verify element is accessible XCTAssertTrue(view.isAccessibilityElement) // Verify label XCTAssertEqual( view.accessibilityLabel, "iPhone, $999, 4.5 stars" ) // Verify traits XCTAssertTrue(view.accessibilityTraits.contains(.button)) } func testDynamicTypeSupport() { let label = UILabel() label.font = UIFont.preferredFont(forTextStyle: .body) // Verify automatic adjustment XCTAssertTrue(label.adjustsFontForContentSizeCategory) } } // UI test with simulated VoiceOver class AccessibilityUITests: XCTestCase { func testLoginFlowAccessibility() { let app = XCUIApplication() app.launch() // Access elements by accessibility label let emailField = app.textFields["Email address"] XCTAssertTrue(emailField.exists) emailField.tap() emailField.typeText("test@example.com") let passwordField = app.secureTextFields["Password"] XCTAssertTrue(passwordField.exists) let loginButton = app.buttons["Sign in"] XCTAssertTrue(loginButton.exists) XCTAssertTrue(loginButton.isEnabled) } // Automatic audit iOS 17+ func testAccessibilityAudit() throws { let app = XCUIApplication() app.launch() // Automatic screen audit try app.performAccessibilityAudit() } } ``` iOS 17+ otomatik denetimi, eksik etiketler, yetersiz kontrast, fazla küçük öğeler gibi yaygın sorunları tespit eder. ### 15. En sık karşılaşılan erişilebilirlik hataları nelerdir? Sık görülen hatalar arasında eksik etiketler, açıklanmamış görseller, Dynamic Type desteğinin bulunmaması ve uygun trait'i olmayan etkileşimli öğeler yer alır. ```swift // CommonMistakes.swift // WRONG: Button without accessible label Button { performAction() } label: { Image(systemName: "plus") } // VoiceOver reads: "Button" - useless // CORRECT: Explicit label Button { performAction() } label: { Image(systemName: "plus") } .accessibilityLabel("Add item") // WRONG: Fixed font size Text("Important") .font(.system(size: 16)) // Doesn't respect Dynamic Type // CORRECT: Semantic text style Text("Important") .font(.body) // Adapts to preferences // WRONG: Informative image without description Image("product-photo") // CORRECT: Content description Image("product-photo") .accessibilityLabel("Product photo: Black audio headphones") // WRONG: Clickable element without button trait Text("Learn more") .onTapGesture { showDetails() } // VoiceOver doesn't know it's interactive // CORRECT: Appropriate traits Text("Learn more") .onTapGesture { showDetails() } .accessibilityAddTraits(.isButton) .accessibilityHint("Double-tap to show details") // WRONG: Insufficient contrast Text("Light gray text") .foregroundColor(.gray.opacity(0.4)) // CORRECT: Minimum contrast ratio 4.5:1 Text("Readable text") .foregroundColor(.secondary) // Respects guidelines // WRONG: Touch target too small Button { } label: { Image(systemName: "xmark") .font(.caption) // Too small (< 44x44 points) } // CORRECT: Minimum 44x44 point area Button { } label: { Image(systemName: "xmark") } .frame(minWidth: 44, minHeight: 44) ``` Bu hatalar Accessibility Inspector ile saptanabilir ve birkaç satır kodla giderilir. ## Sonuç iOS erişilebirliği üç temel sütuna dayanır: görme engelli kullanıcılar için VoiceOver, metin uyarlanması için Dynamic Type ve tutarlı gezinme için semantik traits. Bu kavramlara hâkim olmak, işverenlerin aktif olarak aradığı profesyonel uzmanlığı kanıtlar. ### Kontrol Listesi - VoiceOver'ı uygun label, hint ve traits ile yapılandırın - Dynamic Type'ı semantik metin stilleriyle uygulayın - Aşırı erişilebilirlik boyutlarına yönelik düzenlemeler ekleyin - Verimli gezinme için öğeleri gruplayın - Arayüz değişikliklerinden sonra odağı programlı olarak yönetin - Özel kontrolleri ayarlanabilir hale getirin - Accessibility Inspector ve otomatik testlerle düzenli denetim yapın - Yaygın hatalardan kaçının: eksik etiketler, açıklanmamış görseller, yetersiz kontrast ### Ek Kaynaklar Apple'ın resmi "Human Interface Guidelines - Accessibility" belgeleri başvuru kaynağı olmayı sürdürür. VoiceOver etkinken düzenli test yapmak, otomatik araçların bulamadığı sorunları tespit etmeye yardımcı olur. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/tr/blog/ios/ios-accessibility-interview-voiceover-dynamic-type