SwiftUI:如何根据已拖动的距离更改拖动速度?
SwiftUI: How to change the speed of drag based on distance already dragged?
例如:我有一个可以自由移动的操纵杆,如何让拖动的速度随着距离的增加而变慢?我拖动操纵杆越远,拖动速度越慢。
提前致谢。
我的操纵杆代码,拖动有效但没有边界,如果将它拖动到边缘也不会减慢速度:
import SwiftUI
struct ContentView: View {
@State var isDragging = false
@State var dragValue = CGSize.zero
var body: some View {
VStack {
Text("width: \(dragValue.width)")
Text("height: \(dragValue.height)")
VStack (spacing: 16) {
HStack(spacing: 35) {
Image(systemName: "chevron.left")
.foregroundColor(.gray)
VStack (spacing: 80) {
Image(systemName: "chevron.up")
.foregroundColor(.gray)
Image(systemName: "chevron.down")
.foregroundColor(.gray)
}
Image(systemName: "chevron.right")
.foregroundColor(.gray)
}
}
.offset(x: dragValue.width * 0.05, y: dragValue.height * 0.05)
.frame(width: 150, height: 150)
.background(LinearGradient(gradient: Gradient(colors: [Color(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)), Color(#colorLiteral(red: 0.8705882353, green: 0.8941176471, blue: 0.9450980392, alpha: 1))]), startPoint: .top, endPoint: .bottom))
.clipShape(RoundedRectangle(cornerRadius: isDragging ? (55 - abs(dragValue.height) / 10) : 55, style: .continuous))
.offset(x: dragValue.width, y: dragValue.height)
.shadow(color: Color.black.opacity(0.2), radius: 20, x: 0, y: 20)
.padding(.horizontal, 30)
.gesture(
DragGesture().onChanged { value in
self.dragValue = value.translation
self.isDragging = true
}
.onEnded { value in
self.dragValue = .zero
self.isDragging = false
}
)
.animation(.spring(response: 0.5, dampingFraction: 0.6, blendDuration: 0))
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
reddit 上的一个人帮我解决了这个问题,我只需要将 .gesture 更改为:
.gesture(
DragGesture().onChanged { dragValue in
let translation: CGSize = dragValue.translation
let multiplier: CGFloat = 15
let incrementalW: CGFloat = log(abs(translation.width)) * multiplier
let incrementalH: CGFloat = log(abs(translation.height)) * multiplier
let incremental = CGSize(
width: translation.width > 0 ? incrementalW : -incrementalW,
height: translation.height > 0 ? incrementalH : -incrementalH
)
let resistance = CGSize(
width: abs(translation.width) < abs(incremental.width) ? translation.width : incremental.width,
height: abs(translation.height) < abs(incremental.height) ? translation.height : incremental.height
)
self.dragValue = resistance
self.isDragging = true
}
.onEnded { dragValue in
self.dragValue = .zero
self.isDragging = false
}
)
这是基于渐近曲线的可能解决方案(如果有人觉得它有帮助的话)。
测试 Xcode 11.4 / iOS 13.4
更新:用 Xcode 13.4 / iOS 15.5
重新测试
更改部分
DragGesture().onChanged { value in
let limit: CGFloat = 200 // the less the faster resistance
let xOff = value.translation.width
let yOff = value.translation.height
let dist = sqrt(xOff*xOff + yOff*yOff);
let factor = 1 / (dist / limit + 1)
self.dragValue = CGSize(width: value.translation.width * factor,
height: value.translation.height * factor)
self.isDragging = true
}
例如:我有一个可以自由移动的操纵杆,如何让拖动的速度随着距离的增加而变慢?我拖动操纵杆越远,拖动速度越慢。 提前致谢。
我的操纵杆代码,拖动有效但没有边界,如果将它拖动到边缘也不会减慢速度:
import SwiftUI
struct ContentView: View {
@State var isDragging = false
@State var dragValue = CGSize.zero
var body: some View {
VStack {
Text("width: \(dragValue.width)")
Text("height: \(dragValue.height)")
VStack (spacing: 16) {
HStack(spacing: 35) {
Image(systemName: "chevron.left")
.foregroundColor(.gray)
VStack (spacing: 80) {
Image(systemName: "chevron.up")
.foregroundColor(.gray)
Image(systemName: "chevron.down")
.foregroundColor(.gray)
}
Image(systemName: "chevron.right")
.foregroundColor(.gray)
}
}
.offset(x: dragValue.width * 0.05, y: dragValue.height * 0.05)
.frame(width: 150, height: 150)
.background(LinearGradient(gradient: Gradient(colors: [Color(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)), Color(#colorLiteral(red: 0.8705882353, green: 0.8941176471, blue: 0.9450980392, alpha: 1))]), startPoint: .top, endPoint: .bottom))
.clipShape(RoundedRectangle(cornerRadius: isDragging ? (55 - abs(dragValue.height) / 10) : 55, style: .continuous))
.offset(x: dragValue.width, y: dragValue.height)
.shadow(color: Color.black.opacity(0.2), radius: 20, x: 0, y: 20)
.padding(.horizontal, 30)
.gesture(
DragGesture().onChanged { value in
self.dragValue = value.translation
self.isDragging = true
}
.onEnded { value in
self.dragValue = .zero
self.isDragging = false
}
)
.animation(.spring(response: 0.5, dampingFraction: 0.6, blendDuration: 0))
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
reddit 上的一个人帮我解决了这个问题,我只需要将 .gesture 更改为:
.gesture(
DragGesture().onChanged { dragValue in
let translation: CGSize = dragValue.translation
let multiplier: CGFloat = 15
let incrementalW: CGFloat = log(abs(translation.width)) * multiplier
let incrementalH: CGFloat = log(abs(translation.height)) * multiplier
let incremental = CGSize(
width: translation.width > 0 ? incrementalW : -incrementalW,
height: translation.height > 0 ? incrementalH : -incrementalH
)
let resistance = CGSize(
width: abs(translation.width) < abs(incremental.width) ? translation.width : incremental.width,
height: abs(translation.height) < abs(incremental.height) ? translation.height : incremental.height
)
self.dragValue = resistance
self.isDragging = true
}
.onEnded { dragValue in
self.dragValue = .zero
self.isDragging = false
}
)
这是基于渐近曲线的可能解决方案(如果有人觉得它有帮助的话)。
测试 Xcode 11.4 / iOS 13.4
更新:用 Xcode 13.4 / iOS 15.5
重新测试更改部分
DragGesture().onChanged { value in
let limit: CGFloat = 200 // the less the faster resistance
let xOff = value.translation.width
let yOff = value.translation.height
let dist = sqrt(xOff*xOff + yOff*yOff);
let factor = 1 / (dist / limit + 1)
self.dragValue = CGSize(width: value.translation.width * factor,
height: value.translation.height * factor)
self.isDragging = true
}