Swift / SwiftUI

Guia de alinhamento no SwiftUI

SwiftUI utiliza guias de alinhamento para posicionar visualmente elementos em relação uns aos outros. Essas guias permitem que contêineres, como HStack e VStack, alinhem visualmente os elementos por meio de coordenadas horizontais ou verticais, mas podem ser personalizadas quando necessário para designs específicos.

Compartilhar
Alignment guides in SwiftUI social preview

Guia de Alinhamento no SwiftUI

O contêiner de layout do SwiftUI usa guias de alinhamento para posicionar visualizações em relação uma à outra. Uma guia identifica uma coordenada horizontal ou vertical nas dimensões locais de uma visualização. Durante a colocação, o contêiner lê a guia relevante de cada filho e posiciona os filhos para que essas coordenadas coincidam.

Por exemplo, um HStack usa a guia do centro vertical por padrão. Ele lê essa guia de cada filho e posiciona os filhos para que as posições relatadas fiquem na mesma linha horizontal:

HStack {
Image(.earth)
.resizable()
.scaledToFit()
.frame(width: 120)

Image(.mars)
.resizable()
.scaledToFit()
.frame(width: 48)
}

Nós podemos escolher qual guia de alinhamento vertical um HStack usa passando uma alinhamento para seu inicializador. Para duas visualizações de texto com tamanhos de fonte diferentes, nós podemos querer alinhar suas primeiras linhas de texto em vez do centro.

HStack(alignment: .firstTextBaseline) {
Text("42")
.font(.largeTitle)

Text("minutes")
.font(.body)
}

Nos dois exemplos, o contêiner escolhe a guia usada para alinhamento, enquanto o SwiftUI fornece o valor correspondente para cada filho. Para layouts comuns, esses valores implícitos são frequentemente tudo que precisamos.

Sobrescrevendo a guia de alinhamento de uma visualização

Às vezes, o valor padrão que um filho relata para uma guia de alinhamento não marca o ponto em que queremos que seu pai use para alinhamento. Arte personalizada colocada ao lado do texto é um exemplo. A imagem da nave espacial no código abaixo não tem uma linha de base tipográfica, então o SwiftUI usa sua borda inferior quando o HStack solicita a guia de alinhamento .firstTextBaseline.

HStack(alignment: .firstTextBaseline) {
Image(.rocket)
.resizable()
.scaledToFit()
.frame(width: 48)

Text("Pronto para lançamento")
.font(.title2)
}

A borda inferior da imagem, no topo do foguete, agora coincide com a linha de base do texto. Isso coloca o corpo do foguete muito alto em relação ao texto. Usar a parte inferior do corpo como a linha de base do foguete produziria um alinhamento visual melhor, com a chama se estendendo abaixo da linha de base do texto como uma descender.

Nós podemos sobrescrever a coordenada que uma visualização relata para uma determinada guia de alinhamento usando o modificador alignmentGuide(_:computeValue:). Aqui, nós retornamos a posição na parte inferior do corpo do foguete como o valor .firstTextBaseline da imagem:

<span class="type">HStack</span>(alignment: .<span class="dotAccess">firstTextBaseline</span>) {
<span class="type">Image</span>(.<span class="dotAccess">rocket</span>)
.<span class="call">resizable</span>()
.<span class="call">scaledToFit</span>()
.<span class="call">frame</span>(width: <span class="number">48</span>)
.<span class="call">alignmentGuide</span>(.<span class="dotAccess">firstTextBaseline</span>) { dimensions <span class="keyword">in
dimensions.<span class="property">height</span> * <span class="number">0.81</span>
}

<span class="type">Text</span>(<span class="string">"Ready for launch"</span>)
.<span class="call">font</span>(.<span class="dotAccess">title2</span>)
}

O closure computeValue recebe um valor ViewDimensions contendo o tamanho e as diretrizes de alinhamento da visualização modificada. Como as coordenadas verticais são medidas a partir do topo, retornar 81% da altura da imagem coloca a guia aproximadamente na parte inferior do corpo do foguete. O HStack então posiciona a imagem de modo que essa coordenada, em vez da borda inferior dela, coincida com a linha de base do texto.

Os SF Symbols já fornecem diretrizes de linha de base projetadas para colocação ao lado do texto. Substituir esses valores pode descartar sua alinhamento óptico embutido. Fornecer uma linha de base explícita é mais apropriado para arte personalizada que não tem informações de alinhamento tipográfico própria.

  Definindo uma diretriz de alinhamento personalizada

Muitos layouts podem ser construídos selecionando um dos guias de alinhamento do SwiftUI para um contêiner e, quando necessário, alterando o valor que uma criança individual relata para esse guia. Algumas designs, no entanto, podem exigir que as visualizações se alinhem em torno de um ponto de referência diferente que é específico ao layout ou à sua hierarquia.

Considere um evento de sequência de lançamento com a seguinte hierarquia de visualização:

