Jordan Morgan Profile picture
Jul 10, 2020 29 tweets 11 min read Read on X
💡Tip 11 💡

Many users invert iOS' colors to help with light & color sensitivities. But, ensure you aren't degrading rich media in your app, which users likely expect to remain at their true fidelity.

For images & media, you'll likely want them to forgo this setting. Image
💡Tip 11 Continued 💡

This requires almost zero effort to support - simply set accessibilityIgnoresInvertColors to false (which also will do the same thing for subviews of that view): Image
💡Tip 12 💡

Since iOS 13, users can enable Voice Control which allows one to navigate iOS by calling out prompts on screen.

By default, iOS vends your accessibilityLabel and in some cases, this might be too verbose. Instead, leverage the accessibilityUserInputLabel array. Image
💡Tip 12 Continued 💡

To tackle this, go through your app with Voice Control on and see which parts are too verbose to speak out.

Then, assign something that makes a bit more sense to the control's accessibilityUserInputLabels property: Image
💡Tip 13 💡

Here's a remix of our very first tip, since with iOS 13 UICollectionView now supports a list style.

As with a table view, you'll want to animate a selection when popping and pushing on the navigation stack:
💡Tip 13 Continued 💡

The good news is that the technique is extremely similar to how it works with a table view. For my projects, I use an extension to achieve this: Image
💡Tip 14 💡

When using the iPadOS hover pointer effect, ensure it looks correct and doesn't expand in unexpected ways.

This can happen, for example, in scenarios where your view's bounds expands out larger than its visible contents. Image
💡Tip 14 Continued 💡

To fix this, utilize UIPreviewParameters's visiblePath property to customize the effect's look.

For the example above, I calculate the size of the actual text and apply some padding, and then center it within the control itself. Image
💡Tip 15💡

Sometimes it's contextually relevant for Voice Over to read punctuation literally. For example, when viewing a code sample snippet. You can do this using an attributed string and the accessibilitySpeechPunctuation key.

Here is what happens with and without it: Voice over reading out punc...
💡Tip 15 Continued💡

Instead of assigning to the label's text property directly, here we create an attributed string that contains the accessibilitySpeechPunctuation key with an NSNumber with a true boolean value (i.e. 1).

Then, we assign to the attributedText of the label. Image
💡Tip 16💡

Take the time to evaluate which content pointer effect is appropriate for controls in your app.

These effects change the pointer's shape or augment the views it interacts with, so knowing which fit where makes your app feel at home.

Here's a general guide: iPadOS Pointer Effects Guide
💡Tip 16 Continued💡

The code for this changes depending on the content effect you are after. In broad strokes, you'd use one of these within the pointer interaction delegate method that asks for the UIPointerStyle. Image
💡Tip 17💡

In general, avoid truncation on interactive controls. If you can't expand views, consider variable width strings.

These use a .stringsdict lookup to vend a string based on width. Here, we use "Templates" instead of "Choose from Templates" when the width is tighter. Example of variable width s...
💡Tip 17 Continued💡

If you use NSLocalizedString() along with UIKit controls, they'll choose the right one for you based on the presentation width measured in EMs.

If you want to vend one of these to some other component, varirantFittingPresentationWidth() is here. Code sample of variable wid...
💡Tip 17 Continued💡

Following the example above, here is what our .stringsdict entry might look like.

The key NSStringVariableWidthRuleType has a dictionary of EM values, which correspond to the string you want used in such an environment. .plist entry for variable w...
💡Tip 18 💡

Moving on to view controller considerations. Ensure you're using the correct UIStatusBarStyle for modally presented controllers.

For example, don't use a dark status bar for a dark interface, which will obscure it:
💡Tip 18 Continued 💡

There are a number of properties that control the status bar, but the most common one is preferredStatusBarStyle.

If you find a scenario where the status bar appears hidden and it shouldn't be due to your chrome, return the correct value here: Status bar API example
💡Tip 19💡

It can be easy to get tripped up when implementing common accessibility practices.

Accessibility label, value and hint? When do you use which?

The graphic below demonstrates this: Graphic showing how differe...
💡Tip 19 Continued💡

Thankfully, UIKit and SwiftUI do a lot of this for you. And SwiftUI has modifiers equivalent to the UIKit counterparts.

One easy way to think of these:

label: What is this?
value: What value does this have?
hint: What will this do? Accessibility code sample
💡Tip 20 💡

When using Voice Over, iOS travels according to the locale. So if you're grouping UI, be sure to make tweaks so Voice Over reads them in the right order.

On the left, the labels are read left to right. On the right, the grouped labels are read together logically. Example of accessibility el...
💡Tip 20 Continued 💡

To mitigate issues like this, disable the labels as an accessibility element. Then, using UIAccessibilityElement you can group them together as one unit for Voice Over. Set the label and tell Voice Over where it's at using frames. Example of using UIAccessib...
💡Tip 21💡

Table and collection views can enter into an editing state for quick selection via a two-finger drag down.

Opting into this behavior is good practice (barring any context specific reason to opt-out) as Apple's stock apps support it too.

Here's how it looks:
💡Tip 21 Continued 💡

To opt in, ensure your table or collection view has its allowsMultipleSelectionDuringEditing flag set, and implement the two delegate methods below.

