-
-
Save wildthink/c2c0ef4f9052cc4feafff1cd4e6ed011 to your computer and use it in GitHub Desktop.
Example of making a reusable `draggable()` modifier for SwiftUI Views
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// DraggableView.swift | |
// Created by Jason Jobe on 9/15/21. | |
// | |
import SwiftUI | |
public struct DraggableView: ViewModifier { | |
public enum Axis { case x, y, xy } | |
@Binding var offset: CGPoint | |
var axes: Axis = .xy | |
var adjust: ((CGPoint) -> CGPoint)? = nil | |
var onEnd: ((CGPoint) -> Void)? = .none | |
public func body(content: Content) -> some View { | |
content | |
.gesture(DragGesture(minimumDistance: 0) | |
.onChanged { value in | |
var newOffset = self.offset | |
switch axes { | |
case .x: | |
newOffset.x += value.location.x - value.startLocation.x | |
case .y: | |
newOffset.y += value.location.y - value.startLocation.y | |
case .xy: | |
newOffset.x += value.location.x - value.startLocation.x | |
newOffset.y += value.location.y - value.startLocation.y | |
} | |
// final adjustment here | |
newOffset = adjust(newOffset) | |
self.offset = newOffset | |
} | |
.onEnded { value in | |
onEnd?(offset) | |
} | |
) | |
.offset(x: offset.x, y: offset.y) | |
} | |
private func adjust(_ pt: CGPoint) -> CGPoint { | |
adjust?(pt) ?? pt | |
} | |
} | |
// Wrap `draggable()` in a View extension to have a clean call site | |
public extension View { | |
func draggable(axes: DraggableView.Axis = .xy, | |
offset: Binding<CGPoint>, | |
adjust: ((CGPoint) -> CGPoint)? = nil, | |
onEnd: ((CGPoint) -> Void)?) -> some View { | |
return modifier(DraggableView(offset: offset, axes: axes, adjust: adjust, onEnd: onEnd)) | |
} | |
} | |
struct DraggableView_Previews: PreviewProvider { | |
struct Demo: View { | |
@State var pt: CGPoint = .zero | |
var body: some View { | |
Rectangle() | |
.fill(Color.red) | |
.frame(width: 50, height: 50) | |
.draggable(offset: $pt, onEnd: .none) | |
} | |
} | |
static var previews: some View { | |
Demo() | |
.ignoresSafeArea() | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
public struct DraggableView: ViewModifier { | |
public typealias UpdateCall = (inout CGPoint, DragGesture.Value, Bool) -> Void | |
@Binding var offset: CGPoint | |
var onChanged: UpdateCall | |
public init(_ offset: Binding<CGPoint>, update: @escaping UpdateCall) { | |
self._offset = offset | |
self.onChanged = update | |
} | |
public func body(content: Content) -> some View { | |
content | |
.gesture(DragGesture(minimumDistance: 0) | |
.onChanged { value in | |
onChanged(&offset, value, false) | |
} | |
.onEnded { value in | |
onChanged(&offset, value, true) | |
} | |
) | |
.offset(x: offset.x, y: offset.y) | |
} | |
func calculateNewOffset(from value: DragGesture.Value) -> CGPoint { | |
CGPoint(x: offset.x + value.translation.width, | |
y: offset.y + value.translation.height) | |
} | |
} | |
// Wrap `draggable()` in a View extension to have a clean call site | |
public extension View { | |
func onDragGesture(_ pt: Binding<CGPoint>, update: @escaping DraggableView.UpdateCall) | |
-> some View | |
{ | |
modifier(DraggableView(pt, update: update)) | |
} | |
} | |
struct DraggableView_Previews: PreviewProvider { | |
struct Demo: View { | |
@State var pt: CGPoint = .zero | |
@State var msg: String = "<msg>" | |
@State var frame: CGRect = .zero | |
var body: some View { | |
ZStack (alignment: .bottom){ | |
Rectangle() | |
.fill(Color.red) | |
.frame(width: 50, height: 50) | |
.rectReader($frame) | |
.onDragGesture($pt) { (pt, _, end) in | |
print(pt, end) | |
} | |
Text(msg) | |
} | |
} | |
} | |
static var previews: some View { | |
Demo() | |
.ignoresSafeArea() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment