I've prepared a small example to reproduce my issue. I have a view (in this case, ContentView) that I use as a proxy to wrap some content (OtherView) into a GeometryReader. This is done because the space available is needed in my real view (not in this example) to make some calculations and I'd rather not have proxy.size as argument in 10 different functions.
There's a Binding, a State variable and a DragGesture that updates both variables when ended. For some reason, the update occurs in two steps (first the state, then the binding) rather than all at once:
struct OtherView: View {
		
		let size: CGSize
		@Binding var page: Int
		@State var draggingOffset: CGFloat = 0
		var body: some View {
				Text(text())
						.frame(width: size.width,
									 height: size.height / 2)
						.background(Color.blue)
						.gesture(
								DragGesture(minimumDistance: 15)
										.onChanged { value in
												withAnimation {
														draggingOffset = value.translation.width
												}
										}
										.onEnded { _ in
												print("\n\n* RESET **")
												withAnimation {
														page += 1
														draggingOffset = 0
												}
										}
						)
		}
		func text() -> String {
				print("page: \(page) offset: \(draggingOffset)")
				return "page: \(page) offset: \(draggingOffset)"
		}
}
struct ContentView: View {
		@State private var value: Int = 0
		var body: some View {
				NavigationView {
						GeometryReader { proxy in
								OtherView(size: proxy.size,
													page: $value)
						}
				}
				.navigationViewStyle(StackNavigationViewStyle())
		}
}
This prints:
page: 0 offset: 0.0
...
...
page: 0 offset: -239.3333282470703 RESET **
page: 1 offset: -239.3333282470703
page: 1 offset: 0.0
If now I remove the NavigationView or move the GeometryReader to OtherView, the update occurs in just one step.
RESET **
page: 1 offset: 0.0
Post
Replies
Boosts
Views
Activity
I have a View that performs two different actions depending if a vertical or horizontal scroll is recognized. I'm using a DragGesture for this. This view (let's call it MyView) takes a ViewBuilder in its initializer and can be nested, meaning that I can have MyView wrapped into MyView.
// 1.
MyView {
		VStack {
				 Text("This one is vertical")
				 // 2.
				 MyView {
						 Text("This one is horizontal")
				 }
				 .layout(.horizontal) // listens to horizontal drags
		}
}
.layout(.vertical) // listens to vertical drags
Now, as // 1. MyView is a container to // 2. MyView, if I drag inside this second MyView vertically, even if it will just perform some actions when horizontally, it's still recognizing the touch and preventing the first MyView to react to this touch.
So my question is: following a SwiftUI approach, is there any way to use a DragGesture and fail recognition of the gesture as you'd do in UIKit and that way propagate the touch to let other gestures recognize that touch? Is there any alternative?
I've noticed that if a Binding and a State are changed in the same withAnimation block, the State will be updated first, trigger a redraw, and then the Binding will be updated, triggering a second redraw. When instead of a Binding, I use two State variables, then the redraw happens just once and both values are updated at the same time:
struct ContentView: View {
		
		@Binding var x1: Int
//		@State var x1: Int
		@State var x2: Int = 0
		
		var body: some View {
				VStack {
						Button(action: {
								withAnimation {
										x1 += 1
										x2 += 2
								}
						}, label: {
								Text("Increment")
										.padding()
						})
						Text("Total: \(total)")
				}
		}
		
		var total: Int {
				print("\(x1) + \(x2) = \(x1 + x2)")
				return x1 + x2
		}
}
When hitting the button, the console prints:
0 + 2 = 2
1 + 2 = 3
If I switch and use two State variables, I only see one print:
1 + 2 = 3
Why is this happening? Is there a way to keep using a Binding and a State but trigger a redraw just once?