Anatomia das barras do sistema no SwiftUI: status bar, home indicator e safe area
Como funcionam a status bar e o home indicator no iPhone e como garantir o contraste no SwiftUI com toolbarColorScheme, preferredColorScheme e safe area.

Já tive publicações recusadas na App Store por causa da status bar. O problema era contraste: a cor dos ícones do sistema não combinava com o fundo da tela, e a informação do topo ficava difícil de ler. No Android, o mesmo descuido costuma aparecer no rodapé, com o ícone de voltar branco sobre uma navigation bar branca, e o usuário não encontra como sair da tela.
Esse tipo de erro não depende do framework. No SwiftUI, o cuidado fica nas telas que fogem do padrão, como um player escuro, porque não existe uma API direta para escolher a cor do texto da status bar. Por isso resolvi escrever sobre a anatomia dessas barras, como elas se comportam e como configurá-las no SwiftUI.
O que é do sistema e o que é do app

No iPhone, a tela tem duas faixas que não pertencem ao app. No topo fica a status bar, com hora, rede e bateria, dividindo espaço com o notch ou a Dynamic Island. No rodapé fica o home indicator. Não existe navigation bar com botões como no Android.
O app não desenha o conteúdo dessas faixas. O que ele controla são duas coisas: o que aparece atrás delas e se o texto da status bar vai ser claro ou escuro. A primeira resposta vem do layout do app. A segunda é uma configuração que muita gente nunca toca, e é ela que decide se a hora vai ser legível.
O erro que normalmente acontece
Esse erro chega até a loja porque depende de contexto. Quem desenvolve costuma testar com o aparelho num modo só, claro ou escuro. O usuário usa o outro, ou chega numa tela com fundo próprio, como a splash, o onboarding ou um player de vídeo, e o texto da status bar precisa combinar com esse fundo.
A crença que sustenta o problema é "a barra é do sistema, então o sistema cuida dela". Ela tem uma parte verdadeira: quem desenha a hora e a bateria é o sistema operacional. Mas a cor desses ícones é uma decisão do app, e o sistema não confere se ela combina com o que está atrás.
Como cada framework chama claro e escuro

Antes de escrever código, responda uma pergunta: o fundo atrás da barra é claro ou escuro? Fundo claro pede ícones escuros, e fundo escuro pede ícones claros.
A confusão é que cada framework dá nome a isso de um jeito. No Flutter, o nome descreve o ícone, então ícones escuros são dark. No Compose e no SwiftUI, o nome descreve o fundo, então os mesmos ícones escuros são light.
No SwiftUI: color scheme e safe area

O SwiftUI não tem uma API direta para escolher a cor dos ícones da status bar. No estilo padrão, o iOS escolhe a aparência sozinho para manter o contraste com o conteúdo de baixo. Quando uma tela precisa de um resultado garantido, como um player escuro, o caminho é declarar o color scheme da barra ou da apresentação.
Dentro de um NavigationStack, cada tela pode declarar o color scheme da própria barra:
struct PlayerView: View {
var body: some View {
ZStack {
Color.black.ignoresSafeArea()
Image(systemName: "play.circle.fill")
.font(.system(size: 96))
.foregroundStyle(.white)
}
.navigationTitle("Player")
.toolbarBackground(.black, for: .navigationBar)
.toolbarBackground(.visible, for: .navigationBar)
.toolbarColorScheme(.dark, for: .navigationBar)
}
}O toolbarColorScheme(.dark, for: .navigationBar) deixa a status bar com texto claro enquanto essa tela está no topo da pilha, e ao voltar vale o que a tela anterior declarou. O nome segue a lógica do Compose: .dark descreve o fundo, e o resultado é texto claro. As duas linhas de toolbarBackground são obrigatórias, porque sem o fundo da barra visível o SwiftUI ignora o toolbarColorScheme. Os três modificadores exigem iOS 16.
Para telas sem barra de navegação, como um player aberto com .fullScreenCover, o caminho é o .preferredColorScheme(.dark). Ele vale para a apresentação inteira, então funciona bem numa tela modal, mas numa tela comum da pilha escurece o app todo. Se nenhuma das duas opções servir, sobra o UIKit: uma subclasse de UIHostingController que sobrescreve preferredStatusBarStyle.
Os insets funcionam ao contrário do Android. O SwiftUI respeita a safe area por padrão, e desenhar atrás das barras é uma escolha explícita. No exemplo, só o Color.black usa .ignoresSafeArea(), então o fundo ocupa a tela toda e o ícone continua na área livre.

Antes de mandar para a loja
A maior parte desses erros só aparece em uma combinação específica de tema, navegação e tela. Antes de cada envio, vale passar por três cenários:
Tema claro e escuro. Troque o tema do sistema com o app aberto e navegue por todas as telas.
Telas sem barra de navegação. Splash, onboarding, player, câmera e qualquer tela com imagem atrás da status bar.
iPhone com notch ou Dynamic Island, no modo claro e no escuro. No iOS, o fundo atrás da status bar é sempre o do app, então vale testar as telas escuras com o aparelho no modo claro e as telas claras no modo escuro.
A regra que vale para qualquer framework
Toda decisão sobre as barras do sistema começa pela cor do fundo atrás delas. Com essa resposta, o nome de cada API vira detalhe de tradução, e o resto é aplicar os insets para o conteúdo não ficar embaixo da hora nem dos gestos.
Nenhuma dessas APIs confere a sua configuração. O estilo padrão do iOS tenta manter o contraste, mas quando o app declara a cor errada, o texto branco sobre fundo branco chega até a revisão da loja do mesmo jeito.
Depois das barras, o próximo passo são os outros casos em que o sistema ocupa parte da tela: o teclado e o notch quando o app gira para paisagem. Em todos eles o app precisa saber qual parte é dele.
Continue lendo
Aprimorando a Experiência do Usuário com Teclados no iOS usando Keyboard Actions
Você nunca mais vai lutar contra o Teclado do iOS no Flutter! Veja como resolver um problema comum em aplicativos mobile para iOS que é…
Flutter Widgetbook: Como Documentar seu Design System do jeito certo!
Como o Widgetbook resolve o problema de documentar componentes em projetos Flutter com design system próprio. No Direção Fácil, virou catálogo visual, contrato com o Figma e contexto preciso para o Claude Code gerar telas usando os widgets certos.
Criando um Agente de IA no Claude Code para Controlar meu Smartphone
Automatizando documentação e testes de apps Android com inteligência artificial. Eu estava enfrentando um desafio específico: precisava documentar automaticamente diversos fluxos de uma aplicação Android. Foi quando comecei a explorar maneiras de realizar esse processo, e uma das ferramentas que decidi testar foi o Claude Code.