我是SwiftUI新手--对iOS开发并不陌生。我有很多定制的设计/绘图要做,这在SwiftUI中似乎特别困难。
请容忍我,准备一个长的。
SwiftUI最棒的地方在于它是基于组合的,您可以自动调整尺寸并安装到不同的设备上。
但让我烦恼的是,一旦你必须调整事物的相对大小,你就必须迅速地求助于。GeometryReader似乎与SwiftUI.中的大小概念非常矛盾。
与Autolayout约束相反,您可以轻松地添加依赖于其他视图或超级视图的相对约束,SwiftUI似乎不那么容易做到这一点。
让我说,我想添加一个视图作为一个图像的叠加,确保覆盖将始终有一个相对填充的超级视图一样的利润率百分比。

topY、bottomY、leadingX和trailingX相对于超级视图调整大小的方式,因此内容视图总是适合于“框架”。(这是一个简化的版本-考虑一下“框架”更像是盒子。)
在Autolayout中,我只需要添加概览,然后用一个因子将覆盖vidth限制到superview宽度。这样,随着superview调整大小,覆盖层的填充将随之进行。
在SwiftUI中这样做将让您将基本视图包装在一个GeometryReader中(通过它,selv会使整个视图填充整个屏幕并更改坐标系统)。当前的问题是,当在SwiftUI中添加覆盖时,这与在UIKit的顶部添加视图的方式相同。现在有两种不同的方法使叠加相对。两者都涉及有限像素坐标-这是我在这里的主要抱怨。一旦你进入GeometryReader的世界,你就进入了基于固定像素定位的世界,并在SwiftUI中展示了自动拟合的所有好部分。
因此,要使覆盖填充相对于superview,我可以:
A)使用.padding ()添加EdgeInsets(),其中边嵌入是由超级视图大小的因子计算的。
B)使用.offset()和.frame()来通过计算出的相对于superview大小的因子来抵消叠加的位置和大小。
在使用矩形时,这非常简单:
struct RelativeView: View {
var body: some View {
GeometryReader { geo in
Rectangle() // Superview
.foregroundColor(Color.blue)
.overlay(Rectangle() // Relative overview (content view)
.foregroundColor(Color.yellow)
.padding(EdgeInsets(top: geo.size.height/4, leading: (geo.size.width*0.25)/2, bottom: geo.size.height/4, trailing: (geo.size.width*0.25)/2))
.overlay(Text("Yellow overlay must be 1/2 height and 3/4 width of the blue"))
)
}
}
}

当超级视图是缩放到.fit的图像时,真正的问题就开始了:
struct RelativeView: View {
var body: some View {
GeometryReader { geo in
let realImageWidth = CGFloat(4032)
let viewWidth = geo.size.width
let relativeWidthFactor = viewWidth/realImageWidth
let leadingX = CGFloat(1110)
let trailingX = CGFloat(750)
let topY = CGFloat(770)
let bottomY = CGFloat(936)
Image("TestBackground")
.resizable()
.aspectRatio(contentMode: .fit) // Superview
.overlay(Rectangle() // Relative overview (content view)
.foregroundColor(Color.yellow)
.padding(EdgeInsets(top: topY * relativeWidthFactor, leading: leadingX * relativeWidthFactor, bottom: bottomY * relativeWidthFactor, trailing: trailingX * relativeWidthFactor))
.overlay(Text("Yellow overlay must be 1/2 height and 3/4 width of the blue"))
)
}
}
}

请注意,没有正确居中的中间文本是由矩形不尊重填充引起的。如果我是在一个单独的视图包装黄色矩形,它将在黄色框的中心,而不是超级。(别让我开始.)
当我在另一个视图中使用此视图时,问题就开始浮出水面。然后,GeometryReader开始扰乱动态SwiftUI汽车世界。
import SwiftUI
struct GrainCart3dView: View {
var body: some View {
GeometryReader { geo in
VStack {
Rectangle().foregroundColor(.green)
RelativeContainerView()
}
}
}
}
struct RelativeContainerView: View {
var body: some View {
GeometryReader { geo in
let realImageWidth = CGFloat(4032)
let viewWidth = geo.size.width
let relativeWidthFactor = viewWidth/realImageWidth
let leadingX = CGFloat(1110)
let trailingX = CGFloat(750)
let topY = CGFloat(770)
let bottomY = CGFloat(936)
Image("TestBackground")
.resizable()
.aspectRatio(contentMode: .fit) // Superview
.overlay(RelativeContentView() // Relative overview (content view)
.padding(EdgeInsets(top: topY * relativeWidthFactor, leading: leadingX * relativeWidthFactor, bottom: bottomY * relativeWidthFactor, trailing: trailingX * relativeWidthFactor))
)
}
}
}
struct RelativeContentView: View {
var body: some View {
Rectangle()
.foregroundColor(.yellow)
.overlay(Text("Yellow overlay must be 1/2 height and 3/4 width of the blue"))
}
}在景观中的iPad上,均匀分布导致帧视图不能填充整个宽度,而且由于帧填充是根据宽度计算的,所以现在是错误的。



现在iPad?上的内容视图在哪里?
VStack平均分配这两个视图,但假设我希望在宽度和维护方面适合框架视图,然后将绿色视图与其余视图相匹配。
在Autolayout中,我可以设置框架视图的优先级,但这并不是SwiftUI的工作方式。我使用SwiftUI的唯一方法是将它封装在另一个超级视图中,用另一个GeomotryReader数学地设置绿色视图和框架视图之间的计算关系。更糟糕的是,我必须在这两种情况下都考虑尺寸--没有办法设置一个大小,然后用第二个来自动“填补空白”。
这里我要讨论的问题是,使用GeometryReader的大小和定位似乎与像素紧密相连,与SwiftUI的相对和动态概念相去甚远。
所以,为了澄清我要找的是做一个框架,其中填充比例相对于超级视图的大小,而它垂直分布不均匀。

如何以更好和更通用的方式来处理这个问题?
发布于 2022-01-03 15:34:37
首先,你可以用黄色和蓝色的视图简化第一部分。您只需设置Text的帧大小,添加背景,然后使GeometryReader填充覆盖。这是远,比试图计算出每个边缘的嵌入要容易得多。
代码:
struct ContentView: View {
var body: some View {
Color.blue
.overlay(
GeometryReader { geo in
Text("Yellow overlay must be 1/2 height and 3/4 width of the blue")
.frame(width: geo.size.width * 0.75, height: geo.size.height * 0.5)
.background(Color.yellow)
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
)
}
}接下来,我们做一件类似于上面的事情,但是现在用图像:
struct RelativeView: View {
var body: some View {
Image("TestBackground")
.resizable()
.aspectRatio(contentMode: .fit)
.overlay(
GeometryReader { geo in
Text("Yellow overlay must be 1/2 height and 3/4 width of the blue")
.frame(width: geo.size.width * 0.75, height: geo.size.height * 0.5)
.background(Color.yellow)
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
)
}
}结果:

对于尝试添加另一个视图的下一部分,您也可以进行这些小的调整。这只是使图像比例尺适合屏幕,并给予它优先于绿色。
您可能希望使用VStack(spacing: 0) { ... },以消除绿色与图像之间的差距。
代码:
struct ContentView: View {
var body: some View {
VStack {
Color.green
RelativeView()
.scaledToFit()
.layoutPriority(1)
}
}
}结果:

https://stackoverflow.com/questions/70567767
复制相似问题