# iOS-toegankelijkheidsvragen voor sollicitaties in 2026: VoiceOver en Dynamic Type > Bereid je voor op iOS-sollicitaties met essentiële toegankelijkheidsvragen: VoiceOver, Dynamic Type, semantische traits en audits. - Published: 2026-03-12 - Updated: 2026-05-01 - Author: SharpSkill - Tags: ios, accessibility, voiceover, dynamic-type, swift - Reading time: 14 min --- Toegankelijkheid is een onmisbare vaardigheid geworden in iOS-ontwikkeling. Recruiters beoordelen tegenwoordig systematisch de beheersing van VoiceOver, Dynamic Type en de toegankelijkheids-API's. Deze vragen behandelen de essentiële concepten om in technische sollicitaties te slagen. > **Waarom toegankelijkheid telt in sollicitaties** > > Meer dan een miljard mensen leven met een beperking. Apple hanteert strenge toegankelijkheidsnormen en veel bedrijven publiceren geen ontoegankelijke apps. Deze competentie onderscheidt seniorkandidaten van de rest. ## Basis van iOS-toegankelijkheid ### 1. Wat zijn de belangrijkste toegankelijkheidstools van iOS? iOS biedt een compleet ecosysteem aan toegankelijkheidstools voor verschillende soorten beperkingen. VoiceOver maakt schermlezing mogelijk voor blinden en slechtzienden. Dynamic Type past de tekstgrootte aan. Switch Control biedt bediening via externe schakelaars. Voice Control levert volledige spraakbediening. ```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() } ``` Deze API's maken een dynamische aanpassing van de interface mogelijk op basis van gebruikersvoorkeuren en actieve hulptechnologieën. ### 2. Hoe werkt VoiceOver technisch? VoiceOver doorloopt de interface en identificeert toegankelijke elementen. Elk element legt eigenschappen bloot zoals label, value, traits en acties. De schermlezer synthetiseert deze informatie vocaal terwijl de gebruiker met gebaren navigeert. ```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") } } ``` De toegankelijkheidshiërarchie kan afwijken van de visuele hiërarchie, wat logische navigatie ook bij complexe lay-outs mogelijk maakt. ### 3. Wat is het verschil tussen accessibilityLabel en accessibilityValue? De `accessibilityLabel` identificeert het element permanent, terwijl `accessibilityValue` de huidige, veranderlijke status weergeeft. Dat onderscheid is cruciaal voor dynamische besturingselementen zoals sliders of switches. ```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 kondigt altijd het label aan gevolgd door de waarde, zodat gebruikers begrijpen wat het element is en wat de huidige status is. ### 4. Leg accessibilityTraits en hun belang uit Traits informeren VoiceOver over het type en gedrag van een element. Ze bepalen hoe het element wordt aangekondigd en welke acties beschikbaar zijn. Een knop zonder de trait `.button` wordt niet als zodanig aangekondigd. ```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 zorgen ervoor dat hulptechnologieën een consistente en voorspelbare ervaring leveren. ## Dynamic Type en aanpasbare tekstgroottes ### 5. Hoe implementeer je Dynamic Type correct? Met Dynamic Type kan de gebruiker de systeemtekstgrootte aanpassen. Een correcte implementatie maakt gebruik van semantische tekststijlen en flexibele constraints, zodat de interface zich automatisch aanpast. ```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) } } ``` Zonder `adjustsFontForContentSizeCategory` behouden labels hun oorspronkelijke grootte, zelfs als de gebruiker de voorkeuren wijzigt. ### 6. Hoe ga je om met extreme toegankelijkheidsmaten? De toegankelijkheidsmaten (AX1 tot AX5) kunnen de tekstgrootte verdrievoudigen. De interface moet zich aanpassen met alternatieve lay-outs: verticaal stapelen in plaats van horizontaal, decoratieve elementen verbergen en de tussenruimte aanpassen. ```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 } } } ``` De toegankelijkheidscategorieën beginnen bij `.accessibilityMedium` (AX1). Het ontwerp moet vanaf het begin rekening houden met deze gevallen. ### 7. Hoe maak je afbeeldingen toegankelijk? Afbeeldingen vragen om verschillende behandeling afhankelijk van hun rol: informatieve afbeeldingen hebben een beschrijving nodig, decoratieve worden genegeerd en interactieve vereisen actiegerichte labels. ```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 { } } } ``` Een slecht beschreven afbeelding of een niet-verborgen decoratieve afbeelding maakt de VoiceOver-ervaring aanzienlijk slechter. ## Groeperen en geavanceerde navigatie ### 8. Hoe groepeer je elementen voor VoiceOver? Groeperen combineert meerdere visuele elementen tot één toegankelijk element. Dat versnelt de navigatie en geeft volledige context in één aankondiging. ```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 { } } } ``` Zonder groepering stopt VoiceOver bij elke afzonderlijke Text, wat de navigatie aanzienlijk vertraagt. ### 9. Hoe implementeer je aangepaste toegankelijkheidsacties? Aangepaste acties voegen toegankelijke functionaliteit toe zonder de visuele interface te overladen. VoiceOver-gebruikers benaderen ze via een verticale veegbeweging. ```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 } } ``` Aangepaste acties vervangen op voordelige wijze swipe-gebaren die met VoiceOver moeilijk te ontdekken zijn. ### 10. Hoe beheer je de VoiceOver-focus programmatisch? De focus beheren is essentieel na wijzigingen in de interface: verschijnen van een modal, content laden, navigatie. De focus moet de gebruiker naar de relevante informatie leiden. ```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" ) } } ``` De meldingen `.screenChanged` en `.layoutChanged` informeren VoiceOver over significante wijzigingen in de interface. ## Aanpasbare elementen en complexe besturingselementen ### 11. Hoe maak je een aangepaste slider toegankelijk? Een aangepaste slider moet de trait `.adjustable` implementeren en reageren op verhogings-/verlagingsbewegingen. VoiceOver gebruikt verticaal vegen om de waarde te wijzigen. ```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) } } ``` Zonder de trait `.adjustable` zou een aangepaste slider de gebruiker dwingen elke ster afzonderlijk aan te tikken. ### 12. Hoe maak je een toegankelijke carrousel? Carrousels brengen toegankelijkheidsuitdagingen met zich mee: niet-lineaire navigatie, content buiten beeld en meerdere statussen. De oplossing combineert de trait `.adjustable` met contextuele aankondigingen. ```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) } } ``` Deze aanpak maakt navigatie door de carrousel mogelijk zonder afhankelijk te zijn van standaard veegbewegingen. ### 13. Hoe ga je om met de toegankelijkheid van formulieren? Toegankelijke formulieren koppelen elk veld aan zijn label, geven fouten duidelijk weer en maken vlotte navigatie tussen velden mogelijk. ```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" } } ``` Toegankelijkheidshints communiceren fouten en instructies zonder de visuele interface te overladen. ## Toegankelijkheidsaudits en tests ### 14. Hoe audit je de toegankelijkheid van een iOS-app? Een audit combineert geautomatiseerde tools en handmatige tests. De Accessibility Inspector van Xcode analyseert elementen, VoiceOver valideert de echte ervaring en unit-tests verifiëren toegankelijkheidseigenschappen. ```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() } } ``` De automatische audit op iOS 17+ detecteert veelvoorkomende problemen: ontbrekende labels, onvoldoende contrast, te kleine elementen. ### 15. Wat zijn de meest voorkomende toegankelijkheidsfouten? Veelvoorkomende fouten zijn ontbrekende labels, niet-beschreven afbeeldingen, gebrek aan Dynamic Type-ondersteuning en interactieve elementen zonder gepaste traits. ```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) ``` Deze fouten zijn opspoorbaar met de Accessibility Inspector en oplosbaar met enkele regels code. ## Conclusie iOS-toegankelijkheid steunt op drie pijlers: VoiceOver voor blinde en slechtziende gebruikers, Dynamic Type voor de aanpassing van tekst en semantische traits voor consistente navigatie. Het beheersen van deze concepten toont professionele expertise die werkgevers actief zoeken. ### Controlelijst - VoiceOver configureren met passende labels, hints en traits - Dynamic Type implementeren met semantische tekststijlen - Lay-outs aanpassen voor extreme toegankelijkheidsmaten - Elementen groeperen voor efficiënte navigatie - Focus programmatisch beheren na wijzigingen in de interface - Aangepaste besturingselementen aanpasbaar maken - Regelmatig auditen met de Accessibility Inspector en geautomatiseerde tests - Veelvoorkomende fouten vermijden: ontbrekende labels, onbeschreven afbeeldingen, onvoldoende contrast ### Aanvullende bronnen De officiële Apple-documentatie "Human Interface Guidelines - Accessibility" blijft de referentie. Regelmatig testen met VoiceOver ingeschakeld helpt problemen te vinden die geautomatiseerde tools niet detecteren. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/nl/blog/ios/ios-accessibility-interview-voiceover-dynamic-type