如何将滚动视图中的视图调整大小固定在最顶端,以使视图在增长时向下增长?
上下文:我正在尝试制作视图的滚动视图,可以通过上下拖动其底部的手柄来调整高度。我的问题是,当它调整大小时,它的调整大小相等。我希望顶部保持原状,并且只调整向下的距离。
我认为问题不在于滚动视图,因为如果将其替换为VStack,其行为是相同的。但是,在滚动视图的上下文中,它向上调整大小会使用户无法向上滚动足够远的距离以查看视图的顶部。
完整的示例代码如下。 iPad和iPhone模拟器都存在该问题
在以下屏幕截图中,滚动视图均滚动到两个视图的顶部。第一个屏幕截图显示了调整最顶部项目大小之前的开始状态
第二个屏幕截图显示了调整最顶层项目的大小后的状态-顶层项目现在不在列表中,因此我们看不到向上滚动以查看顶层
此处显示了可在Xcode 11.0上运行的完整代码,以显示问题
struct ScaleItem: View {
static let defaultHeight: CGFloat = 240.0
@State var heightDiff: CGFloat = 0.0
@State var currentHeight: CGFloat = ScaleItem.defaultHeight
var resizingButton: some View {
VStack {
VStack {
Spacer(minLength: 15)
HStack {
Spacer()
Image(systemName: "arrow.up.and.down.square")
.background(Color.white)
Spacer()
}
}
Spacer()
.frame(height: 11)
}
.background(Color.clear)
}
var body: some View {
ZStack {
VStack {
Spacer()
HStack {
Spacer()
Text("Sample")
Spacer()
}
Spacer()
}
.background(Color.red)
.overlay(
RoundedRectangle(cornerRadius: 5.0)
.strokeBorder(Color.black, lineWidth: 1.0)
.shadow(radius: 3.0)
)
.padding()
.frame(
minHeight: self.currentHeight + heightDiff,
idealHeight: self.currentHeight + heightDiff,
maxHeight: self.currentHeight + heightDiff,
alignment: .top
)
resizingButton
.gesture(
DragGesture()
.onChanged({ gesture in
print("Changed")
let location = gesture.location
let startLocation = gesture.startLocation
let deltaY = location.y - startLocation.y
self.heightDiff = deltaY
print(deltaY)
})
.onEnded { gesture in
print("Ended")
let location = gesture.location
let startLocation = gesture.startLocation
let deltaY = location.y - startLocation.y
self.currentHeight = max(ScaleItem.defaultHeight, self.currentHeight + deltaY)
self.heightDiff = 0
print(deltaY)
print(String(describing: gesture))
})
}
}
}
struct ScaleDemoView: View {
var body: some View {
ScrollView {
ForEach(0..<3) { _ in
ScaleItem()
}
}
}
}
答案 0 :(得分:1)
解决此问题的一种方法是在拖动过程中将 ScrollView 重新绘制到其原始位置。创建一个观察者对象,当您的DeltaY更改时,将通知父视图,并且该视图将相应更新。
final class DeltaHeight: ObservableObject
并传递到子视图。.offset(x: 0, y: 0)
添加到您的 ScrollView 这是经过测试的代码:
import SwiftUI
struct ScaleDemoView_Previews: PreviewProvider {
static var previews: some View {
ScaleDemoView()
}
}
struct ScaleItem: View {
@ObservedObject var delta: DeltaHeight
static let defaultHeight: CGFloat = 200.0
@State var heightDiff: CGFloat = 0.0
@State var currentHeight: CGFloat = ScaleItem.defaultHeight
var resizingButton: some View {
VStack {
VStack {
Spacer(minLength: 15)
HStack {
Spacer()
Image(systemName: "arrow.up.and.down.square")
.background(Color.white)
Spacer()
}
}
Spacer()
.frame(height: 11)
}
.background(Color.clear)
}
var body: some View {
ZStack {
VStack {
Spacer()
HStack {
Spacer()
Text("Sample")
Spacer()
}
Spacer()
}
.background(Color.red)
.overlay(
RoundedRectangle(cornerRadius: 5.0)
.strokeBorder(Color.black, lineWidth: 1.0)
.shadow(radius: 3.0)
)
.padding()
.frame(
minHeight: self.currentHeight + heightDiff,
idealHeight: self.currentHeight + heightDiff,
maxHeight: self.currentHeight + heightDiff,
alignment: .top
)
resizingButton
.gesture(
DragGesture()
.onChanged({ gesture in
print("Changed")
let location = gesture.location
let startLocation = gesture.startLocation
let deltaY = location.y - startLocation.y
self.heightDiff = deltaY
print("deltaY: ", deltaY)
self.delta.delta = deltaY
})
.onEnded { gesture in
print("Ended")
let location = gesture.location
let startLocation = gesture.startLocation
let deltaY = location.y - startLocation.y
self.currentHeight = max(ScaleItem.defaultHeight, self.currentHeight + deltaY)
self.heightDiff = 0
print(deltaY)
print(String(describing: gesture))
})
}
}
}
struct ScaleDemoView: View {
@ObservedObject var delta = DeltaHeight()
var body: some View {
ScrollView(.vertical) {
ForEach(0..<3) { _ in
ScaleItem(delta: self.delta)
}
}.offset(x: 0, y: 0)
.background(Color.green)
}
}
final class DeltaHeight: ObservableObject {
@Published var delta: CGFloat = 0.0
}