---
title: 'Interface Design Guidelines'
source: 'https://academia.sh/en/courses/ios-platform-and-tools/interface-design-guidelines'
course: 'iOS Platform and Tools'
language: en
updated: '2026-10-21T05:03:10+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Explains that interface design guidelines set expectations and do not enforce them'
    - "Measures that the user's text size takes precedence over the app default"
    - 'Measures that the orientations the app supports intersect with those the device offers'
    - 'Measures that system colors and app colors merge'
---

# Interface Design Guidelines

Measuring that the user's text size takes precedence over the app default, that screen orientations intersect, and that colors merge.

In the previous lesson the scene handler took precedence over the app handler. In this lesson the same resolution brings together two new parties: the app's default text size and the user's setting. The question does not change: **how are these two definitions resolved when they meet?** Three meetings will be measured: precedence, intersection, and union.

Interface design guidelines are the expectations the platform sets for an interface: the size of a touch target, the readability of text, the consistency of colors in light and dark appearance, and the orientation of the screen. These guidelines are not a compiler check; an app that does not follow them still builds and runs. A guideline sets an expectation, and the platform enforces a part of these expectations through settings. The measurements in this lesson do not measure the guideline itself; they measure the meeting of the three settings the guideline relies on.

```swift
// Meeting.swift — classifies how two definitions are resolved when they meet
struct Meeting {
    let label: String
    let first: Set<String>
    let second: Set<String>
    let result: Set<String>?

    init(_ label: String, first: Set<String>, second: Set<String>, result: Set<String>?) {
        self.label = label
        self.first = first
        self.second = second
        self.result = result
    }

    var resolution: String {
        if first.isEmpty || second.isEmpty
            || first.isSubset(of: second) || second.isSubset(of: first) {
            return "out-of-scope"
        }
        guard let result, !result.isEmpty else { return "rejection" }
        if result == first.union(second) { return "union" }
        if result == first.intersection(second) { return "intersection" }
        if result == first || result == second { return "precedence" }
        return "off-rule"
    }

    static func list(_ values: Set<String>) -> String {
        values.isEmpty ? "empty" : values.sorted().joined(separator: ",")
    }

    func report() {
        let outcome = result.map(Meeting.list) ?? "none"
        print("\(label): first \(Meeting.list(first)) | second \(Meeting.list(second)) | result \(outcome) -> \(resolution)")
    }
}

func summarize(_ meetings: [Meeting]) {
    for resolution in ["precedence", "intersection", "union", "rejection"] {
        let count = meetings.filter { $0.resolution == resolution }.count
        print("\(resolution): \(count)")
    }
}
```

## Dynamic Type

The user chooses a preferred text size in the system settings. Dynamic Type is how this preference reaches the app: instead of writing a fixed point size, the app asks for a text style, and the platform applies the user's size to the style. For interface accessibility this means that a user with low vision can read the app at their own size.

The piece below depends on a platform framework, so it stands as source and no output is claimed for it.

```swift
import SwiftUI

struct SummaryHeader: View {
    var body: some View {
        Text("Meeting summary")
            .font(.body)
    }
}
```

`.font(.body)` does not write a fixed size; it asks for a style. The real point size is determined by the user's setting; the model builds this rule of the platform.

**PS13.** The numbers below are model values: they do not show the real point size of a real style. Because `Meeting` works with sets, the sizes are carried as strings.

The app's default is 16, and the size the user chose is 22. The resolution gives the one the user chose.

```swift
// main.swift — the user's text size takes precedence over the app default
let appDefault = "16"
let userChoice = "22"

let size = Meeting("text-size",
                   first: [appDefault],
                   second: [userChoice],
                   result: [userChoice])
size.report()
```

```
text-size: first 16 | second 22 | result 22 -> precedence
```

The app's 16 dropped out and the user's 22 remained. This is a precedence in which, because the setting belongs to the user, the user's word and not the app's carries. Instead of breaking this precedence, the app builds its interface for growing text: lines get longer and the layout moves downward.

## Screen Orientation

Screen orientation is the set of directions in which the interface can be rotated. The app declares the orientations it supports; the device declares the orientations it offers. The orientations it can turn to are the common part of the two. The property list fragment below declares the app side.

```xml
<key>UISupportedInterfaceOrientations</key>
<array>
    <string>UIInterfaceOrientationPortrait</string>
    <string>UIInterfaceOrientationLandscapeLeft</string>
</array>
```

