当滚动超出工作表中的限制时,防止在 ScrollView 顶部的图像上方填充
Posted
技术标签:
【中文标题】当滚动超出工作表中的限制时,防止在 ScrollView 顶部的图像上方填充【英文标题】:Prevent padding above Image at top of ScrollView when scrolling beyond limit in Sheet 【发布时间】:2020-08-25 03:10:58 【问题描述】:如果您在 ScrollView 中展示一个在下方显示带有文本的图像的工作表,当您向下滚动然后向上滑动时,请注意它会滚动超出限制并且图像上方会出现空白空间,直到弹性滚动效果稳定.然后,您可以向下拉以关闭工作表。
我想防止在 ScrollView 中的图像上方添加空间。相反,我希望该填充出现在图像和第一个文本之间。执行此操作的应用是 App Store - 当您在 Today 中点击故事时,请注意顶部的图像在向上滚动超过限制时始终保持固定在顶部,并且在其下方出现弹性反弹效果。
我怀疑可以利用 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)")
【问题讨论】:
您不想按照***.com/a/63322713/12299030 中的说明禁用退回吗? 虽然这会解决不良行为,但仍需要支持弹性滚动行为,而不是在顶部突然停止滚动 【参考方案1】:好的,这是您的情况的可能解决方案。使用 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()
【讨论】:
【参考方案2】:这可以使用 GeometryReader:
-
将
Image
包裹在GeometryReader
中,这样就可以获得全局坐标空间中的位置。
确保将.scaledToFill()
添加到GeometryReader
,否则它不会占用ScrollView
中的任何空间。或者,您可以通过为 GeometryReader
提供默认框架来解决此问题
使用全局坐标空间中Image的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)")
【讨论】:
这几乎行得通!只是图像保持固定,而像您提到的那样关闭工作表,并且图像纵横比丢失。以上是关于当滚动超出工作表中的限制时,防止在 ScrollView 顶部的图像上方填充的主要内容,如果未能解决你的问题,请参考以下文章
滚动其子uiscrollview时如何防止滚动uiscrollview?
当鼠标悬停在 Firefox 中的嵌入式 iframe 上时,防止父页面滚动