Bre*_*mbs 7 drag-and-drop gesture ios swift swiftui
I\xe2\x80\x99m 使用 .onDrag 修饰符启用拖/放操作:
\nstruct RootView: View {\n \n @State var dragging: Bool = false\n \n var body: some View {\n VStack {\n Color.red.cornerRadius(.some)\n .frame(width: 100, height: 100)\n .onDrag {\n dragging = true\n return NSItemProvider(object: NSString())\n }\n }\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n一旦调用拖动,我就将dragging
标志设置为 true。
\xe2\x80\x99s 执行放置没有问题。但是,如果我调用了拖动但在没有任何移动的情况下结束了它,则 I\xe2\x80\x99m 不会被通知,即dragging
仍然设置为 true。
我\xe2\x80\x99ve尝试将第二个手势集添加到高优先级或同时拖动手势,该手势在没有任何感知到的移动的情况下结束:
\n .gesture(DragGesture(minimumDistance: 0)\n .onEnded({ value in\n if value.translation.height == 0 && value.translation.width == 0 {\n self.dragging = false\n }\n })\n )\n
Run Code Online (Sandbox Code Playgroud)\n无论我是否将其放置在 onDrag 修改器之前/之后,它都会阻止 onDrag 触发。
\n有什么方法可以判断 onDrag 何时结束而没有任何移动,即拖动立即释放?
\n我向 Apple 提交了无法在 SwiftUI 中实现上述功能的问题,这是我得到的回复:
\n\n\n@GestureState 是取消手势后清理的方法。\nonEnded() 告诉您它何时完成。两者的组合\n应该可以让您实现所描述的内容。
\n
至少根据我对提议内容的理解,这没有帮助。当我添加以下内容时:
\n@GestureState var tapGesture = false\n
Run Code Online (Sandbox Code Playgroud)\n和:
\n.onDrag {\n handler()\n}\n.simultaneousGesture(DragGesture()\n .updating($tapGesture) { _,_,_ in\n print("updating")\n }\n .onEnded { _ in\n print("ended")\n })\n
Run Code Online (Sandbox Code Playgroud)\n打印语句从未在拖动时发生。
\n然而如果我这样做了:
\n.onDrag {\n handler()\n }\n .simultaneousGesture(LongPressGesture()\n .updating($tapGesture) { _,_,_ in\n print("updating")\n }\n .onEnded { _ in\n print("ended")\n })\n
Run Code Online (Sandbox Code Playgroud)\n长按手势会拦截并停止拖动。更改最短持续时间似乎没有产生影响。也不是手势修饰符的排序顺序。或者,如果附加 \xe2\x80\x9cdummy\xe2\x80\x9d 拖动设置为高优先级手势。
\n所以我最终得到了一个不完美的创造性解决方法。
\n首先,我在根视图中添加了一个取消委托:
\nstruct RootView: View {\n \n static var cancellation = CancellationDelegate()\n
Run Code Online (Sandbox Code Playgroud)\n...
\n.onDrop(of: [UTType.text], delegate: Self.cancellation)\n
Run Code Online (Sandbox Code Playgroud)\n其目的是确保可拖动视图立即被潜在的放置目标包围。我有一个视图模型,用于记录输入放置目标的时间。
\n import SwiftUI\nimport UniformTypeIdentifiers\nimport ThirdSwiftLibs\n\nstruct CancellationDelegate: DropDelegate {\n\n var viewModel: CancellationViewModel = .init()\n \n // MARK: - Event handlers\n\n /// Dragged over a drop target\n func dropEntered(info: DropInfo) {\n viewModel.dropLastEntered = .now\n }\n \n /// Dragged away from a drop target\n func dropExited(info: DropInfo) {\n\n }\n \n /// Drag begins\n func dropUpdated(info: DropInfo) -> DropProposal? {\n DropProposal(operation: .move)\n }\n \n // MARK: - Mutators\n \n /// The dragged entity is dropped on a target\n /// - Returns: true if drop was successful\n func performDrop(info: DropInfo) -> Bool {\n SchedulerViewModel.shared.reset()\n return true\n }\n \n // MARK: - Checkers\n\n /// Checks whether the drag is considered valid based on\n /// the view it\xe2\x80\x99s attached to\n /// - Returns: true if view is drag/droppable\n func validateDrop(info: DropInfo) -> Bool {\n /// Allow the drop to begin with any String set as the NSItemProvider\n return info.hasItemsConforming(to: [UTType.text])\n }\n}\n\nclass CancellationViewModel {\n var dropLastEntered: Date?\n}\n
Run Code Online (Sandbox Code Playgroud)\n在 card\xe2\x80\x99s 拖动处理程序中,我在 3 秒后检查卡是否已移动。我可以通过参考cancel\xe2\x80\x99s dropLastEntered值来判断。如果它\xe2\x80\x99s少于3秒,那么\xe2\x80\x99s可以合理地假设它已经移动,所以\xe2\x80\x99s我不需要做任何事情。然而,如果它\xe2\x80\x99s是3或更多,那么它\xe2\x80\x99s同样安全地假设阻力在最后3秒内的某个时刻被释放,所以我应该用它作为提示重置应用程序状态。
\nDispatchQueue.main.asyncAfter(deadline: .now() + 3) {\n if let last = RootView.cancellation.viewModel.dropLastEntered {\n if last.seconds(from: .now) <= -3 {\n // reset UI because we last moved a card over the dummy drop target 3 or more seconds ago\n }\n }\n else {\n // reset UI because we\xe2\x80\x99ve never moved any card over the dummy drop target\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n这并不完美的原因是 UI 重置不会立即发生。在测试中,由于未检测到移动,系统决定停用视图上的拖动(视图不再以系统最初激活的拖动状态显示),大约需要 3 秒。所以,从这个角度来说,3秒是好的。但是,如果我在 1 秒后没有任何移动地自行释放卡,则 UI 需要再过 2 秒 (3-1) 才能意识到由于拖动被停用而需要重置。
\n希望这可以帮助。如果有人有更好的解决方案,我\xe2\x80\x99m洗耳恭听!
\n 归档时间: |
|
查看次数: |
858 次 |
最近记录: |