The helper below gives the common orientations of two sets. The file has no output.

```swift
// Orientations.swift — the screen orientations shared by the app and the device
func sharedOrientations(app: Set<String>, device: Set<String>) -> Set<String> {
    app.intersection(device)
}
```

The app supports portrait, upside-down portrait, and left landscape; the device offers portrait, right landscape, and left landscape. Neither set is a subset of the other.

```swift
// main.swift — screen orientations intersect
let appSide: Set = ["portrait", "portrait-upside-down", "landscape-left"]
let deviceSide: Set = ["portrait", "landscape-right", "landscape-left"]

let turns = Meeting("orientation-support",
                    first: appSide,
                    second: deviceSide,
                    result: sharedOrientations(app: appSide, device: deviceSide))
turns.report()
```

```
orientation-support: first landscape-left,portrait,portrait-upside-down | second landscape-left,landscape-right,portrait | result landscape-left,portrait -> intersection
```

`portrait-upside-down` existed only in the app and `landscape-right` only on the device; both dropped out. **PS14.** The orientation names are model names, and these sets do not represent the real orientations of a real device. This measurement shares its resolution with the background modes of the previous lesson: neither side hands over its own value, and only what is common is left.

## Color Palette

The system interface offers its own named colors, and the app adds its own colors. The system colors, which take separate values in light and dark appearance, are ready at hand for the app; the app also defines its own brand colors. The palette that can be used is the sum of the two. The helper below merges the names of the two palettes.

```swift
// Palette.swift — merges the system and app color names
func mergedColorNames(system: [String: String], app: [String: String]) -> Set<String> {
    Set(system.keys).union(app.keys)
}
```

```swift
// main.swift — system and app colors merge
let systemColors = ["background": "system", "text": "system", "accent": "system"]
let appColors = ["brand": "app", "accent": "app"]

let palette = Meeting("color-palette",
                      first: Set(systemColors.keys),
                      second: Set(appColors.keys),
                      result: mergedColorNames(system: systemColors, app: appColors))
palette.report()
```

```
color-palette: first accent,background,text | second accent,brand | result accent,background,brand,text -> union
```

Four names came from two sources. `accent` is present on both sides and appears once in the union. This measurement counts only the union of names; which one is valid when the same name carries different values on the two sides is a separate precedence question and was not measured here.

## The Expectation of Platform Consistency

The basic expectation of the interface design guidelines is consistency: if an app follows the patterns the platform has made familiar, the user uses it without learning it. The back gesture is in the same place and has the same form in every app; the share button carries the same icon; a list row is swiped in the same way. If an app breaks these patterns, the user has to learn a rule specific to that app. Consistency is not a prohibition but a calculation of cost: an app that breaks a pattern takes the user's learning burden upon itself.

The link of this expectation to the settings is this: the platform patterns are written into the system components. When the app uses a system component, it receives automatically the pattern the guideline calls for, Dynamic Type, and the appearance (light or dark). When the app draws its own component, it has to provide each of these behaviors itself. The precedence and union measurements in the earlier sections are both a result of this division of work: the owner of the setting is the user, and the component follows it.

## Accessibility

Interface accessibility is not limited to text size. For a user who uses a screen reader, every control must have a name and a role: an icon button that is only an image means the reader cannot say anything about it. For a user with color blindness, information must not be given by color alone; the state should also be indicated with an icon or text. For a user who chooses to reduce motion, transition animations should be simplified. For a user who chooses dark appearance, colors must be readable on both light and dark backgrounds; for this reason system colors are not fixed colors but names that change with the appearance.

None of these is a requirement for the app; the compiler does not check them. But all of them are settings that belong to the user, and a setting takes precedence over the app's default. The precedence measured in Dynamic Type is only one example of this general rule: whatever the user's setting is, the app must be prepared to meet it.

## Navigation and the Touch Target

Navigation is how the user moves between the sections of an app. The guideline recognizes a few patterns: a hierarchy at the top that can be navigated back through, a tab bar at the bottom that switches between equivalent sections, and a sheet that opens from the top for a temporary task. The pattern must fit the structure of the content: equivalent sections belong in the tab bar, and content that proceeds step by step belongs in a hierarchy. A pattern that does not fit the structure makes the user lose direction.

