Usando o seguinte
@main
struct aspectratioApp: App {
var body: some Scene {
WindowGroup { ContentView().border(.blue, width: 2).padding() }
}
}
e ContentView
sem o aspectRatio
modificador aplicado (veja abaixo), os resultados são como esperado, incl. Canvas
preenche o espaço disponível (que eu acho que é o comportamento padrão), gerando canvas size: (243.0, 78.0)
no console:
Em seguida, vamos aplicar o .aspectRatio(isSquare ? 1 : nil, contentMode: isSquare ? .fit : .fill)
modificador, que acredito que está dizendo para fazer Canvas
um quadrado que se encaixe ou o que quer que preencha o espaço disponível (veja ContentView com aspectRatio abaixo). Logo de início, obtenho um quadrado Canvas
como este:
e um tamanho de tela quadrada impresso no console. A alternância isSquare
mantém o quadrado da tela com um tamanho ligeiramente menor:
canvas size: (157.0, 157.0)
canvas size: (130.0, 130.0)
O que estou esquecendo aqui? A documentação para aspectRatio documenta o parâmetro aspectRatio da seguinte forma: A proporção de largura para altura a ser usada para a visualização resultante. Use nil para manter a proporção atual na visualização resultante. que eu interpreto como significando qualquer que seja a proporção se o modificador não for aplicado.
Não é assim que funciona? O modificador não é stateless, ou seja, ele se lembrou de que a tela era quadrada uma vez e está apenas mantendo isso para sempre, incl. em reinicializações do aplicativo?
ContentView sem AspectRatio
struct ContentView: View {
@State private var isSquare = false
var body: some View {
VStack {
Text("isSquare: \(isSquare)").padding()
Canvas { _, size in print("canvas size: \(size)") }
.border(.teal, width: 2)
.padding()
Toggle(isOn: $isSquare) { Text("Constrain to Square") }.padding()
}
.border(.yellow, width: 2)
.padding()
}
}
ContentView com proporção de aspecto
struct ContentView: View {
@State private var isSquare = false
var body: some View {
VStack {
Text("isSquare: \(isSquare)").padding()
Canvas { _, size in print("canvas size: \(size)") }
.aspectRatio(isSquare ? 1 : nil, contentMode: isSquare ? .fit : .fill)
.border(.teal, width: 2)
.padding()
Toggle(isOn: $isSquare) { Text("Constrain to Square") }.padding()
}
.border(.yellow, width: 2)
.padding()
}
}
Meu palpite é que a proporção padrão é baseada no tamanho ideal da visualização.
O tamanho ideal de a
Canvas
é 10,0 x 10,0. Isso pode ser confirmado aplicando.fixedSize()
-se a aCanvas
mostrado isoladamente:Quando o modificador
.aspectRatio
é aplicado com uma proporção denil
, a proporção de aspecto padrão é usada. Com base no tamanho ideal 10,0 x 10,0, a proporção de aspecto padrão de aCanvas
é quadrado.Passar
nil
paraaspectRatio
não é como.opacity(1)
ou.scaleEffect(1)
onde não muda nada.aspectRatio
não é idempotente.A documentação diz:
Então a visualização precisa ter uma "proporção de aspecto atual". Como
aspectRatio
encontrar isso? Ela dá uma.unspecified
proposta de tamanho para oCanvas
, pedindo seu tamanho ideal. MasCanvas
é suposto ocupar tanto espaço quanto quiser, então ela diz apenas 10x10 (este também é o padrão quando você usareplacingUnspecifiedDimensions
), o que não é muito significativo, mas tem que retornar algo no final do dia.Então
aspectRatio
recebe 10x10 e diz: "ah, a 'proporção de aspecto atual' é 1", e é assim queCanvas
se torna um quadrado, mesmo quando você usanil
comoaspectRatio
.Para outro exemplo de como o SwiftUI fornece propostas de tamanho para visualizações, veja minha resposta aqui
Você pode ver quais propostas suas visualizações recebem, implementando um
NSViewRepresentable
e overridesizeThatFits
. O SwiftUI chama esse método quando quer propor um tamanho para suas visualizações.Substitua
Canvas
por esta visualização e useaspectRatio
. Você verá que a primeira proposta éwidth: nil, height: nil
- também conhecida como.unspecified
proposta. Se você removeraspectRatio
, não haverá nenhuma.unspecified
proposta. Observe que oVStack
chamará isso várias vezes para fazer seu layout.Basicamente, você precisa obter a proporção do aspecto do pai e passá-la para
aspectRatio
, em vez de pedirCanvas
para o tamanho dele. Você poderia, por exemplo, fazer: