SwiftUI 提供了声明式的手势和动画 API,用于创建流畅的交互体验。手势识别器可以直接附加到视图上,动画则通过修饰器实现。
重点:
- 基础手势的使用方法
- 组合手势的实现
- 动画的类型和时机
- 自定义动画曲线
- 转场动画效果
基础手势 Link to heading
- 点击手势:
struct TapDemo: View {
@State private var isPressed = false
var body: some View {
Circle()
.fill(isPressed ? Color.red : Color.blue)
.frame(width: 100, height: 100)
.onTapGesture(count: 1) {
isPressed.toggle()
}
}
}
- 长按手势:
struct LongPressDemo: View {
@State private var isLoading = false
var body: some View {
Circle()
.fill(isLoading ? Color.green : Color.gray)
.frame(width: 100, height: 100)
.onLongPressGesture(minimumDuration: 1) {
isLoading = true
} onPressingChanged: { isPressing in
// 按压状态变化
}
}
}
- 拖拽手势:
struct DragDemo: View {
@State private var offset = CGSize.zero
var body: some View {
Circle()
.fill(Color.blue)
.frame(width: 100, height: 100)
.offset(offset)
.gesture(
DragGesture()
.onChanged { gesture in
offset = gesture.translation
}
.onEnded { _ in
withAnimation {
offset = .zero
}
}
)
}
}
组合手势 Link to heading
- 同时识别:
struct CombinedGestureDemo: View {
@State private var scale = 1.0
@State private var angle = Angle.zero
var body: some View {
Image(systemName: "star.fill")
.font(.system(size: 50))
.foregroundColor(.yellow)
.scaleEffect(scale)
.rotationEffect(angle)
.gesture(
SimultaneousGesture(
MagnificationGesture()
.onChanged { value in
scale = value
},
RotationGesture()
.onChanged { value in
angle = value
}
)
)
}
}
- 顺序识别:
struct SequencedGestureDemo: View {
@State private var offset = CGSize.zero
@State private var isDragging = false
var body: some View {
Circle()
.fill(isDragging ? Color.red : Color.blue)
.frame(width: 100, height: 100)
.offset(offset)
.gesture(
LongPressGesture(minimumDuration: 0.5)
.sequenced(before: DragGesture())
.onChanged { value in
switch value {
case .first(true):
isDragging = true
case .second(true, let drag):
offset = drag?.translation ?? .zero
default:
break
}
}
.onEnded { _ in
isDragging = false
offset = .zero
}
)
}
}
基础动画 Link to heading
- 隐式动画:
struct ImplicitAnimationDemo: View {
@State private var scale = 1.0
var body: some View {
Circle()
.fill(Color.blue)
.frame(width: 100, height: 100)
.scaleEffect(scale)
.animation(.spring(), value: scale)
.onTapGesture {
scale = scale == 1.0 ? 2.0 : 1.0
}
}
}
- 显式动画:
struct ExplicitAnimationDemo: View {
@State private var isRotated = false
var body: some View {
Button("旋转") {
withAnimation(.spring(
response: 0.5,
dampingFraction: 0.5,
blendDuration: 0
)) {
isRotated.toggle()
}
}
.rotationEffect(Angle(degrees: isRotated ? 360 : 0))
}
}
自定义动画 Link to heading
- 动画曲线:
struct CustomAnimationDemo: View {
@State private var offset: CGFloat = 0
var body: some View {
Circle()
.fill(Color.blue)
.frame(width: 50, height: 50)
.offset(x: offset)
.animation(
.timingCurve(0.68, -0.6, 0.32, 1.6),
value: offset
)
.onTapGesture {
offset = offset == 0 ? 200 : 0
}
}
}
- 重复动画:
struct RepeatAnimationDemo: View {
@State private var isAnimating = false
var body: some View {
Circle()
.fill(Color.blue)
.frame(width: 50, height: 50)
.scaleEffect(isAnimating ? 1.5 : 1)
.opacity(isAnimating ? 0.5 : 1)
.animation(
.easeInOut(duration: 1)
.repeatForever(autoreverses: true),
value: isAnimating
)
.onAppear {
isAnimating = true
}
}
}
转场动画 Link to heading
- 视图切换:
struct TransitionDemo: View {
@State private var showDetail = false
var body: some View {
VStack {
if showDetail {
DetailView()
.transition(.move(edge: .trailing))
} else {
SummaryView()
.transition(.move(edge: .leading))
}
}
.animation(.spring(), value: showDetail)
}
}
- 自定义转场:
struct CustomTransitionDemo: View {
@State private var show = false
var body: some View {
VStack {
Button("切换") {
show.toggle()
}
if show {
Rectangle()
.fill(Color.blue)
.frame(width: 200, height: 200)
.transition(
.asymmetric(
insertion: .scale.combined(with: .opacity),
removal: .slide
)
)
}
}
.animation(.spring(), value: show)
}
}
实用技巧 Link to heading
- 动画状态监听:
struct AnimationStateDemo: View {
@State private var isAnimating = false
@State private var completed = false
var body: some View {
Circle()
.fill(completed ? Color.green : Color.blue)
.frame(width: 100, height: 100)
.scaleEffect(isAnimating ? 1.5 : 1)
.animation(.easeInOut(duration: 1), value: isAnimating)
.onChange(of: isAnimating) { newValue in
if !newValue {
completed = true
}
}
.onTapGesture {
isAnimating.toggle()
}
}
}
- 手势状态管理:
struct GestureStateDemo: View {
@GestureState private var dragState = false
@State private var position = CGSize.zero
var body: some View {
Circle()
.fill(dragState ? Color.red : Color.blue)
.frame(width: 100, height: 100)
.offset(position)
.gesture(
DragGesture()
.updating($dragState) { _, state, _ in
state = true
}
.onChanged { gesture in
position = gesture.translation
}
.onEnded { _ in
withAnimation {
position = .zero
}
}
)
}
}
手势和动画是创建优秀用户体验的关键要素。SwiftUI 的声明式语法使复杂的交互变得简单直观,同时保持了代码的可维护性。在实际开发中,合理使用这些特性可以显著提升应用的交互体验。