Reputation: 157
I have a very weird issue with my double-tap gesture. I don't know this is a bug or my codes are wrong. Here, I want to use the Double Tap Gesture to open my DetailView by using NavigationLink. The problem is the page of my DetailView shows randomly which is supposed to match with the page of my SourceView. If I use a regular NavigationLink without using .onTapGesture, the seletected pages are matched. I couldn't find any solution online, but I found there are some bugs in NavigationLink. My codes examples are below. Please let me know if you have the same issue in using both onTapGesture and NavigationLink.
import SwiftUI
struct DataArray: Identifiable {
let id = UUID()
let number: Int
let cities: String
var name1: String?
var name2: String?
var name3: String?
var name4: String?
}
public struct ListDataArray {
static var dot = [
DataArray(number: 1,
cities: "Baltimore"
name1: "John",
name2: "Mike"),
DataArray(number: 2,
cities: "Frederick"),
DataArray(number: 3,
cities: "Catonsville"
name1: "Susan",
name2: "Oliver",
name3: "Jude",
name4: "Erik"),
]
}
struct Home: View {
var datas: [DataArray] = ListDataArray.dot
@State var isDoubleTab: Bool = false
var body: some View {
NavigationView {
LazyVStack(spacing: 10) {
ForEach (datas, id: \.id) { data in
HomeView(data: data)
.onTapGesture(count: 2) {
self.isDoubleTapped.toggle()
}
NavigationLink(
destination: DetailView(data: data),
isActivate: $isDoubleTab) {
EmptyView()
}
Divider()
.padding()
}
}
.navigationBarHidden(true)
}
}
}
struct DetailView: View {
var data = DataArray
var body: some View {
VStact {
Text(data.cities)
}
}
}
struct HomeView: View {
var data: DataArray
var body: some View {
VStack {
HSTack {
Text(data.cities).padding()
Text(data.name1)
if let sur2 = data.name2 {
surName2 = sur
Text(surName2)
}
}
}
}
}
The above examples are closely matched my current code. For simplicity, I just shortened some duplicate names including padding() etc...
Upvotes: 2
Views: 340
Reputation: 30506
The problem is that you're using the init(_:destination:isActive:)
version of NavigationLink
inside
a ForEach
. When you set isDoubleTab
to true, all of the visible NavigationLink
s will attempt to present. This will result in the unexpected behavior that you're seeing.
Instead, you'll need to use a different version of NavigationLink
: init(_:destination:tag:selection:)
. But first, replace @State var isDoubleTab: Bool = false
with a property that stores a DataArray
— something like @State var selectedData: DataArray?
.
@State var selectedData: DataArray? /// here!
...
.onTapGesture(count: 2) {
/// set `selectedData` to the current loop iteration's `data`
/// this will trigger the `NavigationLink`.
selectedData = data
}
Then, replace your old NavigationLink(_:destination:isActive:)
with this alternate version — instead of presenting once a Bool
is set to true
, it will present when tag
matches selectedData
.
NavigationLink(
destination: DetailView(data: data),
tag: data, /// will be presented when `tag` == `selectedData`
selection: $selectedData
) {
EmptyView()
}
Note that this also requires data
, a DataArray
, to conform to Hashable
. That's as simple as adding it inside the struct definition.
struct DataArray: Identifiable, Hashable {
Result:
Full code:
struct Home: View {
var datas: [DataArray] = ListDataArray.dot
// @State var isDoubleTab: Bool = false // remove this
@State var selectedData: DataArray? /// replace with this
var body: some View {
NavigationView {
LazyVStack(spacing: 10) {
ForEach(datas, id: \.id) { data in
HomeView(data: data)
.onTapGesture(count: 2) {
selectedData = data
}
NavigationLink(
destination: DetailView(data: data),
tag: data, /// will be presented when `data` == `selectedData`
selection: $selectedData
) {
EmptyView()
}
Divider()
.padding()
}
}
.navigationBarHidden(true)
}
}
}
struct DetailView: View {
var data: DataArray
var body: some View {
VStack {
Text(data.cities)
}
}
}
struct HomeView: View {
var data: DataArray
var body: some View {
VStack {
HStack {
Text(data.cities).padding()
Text(data.name1 ?? "")
/// not sure what this is for, doesn't seem related to your problem though so I commented it out
// if let sur2 = data.name2 {
// surName2 = sur
// Text(surName2)
// }
}
}
}
}
public struct ListDataArray {
static var dot = [
DataArray(
number: 1,
cities: "Baltimore",
name1: "John",
name2: "Mike"
),
DataArray(
number: 2,
cities: "Frederick"
),
DataArray(
number: 3,
cities: "Catonsville",
name1: "Susan",
name2: "Oliver",
name3: "Jude",
name4: "Erik"
)
]
}
struct DataArray: Identifiable, Hashable {
let id = UUID()
let number: Int
let cities: String
var name1: String?
var name2: String?
var name3: String?
var name4: String?
}
Other notes:
DataArray
to just Data
, since it's a struct and not an array.Upvotes: 1