[英]SwiftUI How to dynamically make a view's leading equal to its top distance to superview
下圖顯示了一個包含三行文本的VStack
。
VStack
位於ZStack
中,該 ZStack 還包含一個藍色方塊。
我已經能夠 position 藍色正方形,使其垂直中心等於“第 1 行” Text
的垂直中心。 無論文本大小如何,都必須保持這種關系。 換句話說,我無法硬編碼藍色方塊的垂直偏移的特定值,因為它取決於右側Text
的大小。
注意文本上方和下方的綠線。 這表示容器視圖的頂部和底部邊緣。
我想做的(但不知道怎么做)是 position 藍色方塊的前緣與藍色方塊的頂部邊緣到頂部綠線的距離相同。
例如,假設藍色框垂直放置后,它的頂部恰好從頂部的綠線向下 12 點。 在這種情況下,盒子應該移動 12 點到容器左邊緣的右側。
這是我正在處理的代碼:
import SwiftUI
extension Alignment {
static let blueBoxAlignment = Alignment(horizontal: .blueBoxLeadingAlignment, vertical: .blueBoxCenterAlignment)
}
extension HorizontalAlignment {
private enum BlueBoxHorizontalAlignment: AlignmentID {
static func defaultValue(in d: ViewDimensions) -> CGFloat {
return d[HorizontalAlignment.leading]
}
}
static let blueBoxLeadingAlignment = HorizontalAlignment(BlueBoxHorizontalAlignment.self)
}
extension VerticalAlignment {
private enum BlueBoxCenterAlignment: AlignmentID {
static func defaultValue(in d: ViewDimensions) -> CGFloat {
return d[VerticalAlignment.center]
}
}
static let blueBoxCenterAlignment = VerticalAlignment(BlueBoxCenterAlignment.self)
}
struct TestView: View {
var body: some View {
ZStack(alignment: .blueBoxAlignment) {
VStack(spacing: 50) {
Text("Line 1")
.alignmentGuide(.blueBoxCenterAlignment) { d in d[VerticalAlignment.center] }
Text("Line 2")
Text("Line 3")
}
.padding([.top, .bottom], 50)
.frame(maxWidth: .infinity)
.border(.green)
Rectangle()
.fill(.blue)
.opacity(0.5)
.frame(width: 50, height: 50)
}
}
}
struct TestView_Previews: PreviewProvider {
static var previews: some View {
TestView()
.edgesIgnoringSafeArea(.bottom)
}
}
可能的方法是使用錨首選項,因為它們允許讀取目標視圖的不同 position 屬性並在視圖布局周期內使用。
這是一個演示。 使用 Xcode 13.2 / iOS 15.2 測試
注意:使用padding
代替offset
,因為 offset 不影響布局,以防萬一。
struct PositionPreferenceKey: PreferenceKey { // << helper key !!
static var defaultValue: [Anchor<CGPoint>] = [] // << use something persistent
static func reduce(value: inout [Anchor<CGPoint>], nextValue: () -> [Anchor<CGPoint>]) {
value.append(contentsOf:nextValue())
}
}
struct TestView: View {
@State private var offset = CGFloat.zero
var body: some View {
ZStack(alignment: .blueBoxAlignment) {
VStack(spacing: 50) {
Text("Line 1")
.alignmentGuide(.blueBoxCenterAlignment) { d in d[VerticalAlignment.center] }
Text("Line 2")
Text("Line 3")
}
.padding([.top, .bottom], 50)
.frame(maxWidth: .infinity)
.border(.green)
Rectangle()
.fill(.blue)
.opacity(0.5)
.frame(width: 50, height: 50)
.anchorPreference(
key: PositionPreferenceKey.self,
value: .top // read position from top !!
) { [$0] }
.padding(.leading, offset) // << apply as X !!
}
.backgroundPreferenceValue(PositionPreferenceKey.self) { prefs in
GeometryReader { gr in
Color.clear.onAppear {
self.offset = gr[prefs[0]].y // << store Y !!
}
}
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.