SwiftUI: Gesture and Offset Are Not Working As Intended

橙三吉。 提交于 2020-04-11 11:46:11

问题


I am using offset and gesture modifiers to move a circle around the screen. When I use this code, everything works as expected:

import SwiftUI

struct MovingCircle: View {

@State private var dragged = CGSize.zero

var body: some View {
    Circle()
        .offset(x: self.dragged.width)
        .frame(width: 20, height: 20)
        .gesture(DragGesture()
            .onChanged{ value in
                self.dragged = value.translation
        }
        .onEnded{ value in
            self.dragged = CGSize.zero
            }
    )
 }
}

However, I do not want to have the circle reset to the original position onEnded. I would like it to remain in place and then be moved again on dragging. When I use the following code, I lose the ability to move the circle again upon re-dragging and it remains in place:

import SwiftUI

struct MovingCircle: View {

@State private var dragged = CGSize.zero

var body: some View {
    Circle()
        .offset(x: self.dragged.width)
        .frame(width: 20, height: 20)
        .gesture(DragGesture()
            .onChanged{ value in
                self.dragged = value.translation
        }
        .onEnded{ value in
            self.dragged = value.translation
            }
    )
}
}

What is the cause of this, have I encountered some bug or have I coded it incorrectly?


回答1:


First, to understand the problem, add a .border(Color.red) to the .frame() modifier:

.frame(width: 20, height: 20).border(Color.red)

You'll see that when the dot is moved, its frame remains in place. That is why later, it won't respond to gestures. The "tappable" area no longer matches the dot. And because the content area is now empty, it is no longer "tappable".

To make the frame move with the dot, invert the order. The .offset() should come later:

.frame(width: 20, height: 20).border(Color.red)
.offset(x: self.dragged.width)

Finally, you will see that after each .onEnded(), the whole thing resets back. One way to solve it, is by accumulating how much you dragged in previous gestures:

struct MovingCircle: View {

    @State private var dragged = CGSize.zero
    @State private var accumulated = CGSize.zero

    var body: some View {
        Circle()
            .frame(width: 20, height: 20).border(Color.red)
            .offset(x: self.dragged.width)

            .gesture(DragGesture()
                .onChanged{ value in
                    self.dragged = CGSize(width: value.translation.width + self.accumulated.width, height: value.translation.height + self.accumulated.height)
            }
            .onEnded{ value in
                self.dragged = CGSize(width: value.translation.width + self.accumulated.width, height: value.translation.height + self.accumulated.height)
                self.accumulated = self.dragged
                }
        )
    }
}



回答2:


Swift 5.x iOS 13

A Less elegant solution, if you don't care to see the object dragged; you just want to be able to drag it; maybe makes more sense if you got a lot going on and you're looking to save CPU.

Obviously you need to find a better starting position for it then absolute zero.

import SwiftUI

var intern:CGPoint = CGPoint(x:0,y:0)

enum DragState {
  case inactive
  case dragging(translation: CGSize)
}

struct ContentView: View {

@State var position:CGPoint = CGPoint(x:0,y:0)
@GestureState private var dragState = DragState.inactive

var body: some View {
 ZStack {
    Circle()
   .frame(width: 24, height: 24, alignment: .center)
 }.offset(x: self.dragOffset.width, y: self.dragOffset.height)
  .gesture(DragGesture(coordinateSpace: .global)
    .updating($dragState, body: { (drag, state, translation) 
      intern = drag.location
    })
    .onEnded { ( value ) in
      self.position = intern
    }
  ).position(position)
 }
}


来源:https://stackoverflow.com/questions/57488175/swiftui-gesture-and-offset-are-not-working-as-intended

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!