How do I anchor resizing views in a scrollview to the top-leading point so that when they grow, they grow downwards?
Context: I'm trying to make a scrollviews of views that I can resize in height by dragging a handle in the bottom of it up and down. My problem is that when it resizes, it resizes equally much up as down. I want the top to stay put and only adjust how far down it goes.
I don't think the problem lies with the scrollview, as the behaviour is the same if I replace it with a VStack. In the context of the scrollview, though, it resizing upwards makes the user not able to scroll up far enough to see the top of the view.
Full sample code follows under the screenshots. The issue is on both iPad and iPhone simulator
In the following screenshots, the scrollview is scrolled to the top in both. The first screenshot shows the start-state before resizing the topmost item
The second screenshot shows the state after resizing the topmost item - the topmost item now goes outside the list so we cannot see scroll up to see the top
Here follows the full code, runnable with Xcode 11.0, to show the issue
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()
}
}
}
}

