swiftlistswiftuiswiftui-listdraggesture

SwiftUI - Loss of .onDelete Functionality in List as a result of DragGesture


The DragGesture() causes loss of functionality of the .onDelete function inside of the list. I made a basic example of the interaction:


class MenuViewModel: ObservableObject {
  @Published var dragEnabled: Bool = true
}

struct RootView: View {
  @StateObject var viewModel = MenuViewModel()

  var body: some View {
    ZStack {
      NavView()
    }
    .environmentObject(viewModel)
    // Can be diabled on ChildView, but will need to be re-enabled on RootView
    .gesture(DragGesture()
      .onChanged { _ in
        guard viewModel.dragEnabled else { return }
        print("Drag Gesture Active")
      })
  }
}

struct NavView: View {
  var body: some View {
    NavigationView {
      NavigationLink {
        ChildView()
      } label: {
        Text("Go to ChildView")
      }
      Text("Drag Gesture needs to be enabled")
    }
    .navigationViewStyle(.stack)
  }
}

struct ChildView: View {
  @EnvironmentObject var viewModel: MenuViewModel
  @State var list = ["1", "2", "3"]

  var body: some View {
    List {
      ForEach(list, id: \.self) { item in
        Text(item)
      }
      .onDelete { indexSet in
        print("OnDelete Works!")
        list.remove(atOffsets: indexSet)
      }
      .navigationTitle("OnDelete Enabled")
    }
    .onAppear {
      viewModel.dragEnabled = false
      print("Drag Gesture Disabled")
    }
    .onDisappear {
      viewModel.dragEnabled = true
      print("Drag Gesture Enabled")
    }
  }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        RootView()
    }
}

Attempted to use:

Nothing is working as expected. Any suggestions would be appreciated! I know in this example the DragGesture does not do anything. I am using the DragGesture in my app so users can drag a side menu into view.


Solution

  • You can use GestureMask to enable/disable parent or child gestures. Do this in RootView:

      var body: some View {
        ZStack {
          NavView()
        }
        .environmentObject(viewModel)
        // Can be diabled on ChildView, but will need to be re-enabled on RootView
        .gesture(DragGesture()
          .onChanged { _ in
            guard viewModel.dragEnabled else { return }
            print("Drag Gesture Active")
          }, including: viewModel.dragEnabled ? .gesture : .subviews) // here
      }
    }