A touch target is the area the user hits with a finger. The guideline expects this area to be wide enough for a finger to hit comfortably: a small icon, even if it looks small, should provide a sufficient area together with the space around it. Space should also be left between two adjacent targets, otherwise the user touches the wrong one. The touch target grows together with the text size: if the user has enlarged the text, the targets also need to grow; otherwise the growing text becomes unreadable in a cramped layout.

All of these topics say the same thing: a guideline is an expectation and not a class of resolution. This is also the reason a result such as 20 will be marked `off-rule` in the bounding measurement at the end of the lesson; unless the guideline says "suitable", the measurement cannot assign a class.

## Light and Dark Appearance

The platform offers two appearances: light and dark. The user chooses one or lets the system change it during the day. The app must follow this choice, because the choice is the user's setting. An app that writes a fixed color code can become unreadable on a dark background, while an app that uses system color names automatically receives a value that changes with the appearance. The `background` and `text` names in the color palette measurement matter for this reason: the name is fixed, and the value behind it changes with the appearance.

The app's own colors must also be defined separately for the two appearances. A brand color must be dark enough on a light background and light enough on a dark background, otherwise the contrast between text and background falls and readability is lost. Contrast is not only about beauty but about accessibility: text with low contrast cannot be read by a user with weak eyesight.

## Safe Area and Adaptive Layout

The safe area is the part of the screen where content can be placed safely. The rounded corners of the device, the region where the camera enters the screen, and the system bars can cover part of the content; the platform defines the safe area outside these regions. Text and controls should be placed inside the safe area. A background color or image, on the other hand, can extend outside the safe area to the edge of the screen, because being covered causes no problem.

Adaptive layout is a layout adapting itself to the size and orientation of the screen, such as one column on a narrow screen and two columns on a wide one. Here there is a link to the screen orientation measurement: the intersection of the orientations the app supports and the device offers says in which orientations the layout must work. If only portrait remains in the intersection, there is no need to write a landscape layout; if the intersection is wide, a layout must be tested for each orientation. So the intersection is also a work list.

## Departing from the Guideline

Because a guideline is an expectation, departing from it is possible, but the departure must have a reason. A game or a drawing app may not follow the standard navigation patterns, because the content itself demands a different form of interaction. A reading app, on the other hand, should not break the standard patterns, because nothing is gained in return for breaking them. The cost of departing is the user's learning burden; if there is a gain that justifies this burden, departing is reasonable.

But some expectations are not open to departure: following the user's text size setting, the accessibility settings, and the choice of appearance. These are settings that belong to the user, and the app cannot treat them as void. This is what the precedence measurement shows: the user's setting comes before the app's default; the app may depart from the guidelines, but it cannot depart against the user's setting.

## The Bounding Measurement

A guideline is an expectation, not a class of resolution. For this reason a result that does not follow the guideline is not counted as one of the four resolutions. In the measurement below, the app says 16 and the user says 22; the result comes out as 20. This is neither the app's value nor the user's value; it is a value in between.

```swift
// main.swift — an off-rule result does not fall into one of the four
let clamped = Meeting("clamped-size",
                      first: ["16"],
                      second: ["22"],
                      result: ["20"])
let inside = Meeting("within-bounds",
                     first: ["16"],
                     second: ["22"],
                     result: ["22"])
clamped.report()
inside.report()
```

```
clamped-size: first 16 | second 22 | result 20 -> off-rule
within-bounds: first 16 | second 22 | result 22 -> precedence
```

20 did not fall into any of the four resolutions and became `off-rule`. A guideline saying "limit the text size" does not mean that this resolution is added to the four classes; if the result is a value that is not the output of the four operations on sets, the measurement does not classify it and marks it as it is.

**PS15.** This measurement is only for 16, 22, and 20 with model values. Whether a real system actually produces an intermediate value was not measured.

## Summary

- Interface design guidelines are the expectations the platform sets for an interface; they are not a compiler check.
- In Dynamic Type the user's setting takes precedence over the app's default: the output is precedence.
- The screen orientations the app supports and those the device offers intersect; only the common orientations remain.
- System colors and app colors merge; a shared name appears once.
- An off-rule result is not counted as one of the four resolutions and is marked separately.
- The resolution of this lesson: **precedence**.

## Next Step

In the earlier lessons the parties that met were settings. In the next lesson the parties that meet are version ranges: the versions the app supports and the versions in which an interface exists intersect, and when they are disjoint the result is rejection.
