SwiftUI .rotationEffect() 框架和偏移

Posted

技术标签:

【中文标题】SwiftUI .rotationEffect() 框架和偏移【英文标题】:SwiftUI .rotationEffect() framing and offsetting 【发布时间】:2019-10-21 21:11:02 【问题描述】:

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

我最初认为 rotationEffect 会简单地将 Text 的框架更新为垂直,但事实并非如此。

我已经尝试手动设置帧大小和(如果需要,偏移)文本,哪种方法可行,但我不喜欢这种解决方案,因为它需要一些猜测和检查文本将出现的位置,如何做大框架等。

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

struct TextAloneView: View 

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

Overlapping Text

【问题讨论】:

【参考方案1】:

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

首先,要捕获现有框架,创建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()
        
    

【讨论】:

【参考方案2】:

RotationEffect 采用第二个参数,即锚点,如果您省略它 - 默认为 .center。

试试这个:

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

【讨论】:

.rotationEffect() 是否有原因使框架保持不变?例如,如果我将旋转后的文本夹在两条水平文本行之间:在其上方和下方,为什么 VStack 将文本放置为好像它们都是水平的?

以上是关于SwiftUI .rotationEffect() 框架和偏移的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 动画并不总是运行

SwiftUI视图旋转腾挪那些事儿

SwiftUI视图旋转腾挪那些事儿

仅使用 .animation 修饰符的显式动画不起作用

使用手势后删除视图和移动内容

如何更改 .sheet 修饰符的过渡?