简体   繁体   English

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

[英]SwiftUI: How to align view to HStack's subview?

I want to align the circle to the TileView "1"'s center at top left.我想将圆圈与左上角的 TileView“1”的中心对齐。 Is there any other like centre constraint of UIView? UIView还有其他类似的中心约束吗?

在此处输入图像描述

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))

        }
    }
}

Here is possible approach using custom alignment guides这是使用自定义对齐指南的可能方法

SwiftUI 中心相关形状

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) { $0[VerticalAlignment.center] }
                        .alignmentGuide(.hCenterred) { $0[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) { $0[VerticalAlignment.center] }
                .alignmentGuide(.hCenterred) { $0[HorizontalAlignment.center] }

        }
    }
}

Test module on GitHub GitHub 上的测试模块

Using GeometryReader and preference使用GeometryReader偏好

人像模式

横向模式

GeometryReader - let us to get geometryProxy of a view ( according to the parent view / space provide by parent ) GeometryReader - 让我们获取一个视图的geometryProxy(根据父视图/父提供的空间)

Note:- child view stays at their own (parent cant force them position or size)注意:- 子视图保持在自己的位置(父母不能强迫他们的位置或大小)

.preference(key:,value:) - this modifier let us get some values( geometryProxy ) out of the view. .preference(key:,value:) - 这个修饰符让我们从视图中获取一些值(geometryProxy)。 so that we can access child's geometry proxy from parent as well.这样我们也可以从父级访问子级的几何代理。

comment below for any question about these topics.关于这些主题的任何问题,请在下方评论。

here is the code, (plus point - you can animate your circle's position easily.(by adding .animation() modifier. Ex- if you want your circle move when user touch on a title)这是代码,(加分 - 您可以轻松地为圆圈的位置设置动画。(通过添加.animation()修饰符。例如,如果您希望在用户触摸标题时移动圆圈)

complete code完整代码


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()
    }   
}

Code explanation ->代码解释->

data model which I want to grab from the child view我想从子视图中获取的数据模型

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

Key钥匙

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 use this, when there are no explicit value set reduce() - getCalled when SwiftUI want to give values (we can use .preference() modifier on many views with the same key) defaultValue - SwiftUI 使用这个,当没有明确的值集时 reduce() - getCalled 当 SwiftUI 想要给出值时(我们可以在具有相同键的许多视图上使用.preference()修饰符)

we add the .preference() modifier which we want to get values我们添加我们想要获取值的.preference()修饰符

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM