\n
struct PlayerView: View {\n var scaled: Bool = false\n var player: Player = Player(name: "Phile", color: .green, age: 42)\n \n var body: some View {\n ZStack(alignment: .topLeading) {\n Rectangle().frame(width: 100, height: 100).foregroundColor(player.color).cornerRadius(15.0).scaleEffect(scaled ? 1.5 : 1)\n \n VStack {\n Text(player.name)\n Text("Age: \\(player.age)")\n }.padding([.top, .leading], 10)\n }.zIndex(scaled ? 2 : 1)\n }\n}\n\n\nstruct ContentView: View {\n @EnvironmentObject var data: PlayerData\n\n @GestureState private var location: CGPoint = .zero\n @State private var highlighted: Int? = nil\n \n private var Content: some View {\n VStack {\n HStack {\n ForEach(0..<3) { i in\n PlayerView(scaled: self.highlighted == i, player: self.data.players[i])\n .background(self.rectReader(index: i))\n }\n }\n .zIndex((0..<3).contains(highlighted ?? -1) ? 2 : 1)\n \n HStack {\n ForEach(3..<6) { i in\n PlayerView(scaled: self.highlighted == i, player: self.data.players[i])\n .background(self.rectReader(index: i))\n }\n }\n .zIndex((3..<6).contains(highlighted ?? -1) ? 2 : 1)\n }\n }\n \n func rectReader(index: Int) -> some View {\n return GeometryReader { (geometry) -> AnyView in\n if geometry.frame(in: .global).contains(self.location) {\n DispatchQueue.main.async {\n self.highlighted = index\n }\n }\n return AnyView(Rectangle().fill(Color.clear))\n }\n }\n\n var body: some View {\n Content\n .gesture(DragGesture(minimumDistance: 0, coordinateSpace: .global)\n .updating($location) { (value, state, transaction) in\n state = value.location\n }.onEnded {_ in\n self.highlighted = nil\n })\n }\n}\n
\n","author":{"@type":"Person","name":"Asperi"},"upvoteCount":16}}}Reputation: 1187
I'm attempting to create a grid of small square views, that when the user hovers over them with their thumb (or swipes across them), the little squares will temporarily "pop up" and shake. Then, if they continue to long press on that view, it would open up another view with more information.
I thought that implementing a drag gesture on the square views would be enough, but it looks like only one view can capture a drag gesture at a time.
Is there way to enable multiple views to capture a drag gesture, or a way to implement a "hover" gesture for iOS?
Here is my main Grid view:
import SwiftUI
struct ContentView: View {
@EnvironmentObject var data: PlayerData
var body: some View {
VStack {
HStack {
PlayerView(player: self.data.players[0])
PlayerView(player: self.data.players[1])
PlayerView(player: self.data.players[2])
}
HStack {
PlayerView(player: self.data.players[3])
PlayerView(player: self.data.players[4])
PlayerView(player: self.data.players[5])
}
HStack {
PlayerView(player: self.data.players[6])
PlayerView(player: self.data.players[7])
PlayerView(player: self.data.players[8])
}
HStack {
PlayerView(player: self.data.players[9])
PlayerView(player: self.data.players[10])
}
}
}
}
And here is my Square view that would hold a small summary to display on the square:
import SwiftUI
struct PlayerView: View {
@State var scaleFactor: CGFloat = 1.0
var player: Player = Player(name: "Phile", color: .green, age: 42)
var body: some View {
ZStack(alignment: .topLeading) {
Rectangle().frame(width: 100, height: 100).foregroundColor(player.color).cornerRadius(15.0).scaleEffect(self.scaleFactor)
VStack {
Text(player.name)
Text("Age: \(player.age)")
}.padding([.top, .leading], 10)
}.gesture(DragGesture().onChanged { _ in
self.scaleFactor = 1.5
}.onEnded {_ in
self.scaleFactor = 1.0
})
}
}
Upvotes: 6
Views: 4783
Reputation: 258461
Here is a demo of possible approach... (it is simplified version of your app data settings, but the idea and direction where to evolve should be clear)
The main idea that you capture drag not in item view but in the content view transferring needed states (or calculable dependent data) into item view when (or if) needed.
struct PlayerView: View {
var scaled: Bool = false
var player: Player = Player(name: "Phile", color: .green, age: 42)
var body: some View {
ZStack(alignment: .topLeading) {
Rectangle().frame(width: 100, height: 100).foregroundColor(player.color).cornerRadius(15.0).scaleEffect(scaled ? 1.5 : 1)
VStack {
Text(player.name)
Text("Age: \(player.age)")
}.padding([.top, .leading], 10)
}.zIndex(scaled ? 2 : 1)
}
}
struct ContentView: View {
@EnvironmentObject var data: PlayerData
@GestureState private var location: CGPoint = .zero
@State private var highlighted: Int? = nil
private var Content: some View {
VStack {
HStack {
ForEach(0..<3) { i in
PlayerView(scaled: self.highlighted == i, player: self.data.players[i])
.background(self.rectReader(index: i))
}
}
.zIndex((0..<3).contains(highlighted ?? -1) ? 2 : 1)
HStack {
ForEach(3..<6) { i in
PlayerView(scaled: self.highlighted == i, player: self.data.players[i])
.background(self.rectReader(index: i))
}
}
.zIndex((3..<6).contains(highlighted ?? -1) ? 2 : 1)
}
}
func rectReader(index: Int) -> some View {
return GeometryReader { (geometry) -> AnyView in
if geometry.frame(in: .global).contains(self.location) {
DispatchQueue.main.async {
self.highlighted = index
}
}
return AnyView(Rectangle().fill(Color.clear))
}
}
var body: some View {
Content
.gesture(DragGesture(minimumDistance: 0, coordinateSpace: .global)
.updating($location) { (value, state, transaction) in
state = value.location
}.onEnded {_ in
self.highlighted = nil
})
}
}
Upvotes: 16