← Todos os Posts

SF Pro: eixos variáveis, dimensionamento óptico e o contrato Dynamic Type

SF Pro, a fonte do sistema da Apple desde o iOS 9 / OS X El Capitan em 2015, é uma fonte variável com três eixos (peso, largura, tamanho óptico), uma família projetada que inclui uma sans (SF Pro), uma variante arredondada (SF Pro Rounded), uma monoespaçada (SF Mono), uma variante compacta (SF Compact, usada no watchOS) e uma companheira serifada (New York)1. A fonte é projetada em torno do contrato Dynamic Type: os onze estilos de texto do SwiftUI (.largeTitle, .title, .body, .callout, .footnote, etc.) usam SF Pro por padrão, e todos eles escalam automaticamente quando o usuário muda seu tamanho de texto preferido em Configurações.

A maioria dos apps usa um ou dois desses onze estilos, ignora o Dynamic Type além do comportamento padrão e nunca toca nos eixos variáveis. O resultado é uma tipografia que funciona, mas não fala o vocabulário da plataforma. O post percorre a família de fontes do sistema, os eixos variáveis, os onze estilos semânticos, o contrato Dynamic Type e os casos onde fontes personalizadas precisam de trabalho explícito de dimensionamento para participar.

TL;DR

  • SF Pro Variable expõe três eixos: peso (wght), largura (wdth) e tamanho óptico (opsz)2. O dimensionamento óptico é automático com base no tamanho em pontos; peso e largura são endereçáveis através de Font.Weight e Font.Width do SwiftUI.
  • A família do sistema inclui SF Pro (padrão), SF Pro Rounded (elementos de UI amigáveis), SF Mono (código, UI técnica), SF Compact (watchOS, contextos estreitos) e New York (companheira serifada para leitura editorial).
  • Os onze estilos de texto do SwiftUI suportam Dynamic Type automaticamente. .body é o padrão seguro; .largeTitle até .caption2 cobrem a hierarquia da plataforma.
  • Fontes personalizadas não escalam com Dynamic Type por padrão. Use Font.custom("Name", size: 16, relativeTo: .body) para optar pelo dimensionamento Dynamic Type da fonte3.
  • @Environment(\.dynamicTypeSize) permite que uma view adapte o layout ao tamanho de texto atual; o intervalo de valores vai de .xSmall a .accessibility5 (12 tamanhos no total)4.

A família de fontes do sistema

A Apple disponibiliza cinco famílias que participam da experiência da fonte do sistema.

SF Pro

O padrão para todas as plataformas Apple. iPhone, iPad, Mac e Vision usam SF Pro para texto de corpo, manchetes e a maior parte da UI. A fonte tem ampla cobertura de idiomas (latino, grego, cirílico, árabe, hebraico, devanágari, etc.), suporta layout da direita para a esquerda nativamente e inclui variantes projetadas para small caps, dígitos alternativos (alinhados vs. tabulares) e alternativas estilísticas.

Acesse no SwiftUI através da fonte do sistema:

Text("Hello").font(.system(.body))                    // SF Pro by default
Text("Hello").font(.system(.body, weight: .semibold)) // SF Pro Semibold
Text("Hello").font(.system(.body, design: .default))  // explicit SF Pro

SF Pro Rounded

Uma variante arredondada projetada para uma UI amigável e acessível: complicações do Apple Watch, anéis do Fitness, partes dos avisos direcionais do Maps, Find My e superfícies selecionadas do Health. As formas arredondadas comunicam suavidade; use-as por tom, não apenas por variedade visual.

Text("Hello").font(.system(.body, design: .rounded))

SF Mono

Família monoespaçada para código, UI de terminal e qualquer contexto onde o alinhamento da célula de caracteres importa. SF Mono é a fonte do Xcode por padrão, da configuração monoespaçada do app Terminal e de qualquer view do SwiftUI que solicite .monospaced.

Text("let x = 42").font(.system(.body, design: .monospaced))

SF Compact

Uma família mais estreita usada no watchOS, em destaques de foco no tvOS e em alguns contextos do Mac onde o espaço horizontal é limitado. As formas mais estreitas preservam a altura-x ao reduzir o avanço horizontal, o que faz com que funcionem nas dimensões da face do Apple Watch onde o SF Pro pareceria apertado.

Apps watchOS usam SF Compact automaticamente através da fonte do sistema; apps iOS geralmente não precisam solicitá-la explicitamente.

New York

Uma família serifada projetada como companheira para leitura editorial. New York aparece no Books para texto longo, no Notes para notas em estilo manuscrito e no SwiftUI através do design .serif.

Text("Long-form essay").font(.system(.body, design: .serif))

A companheira serifada é rara em UI de apps. Recorra a ela deliberadamente (um modo de leitura, uma passagem citada, o corpo de um artigo) em vez de usá-la como padrão.

