我是 SwiftUI 的新手 - 不是 iOS 開發的新手。我有很多自定義設計/繪圖要做,這在 SwiftUI 中似乎特別困難。
請耐心等待,并準備好一個長的。
SwiftUI 的優點在于它基于組合,您可以自動調整大小并適應不同的設備。
但是讓我感到困擾的是,一旦您必須相對于彼此調整事物的大小,您就必須迅速轉向 GeometryReader。而 GeometryReader 似乎與 SwiftUI 中的尺寸概念非常矛盾。
與 Autolayout 約束相反,您可以輕松地添加對其他視圖或超級視圖的依賴的相對約束,SwiftUI 似乎并沒有那么容易做到這一點。
假設我想添加一個視圖作為影像的疊加層,確保疊加層始終具有超級視圖的相對填充 - 例如百分比的邊距。
topY、bottomY、leadingX 和 trailingX 與超級視圖調整大小的方式有關,因此內容視圖將始終適合“框架”。(這是一個簡化版本 - 認為“框架”不僅僅是盒子。)
在 Autolayout 中,我只需添加概覽,然后使用一個因子將覆寫層 vidth 約束到父視圖寬度。這樣,覆寫層的填充將隨著父視圖的大小調整而跟隨。
在 SwiftUI 中執行此操作將使您將基本視圖包裝在 GeometryReader 中(通過它自己導致整個視圖填滿整個螢屏并更改坐標系)。手頭的問題是,在 SwiftUI 中添加疊加層時,這與在 UIKit 中在頂部添加視圖的方式相同。現在有兩種不同的方法可以使疊加相對。兩者都涉及有限的像素坐標 - 這是我在這里的主要抱怨。一旦你進入 GeometryReader 的世界,你就進入了基于固定像素定位的世界,并失去了 SwiftUI 中自動擬合的所有優點。
因此,為了使覆寫填充相對于
超級視圖,我可以:A)使用 EdgeInsets() 添加 .padding,其中邊緣插入是由超級視圖大小的一個因素計算的。
B) 使用 .offset() 和 .frame() 以相對于父視圖大小的計算因子偏移覆寫的位置和大小。
僅使用矩形時,這非常簡單:
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"))
)
}
}
}

請注意,未正確居中的居中文本是由 Rectangles 不遵守填充引起的。如果我將黃色 Rectangle 包裝在單獨的 View 中,它將位于黃色框而不是超級框的中心。(不要讓我開始...)
The problems start to surface when I then use this View in another view. Then the GeometryReader starts to mess with the dynamic SwiftUI auto-world.
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"))
}
}
On iPad in landscape the even distribution results in the frame view not filling the entire width and since the frame padding is calculated based on the width, it's now wrong.

Where is the content view now on the iPad?
The VStack distributes the two view evenly, but let's say I want to fit the frame-view in width and maintain aspect and then fit the green view to to the remaining.
In Autolayout I could just set priority of the frame-view, but thats not how it works with SwiftUI. My only way with SwiftUI is to wrap it in yet another super view with yet another GeomotryReader mathematically setting a calculated relationship between the green- and the frame views. And even worse, I have to se sizes on both - there is no way to set the size of one and "fill the gap" automatically with the second.
My issue at hand here is that it seems like the sizing and positioning using GeometryReader seems very tied to pixels and very far from the relative and dynamic concept of SwiftUI.
So, to clarify what I'm looking for is making a frame where the padding scales relatively to the super view size while distributing it vertically unevenly.

How do I approach this in a better and more generic way?
uj5u.com熱心網友回復:
首先 - 您可以使用黃色和藍色視圖簡化第一部分。您可以只設定 的框架大小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://www.uj5u.com/shujuku/403784.html
標籤:
