[英]SwiftUI How to dynamically make a view's leading equal to its top distance to superview
The image below shows a VStack
with three lines of text.下图显示了一个包含三行文本的VStack
。
The VStack
is inside a ZStack
that also contains a blue square. VStack
位于ZStack
中,该 ZStack 还包含一个蓝色方块。
I have been able to position the blue square such that its vertical center is equal to the vertical center of the Text
that reads "Line 1".我已经能够 position 蓝色正方形,使其垂直中心等于“第 1 行” Text
的垂直中心。 This relationship has to be maintained regardless of the text size.无论文本大小如何,都必须保持这种关系。 In other words, I can't hardcode a specific value for the blue square's vertical offset because it depends on the size of the Text
to its right.换句话说,我无法硬编码蓝色方块的垂直偏移的特定值,因为它取决于右侧Text
的大小。
Notice the green lines above and below the text.注意文本上方和下方的绿线。 This represents the top and bottom edges of the container view.这表示容器视图的顶部和底部边缘。
What I'd like to be able to do (but can't figure out how) is position the blue square's leading edge the same distance to the right as the blue square's top edge is to the top green line.我想做的(但不知道怎么做)是 position 蓝色方块的前缘与蓝色方块的顶部边缘到顶部绿线的距离相同。
For example, say after the blue box is positioned vertically its top happens to be 12 pts down from the top green line.例如,假设蓝色框垂直放置后,它的顶部恰好从顶部的绿线向下 12 点。 In that case the box should move 12 pts to the right of the container's left edge.在这种情况下,盒子应该移动 12 点到容器左边缘的右侧。
Here is the code I've working on:这是我正在处理的代码:
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)
}
}
Possible approach is to use anchor preferences, because they allows to read different position properties of targeted view and use within views layout cycle.可能的方法是使用锚首选项,因为它们允许读取目标视图的不同 position 属性并在视图布局周期内使用。
Here is a demo.这是一个演示。 Tested with Xcode 13.2 / iOS 15.2使用 Xcode 13.2 / iOS 15.2 测试
Note: used padding
instead of offset
, because offset does not affect layout, just in case.注意:使用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.