Layout — position

1. position 介绍 #

SwiftUI 中,position 是一个用于设置 视图位置 的修饰符,它定义了视图 锚点(Anchor Point) 在父容器坐标系中的 绝对位置

特点: #

  • 原点: position 的坐标系统是基于 父容器 的坐标系(视图左上角是原点 (0, 0))。
  • 参考点: position 定义的位置指的是视图相对于其锚点(默认是视图的中心点,见 .anchor) 的绝对位置。
  • 不同于其他布局修饰符(例如 .offset().alignmentGuide())是基于 相对布局 的,position 是一种完全的 绝对布局

2. position 的语法 #

基本语法如下:

func position(x: CGFloat, y: CGFloat) -> some View
  • x:
    • 从父视图的左上角水平向右的偏移量,单位为 点(points)
  • y:
    • 从父视图的左上角垂直向下的偏移量,单位为 点(points)

示例代码: #

Text("Hello, SwiftUI!")
    .position(x: 100, y: 150) // 定义绝对位置

3. 原点(坐标系) #

原点位置:父容器的左上角 (0, 0) #

  • position 修饰符的坐标是 相对于其父视图的左上角(原点(0, 0))来说的。
  • 例如,对于一个父视图大小为 200x200 的容器:
    • position(x: 100, y: 100) 将把子视图放在父视图的中心。
    • position(x: 0, y: 0) 将子视图的 锚点 放在父视图的左上角。

视图锚点:视图默认以中心作为位置参考 #

  • position 定位时以视图的锚点(默认是 中心点)作为坐标参照点。
  • 例如,一个宽高都是 100 的正方形视图,如果使用 position(x: 100, y: 100) 设置其位置,正方形的中心会放置在 (100, 100)

示例 1:中心点定位 #

struct ExampleView: View {
    var body: some View {
        ZStack {
            Color.gray // 父容器背景
                .frame(width: 200, height: 200)
            
            Text("Hello, SwiftUI!")
                .background(Color.red)
                .position(x: 100, y: 100) // 文本的中心放在 (100, 100)
        }
    }
}

结果:

  • 父容器的尺寸是 200x200
  • Text 的中心被放置在 (100, 100),即父容器的中心。

更改锚点(Anchor Point):使用 .position(_:anchor:) #

如果需要更改视图默认的锚点,可以使用该方法:

func position(_ point: CGPoint, anchor: UnitPoint) -> some View
  • 参数:
    • point:目标点位置(绝对位置)。
    • anchor:设置视图的哪个点作为参照位置(默认是 .center)。
      • 可选值包括:.center.top.bottom.leading.trailing.topLeading.topTrailing.bottomLeading.bottomTrailing 等。

示例 2:将锚点设置为左上角 #

struct ExampleView: View {
    var body: some View {
        ZStack {
            Color.gray // 父容器背景
                .frame(width: 200, height: 200)
            
            Text("Hello, SwiftUI!")
                .background(Color.red)
                .position(CGPoint(x: 100, y: 100), anchor: .topLeading) // 以左上角为锚点
        }
    }
}

结果:

  • Text 的左上角被放置在 (100, 100)

4. position 的其他参数设置 #

4.1 使用 CGPoint 替代 xy #

除了分开指定 xy,可以通过提供一个点(CGPoint)来定义位置。

示例: #

Text("Hello, SwiftUI!")
    .position(CGPoint(x: 50, y: 75))

4.2 动态位置调整 #

position 的值可以动态调整。结合 @State 或其他数据源,可以实现视图位置的动态更新。

示例:拖拽操作动态改变位置 #

struct DraggableView: View {
    @State private var viewPosition = CGPoint(x: 100, y: 100) // 初始位置

    var body: some View {
        Text("Drag Me!")
            .padding()
            .background(Color.blue)
            .cornerRadius(8)
            .position(viewPosition) // 使用状态控制位置
            .gesture(
                DragGesture() // 拖拽手势
                    .onChanged { value in
                        viewPosition = value.location // 视图随着拖拽实时更新位置
                    }
            )
    }
}

效果:

  • 子视图 Text 的位置可以通过拖拽动态改变。
  • 使用 DragGesture.onChanged 回调更新位置。

5. positionoffset的区别 #

虽然 positionoffset 都可以用于调整视图的位置,但它们的行为有以下区别:

