如何使用 SwiftUI 制作按钮 draggable/movable?
How to make a button draggable/movable with SwiftUI?
我正在尝试使用 SwiftUI 制作一个可移动的按钮。从它的外观来看,这应该可行。我曾尝试将带有文本的按钮放入另一个 ZStack 中,有一秒钟它正在工作,但是一旦我释放按钮,拖动就停止了,我不能再拖动了。我注意到尽管按钮已经移动,但水龙头仍留在中间。拖动看起来也有问题。
struct CircleButton: View {
@State private var dragAmount = CGSize.zero
var body: some View {
ZStack {
Button(action: performAction){
ZStack {
Circle()
.foregroundColor(.blue)
.frame(width: 100, height: 100)
Text("Move me")
.foregroundColor(.white)
.font(.system(.caption, design: .serif))
}
}
.animation(.default)
.offset(self.dragAmount)
}
.gesture(
DragGesture()
.onChanged { self.dragAmount = [=11=].translation})
}
func performAction(){
print("button pressed")
}
}
我试过这个:
struct CircleButton: View {
@State private var dragAmount = CGSize.zero
var body: some View {
ZStack {
ZStack {
Button(action: performAction){
Circle()
.foregroundColor(.blue)
.frame(width: 100, height: 100)
}
Text("Tap me")
}
.offset(self.dragAmount)
.animation(.default)
}
.gesture(
DragGesture()
.onChanged{ self.dragAmount = [=12=].translation})
}
func performAction(){
print("button pressed")
}
}
这是一个可能的方法演示。
更新:用 Xcode 13.3 / iOS 15.4
重新测试
另请参阅内联注释。
struct CircleButton: View {
@State private var dragAmount: CGPoint?
var body: some View {
GeometryReader { gp in // just to center initial position
ZStack {
Button(action: self.performAction) {
ZStack {
Circle()
.foregroundColor(.blue)
.frame(width: 100, height: 100)
Text("Move me")
.foregroundColor(.white)
.font(.system(.caption, design: .serif))
}
}
// Use .none animation for glue effect
.animation(.default, value: dragAmount)
.position(self.dragAmount ?? CGPoint(x: gp.size.width / 2, y: gp.size.height / 2))
.highPriorityGesture( // << to do no action on drag !!
DragGesture()
.onChanged { self.dragAmount = [=10=].location})
}
.frame(maxWidth: .infinity, maxHeight: .infinity) // full space
}
}
func performAction() {
print("button pressed")
}
}
来点不同的和简洁的怎么样:
import SwiftUI
struct ContentView: View {
var body: some View {
CircleButton()
}
}
struct CircleButton: View {
@State private var pos = CGPoint(x:222,y:222) // just for testing
var body: some View {
theButton.position(self.pos).highPriorityGesture(self.drag)
}
var theButton: some View {
ZStack {
Circle()
.foregroundColor(.blue)
.frame(width: 100, height: 100)
.onTapGesture { self.performAction() }
Text("Tap me")
.foregroundColor(.white)
.font(.system(.caption, design: .serif))
}
}
func performAction(){
print("button pressed")
}
var drag: some Gesture {
DragGesture().onChanged { value in self.pos = CGPoint(x: value.location.x, y: value.location.y)}
}
}
我通过 ohayon 在这里找到了一个简单的解决方案。只是一个 ViewModifier 和一个扩展:
struct DraggablePita: View {
var body: some View {
Image(uiImage: UIImage(named: "pita.png")!)
.draggable() // Add the new, custom modifier to make this draggable
}
}
// Handle dragging
struct DraggableView: ViewModifier {
@State var offset = CGPoint(x: 0, y: 0)
func body(content: Content) -> some View {
content
.gesture(DragGesture(minimumDistance: 0)
.onChanged { value in
self.offset.x += value.location.x - value.startLocation.x
self.offset.y += value.location.y - value.startLocation.y
})
.offset(x: offset.x, y: offset.y)
}
}
// Wrap `draggable()` in a View extension to have a clean call site
extension View {
func draggable() -> some View {
return modifier(DraggableView())
}
}
我正在尝试使用 SwiftUI 制作一个可移动的按钮。从它的外观来看,这应该可行。我曾尝试将带有文本的按钮放入另一个 ZStack 中,有一秒钟它正在工作,但是一旦我释放按钮,拖动就停止了,我不能再拖动了。我注意到尽管按钮已经移动,但水龙头仍留在中间。拖动看起来也有问题。
struct CircleButton: View {
@State private var dragAmount = CGSize.zero
var body: some View {
ZStack {
Button(action: performAction){
ZStack {
Circle()
.foregroundColor(.blue)
.frame(width: 100, height: 100)
Text("Move me")
.foregroundColor(.white)
.font(.system(.caption, design: .serif))
}
}
.animation(.default)
.offset(self.dragAmount)
}
.gesture(
DragGesture()
.onChanged { self.dragAmount = [=11=].translation})
}
func performAction(){
print("button pressed")
}
}
我试过这个:
struct CircleButton: View {
@State private var dragAmount = CGSize.zero
var body: some View {
ZStack {
ZStack {
Button(action: performAction){
Circle()
.foregroundColor(.blue)
.frame(width: 100, height: 100)
}
Text("Tap me")
}
.offset(self.dragAmount)
.animation(.default)
}
.gesture(
DragGesture()
.onChanged{ self.dragAmount = [=12=].translation})
}
func performAction(){
print("button pressed")
}
}
这是一个可能的方法演示。
更新:用 Xcode 13.3 / iOS 15.4
重新测试另请参阅内联注释。
struct CircleButton: View {
@State private var dragAmount: CGPoint?
var body: some View {
GeometryReader { gp in // just to center initial position
ZStack {
Button(action: self.performAction) {
ZStack {
Circle()
.foregroundColor(.blue)
.frame(width: 100, height: 100)
Text("Move me")
.foregroundColor(.white)
.font(.system(.caption, design: .serif))
}
}
// Use .none animation for glue effect
.animation(.default, value: dragAmount)
.position(self.dragAmount ?? CGPoint(x: gp.size.width / 2, y: gp.size.height / 2))
.highPriorityGesture( // << to do no action on drag !!
DragGesture()
.onChanged { self.dragAmount = [=10=].location})
}
.frame(maxWidth: .infinity, maxHeight: .infinity) // full space
}
}
func performAction() {
print("button pressed")
}
}
来点不同的和简洁的怎么样:
import SwiftUI
struct ContentView: View {
var body: some View {
CircleButton()
}
}
struct CircleButton: View {
@State private var pos = CGPoint(x:222,y:222) // just for testing
var body: some View {
theButton.position(self.pos).highPriorityGesture(self.drag)
}
var theButton: some View {
ZStack {
Circle()
.foregroundColor(.blue)
.frame(width: 100, height: 100)
.onTapGesture { self.performAction() }
Text("Tap me")
.foregroundColor(.white)
.font(.system(.caption, design: .serif))
}
}
func performAction(){
print("button pressed")
}
var drag: some Gesture {
DragGesture().onChanged { value in self.pos = CGPoint(x: value.location.x, y: value.location.y)}
}
}
我通过 ohayon 在这里找到了一个简单的解决方案。只是一个 ViewModifier 和一个扩展:
struct DraggablePita: View {
var body: some View {
Image(uiImage: UIImage(named: "pita.png")!)
.draggable() // Add the new, custom modifier to make this draggable
}
}
// Handle dragging
struct DraggableView: ViewModifier {
@State var offset = CGPoint(x: 0, y: 0)
func body(content: Content) -> some View {
content
.gesture(DragGesture(minimumDistance: 0)
.onChanged { value in
self.offset.x += value.location.x - value.startLocation.x
self.offset.y += value.location.y - value.startLocation.y
})
.offset(x: offset.x, y: offset.y)
}
}
// Wrap `draggable()` in a View extension to have a clean call site
extension View {
func draggable() -> some View {
return modifier(DraggableView())
}
}