Os três eixos variáveis

SF Pro Variable codifica três eixos que se combinam para produzir cada glifo:

Peso (wght)

Nove pesos nomeados: .ultraLight, .thin, .light, .regular, .medium, .semibold, .bold, .heavy, .black. A fonte variável interpola continuamente entre eles, mas a API do SwiftUI expõe os valores nomeados.

Text("Heading").font(.system(.title, weight: .semibold))

O peso comunica hierarquia de ênfase: .regular para corpo, .semibold ou .bold para manchetes, .medium para itens ativos da barra de ferramentas, .light para rótulos com menos ênfase. Os estilos semânticos (.headline, .subheadline) vêm com pesos padrão sensatos; recorra a pesos explícitos somente quando o estilo semântico não tiver a forma certa.

Largura (wdth)

Quatro larguras nomeadas na API do iOS 16+: .compressed, .condensed, .standard, .expanded. O eixo de largura afeta o avanço horizontal sem mudar o peso ou o caráter visual. Use-o para uma UI apertada (uma barra de navegação com muitos itens) ou para textura visual (uma manchete em tamanho de display que quer mais presença horizontal).

A largura é aplicada através do modificador de view .fontWidth(_:) do SwiftUI (ou encadeada em uma Font via .width(_:)):

Text("Compressed")
    .font(.system(.title, weight: .bold))
    .fontWidth(.compressed)

A largura raramente é o eixo certo para texto de corpo; funciona bem em tamanhos de display onde a tipografia faz parte do design.

Tamanho óptico (opsz)

O eixo tecnicamente ambicioso. As antigas variantes SF Text e SF Display do SF Pro agora são um gradiente contínuo codificado na fonte variável. Em tamanhos abaixo de 20 pontos, o sistema aplica os ajustes ópticos do SF Text (espaçamento entre letras mais amplo, traços ligeiramente mais pesados, contraformas mais abertas). Acima de 20 pontos, o SF Display assume (espaçamento mais apertado, proporções refinadas). A transição é suave.

O eixo opsz é automático. Os apps não o endereçam explicitamente; o sistema lê o tamanho em pontos solicitado e resolve para o dimensionamento óptico correto. A implicação: a tipografia em tamanhos pequenos de UI tem proporções diferentes da mesma fonte em tamanhos de manchete, e isso é por design. Fontes personalizadas que carecem de dimensionamento óptico parecem boas em um tamanho e erradas em outros; o tratamento automático do SF Pro evita essa armadilha por completo.

Os onze estilos de texto

Font.TextStyle do SwiftUI define onze estilos semânticos que participam do Dynamic Type4:

Estilo Tamanho padrão (Large) Uso comum
.largeTitle 34 pt Cabeçalhos de nível superior, texto hero
.title 28 pt Cabeçalhos de seção
.title2 22 pt Cabeçalhos de subseção
.title3 20 pt Subtítulos menores
.headline 17 pt Corpo enfatizado, títulos de linhas de lista
.body 17 pt Texto de corpo padrão
.callout 16 pt Corpo de apoio, legendas no contexto
.subheadline 15 pt Cabeçalhos secundários, meta texto
.footnote 13 pt Texto pequeno de apoio
.caption 12 pt Legendas de imagem, letras miúdas
.caption2 11 pt Texto mínimo legível

A coluna “Tamanho padrão” mostra o tamanho na configuração “Large” de Dynamic Type do usuário (o padrão do sistema). Cada estilo escala para cima ou para baixo conforme o usuário ajusta seu tamanho de texto preferido; a hierarquia relativa permanece intacta.

A jogada certa de adoção é usar os estilos semânticos diretamente:

VStack(alignment: .leading) {
    Text("Title").font(.title)
    Text("Subtitle").font(.subheadline).foregroundStyle(.secondary)
    Text("Body content here.").font(.body)
}

A hierarquia é preservada em todas as configurações de Dynamic Type, as convenções da Apple HIG são honradas e a tipografia responde às preferências de acessibilidade do usuário sem código por app.

O contrato Dynamic Type

Dynamic Type é a configuração de tamanho de texto controlada pelo usuário em Configurações > Acessibilidade > Tela e tamanho do texto > Texto maior. O valor flui pelo ambiente como DynamicTypeSize, com doze valores de .xSmall a .accessibility54:

  • Tamanhos padrão: .xSmall, .small, .medium, .large (padrão), .xLarge, .xxLarge, .xxxLarge
  • Tamanhos de acessibilidade: .accessibility1, .accessibility2, .accessibility3, .accessibility4, .accessibility5

Apps que usam os estilos de texto semânticos obtêm o dimensionamento de graça. Apps que querem adaptar o layout ao tamanho atual leem o ambiente:

