How to correctly do up an adjustable split view in SwiftUI?

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: [

How to correctly do up an adjustable split view in SwiftUI?_第1张图片

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.

这有点工作,但是在拖动 handle 时,它非常小故障,并且似乎需要大量拖动才能到达某个点。

请告诉我出了什么问题。谢谢你。

  • swift
  • resize
  • swiftui
  • drag
  • splitview

Share

Improve this question

Follow

edited Apr 15, 2020 at 15:18

asked Apr 12, 2020 at 10:22

vincent

22722 silver badges1313 bronze badges

Add a comment

2 Answers

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: 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.

Share

Improve this answer

Follow

edited Jun 11, 2020 at 9:51

answered Jun 10, 2020 at 15:38

André Kuhlmann

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:41
  • 2

    I played around with both codes and I found that actually with my code, if I change DragGesture() to DragGesture(coordinateSpace: .global) the glitches seems to disappear! 

    – vincent

     Jun 14, 2020 at 13:08
  • 1

    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:39
  • If you use DragGesture(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:09
  • This solution doesn't seem to work anymore. The system appears to keep calling the "updating" callback even after the mouse button is released. 

    – Gregory Furmanek

     Sep 5 at 20:55

Add a comment

Report 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()
    }
}

Share

Improve this answer

Follow

answered May 18, 2021 at 6:01

David Reich

 

How to change the height of the object by using DragGesture in SwiftUI?

Ask Question

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()
      }
    }
}
  • swiftui

Share

Follow

edited Jan 22, 2021 at 17:10

asked Jan 22, 2021 at 14:17

Roman Mahotskyi

4,85666 gold badges3737 silver badges7171 bronze badges

Add a comment

1 Answer

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)

Share

Follow

answered Jan 22, 2021 at 15:09

How to correctly do up an adjustable split view in SwiftUI?_第2张图片

Raja Kishan

17k22 gold badges2727 silver badges5353 bronze badges

  • Thanks, the problem was in height property. I miscalculated 

    – Roman Mahotskyi

     Jan 22, 2021 at 17:06 
  • I also want to implement similar functionality. Pls tell what u change to fix. 

    – Gypsa

     Jan 6, 2022 at 9:12

 

你可能感兴趣的:(swiftui,php,ios)