SwiftUI — GestureState

GestureState 的使用与场景 #

GestureStateSwiftUI 中一个专门用于捕获和管理手势状态的属性包装器(@GestureState)。它适用于在 手势操作中临时存储状态,并能让手势操作在结束时自动重置状态值。

GestureState 常用于在以下场景:

  1. 临时存储手势的状态值(如拖动的偏移量、缩放的比例等)。
  2. 在手势进行的过程中动态响应并更新界面。
  3. 手势结束时,自动将状态重置,加速状态管理。

1. 使用场景 #

以下是一些常见场景,GestureState 会非常有用:

1.1 拖动手势DragGesture#

  • 临时存储手势的偏移(例如手指拖动过程中的位移距离)。
  • 动态更新视图的位置,展示拖动效果。

1.2 缩放手势MagnificationGesture#

  • 临时存储手势的缩放比例。
  • 动态调整视图的缩放倍数。

1.3 旋转手势RotationGesture#

  • 临时存储旋转角度。
  • 用户旋转图片或对象时,更新旋转角度。

1.4 组合手势SimultaneousGestureCombinedGesture#

  • 同时捕获多个手势,例如支持拖动和缩放,需要在手势结束时将状态恢复到初始值。

特点 #

  • @State 类似,但 GestureState 在手势结束后会自动复位为初始值
  • 避免了手势数据需要重复重置的麻烦。

2. 语法与基本使用 #

GestureState 的基本用法包括:

  1. @GestureState 定义一个变量,作为临时状态值。
  2. 配合 SwiftUI 手势(如 DragGesture, MagnificationGesture)实时更新状态。
  3. 手势结束后,GestureState 会自动还原到初始状态。

基本语法 #

@GestureState var temporaryState: ValueType = initialValue
  • ValueType:状态的类型,例如 CGSize(拖动的偏移量)、Double(缩放比例或旋转角度)。
  • initialValue:手势未激活时的默认初始值。

3. 示例与实际应用 #

3.1 拖动手势(DragGesture #

示例:用 GestureState 存储临时偏移量 #

下面的示例中,用户在拖动时动态移动一个视图。

import SwiftUI

struct DragGestureExample: View {
    @GestureState private var dragOffset: CGSize = .zero // 临时存储拖动位移
    @State private var finalOffset: CGSize = .zero // 最终视图位置

    var body: some View {
        Circle()
            .fill(Color.blue)
            .frame(width: 100, height: 100)
            .offset(x: finalOffset.width + dragOffset.width, y: finalOffset.height + dragOffset.height) // 结合拖动的偏移
            .gesture(
                DragGesture()
                    .updating($dragOffset) { value, gestureState, _ in
                        // 创建 `dragOffset` 变化
                        gestureState = value.translation
                    }
                    .onEnded { value in
                        // 手势结束时更新最终偏移
                        finalOffset.width += value.translation.width
                        finalOffset.height += value.translation.height
                    }
            )
    }
}

执行流程

  1. @GestureState dragOffset 临时存储拖动的偏移。
  2. 用户拖动过程中,gesture.updating 实时更新状态至 dragOffset
  3. 手势结束,偏移自动回归初始值(dragOffset = .zero),同时将最终偏移存储在 finalOffset 中。

3.2 缩放手势(MagnificationGesture #

示例:缩放视图 #

通过 GestureState 临时存储缩放比例,动态放大或缩小视图。

import SwiftUI

struct MagnificationGestureExample: View {
    @GestureState private var magnificationValue: CGFloat = 1.0 // 临时存储缩放比例
    @State private var scale: CGFloat = 1.0 // 最终缩放比例

    var body: some View {
        Image(systemName: "photo")
            .resizable()
            .scaledToFit()
            .frame(width: 200, height: 200)
            .scaleEffect(scale * magnificationValue) // 缩放效果
            .gesture(
                MagnificationGesture()
                    .updating($magnificationValue) { value, gestureState, _ in
                        gestureState = value // 临时更新缩放状态
                    }
                    .onEnded { value in
                        // 手势结束时,累积缩放比例
                        scale *= value
                    }
            )
    }
}

执行流程

  1. 用户触发捏合手势时,magnificationValue 存储缩放比例。
  2. 视图动态响应缩放比例变化。
  3. 手势结束时,magnificationValue 自动复位为 1.0,记录的缩放累积到最终比例 scale

3.3 旋转手势(RotationGesture #

示例:旋转图片 #

通过 GestureState 存储临时旋转角度。

import SwiftUI

struct RotationGestureExample: View {
    @GestureState private var rotationDegrees: Angle = .zero
    @State private var finalRotation: Angle = .zero

    var body: some View {
        Image(systemName: "arrow.left.circle.fill")
            .resizable()
            .frame(width: 150, height: 150)
            .rotationEffect(finalRotation + rotationDegrees) // 旋转效果
            .gesture(
                RotationGesture()
                    .updating($rotationDegrees) { value, gestureState, _ in
                        gestureState = value // 存储临时旋转角度
                    }
                    .onEnded { value in
                        // 累加旋转角度
                        finalRotation += value
                    }
            )
    }
}

3.4 组合手势:拖动 + 缩放 #

通过同时支持拖动和缩放手势,实现更复杂的交互。

struct CombinedGestureExample: View {
    @GestureState private var dragOffset: CGSize = .zero
    @GestureState private var magnification: CGFloat = 1.0

    @State private var finalOffset: CGSize = .zero
    @State private var currentScale: CGFloat = 1.0

    var body: some View {
        Circle()
            .fill(Color.red)
            .frame(width: 100, height: 100)
            .offset(x: finalOffset.width + dragOffset.width, y: finalOffset.height + dragOffset.height)
            .scaleEffect(currentScale * magnification) // 缩放效果
            .gesture(
                SimultaneousGesture( // 同时捕捉缩放和拖动
                    DragGesture()
                        .updating($dragOffset) { value, gestureState, _ in
                            gestureState = value.translation
                        }
                        .onEnded { value in
                            finalOffset.width += value.translation.width
                            finalOffset.height += value.translation.height
                        },

                    MagnificationGesture()
                        .updating($magnification) { value, gestureState, _ in
                            gestureState = value
                        }
                        .onEnded { value in
                            currentScale *= value
                        }
                )
            )
    }
}

4. GestureState@State 的区别 #

特性GestureState@State
用途临时存储手势状态,仅在手势过程中生效。常规状态存储,贯穿整个组件生命周期。
状态重置手势完成后状态会自动重置为初始值。必须手动重置状态值。
常见场景拖动、缩放、旋转等动态、临时的状态更新。长期存储位置、颜色、大小等静态变量。
手势结合使用通常用于 gesture.updating 更新临时状态。通常与 gesture.onEnded 更新最终状态。

5. 总结 #

@GestureState 的主要特点和最佳使用场景:

  • 特点
    1. 临时状态存储,手势结束时自动清空。
    2. 适合处理短生命周期的状态。
  • 最佳场景
    • 拖动(DragGesture)的角度或偏移。
    • 缩放(MagnificationGesture)中的比例变化。
    • 旋转(RotationGesture)中的角度变化。
    • 多手势组合处理时,用作各个手势的临时数据容器。
本文共 1794 字,上次修改于 Jan 12, 2025