struct AdaptiveLayout: View {
    @Environment(\.dynamicTypeSize) var dynamicTypeSize

    var body: some View {
        if dynamicTypeSize.isAccessibilitySize {
            VStack { content }    // stack vertically at accessibility sizes
        } else {
            HStack { content }    // horizontal at standard sizes
        }
    }
}

A propriedade isAccessibilitySize cobre os cinco tamanhos de acessibilidade (onde o texto é grande o suficiente para que layouts horizontais frequentemente quebrem). O padrão é correto para qualquer layout que dependa de o texto caber horizontalmente.

Apps podem restringir o intervalo suportado com o modificador .dynamicTypeSize(_:):

ContentView()
    .dynamicTypeSize(.large ... .accessibility3)

A restrição limita a configuração de Dynamic Type para a subárvore modificada. Use-a quando o layout de uma view genuinamente não consegue acomodar o intervalo completo; o padrão certo é suportar todos os tamanhos e adaptar o layout.

Fontes personalizadas e Dynamic Type

Fontes personalizadas (uma fonte de marca distribuída através do array UIAppFonts do Info.plist) não escalam com Dynamic Type por padrão. A correção mais simples é o parâmetro relativeTo: de Font.custom:

// Doesn't scale with Dynamic Type
Text("Brand").font(.custom("MyBrandFont", size: 16))

// Scales with Dynamic Type relative to body
Text("Brand").font(.custom("MyBrandFont", size: 16, relativeTo: .body))

O parâmetro relativeTo: diz ao SwiftUI para escalar a fonte personalizada usando a curva de dimensionamento do estilo body. O tamanho da fonte na configuração “Large” do usuário é os 16pt solicitados; em configurações maiores, o SwiftUI aplica o mesmo multiplicador que o estilo body usaria.

Para um dimensionamento mais sofisticado (curvas diferentes em tamanhos diferentes, tratamento óptico personalizado), use UIFontMetrics do UIKit diretamente. O padrão é mais verboso, mas suporta ajustes por tamanho que fontes personalizadas frequentemente precisam.

Quando a tipografia falha

Três modos de falha que vale nomear:

Fontes personalizadas de tamanho fixo em todos os lugares. A falha de acessibilidade mais comum em apps iOS: uma fonte de marca distribuída com Font.custom("BrandFont", size: 16) (sem relativeTo:) ignora o Dynamic Type por completo. Usuários com tamanhos de texto de acessibilidade veem o texto da marca em 16pt enquanto o texto do sistema escala para 28pt+; a hierarquia visual se inverte. A correção é relativeTo: em todo uso de fonte personalizada, auditado via AccessibilityInspector na configuração máxima de Dynamic Type.

Pesos codificados manualmente para ênfase. Um subtítulo estilizado com .font(.body).fontWeight(.bold) é frágil: em tamanhos de acessibilidade, o body em negrito se torna quase indistinguível de um body que já é grande. O estilo semântico .headline lida com a ênfase corretamente em todo o intervalo de Dynamic Type; use-o em vez de body+bold.

Layouts que quebram em tamanhos de acessibilidade. Uma pilha horizontal de texto + ícone + texto que transborda em .accessibility3 é um bug de layout que o Dynamic Type expõe. A correção é o padrão de layout adaptativo dynamicTypeSize.isAccessibilitySize acima; o teste é rodar o app no Dynamic Type máximo durante o QA, não apenas no tamanho padrão.

O que esse padrão significa para apps iOS 26+

Três conclusões.

  1. Use os estilos de texto semânticos, não tamanhos em pontos ajustados manualmente. .body, .headline, .title2, etc. carregam Dynamic Type, dimensionamento óptico e hierarquia correta para a plataforma. Um Font.system(size: 17) ajustado manualmente derrota todo recurso do sistema e envelhece mal quando a Apple ajusta a rampa padrão.

  2. Sempre passe relativeTo: em fontes personalizadas. Uma fonte de marca distribuída com Font.custom(_, size: _, relativeTo: .body) participa do Dynamic Type. Uma fonte de marca distribuída sem isso é uma regressão de acessibilidade por usuário que o QA só vai pegar no tamanho máximo de texto.

  3. Teste layouts em tamanhos de acessibilidade do Dynamic Type. A configuração .accessibility3 é aproximadamente 2x o padrão Large. Layouts que parecem bons em tamanhos padrão rotineiramente quebram em tamanhos de acessibilidade. A correção é a adaptação no nível do layout através do ambiente dynamicTypeSize, não optar por sair via restrições .dynamicTypeSize(...).

