在 SwiftUI 中使用 aspectRatio 的方形文本

Square Text using aspectRatio in SwiftUI

我正在尝试使用 Swift UI…

实现以下布局

struct ContentView : View {
    var body: some View {
        List(1...5) { index  in

            HStack {
                HStack {
                    Text("Item number \(index)")
                    Spacer()
                    }.padding([.leading, .top, .bottom])
                    .background(Color.blue)

                Text("i")
                    .font(.title)
                    .italic()
                    .padding()
                    .aspectRatio(1, contentMode: .fill)
                    .background(Color.pink)

                }.background(Color.yellow)
        }
    }
}

我希望 Text("i") 是方形的,但是设置 .aspectRatio(1, contentMode: .fill) 似乎没有任何作用……

我可以将文本的框架宽度和高度设置为正方形,但设置纵横比似乎应该以更动态的方式实现我想要的效果。

我错过了什么?

我成功地在 SwiftUI 中重新创建了您的第一个屏幕截图中的视图。我不确定你想要多少填充,所以我为这个值定义了一个 private 不可变变量

蓝色视图将包含文本内容并且可以更改大小,因此通过使用 GeometryReader 您可以获得蓝色视图的大小,然后使用该大小的高度值到设置粉红色视图的宽度和高度。这意味着无论蓝色视图的高度是多少,粉红色视图都会保持相同的纵横比

下面的 SizeGetter 视图用于使用 GeometryReader 获取任何视图大小,然后将该值绑定回 ContentView 中的 @State 变量。因为正在使用 @State@Binding 属性 包装器,所以每当 blueViewSize 更新时,SwiftUI 将自动刷新视图。

SizeGetter 视图可用于任何视图,并使用 .background() 修饰符实现,如下所示

struct SizeGetter: View {

    @Binding var size: CGSize;

    var body: some View {

        // Get the size of the view using a GeometryReader
        GeometryReader { geometry in
            Group { () -> AnyView in

                // Get the size from the geometry
                let size = geometry.frame(in: .global).size;

                // If the size has changed, update the size on the main thread
                // Checking if the size has changed stops an infinite layout loop
                if (size != self.size) {
                    DispatchQueue.main.async {
                        self.size = size;
                    }
                }

                // Return an empty view
                return AnyView(EmptyView());
            }
        }
    }
}

struct ContentView: View {

    private let padding: Length = 10;
    @State private var blueViewSize: CGSize = .zero;

    var body: some View {

        List(1...5) { index  in

            // The yellow view
            HStack(spacing: self.padding) {

                // The blue view
                HStack(spacing: 0) {
                    VStack(spacing: 0) {
                        Text("Item number \(index)")
                            .padding(self.padding);
                    }
                    Spacer();
                }
                .background(SizeGetter(size: self.$blueViewSize))
                .background(Color.blue);

                // The pink view
                VStack(spacing: 0) {
                    Text("i")
                        .font(.title)
                        .italic();
                }
                .frame(
                    width: self.blueViewSize.height,
                    height: self.blueViewSize.height
                )
                .background(Color.pink);
            }
            .padding(self.padding)
            .background(Color.yellow);
        }
    }
}

在我看来,最好设置 VStackHStack 的背景颜色,而不是直接设置 Text 视图,因为这样您就可以向其中添加更多文本和其他视图堆栈,而不必为每个堆栈设置背景颜色

我正在搜索非常相似的主题“SwiftUI 中的方形文本”,遇到了您的问题,我想我已经找到了实现所需布局的非常简单的方法,使用 GeometryProxy 设置方形视图的宽度和高度提供 geometry.size.

检查下面的代码,一个可以在列表视图上下文中使用的 TableCellView 示例:

import SwiftUI

struct TableCellView: View {
    var index: Int
    
    var body: some View {
        HStack {
            HStack {
                Text("Item number \(index)")
                    .padding([.top, .leading, .bottom])
                Spacer()
            }
            .background(Color(.systemBlue))
            .layoutPriority(1)
            
            GeometryReader { geometry in
                self.squareView(geometry: geometry)
            }
            .padding(.trailing)
        }
        .background(Color(.systemYellow))
        .padding(.trailing)
    }
    
    func squareView(geometry: GeometryProxy) -> some View {
        Text("i")
            .frame(width: geometry.size.height, height: geometry.size.height)
            .background(Color(.systemPink))
    }
}

问题是由于 left/right 边使用了不同的字体,因此填充会产生不同的结果区域。

这是可能的解决方案。这个想法是根据左侧文本的默认视图大小提供右侧矩形(这也可以自动跟踪动态字体大小)。

测试 Xcode 12 / iOS 14

struct ContentView: View {
    @State private var height = CGFloat.zero
    var body: some View {
        List(1...5, id: \.self) { index  in

            HStack(spacing: 8) {
                HStack {
                    Text("Item number \(index)")
                    Spacer()
                    }
                    .padding([.leading, .top, .bottom])
                    .background(GeometryReader {
                        Color.blue.preference(key: ViewHeightKey.self, value: [=10=].frame(in: .local).size.height)
                    })


                Text("i")
                    .italic()
                    .font(.title)
                    .frame(width: height, height: height)
                    .background(Color.pink)

                }
                .padding(8)
                .background(Color.yellow)
                .onPreferenceChange(ViewHeightKey.self) {
                    self.height = [=10=]
                }
        }
    }
}

struct ViewHeightKey: PreferenceKey {
    typealias Value = CGFloat
    static var defaultValue = CGFloat.zero
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value += nextValue()
    }
}

我想这就是您要找的:

List(1..<6) { index  in
                HStack {
                    HStack {
                        Text("Item number \(index)")
                        
                        Spacer()
                    }
                    .padding([.leading, .top, .bottom])
                    .background(Color.blue)
                    
                    Text("i")
                        .font(.title)
                        .italic()
                        .frame(maxWidth: .infinity, maxHeight: .infinity)
                        .aspectRatio(1, contentMode: .fill)
                        .background(Color.pink)
                        .fixedSize(horizontal: true, vertical: false)
                        .padding(.leading, 6)
                }
                .padding(6)
                .background(Color.yellow)
            }

答案是这样说的,我不建议给 SwiftUI 太多的自由来决定大小。目前 SwiftUI 最大的问题之一是它决定如何使视图相互适应的方式。如果 SwiftUI 方面出现问题 not-so-good,它可能会导致对 UIKit 的 sizeToFit 方法的调用过多,这可能会降低应用程序的速度,甚至使其崩溃。

但是,如果您在几种不同的情况下尝试过此解决方案并且它有效,您可以假设在您的情况下,让 SwiftUI 选择决定大小是没有问题的。