Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass GestureState property down through an ObservableObject

Tags:

swiftui

I have an ObservableObject that has a member property with the @GestureState wrapper. In my View, how do I get access to the GestureState property?

I've already tried using dot notation with the $ binding to try to expose the GestureState but it doesn't like that

My AppState ObservableObject:

class AppState: ObservableObject {
    let objectWillChange = ObservableObjectPublisher()

    @GestureState var currentState: LongPressState = .inactive

    public enum LongPressState: String {
        case inactive = "inactive"
        case pressing = "pressing"
        case holding = "holding"
    }
}

The implementation of the object in my code:

@ObservedObject var appState: AppState
.
.
.
let longPress = LongPressGesture(minimumDuration: minLongPressDuration)
   .sequenced(before: LongPressGesture(minimumDuration: 5))
   .updating(appState.$currentState) { value, state, transaction in
      switch value {
      case .first(true):
         state = .pressing
      case .second(true, false):
         state = .holding
      default:
         state = .inactive
      }
}

I'm actually not getting any buildtime errors in this View but it invalidates the View higher up the hierarchy. If I replace the @ObservedObject with a local @GestureState property then it works fine.

like image 494
palstatt Avatar asked Oct 16 '22 12:10

palstatt


1 Answers

I have found a workaround that works perfectly.

The idea is simple: You have your currentState two times.

  1. Inside your view as a GestureState
  2. Inside your ObservableObject class as Published

This is necessary since GestureState can only be declared within a view. The only thing left to do now is to somehow sync them.

Here is one possible solution: (using onChange(of:))

class AppState: ObservableObject {

    @Published var currentState: LongPressState = .inactive
    
    enum LongPressState: String { ... }
    ...
}
struct ContentView: View {

    @StateObject private var appState = AppState()
    @GestureState private var currentState: AppState.LongPressState = .inactive

     
    var body: some View {
        SomeView() 
            .gesture(
                 LongPressGesture() 
                     .updating($currentState) { value, state, transaction in
                         ...
                     }
            )
            .onChange(of: currentState) { appState.currentState = $0 } 
    }
}

Note

I have found that the animation was kind of buggy. Adding an onEnded to the gesture fixed it (DragGesture).

.onEnded { 
   appState.currentState = .inactive //if you are using DragGesture: .zero (just set to it's initial state again)
}
like image 92
Mofawaw Avatar answered Oct 21 '22 06:10

Mofawaw