在 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);
}
}
}
在我看来,最好设置 VStack
或 HStack
的背景颜色,而不是直接设置 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 选择决定大小是没有问题的。
我正在尝试使用 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);
}
}
}
在我看来,最好设置 VStack
或 HStack
的背景颜色,而不是直接设置 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 选择决定大小是没有问题的。