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.

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.
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.
// 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.
// 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.
// 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.
// 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.
// 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.
// 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.
iOS mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
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.
// 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.
// 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.
// 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.
// 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.
iOS mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
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.
// 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.
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.
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.
// 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.
// 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.
Pratik yapmaya başla!
Mülakat simülatörleri ve teknik testlerle bilgini test et.
Etiketler
Paylaş
İlgili makaleler

Swift'te Combine vs async/await: Aşamalı Geçiş Desenleri
Swift'te Combine'dan async/await'e geçiş için kapsamlı kılavuz: aşamalı stratejiler, köprüleme desenleri ve iOS kod tabanlarında paradigma birlikte yaşamı.

Swift Macros: pratik metaprogramlama örnekleri
Swift Macros için kapsamlı rehber: freestanding ve attached makro oluşturma, swift-syntax ile AST manipülasyonu ve tekrar eden kodu ortadan kaldıran pratik örnekler.

StoreKit 2 Mülakatı: Abonelik Yönetimi ve Makbuz Doğrulama
StoreKit 2, abonelik yönetimi, makbuz doğrulama ve uygulama içi satın alma uygulaması hakkında iOS mülakat sorularında pratik Swift kod örnekleriyle uzmanlaşın.