<span class="type">HStack</span> {
<span class="type">Text</span>(time)
.<span class="call">font</span>(.<span class="dotAccess">subheadline</span>.<span class="call">monospacedDigit</span>())
.<span class="call">foregroundStyle</span>(.<span class="dotAccess">secondary</span>)

<span class="type">Circle</span>()
.<span class="call">fill</span>(.<span class="dotAccess">blue</span>)
.<span class="call">frame</span>(width: <span class="number">10</span>, height: <span class="number">10</span>)

<span class="type">VStack</span>(alignment: .<span class="dotAccess">leading</span>) {
<span class="type">Text</span>(category)
.<span class="call">font</span>(.<span class="dotAccess">caption</span>)
.<span class="call">foregroundStyle</span>(.<span class="dotAccess">secondary</span>)

<span class="type">Text</span>(title)
.<span class="call">font</span>(.<span class="dotAccess">headline</span>)

<span class="type">Text</span>(description)
.<span class="call">font</span>(.<span class="dotAccess">subheadline</span>)
.<span class="call">foregroundStyle</span>(.<span class="dotAccess">secondary</span>)
}
}

Pelo padrão, as três crianças se alinham por seus centros verticais, mas isso não produz o alinhamento que queremos para esse design.

Seria melhor alinhar a linha de base do texto da hora e a borda inferior do marcador circular com a linha de base do título, mas isso não é fácil com as diretrizes existentes. O texto do título não é uma criança direta do HStack, e nenhuma das diretrizes relatadas por seu contêiner envolvente VStack identifica a linha de base do título.

Nós podemos resolver esse problema com uma diretriz personalizada. Uma diretriz personalizada pode fornecer um ponto de referência fornecido por uma criança aninhada uma identidade separada que um contêiner externo pode selecionar para alinhamento.

Definimos o guia personalizado com um tipo que segue o protocolo AlignmentID. No seu método defaultValue(in:), retornamos o valor que uma visualização deve reportar quando nenhum valor explícito é fornecido. Aqui, usamos .firstTextBaseline como padrão. Em seguida, expomos o guia como um valor estático em VerticalAlignment para poder passá-lo aos APIs de alinhamento.

struct EventTitleAlignment: AlignmentID {
static func defaultValue(
in dimensions: ViewDimensions
) -> CGFloat {
dimensions[VerticalAlignment.firstTextBaseline]
}
}

extension VerticalAlignment {
static let eventTitle = VerticalAlignment(
EventTitleAlignment.self
)
}

Podemos usar nosso guia de alinhamento personalizado como um integrado passando-o ao parâmetro alignment do contêiner e fornecendo um valor explícito na visualização filha que define o ponto de referência desejado.

HStack(alignment: .eventTitle) {
Text(time)
.font(.subheadline.monospacedDigit())
.foregroundStyle(.secondary)

Circle()
.fill(.blue)
.frame(width: 10, height: 10)

VStack(alignment: .leading) {
Text(category)
.font(.caption)
.foregroundStyle(.secondary)

Text(title)
.font(.headline)
.alignmentGuide(.eventTitle) { dimensions in
dimensions[VerticalAlignment.firstTextBaseline]
}

Text(description)
.font(.subheadline)
.foregroundStyle(.secondary)
}
}

Durante a criação da disposição, o HStack solicita .eventTitle de cada filho direto. Nem o texto do tempo nem o círculo fornecem um valor explícito, então eles retornam os padrões definidos por EventTitleAlignment. Quando o SwiftUI solicita .eventTitle do VStack, ele encontra o valor explícito fornecido pelo título para o mesmo guia. Um valor explícito de um descendente tem precedência sobre o valor padrão do guia para a visualização contenedora. É por isso que o VStack não recorre ao seu primeiro baseline de texto e não precisa de um modificador alignmentGuide próprio.

O HStack então posiciona seus filhos de modo que a linha base do tempo, a borda inferior do círculo e o baseline projetado do título coincidam.

Quando múltiplos descendentes relatam valores explícitos para o mesmo guia, o SwiftUI os mescla em um único valor. Quando um guia personalizado é destinado a identificar um ponto de referência específico, apenas a visualização que define esse ponto deve reportar um valor explícito para ele.

Guias de alinhamento são uma parte poderosa, mas frequentemente subutilizada do sistema de layout do SwiftUI. Eles nos permitem descrever relações entre visualizações em termos de pontos significativos no seu conteúdo, sem depender de medidas de quadros ou deslocamentos fixos. Guias incorporadas abrangem casos comuns, enquanto valores explícitos e guias personalizadas tornam o mesmo mecanismo adaptável a requisitos de layout mais especializados.

Se você está procurando construir uma base sólida no SwiftUI, meu livro Fundamentos do SwiftUI mergulha profundamente nos princípios e APIs fundamentais do framework para ajudá-lo a entender como ele funciona por trás das cortinas e a usá-lo efetivamente em seus projetos. E meu novo livro A Maneira do SwiftUI ajuda você a adotar padrões recomendados, evitar armadilhas comuns e usar as ferramentas nativas do SwiftUI de maneira adequada para trabalhar com o framework em vez de contra ele.

Para mais recursos sobre Swift e SwiftUI, confira meus outros livros e pacotes de livros.

Fonte original

Conteúdo traduzido e adaptado pela redação do Notícias Mobile. Confira também a matéria na fonte original.

Leia a matéria completa