实现 SwiftUI ScrollView 对齐放置行为(也许使用“scrollViewWillEndDragging”等效项)?

Jef*_* C. 6 scrollview uiscrollview uicollectionview swiftui scrollviewreader

有一种常见的处理滚动的方法,UICollectionView这样当用户停止滚动时,它会逐渐减慢速度,并停止在集合视图中最近的项目与集合视图边界的边缘对齐的位置。

这与页面视图的不同之处在于,用户可以通过单次滑动快速滚动浏览连接视图中的多个项目,并且屏幕上会同时显示多个项目。

您可以在苹果自己的应用程序中看到这一点:电视、音乐、书籍和播客在每个各自的应用程序中浏览水平列表时都表现出这种行为。

使用UICollectionView(或UIScrollView,但在集合视图中更常见),通常使用该scrollViewWillEndDragging(scrollView:velocity:targetContentOffset:)方法来实现,以检测用户何时停止滚动并为其提供要滚动到的更新的目标偏移量。

以下问题的最佳答案(作者:Rob Mayoff)详细描述了这一点:

如何像App Store那样将水平分页对齐到多行集合视图?

我的问题:如何使用 SwiftUI 的 ScrollView 做到这一点?

我有一个水平 ScrollView,其中包含一个 HGrid,其中包含一组项目。我想实现一个类似于苹果应用程序中的水平浏览器。我可以使用 UICollectionView 来完成此操作,如上所述,但更喜欢在 SwiftUI 中“本地”执行此操作,而不包装 UICollectionView。

我希望使用 ScrollViewReader 和某种方式可以做到这scrollTo(_:anchor:)一点,但它似乎没有任何东西可以满足这种需求。

这种行为是否可以使用 SwiftUI 本地实现,如果可以的话,如何实现?

Jef*_* C. 6

在 iOS 16 或更早版本中,我始终无法找到一个令人满意的选项来以干净的方式完成此操作。我本来打算只封装 UIKit UICollectionView,但决定等到 WWDC 2023 看看 Apple 可能会推出什么。

我很高兴我这么做了,因为 ScrollView 在 iOS 17 中得到了一些重大改进 - 包括定义 SwiftUI 处理静止元素的位置和方式的能力ScrollView

也就是说,这是使用以 a 作为参数的新scrollTargetBehavior修饰符 ( docsScrollTargetBehavior ) 来完成的。出于我的目的,.viewAligned提供了我需要的开箱即用的东西,但还有一个.paging选项,有些人可能会觉得有用。

您还可以定义自己的 ScrollTargetBehavior,我相信这可以为未来的各种用途提供支持。

对于该.viewAligned行为,您需要定义父视图(即 HStack),ScrollView 将使用修饰符与其组成视图对齐.scrollTargetLayout()。您还可以使用.scrollTarget()修改器对各个视图执行此操作。您可以在此处阅读所有这些内容的文档。

WWDC 2023 会议“超越滚动视图”详细介绍了 ScrollView 的新增功能。你可以在这里观看

无论如何,我在自己的应用程序中对此进行了测试,效果很好。我很乐意等到 iOS 17 发布(并且在 iOS 16 中根本不进行视图对齐),所以我认为这解决了我的问题。