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

    1. struct ContentView: View {
    2. @State private var gestureTranslation = CGSize.zero
    3. @State private var prevTranslation = CGSize.zero
    4. var body: some View {
    5. VStack {
    6. Rectangle()
    7. .fill(Color.red)
    8. .frame(height: (UIScreen.main.bounds.height / 2) + self.gestureTranslation.height)
    9. RoundedRectangle(cornerRadius: 5)
    10. .frame(width: 40, height: 3)
    11. .foregroundColor(Color.gray)
    12. .padding(2)
    13. .gesture(DragGesture()
    14. .onChanged({ value in
    15. self.gestureTranslation = CGSize(width: value.translation.width + self.prevTranslation.width, height: value.translation.height + self.prevTranslation.height)
    16. })
    17. .onEnded({ value in
    18. self.gestureTranslation = CGSize(width: value.translation.width + self.prevTranslation.width, height: value.translation.height + self.prevTranslation.height)
    19. self.prevTranslation = self.gestureTranslation
    20. })
    21. )
    22. Rectangle()
    23. .fill(Color.green)
    24. .frame(height: (UIScreen.main.bounds.height / 2) - self.gestureTranslation.height)
    25. }
    26. }
    27. }

    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.

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

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

    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) 下方来尽可能地掩盖持久句柄位置。

    我希望其他人对这个问题有更好的解决方案。 现在,这就是我所拥有的:

    1. import PlaygroundSupport
    2. import SwiftUI
    3. struct SplitView<PrimaryView: View, SecondaryView: View>: View {
    4. // MARK: Props
    5. @GestureState private var offset: CGFloat = 0
    6. @State private var storedOffset: CGFloat = 0
    7. let primaryView: PrimaryView
    8. let secondaryView: SecondaryView
    9. // MARK: Initilization
    10. init(
    11. @ViewBuilder top: @escaping () -> PrimaryView,
    12. @ViewBuilder bottom: @escaping () -> SecondaryView)
    13. {
    14. self.primaryView = top()
    15. self.secondaryView = bottom()
    16. }
    17. // MARK: Body
    18. var body: some View {
    19. GeometryReader { proxy in
    20. VStack(spacing: 0) {
    21. self.primaryView
    22. .frame(height: (proxy.size.height / 2) + self.totalOffset)
    23. .zIndex(1)
    24. self.handle
    25. .gesture(
    26. DragGesture()
    27. .updating(self.$offset, body: { value, state, _ in
    28. state = value.translation.height
    29. })
    30. .onEnded { value in
    31. self.storedOffset += value.translation.height
    32. }
    33. )
    34. .offset(y: -self.offset)
    35. .zIndex(0)
    36. self.secondaryView.zIndex(1)
    37. }
    38. }
    39. }
    40. // MARK: Computed Props
    41. var handle: some View {
    42. RoundedRectangle(cornerRadius: 5)
    43. .frame(width: 40, height: 3)
    44. .foregroundColor(Color.gray)
    45. .padding(2)
    46. }
    47. var totalOffset: CGFloat {
    48. storedOffset + offset
    49. }
    50. }
    51. // MARK: - Playground
    52. let splitView = SplitView(top: {
    53. Rectangle().foregroundColor(.red)
    54. }, bottom: {
    55. Rectangle().foregroundColor(.green)
    56. })
    57. 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:

    1. let MIN_HEIGHT = CGFloat(50)
    2. struct DragViewSizeView: View {
    3. @State var height: CGFloat = MIN_HEIGHT
    4. var body: some View {
    5. VStack {
    6. Rectangle()
    7. .fill(Color.red)
    8. .frame(width: .infinity, height: height)
    9. HStack {
    10. Spacer()
    11. Rectangle()
    12. .fill(Color.gray)
    13. .frame(width: 100, height: 10)
    14. .cornerRadius(10)
    15. .gesture(
    16. DragGesture()
    17. .onChanged { value in
    18. height = max(MIN_HEIGHT, height + value.translation.height)
    19. }
    20. )
    21. Spacer()
    22. }
    23. VStack {
    24. Text("my o my")
    25. Spacer()
    26. Text("hoo hah")
    27. }
    28. }
    29. }
    30. }
    31. struct DragTestView: View {
    32. var body: some View {
    33. VStack {
    34. DragViewSizeView()
    35. Spacer() // If comment this line the result will be as on the bottom GIF example
    36. }
    37. }
    38. }
    39. struct DragTestView_Previews: PreviewProvider {
    40. static var previews: some View {
    41. DragTestView()
    42. }
    43. }

    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

    1. import SwiftUI
    2. struct ContentView: View {
    3. var body: some View {
    4. VStack {
    5. CustomDraggableComponent()
    6. Spacer() // If comment this line the result will be as on the bottom GIF example
    7. }
    8. }
    1. import SwiftUI
    2. let MIN_HEIGHT = 50
    3. struct CustomDraggableComponent: View {
    4. @State var height: CGFloat = MIN_HEIGHT
    5. var body: some View {
    6. VStack {
    7. Rectangle()
    8. .fill(Color.red)
    9. .frame(width: .infinity, height: height)
    10. HStack {
    11. Spacer()
    12. Rectangle()
    13. .fill(Color.gray)
    14. .frame(width: 100, height: 10)
    15. .cornerRadius(10)
    16. .gesture(
    17. DragGesture()
    18. .onChanged { value in
    19. height = value.translation.height + MIN_HEIGHT
    20. }
    21. )
    22. Spacer()
    23. }
    24. }
    25. }

    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

    1. .gesture(
    2. DragGesture()
    3. .onChanged { value in
    4. height = max(MIN_HEIGHT, height + value.translation.height)
    5. }
    6. )

    Also, remove the infinite width. It's invalid.

    1. .frame(minHeight: 0, maxHeight: height)
    2. or
    3. .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: height)

    Share

    Follow

    answered Jan 22, 2021 at 15:09

    Raja Kishan's user avatar

    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

     

  • 相关阅读:
    系列三、其他流
    NOI2022游记
    Hive 到底有什么用?
    新库上线 | CnOpenData国际货运代理信息数据
    零水印算法的理解
    java毕业设计哈尔滨文旅信息网站(附源码、数据库)
    liunx常见指令
    Vue-Router学习记录
    正则表达式
    设计原则之【接口隔离原则】
  • 原文地址:https://blog.csdn.net/weixin_42610770/article/details/133897488