Catalyst apps also get a boost as this bridges over when a user selects multiple rows with a modifier key. Code to implement two finge...
💡Tip 22💡

Almost every app has data coming in over the wire, so try to opt to load it in asynchronously in terms of your architecture and, more importantly, your user interface.

Don’t toss up a modal or something similar to block them from continuing: A user interface example sh...
💡Tip 22 Continued💡

How this looks implementation wise will vary, but for a quick example here is how I did this in my own work.

By using a Combine Future, you can easily handle updating your interface when a new result comes in. But remember, this tip is more UX focused. A code sample showing how t...
💡Tip 23💡

Sometimes an interface my have dense information like this table cell, which makes navigation hard for Voice Over.

Here, we only expose the title and favorite rating and let the user get the rest of the data if they want by using the AXCustomContentProvider protocol. An example showing how to u...
💡Tip 23 Continued 💡

If the user selects "More Content" using the rotor control, now the rest of the data included here will be used for Voice Over.

To give the user this kind of choice, adopt AXCustomContentProvider and return an array of AXCustomContent: Code sample using AXCustomC...
💡Tip 24💡

When supporting drag & drop in collection or table views, customize the drag preview to match your app’s aesthetic. For example, you might round the corners of the drag view.

Here, the left side takes the cell's look by default while the right rounds all the corners. Example of a cell being dru...
💡Tip 24 Continued💡

To create one, leverage the previewProvider closure on UIDragItem. There, you can return a UIDragPreviewParameters instance with a UIBezierPath describing the look you're after.

Here, the cell's contentView is inset and used for the path. Code sample of UIDragPrevie...

• • •

Missing some Tweet in this thread? You can try to force a refresh
 

Keep Current with Jordan Morgan

Jordan Morgan Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

PDF

Twitter may remove this content at anytime! Save it as PDF for later use!

Try unrolling a thread yourself!

how to unroll video
  1. Follow @ThreadReaderApp to mention us!

  2. From a Twitter thread mention us with a keyword "unroll"
@threadreaderapp unroll

Practice here first or read more on our help page!

More from @JordanMorgan10

Jul 19, 2021
Day 1:
One thing you can do to make your iOS app better today 📱

1️⃣ Activate Voice Over
2️⃣ Now, turn on screen curtain (3 finger triple tap)
3️⃣ Try to navigate your app and look for places to improve VoiceOver navigation
🍻Day 2🍻

1️⃣ Hitting performance issues and not sure where to start?
2️⃣ Use os_signpost and .pointsOfInterest category to help benchmark your code
3️⃣ Then, use instruments to dial in on what the problems might be or where to start tuning your code
📱Day 3📱

1️⃣ Enable malloc stack logging for your scheme and run the Allocations template in Instruments
2️⃣ Open a few highly trafficked view controllers
and close them
3️⃣ Search for them in Instruments and make
sure they've deallocated
Read 48 tweets
May 30, 2020
🎨 iOS Design and UX Tips 🎨

I’ll be extrapolating out things I’ve learned, read from the HIG and have seen that make an iOS app look and feel good.

Tip 1) Make a table view row animate its selection state as it’s popping and pushing on the nav Stack.
Tip 1 Continued:

UITableViewControllers get this behavior for free. If you roll your own table view you’ll need to do it yourself. You can do so by grabbing the controller’s transition coordinator as seen in this (gasp!) Objective-C code here called in viewWillAppear 💫 Image
💡Tip 2 💡

If you've got text whose primary function is long-form reading, make it easy to parse it by using the view's readableContentGuide. Image
Read 46 tweets
Jan 19, 2019
For the past 2+ years I’ve been building my side project using #CloudKit and I’d love to share what I’ve learned.

There isn’t a ton of info out there on it, and I’ve got some tips that I’m not sure I’ll get around to blogging about so I’ll add them here in a thread 👇
First off - subscriptions. There are three main types:
CKDatabaseSubscription
CKRecordZoneSubscription
CKQuerySubscription

Unless you’re *only* using the default zone, you should start with CKDatabaseSubscription.
The reason is it vends custom zone changes and more importantly changes from a shared database. RecordZoneSub can’t do this, neither can a query sub.

If you know you’re for sure not supporting sharing, you can get away with a recordZoneSub *mostly*.
Read 25 tweets
Aug 1, 2018
A fun bit of #macOS development history I stumbled upon today, Apple Tech Note 2034.

Essentially it included a bunch of guidelines and tips on macOS development but contained such inflammatory assertions that Apple straight up *pulled* it down (2033/5 are still there)!
Among some of the the things that didn't sit very well with devs in the note:

- Positing Objective-C as being a more portable cross-platform language than C++
- Promoting hard-coded pathname APIs for accessing files over traditional Mac APIs
Reading over it, quite a bit of the punch is lost on me since I started in this industry in a professional sense in 2012. But the same conversations happen today - Objective-C vs Swift being a similar parallel in that it garners hot opinions, fast.
Read 5 tweets

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3/month or $30/year) and get exclusive features!

Become Premium

Don't want to be a Premium member but still want to support us?

Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal

Or Donate anonymously using crypto!

Ethereum

0xfe58350B80634f60Fa6Dc149a72b4DFbc17D341E copy

Bitcoin

3ATGMxNzCUFzxpMCHL5sWSt4DVtS8UqXpi copy

Thank you for your support!

Follow Us!

:(