上一篇文章我们了解了如何使用.gesture
修饰符和@GestureState
属性包装器,让我们看看另一种常见的手势:DragGesture
拖拽手势。
下面先看个效果图:
这个效果中,我们实现了一个Text
文本,并添加了拖拽手势,可以拖动到屏幕的任意位置,松手后停留在目标位置,而非回到原来的起点位置。
UI组件就不多说了,我们在Text
组件上添加了.gesture
修饰符,并在该修饰符内部添加了DragGesture
手势,然后给DragGesture
添加了.updating
和.onEnded
修饰符。
关于.updating
修饰符上一篇文章已经介绍过了(SwiftUI中的手势(TapGesture、LongPressGesture、GestureState的使用)),这里创建了一个@GestureState
修饰的dragOffset
变量,用于绑定到.updating
修饰符上的参数上。在.updating
修饰符的body
中进行了赋值操作,这样在拖拽过程中,state
将不断地被赋予新的值。
@GestureState private var dragOffset: CGSize = .zero
.updating($dragOffset, body: { value, state, _ in
state = value.translation
})
同时我们对Text
组件添加了.offset(dragOffset)
修饰符,并传入dragOffset
变量,到此就可以拖拽Text
组件了,但是松手后,它会自动地回到原来的位置。
.offset(dragOffset)
为了解决这个问题,我们还需要再来一个变量position
记录松手时的位置信息。
@State private var position: CGSize = .zero
并且在.onEnded
修饰符的闭包中,给position
赋值。该闭包中返回了当前的手势信息变量,而移动信息储存在该变量的translation
中。
.onEnded({ value in
position.width += value.translation.width
position.height += value.translation.height
})
这里更新了position
信息,这里主要注意用了+=
运算符,因为拖动不止一次,上一次的结束位置即是下一次的起点位置。如果说拖拽松手后想回到原点,那就移出关于position
的相关代码。
最后在给Text
添加一个.offset(position)
修饰符,并传入position
变量。
.offset(position)
到现在已经添加了两个.offset()
修饰符,也可以添加一个,但是要将position
和dragOffset
两个变量加起来传入进去,比如这样:
.offset(CGSize(width: dragOffset.width + position.width, height: dragOffset.height + position.height))
至此,该动画就全部完成了。另外上面的视图中,在界面的顶部加了两个Text
,用来显示dragOffset
和position
的数值,可以看出,每次动作结束@GestureState
修饰的dragOffset
都恢复了初始值。
完整代码如下:
struct DragGestureDemo: View {
@GestureState private var dragOffset: CGSize = .zero
@State private var position: CGSize = .zero
var body: some View {
ZStack {
VStack {
Text("dragOffset: \(dragOffset)")
Text("position: \(position)")
Spacer()
}
Text("Drag me!")
.font(.title)
.padding()
.background(Color.cyan)
.cornerRadius(10.0)
.offset(dragOffset)
.offset(position)
.gesture(
DragGesture()
.updating($dragOffset, body: { value, state, _ in
state = value.translation
})
.onEnded({ value in
position.width += value.translation.width
position.height += value.translation.height
})
)
}
}
}
可能有人会说,我习惯用了.onChange()
修饰符,不习惯用.updating()
修饰符,下面这个就是用.onChange()
修饰符实现的拖动。
这里需要两个@State
修饰的变量来记录信息,dragOffset
记录相对于最初位置的偏移量,position
记录松手后相对于最初位置的偏移量。
给Text
设置一个.offset()
修饰符即可,要传入dragOffset
,dragOffset
是被@State
修饰器修饰的,在松手后该值不会重置。
在.onChange()
修饰符闭包内,计算dragOffset
的值,dragOffset = 上一次位置偏移量 + 手势偏移量。
在.onEnded()
修饰符闭包内,计算position
的值,position = dragOffset,因为dragOffset
不会重置,且也是手离开的时候的偏移量。给position
赋值,是为了在.onChange()
修饰符闭包内给dragOffset
赋值。
最终代码如下:
struct DragGestureDemo2: View {
@State private var dragOffset: CGSize = .zero
@State private var position: CGSize = .zero
var body: some View {
ZStack {
VStack {
Text("dragOffset: \(dragOffset)")
Text("position: \(position)")
Spacer()
}
Text("Drag me!")
.font(.title)
.padding()
.background(Color.cyan)
.cornerRadius(10.0)
.offset(dragOffset)
.gesture(
DragGesture()
.onChanged({ value in
dragOffset.width = position.width + value.translation.width
dragOffset.height = position.height + value.translation.height
})
.onEnded({ _ in
position = dragOffset
})
)
}
}
}
如果想要在松手后回到原点,那就不需要position
记录位置了,直接修改成下面代码即可:
.gesture(
DragGesture()
.onChanged({ value in
dragOffset = value.translation
})
.onEnded({ _ in
dragOffset = .zero
})
)
以上就是两种方式实现的拖拽动画,下面看一个比较酷的左右滑动卡片的拖拽动画。
关于这个拖拽动画效果,不做过多的说明了,附上源码,有问题可以留言。
struct DragGestureDemo3: View {
@State private var offset: CGSize = .zero
var body: some View {
Image("liuyifei")
.resizable()
.frame(width: 260)
.aspectRatio(1.0, contentMode: .fit)
.offset(offset)
.scaleEffect(getScale())
.rotationEffect(Angle(degrees: getRotation()))
.gesture(
DragGesture()
.onChanged({ value in
withAnimation(.spring()) {
offset = value.translation
}
})
.onEnded({ _ in
withAnimation(.spring()) {
offset = .zero
}
})
)
}
func getScale() -> CGFloat {
let max = UIScreen.main.bounds.width / 2
let current = abs(offset.width)
let percentage = current / max
return 1.0 - min(percentage, 0.5) * 0.5
}
func getRotation() -> Double {
let max = UIScreen.main.bounds.width / 2
let current = offset.width
let percentage = Double(current / max)
let maxAngle: Double = 10
return percentage * maxAngle
}
}
最后,希望能够帮助到有需要的朋友,如果觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。
标签:修饰符,DragGesture,dragOffset,value,width,Drag,offset,position,手势 From: https://blog.csdn.net/guoyongming925/article/details/139150139