SwiftUI:如何将视图与 HStack 的子视图对齐?

SwiftUI: How to align view to HStack's subview?

我想将圆圈与左上角的 TileView“1”中心对齐。还有类似UIView的center constraint吗?

struct BoardView: View {
    var body: some View {
        ZStack {
            VStack {
                HStack {
                    TileView(number: 1)
                    TileView(number: 2)
                }
                HStack {
                    TileView(number: 3)
                    TileView(number: 4)
                }
            }
            Circle()
                .frame(width: 30, height: 30)
                .foregroundColor(Color.red.opacity(0.8))

        }
    }
}

这是使用自定义对齐指南的可能方法

extension VerticalAlignment {
   private enum VCenterAlignment: AlignmentID {
      static func defaultValue(in dimensions: ViewDimensions) -> CGFloat {
         return dimensions[VerticalAlignment.center]
      }
   }
   static let vCenterred = VerticalAlignment(VCenterAlignment.self)
}

extension HorizontalAlignment {
   private enum HCenterAlignment: AlignmentID {
      static func defaultValue(in dimensions: ViewDimensions) -> CGFloat {
         return dimensions[HorizontalAlignment.center]
      }
   }
   static let hCenterred = HorizontalAlignment(HCenterAlignment.self)
}

struct BoardView: View {
    var body: some View {
        ZStack(alignment: Alignment(horizontal: .hCenterred, vertical: .vCenterred)) {
            VStack {
                HStack {
                    TileView(number: 1)
                        .alignmentGuide(.vCenterred) { [=10=][VerticalAlignment.center] }
                        .alignmentGuide(.hCenterred) { [=10=][HorizontalAlignment.center] }
                    TileView(number: 2)
                }
                HStack {
                    TileView(number: 3)
                    TileView(number: 4)
                }
            }
            Circle()
                .frame(width: 30, height: 30)
                .foregroundColor(Color.red.opacity(0.8))
                .alignmentGuide(.vCenterred) { [=10=][VerticalAlignment.center] }
                .alignmentGuide(.hCenterred) { [=10=][HorizontalAlignment.center] }

        }
    }
}

使用GeometryReader首选项

GeometryReader - 让我们获取视图的 geometryProxy(根据 parent 视图/space 由 parent 提供)

注意:- child 视图保留在它们自己的位置(parent 不能强制它们的位置或大小)

.preference(key:,value:) - 这个修饰符让我们从视图中获取一些值(geometryProxy)。这样我们也可以从 parent 访问 child 的几何代理。

如有任何关于这些主题的问题,请在下方发表评论。

这是代码, (加点 - 你可以轻松地为你的圈子的位置设置动画。(通过添加 .animation() 修饰符。例如 - 如果你希望你的圈子在用户触摸标题时移动)

完整代码


import SwiftUI

struct ContentView: View {

    @State var centerCoordinateOfRectangele: MyPreferenceData = MyPreferenceData(x: 0, y: 0)
    var body: some View {

        ZStack {
            VStack {
                HStack {
                    GeometryReader { (geometry)  in
                        TileView(number: 1)
                            .preference(key: MyPreferenceKey.self, value: MyPreferenceData(x: geometry.frame(in: .named("spaceIWantToMoveMyView")).midX, y:  geometry.frame(in: .named("spaceIWantToMoveMyView")).midY))

                    }
                    TileView(number: 2)
                }.onPreferenceChange(MyPreferenceKey.self) { (value) in
                        self.centerCoordinateOfRectangele = value
                }
                HStack {
                    TileView(number: 3)
                    TileView(number: 4)
                }
            }
            Circle()
                .frame(width: 30, height: 30)
                .foregroundColor(Color.red.opacity(0.8))
                .position(x: centerCoordinateOfRectangele.x, y: centerCoordinateOfRectangele.y)

        }
        .coordinateSpace(name: "spaceIWantToMoveMyView") //to get values relative to this layout
                                                        // watch where i have use this name


    }
}

struct TileView: View{
    let number: Int

    var body: some View{
        GeometryReader { (geometry) in
            RoundedRectangle(cornerRadius: 30)
        }
    }
}

struct MyPreferenceData:Equatable {
    let x: CGFloat
    let y: CGFloat
}

struct MyPreferenceKey: PreferenceKey {

    typealias Value = MyPreferenceData

    static var defaultValue: MyPreferenceData = MyPreferenceData(x: 0, y: 0)

    static func reduce(value: inout MyPreferenceData, nextValue: () -> MyPreferenceData) {
        value = nextValue()
    }   
}

代码解释 ->

我想从 child 视图中获取的数据模型

struct MyPreferenceData:Equatable {
    let x: CGFloat
    let y: CGFloat
}

关键

struct MyPreferenceKey: PreferenceKey {

    typealias Value = MyPreferenceData

    static var defaultValue: MyPreferenceData = MyPreferenceData(x: 0, y: 0)

    static func reduce(value: inout MyPreferenceData, nextValue: () -> MyPreferenceData) {
        value = nextValue()
    }
}

defaultValue - SwiftUI 在没有明确设置值时使用它 reduce() - 当 SwiftUI 想要提供值时调用(我们可以在具有相同键的许多视图上使用 .preference() 修饰符)

我们添加我们想要获取值的 .preference() 修饰符