Why Accessibility Is Not Just a Checkbox, But a Superpower for Your App
\n\nImagine: you've created a cool app. A beautiful interface, lightning-fast animations, perfect Swift code. You're proud of it. But suddenly it turns out that 15% of users can't use it. For them, the buttons are too small, the text blends into the background, and VoiceOver "reads" gibberish instead of the menu. Sound familiar?
\n\nAccessibility is not about "mandatory requirements" or "doing it for the sake of a checkbox." It's about inclusivity, about care, and, importantly, about business. iOS has a powerful framework that allows you to adapt your app to the needs of people with visual, hearing, motor, and cognitive impairments. And Swift gives us all the tools to do this elegantly.
\n\nIn this article, we won't give boring lectures. We'll break down specific code examples that you can immediately implement in your project. Let's go!
\n\nVoiceOver: How to "Teach" Your App to Describe Itself
\n\nVoiceOver is a screen reader built into iOS. It "speaks" everything that happens on the screen: text, buttons, gestures. But if the developer hasn't prepared the interface, VoiceOver can turn into chaos. Let's fix that.
\n\nBasic Setup: accessibilityLabel and accessibilityTraits
\n\nThe simplest way to help VoiceOver is to set labels and types for elements. For example, you have a button with a heart icon. Without a label, VoiceOver will just say "button," but with a label, it will say "Add to Favorites."
\n\nlet favoriteButton = UIButton(type: .system)\nfavoriteButton.setImage(UIImage(systemName: "heart"), for: .normal)\nfavoriteButton.accessibilityLabel = "Add to Favorites"\nfavoriteButton.accessibilityTraits = .button\n\nNote the accessibilityTraits. This is a hint for VoiceOver on how to interact with the element. For a button — .button, for an image — .image, for a heading — .header. Use .selected, .notEnabled, and others to accurately convey the state.
Grouping Elements: How to Prevent VoiceOver from "Chattering" the User
\n\nSometimes it makes sense to combine several elements into one logical block. For example, a product card: image, name, price. If VoiceOver reads them separately, the user will get tired. The solution is UIAccessibilityElement or shouldGroupAccessibilityChildren.
cardView.shouldGroupAccessibilityChildren = true\ncardView.accessibilityLabel = "Nike Air Sneakers, 12,990 rubles"\ncardView.accessibilityTraits = .button\n\nNow VoiceOver will read the entire card as one element. Brief, concise, clear.
\n\nCustom Actions: When You Need More Than Just a "Tap"
\n\nThere are complex elements: sliders, switches, swipeable lists. For these, it's important to add custom actions. For example, for a cell with "Delete" and "Edit" functions:
\n\nlet deleteAction = UIAccessibilityCustomAction(\n name: "Delete",\n target: self,\n selector: #selector(deleteItem)\n)\nlet editAction = UIAccessibilityCustomAction(\n name: "Edit",\n target: self,\n selector: #selector(editItem)\n)\ncell.accessibilityCustomActions = [deleteAction, editAction]\n\nA VoiceOver user swipes down or up to select an action. This significantly improves the experience.
\n\nDynamic Type: Text That Adapts to Everyone
\n\nPeople with low vision often increase the font size in iOS settings. If your app doesn't support Dynamic Type, the text will be cut off, overlap, or simply become unreadable. How to avoid this?
\n\nUsing Dynamic Font Styles
\n\nInstead of a hardcoded UIFont.systemFont(ofSize: 16), use UIFont.preferredFont(forTextStyle: .body).
label.font = UIFont.preferredFont(forText