Skip to content

Instantly share code, notes, and snippets.

@Wilsonilo
Last active November 2, 2022 01:14
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Wilsonilo/d6b4aece2a49d697b1f81559278c5d6c to your computer and use it in GitHub Desktop.
Save Wilsonilo/d6b4aece2a49d697b1f81559278c5d6c to your computer and use it in GitHub Desktop.
Drag and Drop SwiftUI with ScrollView and HStack
//: HSTack /Scroll View With Drag and Drop
// By Wilson Munoz / @yosoywil
// Inspiration 1: https://gist.github.com/tarasis/f9bac6d98de5433f1ddbadaef02f9a29
// Inspiration 2: https://swiftui-lab.com/drag-drop-with-swiftui/
// Really dirty but functional, need to stress test with several items in the ScrollView/HStack and check memory.
import SwiftUI
import PlaygroundSupport
import Combine
protocol middleManProtocol {
func swapPerson(myName:String, nameMovingElement:String)->Bool
}
class MiddleMan:ObservableObject{
@Published var people = [PersonRow]()
}
struct Person: Identifiable, Hashable {
var id = UUID()
var name: String
}
struct PersonRow: View, Identifiable {
var id = UUID()
var person: Person
@State var targeted: Bool = true
var delegate: middleManProtocol
@State var positionOnScroll:CGPoint = .zero
var body: some View {
HStack {
Image(systemName: "person.circle")
Text(person.name)
Text(person.id.description)
}
.onDrop(of: ["public.utf8-plain-text"], isTargeted: self.$targeted,
perform: { (provider) -> Bool in
guard let personSwaping = provider.first, let name = personSwaping.suggestedName
else { return false}
return self.delegate.swapPerson(myName: self.person.id.uuidString,
nameMovingElement: name)
})
.onDrag {
let item = NSItemProvider(object: NSString(string: self.person.name))
item.suggestedName = self.person.id.uuidString
return item
}
}
}
struct MyView : View, middleManProtocol{
func swapPerson(myName:String, nameMovingElement:String)->Bool{
print("swap running")
//Get index of person (me) and get the index of the one moving
guard let indexOfElementMoving = self.delegate.people.firstIndex(where: {$0.person.id.uuidString == nameMovingElement}) else {
return false}
print("got index of moving provider", indexOfElementMoving)
guard let myIndex = self.delegate.people.firstIndex(where: {$0.person.id.uuidString == myName}) else {return false}
print("got my index", myIndex)
//Swap
self.delegate.people.swapAt(myIndex, indexOfElementMoving)
return true
}
@ObservedObject var delegate: MiddleMan = MiddleMan()
@State var targeted: Bool = true
var body: some View {
ScrollView(.horizontal, showsIndicators: true){
HStack {
ForEach(delegate.people) { person in
person
.frame(width: 100, height:100)
}
}.frame(height:100)
}.onAppear {
//Do your call to server or model or whatever
//I don't like the delegate setter like this, pretty sure i can create a better approach, just doing it like this for now.
self.delegate.people = [
PersonRow(person: Person(name: "George"), delegate: self),
PersonRow(person: Person(name: "Michael"), delegate: self),
PersonRow(person: Person(name: "Wilson"), delegate: self),
PersonRow(person: Person(name: "Jocko"), delegate: self),
PersonRow(person: Person(name: "Jhon"), delegate: self)
]
}
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.setLiveView(MyView())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment