SwiftUI .rotationEffect()のフレーミングとオフセット


8

.rotationEffect()をテキストに適用すると、期待どおりにテキストが回転しますが、フレームは変更されません。これは、HStackのVStackのように、回転されたビューを回転されていないビューとスタックするときに問題になり、それらがオーバーラップします。

最初に、rotationEffectはテキストのフレームを垂直に更新するだけだと思っていましたが、そうではありません。

私は手動でフレームサイズと(必要に応じてオフセットする)テキストを設定してみましたが、これは一種の動作ですが、テキストが表示される場所や大きさを推測および確認する必要があるため、このソリューションは好きではありませんフレームなど

これは回転されたテキストがどのように行われるか、またはこれに対するよりエレガントな解決策はありますか?

struct TextAloneView: View {

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

重複するテキスト

回答:


6

この場合、フレームを自分で調整する必要があります。そのためには、フレームをキャプチャしてから、調整を適用する必要があります。

最初に、既存のフレームをキャプチャーするために設定を作成します。これは、子ビューから親にデータを渡すためのシステムです。

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 }
    }
}

これにより、.captureSize(in: $binding)ビューに新しいメソッドが作成されます。

これを使用して、フレームを回転させる新しい種類のビューを作成できます。

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)
    }
}

そして、便宜上、それを適用する拡張機能:

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

これで、コードは期待どおりに機能するはずです。

struct TextAloneView: View {

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

0

RotationEffectは、アンカーポイントである2番目の引数を取ります(省略した場合)。デフォルトは.centerです。

代わりにこれを試してください:

.rotationEffect(.degrees(-90), anchor: .bottomTrailing)

4
.rotationEffect()がフレームを変更しない理由はありますか?たとえば、回転したテキストを2つの水平なテキスト行の間に挟んだ場合、その上下に、VStackがテキストをすべて水平であるかのように配置するのはなぜですか?
EZhou00
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.