SwiftUI .rotationEffect () enquadramento e deslocamento

8

Ao aplicar .rotationEffect () a um Texto, ele gira o texto conforme o esperado, mas seu quadro permanece inalterado. Isso se torna um problema ao empilhar vistas giradas com vistas não giradas, como com um VStack do HStack, fazendo com que elas se sobreponham.

Inicialmente, pensei que o rotationEffect simplesmente atualizasse o quadro do Texto para ser vertical, mas esse não é o caso.

Tentei definir manualmente o tamanho do quadro e (se necessário, compensar) o Texto, que funciona, mas não gosto dessa solução, pois requer algumas adivinhações e verificações de onde o Texto aparecerá, qual o tamanho a ser feito. o quadro etc.

É assim que o texto rotacionado é feito ou existe uma solução mais elegante para isso?

struct TextAloneView: View {

    var body: some View {
        VStack {
            Text("Horizontal text")
            Text("Vertical text").rotationEffect(.degrees(-90))
        }
    }
}

Sobreposição de texto

EZhou00
fonte

Respostas:

6

Você precisa ajustar o quadro neste caso. Isso requer a captura do quadro e a aplicação do ajuste.

Primeiro, para capturar o quadro existente, crie uma preferência , que é um sistema para transmitir dados dos modos de exibição filho para seus pais:

private struct SizeKey: PreferenceKey {
    static let defaultValue: CGSize = .zero
    static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
        value = nextValue()
    }
}

extension View {
    func captureSize(in binding: Binding<CGSize>) -> some View {
        overlay(GeometryReader { proxy in
            Color.clear.preference(key: SizeKey.self, value: proxy.size)
        })
            .onPreferenceChange(SizeKey.self) { size in binding.wrappedValue = size }
    }
}

Isso cria um novo .captureSize(in: $binding)método no Views.

Usando isso, podemos criar um novo tipo de View que gira seu quadro:

struct Rotated<Rotated: View>: View {
    var view: Rotated
    var angle: Angle

    init(_ view: Rotated, angle: Angle = .degrees(-90)) {
        self.view = view
        self.angle = angle
    }

    @State private var size: CGSize = .zero

    var body: some View {
        // Rotate the frame, and compute the smallest integral frame that contains it
        let newFrame = CGRect(origin: .zero, size: size)
            .offsetBy(dx: -size.width/2, dy: -size.height/2)
            .applying(.init(rotationAngle: CGFloat(angle.radians)))
            .integral

        return view
            .fixedSize()                    // Don't change the view's ideal frame
            .captureSize(in: $size)         // Capture the size of the view's ideal frame
            .rotationEffect(angle)          // Rotate the view
            .frame(width: newFrame.width,   // And apply the new frame
                   height: newFrame.height)
    }
}

E por conveniência, uma extensão para aplicá-lo:

extension View {
    func rotated(_ angle: Angle = .degrees(-90)) -> some View {
        Rotated(self, angle: angle)
    }
}

E agora seu código deve funcionar conforme o esperado:

struct TextAloneView: View {

    var body: some View {
        VStack {
            Text("Horizontal text")
            Text("Vertical text").rotated()
        }
    }
}
Rob Napier
fonte
0

RotationEffect usa um segundo argumento, que é o ponto de ancoragem, se você o omitir - o padrão é .center.

Tente isso:

.rotationEffect(.degrees(-90), anchor: .bottomTrailing)
LuLuGaGa
fonte
4
Existe uma razão para o .rotationEffect () deixar o quadro inalterado? Por exemplo, se eu colocar o texto girado entre duas linhas horizontais: acima e abaixo, por que o VStack coloca os textos como se estivessem todos na horizontal?
EZhou00