SwiftUI .rotationEffect()框架和偏移量

时间:2019-10-21 21:11:02

标签: swift swiftui

将.rotationEffect()应用于文本时,它将按预期旋转文本,但是其框架保持不变。在将旋转视图与非旋转视图(例如HStack的VStack)堆叠在一起时,这将成为一个问题。

我最初以为rotationEffect会将文本的框架更新为垂直,但事实并非如此。

我已经尝试过手动设置帧大小和(如果需要,可以偏移)文本,这是一种可行的方法,但是我不喜欢这种解决方案,因为它需要猜测并检查文本的显示位置,如何大到可以制作框架等。

这只是旋转文本的方式,还是对此有更优雅的解决方案?

struct TextAloneView: View {

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

Overlapping Text

2 个答案:

答案 0 :(得分:9)

在这种情况下,您需要自行调整框架。这需要捕获帧是什么,然后应用调整。

首先,要捕获现有框架,请创建a preference,这是一个用于将数据从子视图传递给父母的系统:

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

答案 1 :(得分:0)

RotationEffect接受第二个参数,即锚点(如果省略)-默认值为.center。

尝试以下方法:

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