# Preguntas de entrevista sobre accesibilidad iOS en 2026: VoiceOver y Dynamic Type > Prepárate para entrevistas iOS con preguntas clave de accesibilidad: VoiceOver, Dynamic Type, traits semánticos y auditorías. - Published: 2026-03-12 - Updated: 2026-05-01 - Author: SharpSkill - Tags: ios, accessibility, voiceover, dynamic-type, swift - Reading time: 14 min --- La accesibilidad se ha vuelto una habilidad crítica en el desarrollo iOS. Los reclutadores ahora evalúan sistemáticamente el dominio de VoiceOver, Dynamic Type y las APIs de accesibilidad. Estas preguntas cubren los conceptos esenciales para tener éxito en entrevistas técnicas. > **Por qué la accesibilidad importa en las entrevistas** > > Más de mil millones de personas viven con alguna discapacidad. Apple impone estándares estrictos de accesibilidad y muchas empresas se niegan a publicar apps inaccesibles. Esta competencia diferencia a los candidatos senior del resto. ## Fundamentos de accesibilidad iOS ### 1. ¿Cuáles son las principales herramientas de accesibilidad de iOS? iOS ofrece un ecosistema completo de herramientas de accesibilidad orientadas a distintos tipos de discapacidad. VoiceOver permite la lectura de pantalla para personas con discapacidad visual. Dynamic Type adapta el tamaño del texto. Switch Control habilita el control mediante interruptores externos. Voice Control ofrece comandos de voz completos. ```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() } ``` Estas APIs permiten adaptar la interfaz dinámicamente según las preferencias del usuario y las tecnologías de asistencia activas. ### 2. ¿Cómo funciona VoiceOver técnicamente? VoiceOver recorre la interfaz identificando los elementos accesibles. Cada elemento expone propiedades como su label, value, traits y acciones. El lector de pantalla sintetiza vocalmente esa información a medida que el usuario navega con gestos. ```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") } } ``` La jerarquía de accesibilidad puede diferir de la jerarquía visual, lo que permite una navegación lógica incluso con layouts complejos. ### 3. ¿Cuál es la diferencia entre accessibilityLabel y accessibilityValue? El `accessibilityLabel` identifica permanentemente al elemento, mientras que `accessibilityValue` representa su estado actual, que puede cambiar. Esta distinción resulta crucial para controles dinámicos como sliders o 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 siempre anuncia el label seguido del value, permitiendo al usuario entender qué es el elemento y cuál es su estado actual. ### 4. Explica accessibilityTraits y su importancia Los traits informan a VoiceOver sobre el tipo y comportamiento de un elemento. Determinan cómo se anuncia el elemento y qué acciones están disponibles. Un botón sin el trait `.button` no será anunciado como tal. ```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 : []) } } ``` Los traits permiten que las tecnologías de asistencia ofrezcan una experiencia coherente y predecible para los usuarios. ## Dynamic Type y tamaños de texto adaptables ### 5. ¿Cómo se implementa Dynamic Type correctamente? Dynamic Type permite al usuario ajustar el tamaño del texto del sistema. Una implementación correcta utiliza estilos de texto semánticos y restricciones flexibles para que la interfaz se adapte automáticamente. ```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) } } ``` Sin `adjustsFontForContentSizeCategory`, los labels conservan su tamaño inicial aunque el usuario cambie sus preferencias. ### 6. ¿Cómo se gestionan los tamaños de accesibilidad extremos? Los tamaños de accesibilidad (AX1 a AX5) pueden triplicar el tamaño del texto. La interfaz debe adaptarse con layouts alternativos: apilar verticalmente en vez de horizontalmente, ocultar elementos decorativos y ajustar el espaciado. ```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 } } } ``` Las categorías de accesibilidad comienzan en `.accessibilityMedium` (AX1). El diseño debe anticipar estos casos desde el principio. ### 7. ¿Cómo se hacen accesibles las imágenes? Las imágenes requieren tratamiento distinto según su rol: las informativas necesitan descripción, las decorativas deben ignorarse y las interactivas requieren etiquetas de acción. ```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 { } } } ``` Una imagen mal descrita o una decorativa sin enmascarar degrada significativamente la experiencia con VoiceOver. ## Agrupación y navegación avanzada ### 8. ¿Cómo se agrupan elementos para VoiceOver? La agrupación combina varios elementos visuales en uno solo accesible. Esto acelera la navegación y aporta el contexto completo en un único anuncio. ```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 { } } } ``` Sin agrupación, VoiceOver se detiene en cada Text individualmente, ralentizando notablemente la navegación. ### 9. ¿Cómo se implementan acciones de accesibilidad personalizadas? Las acciones personalizadas añaden funcionalidad accesible sin saturar la interfaz visual. Los usuarios de VoiceOver acceden a ellas con un swipe vertical. ```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 } } ``` Las acciones personalizadas reemplazan ventajosamente a los gestos de swipe, difíciles de descubrir con VoiceOver. ### 10. ¿Cómo se gestiona el foco de VoiceOver de forma programática? Controlar el foco resulta esencial tras cambios en la interfaz: aparición de un modal, carga de contenido, navegación. El foco debe guiar al usuario hacia la información relevante. ```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" ) } } ``` Las notificaciones `.screenChanged` y `.layoutChanged` informan a VoiceOver sobre cambios significativos en la interfaz. ## Elementos ajustables y controles complejos ### 11. ¿Cómo se hace accesible un slider personalizado? Un slider personalizado debe implementar el trait `.adjustable` y responder a gestos de incremento/decremento. VoiceOver utiliza el swipe vertical para modificar el valor. ```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) } } ``` Sin el trait `.adjustable`, un slider personalizado obligaría a tocar cada estrella individualmente. ### 12. ¿Cómo se crea un carrusel accesible? Los carruseles plantean retos de accesibilidad: navegación no lineal, contenido fuera de pantalla y múltiples estados. La solución combina el trait `.adjustable` con anuncios contextuales. ```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) } } ``` Este enfoque permite navegar el carrusel sin depender de los gestos estándar de swipe. ### 13. ¿Cómo se gestiona la accesibilidad de los formularios? Los formularios accesibles asocian cada campo con su etiqueta, indican errores con claridad y permiten una navegación fluida entre campos. ```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" } } ``` Las hints de accesibilidad comunican errores e instrucciones sin saturar la interfaz visual. ## Auditorías y pruebas de accesibilidad ### 14. ¿Cómo se audita la accesibilidad de una app iOS? Una auditoría combina herramientas automáticas y pruebas manuales. El Accessibility Inspector de Xcode analiza los elementos, VoiceOver valida la experiencia real y los tests unitarios verifican las propiedades de accesibilidad. ```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() } } ``` La auditoría automática de iOS 17+ detecta problemas comunes: labels faltantes, contraste insuficiente, elementos demasiado pequeños. ### 15. ¿Cuáles son los errores de accesibilidad más comunes? Los errores frecuentes incluyen labels ausentes, imágenes sin descripción, falta de soporte de Dynamic Type y elementos interactivos sin traits adecuados. ```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) ``` Estos errores son detectables con el Accessibility Inspector y se corrigen con pocas líneas de código. ## Conclusión La accesibilidad iOS se sostiene sobre tres pilares: VoiceOver para personas con discapacidad visual, Dynamic Type para la adaptación del texto y los traits semánticos para una navegación coherente. Dominar estos conceptos demuestra una experiencia profesional muy demandada por las empresas. ### Lista de repaso - Configurar VoiceOver con labels, hints y traits adecuados - Implementar Dynamic Type con estilos de texto semánticos - Adaptar los layouts para tamaños de accesibilidad extremos - Agrupar elementos para una navegación eficiente - Gestionar el foco de forma programática tras cambios en la interfaz - Hacer ajustables los controles personalizados - Auditar regularmente con el Accessibility Inspector y tests automáticos - Evitar errores comunes: labels ausentes, imágenes sin descripción, contraste insuficiente ### Recursos adicionales La documentación oficial de Apple "Human Interface Guidelines - Accessibility" sigue siendo la referencia. Probar regularmente con VoiceOver activado ayuda a detectar problemas que las herramientas automáticas no pueden identificar. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/es/blog/ios/ios-accessibility-interview-voiceover-dynamic-type