5.1 position(绝对定位) #

  • 绝对坐标系,用来设置子视图在 父容器内的具体位置
  • 视图将从布局中“脱离”,不再参与父容器的其他布局运算。
  • 默认参考点是 视图的中心

5.2 offset(相对偏移) #

  • 是视图相对于其当前位置(受父容器布局影响)的 相对偏移量
  • 不会将视图从布局中移除,仍然保留其布局本身的尺寸和位置。

示例对比: #

struct PositionOffsetComparison: View {
    var body: some View {
        ZStack {
            Color.gray.frame(width: 200, height: 200)
            
            // 使用 position,视图绝对定位
            Text("Position")
                .background(Color.red)
                .position(x: 50, y: 50) // 放置在 (50, 50)
            
            // 使用 offset,相对偏移
            Text("Offset")
                .background(Color.blue)
                .offset(x: -50, y: -50) // 偏移当前默认位置
        }
    }
}

区别:

  1. position

    • Text 被设置在父容器 (50, 50) 的绝对位置。
    • 会脱离默认的布局(不再参与父容器布局计算)。
  2. offset

    • Text 的位置基于默认布局,在渲染后从默认位置做相对偏移(仍然参与布局计算)。

6. 动画和 position #

position 和动画结合使用,可以实现视图的位置过渡。配合 SwiftUI 的动画语法,使 UI 更具动态效果。

示例:视图位置动画 #

struct AnimatedPositionView: View {
    @State private var togglePosition = false

    var body: some View {
        Text("Animated Position")
            .padding(10)
            .background(Color.pink)
            .cornerRadius(8)
            .position(x: togglePosition ? 300 : 50, y: 300) // 动态改变 x 坐标
            .animation(.easeInOut(duration: 1), value: togglePosition) // 添加动画
            .onTapGesture {
                togglePosition.toggle() // 改变位置开关
            }
    }
}

效果:

  • 点击视图,将动态改变位置,每次切换的动画时间为 1 秒。

7. 总结 #

要点内容
position 原点视图相对于其父容器的左上角((0, 0))进行定位。
默认锚点position 默认以 视图中心 为定位锚点。
绝对布局 vs 相对布局position 将视图完全移出父容器的布局运算(绝对位置),而 offset 是基于当前布局的偏移调整(相对位置)。
动态更新可以结合 @State 或手势动态调整视图的位置。
动画支持通过配合动画实现平滑的视图位置变换效果。

position 是 SwiftUI 中处理 绝对布局 的重要工具,适合在复杂界面中完全指定视图位置,如游戏开发、拖拽布局,或需要精确控制子视图的场景。

anchor #

在 SwiftUI 中,positionanchor定位视图的方法,用于指定视图在父布局中的位置和对齐方式。它们有不同的功能,但可以协同工作。以下是具体介绍以及它们之间的关系!


position 是什么? #

  • position 是一个预定义的几何属性,用来指定视图的 绝对位置坐标
  • 它的核心作用是在父容器的坐标空间中设置视图的位置,而不依赖视图的对齐方式或大小。
  • 基于中心点position 定位的是 视图的中心点,而不是视图的左上角。

声明方式 #

func position(_ position: CGPoint) -> some View
func position(x: CGFloat, y: CGFloat) -> some View
  • 参数:
    • x:视图中心的 X 坐标。
    • y:视图中心的 Y 坐标。

特点 #

  1. 当使用 position 后,视图将不再遵循父视图的布局规则,由你指定的位置控制。
  2. position 会将视图放到绝对位置,而不会考虑它与其他视图的相对布局。

简单示例 #

struct PositionExample: View {
    var body: some View {
        Rectangle()
            .fill(Color.blue)
            .frame(width: 100, height: 100)
            .position(x: 150, y: 150) // 将视图中心定位到 (150, 150)
            .border(Color.red, width: 2)
    }
}

运行效果:

  • 蓝色矩形的中心点会被放置在 (150, 150)。
  • 如果父容器的尺寸是 (300, 300),矩形的左上角会在大约 (100, 100)(因为 position 指的是中心点)。

anchor 是什么? #

  • anchor 是一个描述视图内部对齐点的工具,用于定义视图的布局基准点。
  • 它通过跟父视图的对齐规则和布局系统协作,来设置视图的内部对齐点。
  • 基于内部点:锚点用于指定 视图的某个位置点(而不是总是中心)作为对齐或定位的基准。

