如何解决滚动超出图纸中的限制时,防止在ScrollView顶部的Image上方填充
如果您呈现一个在ScrollView中显示下面带有文本的图像的图纸,则当您向下滚动然后向后轻弹时,请注意它将滚动超出限制,并且图像上方会出现空白,直到弹性滚动效果消失为止。然后,您可以向下拉以关闭工作表。
我想防止在ScrollView中的Image上方添加空间。相反,我希望该填充出现在图像和第一个文本之间。一个执行此操作的应用程序是App Store-当您在“今日”中点击一个故事时,请注意,向上滚动超过限制时,顶部的图像始终保持固定在顶部,并且在其下方出现了弹性反弹效果。
我怀疑可以使用GeometryReader来获取全局CoordinateSpace中的位置,但是我不清楚如何利用它来获得所需的行为。
struct ContentView: View {
@State private var sheetVisible = false
var body: some View {
VStack {
Button(action: {
sheetVisible = true
},label: {
Text("Present Sheet")
})
}
.sheet(isPresented: $sheetVisible) {
DetailsView(image: UIImage(named: "test"))
}
}
}
struct DetailsView: View {
var image: UIImage?
var body: some View {
ScrollView {
Image(uiImage: image ?? UIImage())
.resizable()
.aspectRatio((image?.size.width ?? 1) / (image?.size.height ?? 1),contentMode: .fill)
.background(Color(.secondarySystemFill))
ForEach(0..<50) { index in
Text("\(index)")
}
}
}
}
解决方法
使用GeometryReader可以实现:
- 将
Image
包裹在GeometryReader
中,以便有可能获得全局坐标空间中的位置。 - 请确保将
.scaledToFill()
添加到GeometryReader
中,否则将不会占用ScrollView
中的任何空间。或者,您可以通过为GeometryReader
提供默认框架来解决此问题 - 在“拖动”时,使用全局坐标空间中图像的
minY
来设置offset
。这样就可以模拟图像处于固定位置。
此技术存在问题。向下拖动工作表时,图像将停留在其位置,看起来很奇怪。我还没有找到解决方法,但是也许这个答案会对您有所帮助。
请参见下面的示例。
struct DetailsView: View {
func getOffsetY(basedOn geo: GeometryProxy) -> CGFloat {
// Find Y position
let minY = geo.frame(in: .global).minY
let emptySpaceAboveSheet: CGFloat = 40
// Don't offset view when scrolling down
if minY <= emptySpaceAboveSheet {
return 0
}
// Offset the view when it goes above to simulate it standing still
return -minY + emptySpaceAboveSheet
}
var body: some View {
ScrollView {
GeometryReader { imageGeo in
Image(systemName: "option")
.resizable()
.scaledToFill()
.background(Color(.secondarySystemFill))
.offset(x: 0,y: self.getOffsetY(basedOn: imageGeo))
}
// Need this to make sure the geometryreader has a size
.scaledToFill()
ForEach(0..<50) { index in
Text("\(index)")
}
}
}
}
,
好的,这是针对您情况的可能解决方案。在Xcode 12b5 / iOS 14上进行了测试。
这个想法是要有一个内部容器,它可以在内部滚动,但是要在滚动视图的坐标空间中读取其坐标,补偿相对于容器偏移的图像位置,容器可以继续滚动,这样可以使其他所有内容(即图像下方)仍然弹跳。
struct DemoView: View {
var image: UIImage?
@State private var offset = CGFloat.zero
var body: some View {
ScrollView {
VStack { // internal container
Image(uiImage: image ?? UIImage())
.resizable()
.aspectRatio((image?.size.width ?? 1) / (image?.size.height ?? 1),contentMode: .fill)
.background(Color(.secondarySystemFill))
.offset(y: offset < 0 ? offset : 0) // compansate offset
ForEach(0..<50) { index in
Text("\(index)")
}
}
.background(GeometryReader {
// read current position of container inside scroll view
Color.clear.preference(key: ViewOffsetKey.self,value: -$0.frame(in: .named("scroll")).origin.y)
})
.onPreferenceChange(ViewOffsetKey.self) {
self.offset = $0 // needed offset to shift back image
}
}
.coordinateSpace(name: "scroll")
}
}
struct ViewOffsetKey: PreferenceKey {
typealias Value = CGFloat
static var defaultValue = CGFloat.zero
static func reduce(value: inout Value,nextValue: () -> Value) {
value += nextValue()
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。