所以我有一些ScrollView看法:
ScrollView {
ForEach(cities) { city in
NavigationLink(destination: ...) {
CityRow(city: city)
}
.buttonStyle(BackgroundButtonStyle())
}
}
Run Code Online (Sandbox Code Playgroud)
在每个视图中,我都有一个拖动手势:
let drag = DragGesture()
.updating($gestureState) { value, gestureState, _ in
// ...
}
.onEnded { value in
// ...
}
Run Code Online (Sandbox Code Playgroud)
我分配给视图的一部分:
ZStack(alignment: .leading) {
HStack {
// ...
}
HStack {
// ...
}
.gesture(drag)
}
Run Code Online (Sandbox Code Playgroud)
一旦我附加了手势,就ScrollView停止滚动。使其滚动的唯一方法是从没有手势的部分开始滚动。我如何避免这种情况并使两者一起工作。在UIKit中,就像true在shouldRecognizeSimultaneouslyWithmethod中指定一样简单。我如何在SwiftUI中拥有相同的功能?
在SwiftUI中,我尝试使用.simultaneousGesture(drag)和附加手势.highPriorityGesture(drag)-它们的工作原理都与相同.gesture(drag)。我还尝试GestureMask为including:参数提供所有可能的静态值-我可以滚动工作,也可以拖动手势工作。从来没有两个。
Mac*_*c3n 24
您可以将 minimumDistance 设置为某个值(例如 30)。然后拖动仅在您水平拖动并达到最小距离时才起作用,否则滚动视图或列表手势会覆盖视图手势
.gesture(DragGesture(minimumDistance: 30, coordinateSpace: .local)
Run Code Online (Sandbox Code Playgroud)
Osc*_*car 15
就在之前
.gesture(drag)
Run Code Online (Sandbox Code Playgroud)
你可以加
.onTapGesture { }
Run Code Online (Sandbox Code Playgroud)
这对我有用,显然添加一个 tapGesture 避免了两个 DragGestures 之间的混淆。
我希望这有帮助
Tom*_*art 10
我根据 Michel 的回答创建了一个易于使用的扩展。
struct NoButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
}
}
extension View {
func delayTouches() -> some View {
Button(action: {}) {
highPriorityGesture(TapGesture())
}
.buttonStyle(NoButtonStyle())
}
}
Run Code Online (Sandbox Code Playgroud)
您在使用拖动手势后应用它。
例子:
ScrollView {
YourView()
.gesture(DragGesture(minimumDistance: 0)
.onChanged { _ in }
.onEnded { _ in }
)
.delayTouches()
}
Run Code Online (Sandbox Code Playgroud)
我找不到一个纯粹的 SwiftUI 解决方案,所以我使用 UIViewRepresentable 作为解决方法。与此同时,我已向 Apple 提交了一个错误。基本上,我创建了一个带有平移手势的清晰视图,我将在我想要添加手势的任何 SwiftUI 视图上呈现该视图。这不是一个完美的解决方案,但也许对您来说已经足够了。
public struct ClearDragGestureView: UIViewRepresentable {
public let onChanged: (ClearDragGestureView.Value) -> Void
public let onEnded: (ClearDragGestureView.Value) -> Void
/// This API is meant to mirror DragGesture,.Value as that has no accessible initializers
public struct Value {
/// The time associated with the current event.
public let time: Date
/// The location of the current event.
public let location: CGPoint
/// The location of the first event.
public let startLocation: CGPoint
public let velocity: CGPoint
/// The total translation from the first event to the current
/// event. Equivalent to `location.{x,y} -
/// startLocation.{x,y}`.
public var translation: CGSize {
return CGSize(width: location.x - startLocation.x, height: location.y - startLocation.y)
}
/// A prediction of where the final location would be if
/// dragging stopped now, based on the current drag velocity.
public var predictedEndLocation: CGPoint {
let endTranslation = predictedEndTranslation
return CGPoint(x: location.x + endTranslation.width, y: location.y + endTranslation.height)
}
public var predictedEndTranslation: CGSize {
return CGSize(width: estimatedTranslation(fromVelocity: velocity.x), height: estimatedTranslation(fromVelocity: velocity.y))
}
private func estimatedTranslation(fromVelocity velocity: CGFloat) -> CGFloat {
// This is a guess. I couldn't find any documentation anywhere on what this should be
let acceleration: CGFloat = 500
let timeToStop = velocity / acceleration
return velocity * timeToStop / 2
}
}
public class Coordinator: NSObject, UIGestureRecognizerDelegate {
let onChanged: (ClearDragGestureView.Value) -> Void
let onEnded: (ClearDragGestureView.Value) -> Void
private var startLocation = CGPoint.zero
init(onChanged: @escaping (ClearDragGestureView.Value) -> Void, onEnded: @escaping (ClearDragGestureView.Value) -> Void) {
self.onChanged = onChanged
self.onEnded = onEnded
}
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
@objc func gestureRecognizerPanned(_ gesture: UIPanGestureRecognizer) {
guard let view = gesture.view else {
Log.assertFailure("Missing view on gesture")
return
}
switch gesture.state {
case .possible, .cancelled, .failed:
break
case .began:
startLocation = gesture.location(in: view)
case .changed:
let value = ClearDragGestureView.Value(time: Date(),
location: gesture.location(in: view),
startLocation: startLocation,
velocity: gesture.velocity(in: view))
onChanged(value)
case .ended:
let value = ClearDragGestureView.Value(time: Date(),
location: gesture.location(in: view),
startLocation: startLocation,
velocity: gesture.velocity(in: view))
onEnded(value)
@unknown default:
break
}
}
}
public func makeCoordinator() -> ClearDragGestureView.Coordinator {
return Coordinator(onChanged: onChanged, onEnded: onEnded)
}
public func makeUIView(context: UIViewRepresentableContext<ClearDragGestureView>) -> UIView {
let view = UIView()
view.backgroundColor = .clear
let drag = UIPanGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.gestureRecognizerPanned))
drag.delegate = context.coordinator
view.addGestureRecognizer(drag)
return view
}
public func updateUIView(_ uiView: UIView,
context: UIViewRepresentableContext<ClearDragGestureView>) {
}
}
Run Code Online (Sandbox Code Playgroud)
我终于找到了一个似乎对我有用的解决方案。我发现Button了神奇的生物。它们正确传播事件,即使您在 aScrollView或List.
现在,你会说
是的,但是米歇尔,我不想要一个带有一些效果的轻按按钮,我想要长按某物或拖动某物。
很公平。但是,如果您知道如何做事,您必须将Button知识视为真正使其下的一切label:正常工作的东西!因为 Button 实际上会尝试行为,并将其手势委托给下面的控件(如果它们确实实现了)onTapGesture,所以您可以获得一个切换info.circle按钮或一个可以在内部点击的按钮。换句话说,出现在(但不是之前)之后的所有手势onTapGesture {}都将起作用。
作为一个复杂的代码示例,您必须具备的内容如下:
ScrollView {
Button(action: {}) { // Makes everything behave in the "label:"
content // Notice this uses the ViewModifier ways ... hint hint
.onTapGesture {} // This view overrides the Button
.gesture(LongPressGesture(minimumDuration: 0.01)
.sequenced(before: DragGesture(coordinateSpace: .global))
.updating(self.$dragState) { ...
Run Code Online (Sandbox Code Playgroud)
这个例子使用了一个复杂的手势,因为我想展示它们确实有效,只要那个难以捉摸的Button/onTapGesture组合在那里。
现在你会注意到这并不完全完美,长按实际上也被按钮长按了,然后它才会将长按委托给你的(所以这个例子将有超过 0.01 秒的长按)。此外,ButtonStyle如果您想删除按下的效果,则必须有一个。换句话说,YMMV,很多测试,但对于我自己的使用,这是我能够在项目列表中进行实际长按/拖动工作的最接近的。
我在拖动滑块时遇到了类似的问题:
这是工作答案代码,带有“DispatchQueue.main.asyncAfter”的“技巧”
也许你可以为你的 ScrollView 尝试类似的东西。
struct ContentView: View {
@State var pos = CGSize.zero
@State var prev = CGSize.zero
@State var value = 0.0
@State var flag = true
var body: some View {
let drag = DragGesture()
.onChanged { value in
self.pos = CGSize(width: value.translation.width + self.prev.width, height: value.translation.height + self.prev.height)
}
.onEnded { value in
self.pos = CGSize(width: value.translation.width + self.prev.width, height: value.translation.height + self.prev.height)
self.prev = self.pos
}
return VStack {
Slider(value: $value, in: 0...100, step: 1) { _ in
self.flag = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.flag = true
}
}
}
.frame(width: 250, height: 40, alignment: .center)
.overlay(RoundedRectangle(cornerRadius: 25).stroke(lineWidth: 2).foregroundColor(Color.black))
.offset(x: self.pos.width, y: self.pos.height)
.gesture(flag ? drag : nil)
}
Run Code Online (Sandbox Code Playgroud)
}
| 归档时间: |
|
| 查看次数: |
1273 次 |
| 最近记录: |