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.
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
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: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:09– Gregory Furmanek
Sep 5 at 20:55Add 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
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()
}
}
}
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
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
Raja Kishan
17k22 gold badges2727 silver badges5353 bronze badges
height
property. I miscalculated – Roman Mahotskyi
Jan 22, 2021 at 17:06– Gypsa
Jan 6, 2022 at 9:12