如何设置 anchor #

在许多布局 API 中,可以通过 Anchor 类型(如 .top, .center, .bottomTrailing 等)定义锚点。

常见的锚点类型: #

  • 预定义的 Anchor (UnitPoint)
    • .center:中心点。
    • .top:顶部的中心。
    • .topLeading:左上角。
    • .topTrailing:右上角。
    • .bottom:底部的中心。
    • .bottomLeading:左下角。
    • .bottomTrailing:右下角。
    • .leading:左边的中点。
    • .trailing:右边的中点。

positionanchor 的关系 #

核心区别: #

  1. position

    • 控制视图的在父视图坐标空间中的绝对位置
    • 默认基于 视图的中心点
    • 修改 position 会直接改变该视图在屏幕上的绝对位置。
  2. anchor

    • 用于定义视图的 对齐参考点,即控制视图本身内部的哪个点参与布局。
    • 改变锚点,不会直接影响视图的绝对位置,但会影响它的布局。

二者的关系: #

anchor 通常定义视图的对齐点,而 position 则定义这个对齐点相对于父视图的位置。

简单示例结合 #

以下代码设置锚点和位置,展示两者如何协同工作:

struct PositionAndAnchorExample: View {
    var body: some View {
        Rectangle()
            .fill(Color.blue)
            .frame(width: 100, height: 100)
            .position(x: 150, y: 150)    // 定义视图的中心在父视图的 (150,150)
            .anchorPreference(key: AnchorKey.self, value: .topLeading) { anchor in
                return anchor           // 设置锚点为左上角
            }
            .border(Color.red, width: 2)
    }
}

position 使用场景 #

  • 场景 1:绝对定位
    position 允许你完全控制视图的位置,非常适合自由布局的场景。
struct AbsolutePositionExample: View {
    var body: some View {
        ZStack {
            Color.white.edgesIgnoringSafeArea(.all)

            Text("Hello, World!")  // 文本放置到特定点
                .position(x: 200, y: 300)
                .foregroundColor(.blue)
        }
    }
}
  • 场景 2:动画效果
    可以通过修改 position 来实现平移和动画移动效果:
struct AnimatedPositionExample: View {
    @State private var x: CGFloat = 50
    @State private var y: CGFloat = 100

    var body: some View {
        VStack {
            Rectangle()
                .fill(Color.green)
                .frame(width: 100, height: 100)
                .position(x: x, y: y)
                .animation(.easeInOut, value: x)

            Button("Move") {
                x = 200 // 改变 X 坐标的位置
                y = 300 // 改变 Y 坐标的位置
            }
        }
    }
}

anchor 使用场景 #

场景 1:对齐布局的视图锚点 #

比如在 VStack 中对视图的对齐点进行控制。

struct AnchorAlignmentExample: View {
    var body: some View {
        VStack(alignment: .leading) { // 定义所有子视图按左对齐
            Text("First Line")       // 默认按 anchor 的 `.leading` 对齐。
            Text("Second Line")
                .alignmentGuide(.leading) { d in
                    d[.trailing]     // 修改对齐点为视图右边缘
                }
        }
        .border(Color.red)
    }
}

场景 2:基于锚点的偏移 #

锚点可以用于设置旋转效果或视图的变换中心。

struct AnchorRotationExample: View {
    var body: some View {
        Rectangle()
            .fill(Color.blue)
            .frame(width: 100, height: 100)
            .rotationEffect(.degrees(45), anchor: .topLeading)
            .border(Color.red)
    }
}
  • 解释: 这里我们设置锚点为 .topLeading(左上角),因此矩形会基于左上角旋转,而不是基于中心点。

总结:positionanchor 的异同 #

特性positionanchor
核心功能设置视图中心点在父视图的绝对坐标位置定义视图内部哪个点参与对齐或变换
影响范围改变视图在父视图中的位置改变视图的布局基准点或对齐方式
单位或参考点父视图中的坐标系统视图自身内部的点(如中心或边角点)
常见用途自由布局、动画对齐、旋转、偏移
会忽略父视图布局规则?

实际开发: #

  1. 使用 position:如果需要精确定位到父视图的某个位置。
  2. 使用 anchor:如果需要定义视图的参考点用于对齐或变换。

两者可以独立使用,也可以结合起来使用来满足复杂的布局需求。

本文共 3419 字,上次修改于 Jan 10, 2025