O cluster Apple Ecosystem completo: App Intents tipados; servidores MCP; a questão de roteamento; Foundation Models; a distinção runtime vs ferramentas LLM; três superfícies; o padrão de fonte única de verdade; Two MCP Servers; hooks para desenvolvimento Apple; Live Activities; o contrato runtime do watchOS; internals do SwiftUI; modelo mental espacial do RealityKit; disciplina de schema do SwiftData; padrões Liquid Glass; shipping multiplataforma; a matriz de plataformas; framework Vision; Symbol Effects; inferência Core ML; API Writing Tools; Swift Testing; Privacy Manifest; Acessibilidade como plataforma; sobre o que me recuso a escrever. O hub está na Série Apple Ecosystem. Para um contexto mais amplo de iOS-com-agentes-de-IA, veja o guia iOS Agent Development.

FAQ

Qual a diferença entre SF Pro e SF Pro Display / SF Pro Text?

Do iOS 9 (quando o SF foi distribuído pela primeira vez como fonte do sistema) ao iOS 16, o SF foi distribuído como duas fontes separadas: SF Text para tamanhos abaixo de 20pt e SF Display para tamanhos de 20pt ou mais, cada uma com espaçamento entre letras e pesos de traço ajustados manualmente para sua faixa de tamanhos. SF Pro Variable consolida a mesma divisão Text/Display em um eixo contínuo de tamanho óptico (opsz). As duas fontes não são mais separadas; a fonte variável lida com a transição automaticamente com base no tamanho em pontos solicitado.

Como obtenho dígitos monoespaçados em texto de corpo?

Use .monospacedDigit() no SwiftUI:

Text("\(score)").font(.body).monospacedDigit()

O modificador troca os dígitos proporcionais da fonte do corpo por dígitos monoespaçados, mantendo o resto do texto proporcional. Use-o para qualquer UI onde os dígitos precisam se alinhar entre linhas (cronômetros, placares, exibições de saldo).

Devo usar SF Pro Rounded em toda a UI?

Não. SF Pro Rounded carrega um tom (amigável, acessível) que se encaixa em alguns contextos e em outros não. As complicações do app Watch, o teclado numérico do Phone do iPhone e certas superfícies do app Health o usam. Um app de produtividade, um app de banco ou uma ferramenta de desenvolvedor geralmente não devem. Recorra a .rounded deliberadamente, não como padrão.

Qual é o intervalo certo de Dynamic Type para um app iPhone?

Por padrão, suporte todos os tamanhos de .xSmall a .accessibility5. Os tamanhos de acessibilidade (.accessibility1 até .accessibility5) são como usuários com baixa visão, dificuldades motoras ou outras necessidades de acessibilidade usam o iPhone. Um app que opta por sair via restrições .dynamicTypeSize(...) falha com esses usuários. A jogada certa é a adaptação de layout (o padrão isAccessibilitySize), não optar por sair do intervalo de tamanhos.

Posso distribuir uma fonte variável personalizada com meu app?

Sim. Fontes variáveis são distribuídas como qualquer outra fonte personalizada (adicione ao UIAppFonts do Info.plist, referencie por Font.custom). Para endereçar os eixos variáveis a partir do SwiftUI, use as APIs subjacentes do CTFont de Font.custom através de UIFontDescriptor.SymbolicTraits ou, para controle total do eixo, recorra a CTFontCreateCopyWithAttributes com kCTFontVariationAttribute. A ponte do SwiftUI para os eixos variáveis é mais verbosa do que para fontes do sistema; para a maioria dos apps, as fontes do sistema cobrem os casos.

Referências


  1. Apple Developer: Fonts. A visão geral da família de fontes do sistema, incluindo SF Pro, SF Pro Rounded, SF Mono, SF Compact e New York. 

  2. Apple Developer: Meet the expanded San Francisco font family (sessão WWDC 2022 110381). A introdução do design de três eixos do SF Pro Variable (peso, largura, tamanho óptico). 

  3. Documentação Apple Developer: Font.custom(_:size:relativeTo:). O inicializador de fonte personalizada que opta pela fonte no dimensionamento Dynamic Type relativo a um estilo de texto escolhido. 

  4. Documentação Apple Developer: DynamicTypeSize. O enum de doze valores de .xSmall a .accessibility5 mais o predicado isAccessibilitySize para adaptação no nível do layout. 

Artigos relacionados

Liquid Glass in SwiftUI: Three Patterns From Shipping Return on iOS 26

Apple's Liquid Glass is a one-line SwiftUI API. Three patterns from Return go beyond .glassEffect(): glass on text via C…

19 min de leitura

Accessibility As Platform: Personal Voice, Live Speech, Eye Tracking, Music Haptics

Personal Voice, Live Speech, Eye Tracking, Music Haptics, Vocal Shortcuts: accessibility as platform features, not app r…

14 min de leitura

The Design Engineer's Agent Stack

Design engineers need agent infrastructure that enforces visual consistency, typography discipline, color compliance, an…

14 min de leitura