Skip to main content

Search

Items tagged with: iosdev


Day 131. Looking at how to implement the magic tap in #SwiftUI? There is an accessibilityAction(_:_:) with an action kind parameter, you can pass .magicTap, and a closure to handle that action.

A reminder of what the magic tap is: https://iosdev.space/@dadederk/109678705836709212

#365DaysIOSAccessibility

#accessibility #iOSDev

Day 60. VoiceOver has a very cool gesture called the Magic Tap (double tap with two fingers). It should execute the most important task for the current state of the app. Examples: start/stop a timer, play/pause music, take a photo, compose a tweet...

You just need to override accessibilityPerformMagicTap() to capture that gesture, execute the desired code, and return true if handled successfully.

#365DaysIOSAccessibility

https://developer.apple.com/documentation/objectivec/nsobject/1615137-accessibilityperformmagictap

#accessibility #iOSDev #a11y #VoiceOver
The twitter app is open. It doesn't matter where the focus is, if I double tap with two fingers, the compose tweet screen will be presented. Presumably Twitter is overriding the function accessibilityPerformMagicTap and executing the code that presents that screen and then returning true.


Day 129. If you have any web views in your app (Terms and Conditions and Privacy Policy, I'm looking at you), you can also support Dynamic Type for them. You can set an Apple system font with the same styles available for native development in your CSS.

#365DaysIOSAccessibility

#accessibility #iOSDev
Two examples of a web view. One with the default text size and another one much bigger with the AX5 Dynamic Type. To achieve that, you can define the font in the css. Some examples are: -apple-system-body,  -apple-system-headline -apple-system-footnote...


Day 125. You may have noticed that navigation/tool/tab bars don't scale with large #DynamicType sizes. It would take too much space from the screen leaving very little real estate for the actual content. A tap and hold will show the Large Content Viewer.

#365DaysIOSAccessibility

#accessibility #iOSDev
Apple TV app with the AX5 Dynamic Type configuration. The navigation bar and the tab bar look just the same as with the default configuration. A tap and hold to one of the tabs or the title in the navigation bar, though, will show a big square in the middle of the screen with a large icon or/and text previewing what we are touching.


Day 122. Ever wondered what the ideal width is for labels so the text is readable? Well, it depends. But readableContentGuide has you covered. You can configure the optimal width independently of #DynamicType size or Size Classes.

https://developer.apple.com/documentation/uikit/uiview/1622644-readablecontentguide

#365DaysIOSAccessibility

#accessibility #iOSDev
There is an iPad with a title and some text. It has the default text size and the text has fairly large margins on both sides so the text can be read without having to move your head too much. You could fix the width, but then for large sizes, it wouldn't work that well. Instead, you can configure the width anchor of your labels to be equal to the width anchor of the readable content guide. That way, the width of the label will be optimised for the dynamic type text size chosen. Another example shows how margins are smaller for extra-extra-extra large sizes, and for AX5, there are barely any margins and the text goes almost side to side of the iPad.


Day 121. An alternative layout for large font sizes can be provided with #AutoLayout by having three sets of constraints (common, default, and alternative constraints) and activate/deactivate them depending on the content size category.

#365DaysIOSAccessibility

#accessibility #iOSDev


Day 120. If you are displaying something in two or more columns, you may want to change that to as little as one column when using some of the largest #DynamicType settings to keep the text readable.

#365DaysIOSAccessibility

#accessibility #iOSDev
Two examples of the Weather app. The first one uses the Large (default) font. In two columns, it displays information like UV Index, Sunset time, Wind, or Rainfall. The second one uses the largest font size available and the UI switches to one column so there's more room for text and it can go side to side, making it much more readable than if it had just the space of half the screen for such large font sizes.


Day 117. Sometimes, with large font sizes, there's no other way around it but to offer an alternative layout. Small tweaks are often enough. Otherwise, the text will be barely readable. Larger text shouldn't mean less content or a worse experience.

#365DaysIOSAccessibility

#accessibility #iOSDev
Apple music app. It shows a hypothetical case where the app scales text but doesn't change the layout. Only a few letters of the name of the song fit in each cell and there is no room for the name of the artist. In the other one, it shows how it actually works. The cover art comes above the text, instead of to the left, and the name of the song has three lines instead of just one. The code shows a way of doing this. You can override traitCollectionDidChange to get notified if the user changes the dynamic type size. In there, you can check the preferredContentSizeCategory from the trait collection, and it has a property called isAccessibilityCategory. In that case, you can offer an alternative layout.


Day 115. I wish the adjustsFontForContentSizeCategory was true by default. Instead, you need to configure it that way so elements adjust their font size as the content size category (dynamic type) changes when a preferred font is used for a given style.

https://developer.apple.com/documentation/uikit/uicontentsizecategoryadjusting/1771731-adjustsfontforcontentsizecategor

#365DaysIOSAccessibility

#accessibility #iOSDev
Screen with the Cineaste app open. Then, the Text Size menu in Control Centre is opened and the largest font size is selected. If the .adjustFontForContentSizeCateogry property is true for the labels in the app, the app will react and increase the font size immediately without needing to close and open the app again, or pop and push the presented screen. But if it is false, the app would not change at a first glance. You'd have to pop and push the screen or close and open the app again for the new text size to take effect.


Day 116. It is possible to use custom fonts and for them to still work great with Dynamic Type. You need to define the default size and use UIFontMetrics to get the scaled font for each one of the styles you'd like to use.

https://developer.apple.com/documentation/uikit/uifontmetrics/2877385-scaledfont

#365DaysIOSAccessibility

#accessibility #iOSDev
Four labels with the text "Large Title" to exemplify the size for extra small, large (default), extra-extra-extra large, and 5th accessibility size. There is some code showing how to do that with a custom font, in this case: ComicNeue-Regular. It creates a font with the name of the font and the size 34.0 (for the default size). Then it creates a UIFontMetrics for the .largeTitle text style. And finally, it configures the font for the label y calling scaledFont on the font metrics created, and passing the custom font as a parameter.


Day 114. When working with Dynamic Type, I find it useful to remember that sizes for the different text styles won't scale linearly, nor will they do proportionally between them. For larger dynamic type sizes, styles will come closer together in size.

#365DaysIOSAccessibility

#accessibility #iOSDev
Graph showing how the text size changes, in points, for the different text styles, as the dynamic type size changes. It is noticeable how styles get closer and closer to each other as the user picks a larger dynamic text size. Tere is always a hierarchy, but for small sizes, it is much more distinguishible. It seems a good strategy though, larger styles are already quite large and smaller ones will benefit from growing even more in order to become readable on mobile deices.


Day 113. Dynamic Type is a feature that lets a user change the font size (smaller or larger) of the whole system or a particular app. To support it, choose a preferred font based on one of the 11 supported text styles: Large title, Title 1, Title 2, Title 3, Headline, Subhead, Body...

I also recommend checking out @RobW's app as a reference and to easily visualize the different styles for the different scales: https://apps.apple.com/gb/app/dynamic-type/id1644772082

#365DaysIOSAccessibility

#accessibility #iOSDev
Four apps showcasing the available text styles: Large title, title 1, title 2, title 3, headline, subhead, body, callout, caption 1, caption 2, and footnote. Each one of the four apps has a different dynamic type option selected: extra small, large (which is the default), extra extra extra large, and accessibility extra extra extra large. So it is easy to compare how these styles change for different dynamic type options. To support it, you can configure a preferredFont for a label, passing a textStyle.


Day 112. With accessibilityRepresentation(representation:), you can create a custom component and it can be perceived by assistive technologies as the view you pass as representation. No need to manually configure accessibility attributes.

It is one of the most interesting additions to SwiftUI to help you develop accessible UI components. If your custom component behaves similarly to a native one, this is the way to go.

https://developer.apple.com/documentation/swiftui/view/accessibilityrepresentation(representation:)

#365DaysIOSAccessibility

#accessibility #iOSDev
A budgeting planner app with custom sliders to configure categories like food and entertainment. All it takes to make it accessible is to use the .accessibilityRepresentation modifier and pass a native Slider configured accordingly. Example and code from “SwiftUI Accessibility: Beyond the basics” from WWDC 21.


Just a small #tip to fellow app makers - fedi instances are not a billion dollar corp that you can just throw whatever at it. Every request your app makes is a little bit of pressure on someone’s instance that they are probably paying for themselves. So when making API requests, you really need your lean hat on - MVP is not good enough, you need to do better!

#devlife #iosdev #feditips


Day 110. As with UIKit, in SwiftUI you can also add/remove a11y traits. But because of its declarative nature, you'll have to approach it in a slightly different way. A little nuance, but something that made me scratch my UIKit head when learning #SwitUI.

#365DaysIOSAccessibility

#accessibility #iOSDev
Two upsell  to premium buttons. One of them says "Monthly, $2.99 per month, billed monthly". The other one is the same but has the colours inverted and a check icon inside a circle, indicating that the option is selected. In UIKit, you would probably initialise the component with the button trait and somewhere else in the code, you'd check if it is selected and insert the selected trait, or remove it otherwise. With SwitUI, on the other hand, you'd probably use the accessibilityAddTraits modifier and use a ternary condition, if it is selected, you add both the isSelected and isButton traits, if not, just the isButton.


Day 109. The equivalent of using a .semanticGroup accessibilityContainerType in UIKit, would be to use the .accessibilityElement(children: ) modifier with the .contain option in SwiftUI.

Here's a refresher with some use-cases for when you'd like a semantic container to have an accessibility label in UIKit:
https://iosdev.space/@dadederk/109846416935602212

#365DaysIOSAccessibility

#accessibility #iOSDev

Day 88. Here are a few examples where Apple seems to use the semanticGroup accessibilityContainerType, other than for the tab bar and toolbar, to serve for inspiration on when it might be useful in your own apps.

A reminder that this configuration causes VoiceOver to announce the accessibility label of the container view, before what it would normally announce for an element, only when the focus moves from outside to inside the container.

#365DaysIOSAccessibility

#accessibility #iOSDev
Three examples where Apple seems to be configuring the semanticGroup type to the accessibilityContainerType property of a container view in some of its Apps. It causes for VoiceOver to announce the accessibility label of the container, before what it would normally announce for an element, only when the focus moves from outside to inside the container. First example is the Messages app. The bar with the apps is called Apps. Second example is the Reminders app. The editing options bar is called Quick Actions. Third example is the camera app. The bar with extra camera controls that is hidden by default is called Camera Controls.


Day 88. Here are a few examples where Apple seems to use the semanticGroup accessibilityContainerType, other than for the tab bar and toolbar, to serve for inspiration on when it might be useful in your own apps.

A reminder that this configuration causes VoiceOver to announce the accessibility label of the container view, before what it would normally announce for an element, only when the focus moves from outside to inside the container.

#365DaysIOSAccessibility

#accessibility #iOSDev
Three examples where Apple seems to be configuring the semanticGroup type to the accessibilityContainerType property of a container view in some of its Apps. It causes for VoiceOver to announce the accessibility label of the container, before what it would normally announce for an element, only when the focus moves from outside to inside the container. First example is the Messages app. The bar with the apps is called Apps. Second example is the Reminders app. The editing options bar is called Quick Actions. Third example is the camera app. The bar with extra camera controls that is hidden by default is called Camera Controls.


Day 106. Apple asks us to consider the combine behavior, before using ignore, for .accessibilityElement(children: ). And for good reason, if combine works, and later on you decide to change the UI, the accessibility attributes will be updated for you.

#365DaysIOSAccessibility

#accessibility #iOSDev
Two instances of the Stock Analyzer app. The first one shows a row with a criteria i.e. Price-to-earnings, and the value for that criteria i.e. 29.13x. The second one is the same but we've added a rating code represented by a capital letter i.e. C. If we use .accessibilityElement(children: .combine), that code will work even if we update the UI like that. If we use .accessibilityElement() and therefore, we configure an accessibility label manually, we need to remember to update the accessibility label as we update the UI.


Every single App Store refund I get for one of my apps breaks my heart a little. Especially when it's for my Adaptivity app. How can you buy that without realising what features it has and whether you want it or not?

The rate of refunds to purchases seems to be disproportionately higher in Germany for some reason.

#iOSDev #indieDev


Day 103. If you want to know everything about how to "Tailor the #VoiceOver experience in your data-rich apps" with the Accessibility Custom Content API, there is a WWDC21 session.

When implementing accessibilityCustomContent, for any supplementary information, it returns an array that VoiceOver will announce in that given order. The value of the AXCustomContent first, then the label.

https://developer.apple.com/videos/play/wwdc2021/10121/

#365DaysIOSAccessibility

#accessibility #iOSDev


Day 104. Grouping elements in #SwiftUI is extremely easy! You can use the .accessibility(children: .combine) modifier. And that's it! It merges properties. For example, generating an accessibility label by joining the children's ones, separated by commas.

#365DaysIOSAccessibility

#accessibility #iOSDev
Stock Analyzer app in the onboarding screen. There is a row, that by default VoiceOver will treat as four different accessibility elements, needing 4 swipes to navigate it. The labels are: "Price-to-Earnings", "29.13", "X", "Cap C" These components are in an HStack. If you apply the .accessibilityElement(children: ) modifier, passing the .combine argument, groups all the elements together and the new accessibility label will be: "Price-to-Earnings, 29.13, X, Cap C"


Day 101. Too much data can overwhelm users. Very little is an incomplete experience. Finding a balance on verbosity is hard because different #VoiceOver users may have different preferences. To help with this issue, the AXCustomContent APIs let you mark data as optional.

https://developer.apple.com/documentation/accessibility/axcustomcontent

#365DaysIOSAccessibility

#accessibility #iOSDev
Podcast app is showing the episodes for the Empower Apps show. By default, VoiceOver would speak something like this for an episode: The 13th of July. 129. NSSpain X with Luis Ascribe. A very long description. And, 41 minutes. It is so much content! And important information like the duration is buried at the end of it. If AXCustomContent is implemented, and the description is configured as custom content. VoiceOver would read the date, title, and duration. The user can then use the rotor, select More Content, and swipe down. VoiceOver would then announce the description.


Day 100! We've spoken a lot about #VoiceOver in the past 100 days. But, who invented VoiceOver? The answer to this question and other fascinating stories from Mike Shebanek in these amazing episodes of 13 Letters.

Who invented VoiceOver? https://open.spotify.com/episode/54ug7mJjTvtzPjDkqMnRKX?si=8898122325c3428c

Accessibility should be free https://open.spotify.com/episode/3vl7ClBhf8SPc91Wr8bkjp?si=8a308f23d5964029

#365DaysIOSAccessibility

#accessibility #iOSDev
Drawing of a portrait of Mike Shebanek. At the top, there's the cover art for the 13 Letters podcast by Be My Eyes, and the title "Who Invented VoiceOver?"


It's my turn to look for a new job.

iOS / macOS, Swift / SwiftUI
16 years experience programming on Apple's platforms
Remote from 🇫🇷

Feel free to direct message me if you hire or want to recommend something 🙏

#swift #swiftlang #swiftui #iosdev #macdev


Day 87. VoiceOver announces "Tab bar" or "Toolbar", the first time you select an element in one of these components. If you are implementing your custom versions of these, you can mirror this behavior, as seen in previous tweets.

https://iosdev.space/@dadederk/109836856554115238

#365DaysIOSAccessibility

#accessibility #iOSDev

Day 86. Have you noticed that the first time you select an element on Apple Podcast's mini player, VoiceOver says "Mini player" and then describes the selected element? It gives the user more context on what "feature" those elements belong to.

This can be achieved in UIKit by configuring the accessibility container type to be a .semanticGroup and giving it an accessibility label, in this case: "Mini player".

https://developer.apple.com/documentation/uikit/uiaccessibilitycontainertype/semanticgroup

#365DaysIOSAccessibility

#accessibility #iOSDev
Apple podcast app is open. When the user selects the play button in the mini player, VoiceOver will say "Mini Player, Play, Button". Swipe right and VoiceOver will say "Fast-forward, 30 seconds, Button". Swipe left again and VoiceOver will say "Play, button". For achieving that, you can override accessibilityContainerType for the mini player's container view and return .semanticGroup. You need to also override the accessibility label and return the string "Mini Player".

Two examples. The first one shows the Apple TV app. When VoiceOver's focus moves from outside the tab bar to one of the elements in the tab bar, it will prepend the accessibility label of the container view, in this case "Tab bar" to the regular announcement for that element. In this example: "Tab bar, Watch Now, Tab, 1 of 5". Same happens for toolbars. The second example shows the Mail app. When moving the focus from outside the toolbar to the toggle filtering button, it will say: "Toolbar, selected, Toggle Filtering, Button".


Day 86. Have you noticed that the first time you select an element on Apple Podcast's mini player, VoiceOver says "Mini player" and then describes the selected element? It gives the user more context on what "feature" those elements belong to.

This can be achieved in UIKit by configuring the accessibility container type to be a .semanticGroup and giving it an accessibility label, in this case: "Mini player".

https://developer.apple.com/documentation/uikit/uiaccessibilitycontainertype/semanticgroup

#365DaysIOSAccessibility

#accessibility #iOSDev
Apple podcast app is open. When the user selects the play button in the mini player, VoiceOver will say "Mini Player, Play, Button". Swipe right and VoiceOver will say "Fast-forward, 30 seconds, Button". Swipe left again and VoiceOver will say "Play, button". For achieving that, you can override accessibilityContainerType for the mini player's container view and return .semanticGroup. You need to also override the accessibility label and return the string "Mini Player".


Day 74. When presenting a UI component that overlays the existing UI, you may have found that VoiceOver starts to randomly jump between the overlaid UI and the elements underneath. To avoid that, you can set its accessibilityViewIsModal to true.

#365DaysIOSAccessibility

#accessibility #iOSDev
An app for reading blog posts. There is an overlay indicating the user that he has read all the free posts for the month and that it needs to become a premium user for unlimited reading. With the accessibilityViewIsModal set to false, which is the default value, VoiceOver would go from elements in the screen from top-left to bottom-right, when navigating by swiping to the right. The result is as if both views were blended providing a very messy and confusing experience where VoiceOver's focus jumps from elements in the article view, to elements in the overlaying view for converting to premium. If accessibilityViewIsModal is set to true, on the other hand, VoiceOver will only focus on elements on that view, treating it as a modal. That's assuming the post view and the convert to premium view, have the same superview.


Day 75. Even when setting a view as modal for accessibility, you may notice that VoiceOver's focus stays in the same place, instead of moving to the presented view. You can post a screen changed notification and pass the view that should get the focus.

#365DaysIOSAccessibility

#accessibility #iOSDev
Youtube app. VoiceOver focus is on the more options button. The user double taps the screen. The options menu is open, but the focus can remain in the more options button when presenting a modal view in a custom way. For moving the focus to the presented view, you can post a notification to UIAccessibility. The type of the notification is screen changed and the argument, the view you'd like to get the focus, in this case, the draggable view that lets you also close the menu.


Day 67. Custom actions work as great on Switch Control as they do in VoiceOver. It makes navigation much faster and you’ll be able to find all those custom actions in the Switch Control menu.

For those unfamiliar with Switch Control, I recommend watching this video from Todd Stabelfeldt explaining how he uses it at WWDC 2017. I was lucky enough to be there in the audience! I actually recommend watching the whole talk!

https://youtube.com/watch?v=kj9UodcwIes

#365DaysIOSAccessibility

#accessibility #iOSDev
The Twitter app is open. Switch Control is on and the cursor is on top of the first tweet in the timeline. Switch Control's menu is being shown and in there you can find the main actions you'd expect to perform on a tweet: reply, retweet, like, or share, among others. Making navigation and interaction much faster than if we were not grouping tweets as single accessibility elements and moving actions to custom actions.


Day 60. VoiceOver has a very cool gesture called the Magic Tap (double tap with two fingers). It should execute the most important task for the current state of the app. Examples: start/stop a timer, play/pause music, take a photo, compose a tweet...

You just need to override accessibilityPerformMagicTap() to capture that gesture, execute the desired code, and return true if handled successfully.

#365DaysIOSAccessibility

https://developer.apple.com/documentation/objectivec/nsobject/1615137-accessibilityperformmagictap

#accessibility #iOSDev #a11y #VoiceOver
The twitter app is open. It doesn't matter where the focus is, if I double tap with two fingers, the compose tweet screen will be presented. Presumably Twitter is overriding the function accessibilityPerformMagicTap and executing the code that presents that screen and then returning true.


Day 54. Convey important information in multiple modes (sounds, haptics, color, iconography, messaging...) so no one misses it. Take Spotify's shuffle button. It is green when on, and white when off, but it has also a dot indicator. #365DaysIOSAccessibility

https://wearecolorblind.com/examples/spotify-shuffle-and-repeat-buttons/

#accessibility #iOSDev
Two sketches of the Spotify app. One of them has the shuffle button off and the other one is on. For the on state, it has a different colour, green, and it is also adding a dot indicator underneath the icon. It also shows how VoiceOver announces different states, on or off.


Day 42. I'll never recommend creating a custom component if there is a native one that does the job. But if you develop a custom tab bar, .tabBar accessibility trait comes to the rescue. Apply to a container view and its buttons will be announced as tabs. #365DaysIOSAccessibility

I really recommend this article by Bas Broek with other considerations to have when building a custom tab bar. "Building an Accessible Custom Tab Bar": https://www.basbroek.nl/custom-tab-bar-accessibility

#accessibility #iOSDev #VoiceOver
An app that looks like Swarm has a custom tab bar. If the container view of the tab bar has the tabBar accessibility trait and its property isAccessibilityElement is false, its three buttons would be announced as: “selected, personal feed, Tab, 1 of 3”, “check-in, Tab, 2 of 3”, “friends, Tab, 3 of 3”. It indicates they’re tabs and their position in the custom tab bar.