如何在 SwiftUI 中正确设置可调整的拆分视图?
How to correctly do up an adjustable split view in SwiftUI?
这是我第一次尝试 SwiftUI,我正在尝试创建一个充当拆分视图的 SwiftUI 视图,在两个视图的中心有一个可调节的手柄。
这是我当前的代码实现示例:
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)
}
}
}
现在的样子:
[
这个还可以,但是拖动手柄的时候非常卡顿,好像要拖很多次才能到某个点。
请告诉我哪里出了问题。谢谢。
据我观察,问题似乎出在手柄被拖动时被重新定位。为了抵消这一点,我在手柄上设置了一个反向偏移,所以它保持在原位。我试图通过将其隐藏在其他视图 (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)
只需将代码粘贴到 XCode Playground / Swift Playgrounds
如果您找到改进我的代码的方法,请告诉我。
有关更简单的解决方案,请参阅 。
我的版本:
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()
}
}
这是我第一次尝试 SwiftUI,我正在尝试创建一个充当拆分视图的 SwiftUI 视图,在两个视图的中心有一个可调节的手柄。
这是我当前的代码实现示例:
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)
}
}
}
现在的样子:
[
这个还可以,但是拖动手柄的时候非常卡顿,好像要拖很多次才能到某个点。
请告诉我哪里出了问题。谢谢。
据我观察,问题似乎出在手柄被拖动时被重新定位。为了抵消这一点,我在手柄上设置了一个反向偏移,所以它保持在原位。我试图通过将其隐藏在其他视图 (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)
只需将代码粘贴到 XCode Playground / Swift Playgrounds
如果您找到改进我的代码的方法,请告诉我。
有关更简单的解决方案,请参阅
我的版本:
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()
}
}