简体   繁体   English

SwiftUI 如何动态地使视图的前导等于其与超级视图的顶部距离

[英]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 !!
                }
            }
        }
    }
}

backup 备份

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 我怎样才能让 SwiftUI 视图完全填充它的超级视图? - How can I get a SwiftUI View to completely fill its superview? SwiftUI 如何在其超级视图的左上方放置文本? - SwiftUI How to put a TEXT in left top of it's superview? 子视图的大小不等于其父视图 - Subview's size not equal to its superview 如何使用自动布局使其超级视图调整方形视图的大小 - How to make a square view resize with its superview using auto layout 制作按钮动作覆盖它的超级视图 swiftUI - make Button action cover it's superview swiftUI SwiftUI 如何将view的leading对齐到最super view的leading? - SwiftUI how to align the view‘s leading to the most super view’s leading? 当这个视图的高度、宽度与它的 superView 相等时,即使在像 iPad 这样的大屏幕中,我如何才能使任何视图呈球形圆形 - How Can I make any View Rounded In Ball Shape Even In Big Screens Like iPad when this view equal height , width to it's superView 从nib尊重superview的顶部布局指南(导航栏底部)加载视图 - Make view loaded from nib respect superview's top layout guide (Navigation Bar Bottom) 如何使用自动布局动态更改超级视图的高度 - How to dynamically change superview's height with autolayout 如何将Modal Popup的大小更改为等于其Superview的大小? - How to change Modal Popup size to equal to its superview size?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM