This is my first time trying out SwiftUI, and I am trying to create a SwiftUI view that acts as a split view, with an adjustable handle in the center of the two views.
Here's my current code implementation example:
- struct ContentView: View {
-
- @State private var gestureTranslation = CGSize.zero
- @State private var prevTranslation = CGSize.zero
-
- var body: some View {
-
- VStack {
- Rectangle()
- .fill(Color.red)
- .frame(height: (UIScreen.main.bounds.height / 2) + self.gestureTranslation.height)
- RoundedRectangle(cornerRadius: 5)
- .frame(width: 40, height: 3)
- .foregroundColor(Color.gray)
- .padding(2)
- .gesture(DragGesture()
- .onChanged({ value in
- self.gestureTranslation = CGSize(width: value.translation.width + self.prevTranslation.width, height: value.translation.height + self.prevTranslation.height)
-
- })
- .onEnded({ value in
- self.gestureTranslation = CGSize(width: value.translation.width + self.prevTranslation.width, height: value.translation.height + self.prevTranslation.height)
-
- self.prevTranslation = self.gestureTranslation
- })
- )
- Rectangle()
- .fill(Color.green)
- .frame(height: (UIScreen.main.bounds.height / 2) - self.gestureTranslation.height)
- }
- }
- }
How it looks like now: [

This kinda works, but when dragging the handle, it is very glitchy, and that it seems to require a lot of dragging to reach a certain point.
Please advice me what went wrong. Thank you.
Follow
asked Apr 12, 2020 at 10:22
22722 silver badges1313 bronze badges
Sorted by:
Highest score (default) Trending (recent votes count more) Date modified (newest first) Date created (oldest first)
2
From what I have observed, the issue seems to be coming from the handle being repositioned while being dragged along. To counteract that I have set an inverse offset on the handle, so it stays in place. I have tried to cover up the persistent handle position as best as I can, by hiding it beneath the other views (zIndex).
I hope somebody else got a better solution to this question. For now, this is all that I have got:
最佳答案
从我观察到的情况来看,问题似乎来自在拖动时重新定位的 handle 。为了抵消这一点,我在 handle 上设置了一个反向偏移,使其保持原位。我试图通过将其隐藏在其他 View (zIndex) 下方来尽可能地掩盖持久句柄位置。
我希望其他人对这个问题有更好的解决方案。 现在,这就是我所拥有的:
- import PlaygroundSupport
- import SwiftUI
-
- struct SplitView<PrimaryView: View, SecondaryView: View>: View {
-
- // MARK: Props
-
- @GestureState private var offset: CGFloat = 0
- @State private var storedOffset: CGFloat = 0
-
- let primaryView: PrimaryView
- let secondaryView: SecondaryView
-
-
- // MARK: Initilization
-
- init(
- @ViewBuilder top: @escaping () -> PrimaryView,
- @ViewBuilder bottom: @escaping () -> SecondaryView)
- {
- self.primaryView = top()
- self.secondaryView = bottom()
- }
-
-
- // MARK: Body
-
- var body: some View {
- GeometryReader { proxy in
- VStack(spacing: 0) {
- self.primaryView
- .frame(height: (proxy.size.height / 2) + self.totalOffset)
- .zIndex(1)
-
- self.handle
- .gesture(
- DragGesture()
- .updating(self.$offset, body: { value, state, _ in
- state = value.translation.height
- })
- .onEnded { value in
- self.storedOffset += value.translation.height
- }
- )
- .offset(y: -self.offset)
- .zIndex(0)
-
- self.secondaryView.zIndex(1)
- }
- }
- }
-
-
- // MARK: Computed Props
-
- var handle: some View {
- RoundedRectangle(cornerRadius: 5)
- .frame(width: 40, height: 3)
- .foregroundColor(Color.gray)
- .padding(2)
- }
-
- var totalOffset: CGFloat {
- storedOffset + offset
- }
- }
-
-
- // MARK: - Playground
-
- let splitView = SplitView(top: {
- Rectangle().foregroundColor(.red)
- }, bottom: {
- Rectangle().foregroundColor(.green)
- })
-
- PlaygroundPage.current.setLiveView(splitView)
Just paste the code inside XCode Playground / Swift Playgrounds
If you found a way to improve my code please let me know.
Follow
answered Jun 10, 2020 at 15:38
4,40833 gold badges2424 silver badges4242 bronze badges
1
Previously kind of gave up on this question, but now that I get an answer from you, I think it would be good to revisit. Indeed this solution does not appear to be the best, as the handle would 'disappear' until dragging ends. I'll play around with it and see how to improve it– vincent
Jun 13, 2020 at 5:412
I played around with both codes and I found that actually with my code, if I changeDragGesture() to DragGesture(coordinateSpace: .global) the glitches seems to disappear! – vincent
Jun 14, 2020 at 13:081
Since your solution does work and that it did indirectly made me find the solution that matches what I need, so I will mark this as solved.– vincent
Jun 17, 2020 at 4:39DragGesture(coordinateSpace: .global) then you can remove the inverse offset to the handle (.offset(y: -self.offset)) and then everything behaves perfectly. – noe
Aug 5, 2020 at 11:09Report this ad
2
See How to change the height of the object by using DragGesture in SwiftUI? for a simpler solution.
My version of that:
- let MIN_HEIGHT = CGFloat(50)
-
- struct DragViewSizeView: View {
- @State var height: CGFloat = MIN_HEIGHT
-
- var body: some View {
- VStack {
- Rectangle()
- .fill(Color.red)
- .frame(width: .infinity, height: height)
-
- HStack {
- Spacer()
- Rectangle()
- .fill(Color.gray)
- .frame(width: 100, height: 10)
- .cornerRadius(10)
- .gesture(
- DragGesture()
- .onChanged { value in
- height = max(MIN_HEIGHT, height + value.translation.height)
- }
- )
- Spacer()
- }
-
- VStack {
- Text("my o my")
- Spacer()
- Text("hoo hah")
- }
- }
- }
- }
-
- struct DragTestView: View {
-
- var body: some View {
- VStack {
- DragViewSizeView()
-
- Spacer() // If comment this line the result will be as on the bottom GIF example
- }
- }
- }
-
- struct DragTestView_Previews: PreviewProvider {
- static var previews: some View {
- DragTestView()
- }
- }
Follow
answered May 18, 2021 at 6:01
Asked 2 years, 8 months ago
Modified 2 years, 8 months ago
Viewed 1k times
Report this ad
1
I'm trying to increase the height of the shape by using the "dragger" (rounded grey rectangle) element. I use the DragGesture helper provided by SwiftUI to get the current position of the user finger. Unfortunately, during the drag event content is jumping for some reason.
Could you please help me to find the root cause of the problem?
This is how it looks during the drag event
If I remove Spacer() everything is okay but not what I wanted
This is my code snippets
- import SwiftUI
-
- struct ContentView: View {
-
- var body: some View {
- VStack {
- CustomDraggableComponent()
-
- Spacer() // If comment this line the result will be as on the bottom GIF example
- }
- }
- import SwiftUI
-
- let MIN_HEIGHT = 50
-
- struct CustomDraggableComponent: View {
- @State var height: CGFloat = MIN_HEIGHT
-
- var body: some View {
- VStack {
- Rectangle()
- .fill(Color.red)
- .frame(width: .infinity, height: height)
-
- HStack {
- Spacer()
- Rectangle()
- .fill(Color.gray)
- .frame(width: 100, height: 10)
- .cornerRadius(10)
- .gesture(
- DragGesture()
- .onChanged { value in
- height = value.translation.height + MIN_HEIGHT
- }
- )
- Spacer()
- }
- }
- }
Follow
asked Jan 22, 2021 at 14:17
4,85666 gold badges3737 silver badges7171 bronze badges
Sorted by:
Highest score (default) Trending (recent votes count more) Date modified (newest first) Date created (oldest first)
2
The correct calculation is
- .gesture(
- DragGesture()
- .onChanged { value in
- height = max(MIN_HEIGHT, height + value.translation.height)
- }
- )
Also, remove the infinite width. It's invalid.
- .frame(minHeight: 0, maxHeight: height)
-
- or
-
- .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: height)
Follow
answered Jan 22, 2021 at 15:09
17k22 gold badges2727 silver badges5353 bronze badges
height property. I miscalculated Jan 22, 2021 at 17:06 – Gypsa
Jan 6, 2022 at 9:12