0
点赞
收藏
分享

微信扫一扫

SwiftUI极简教程38:ScrollViewReader滚动视图锚点的使用

在本章中,你将学会​​ScrollViewReader​​滚动视图锚点的使用。

在开发社交类型的​​App​​时,我们常常会遇到选择多张图片的场景,交互动作为选择多张图片,图片先放在“暂留区”,然后再提交发布。

SwiftUI极简教程38:ScrollViewReader滚动视图锚点的使用_SwiftUI

那么本章,我们就尝试使用​​ScrollViewReader​​滚动视图锚点来完成这个交互。

项目搭建

首先,创建一个新项目,命名为​​SwiftUIScrollViewReader ​​。

SwiftUI极简教程38:ScrollViewReader滚动视图锚点的使用_数组_02

素材准备

我们先导入一批图片,作为待选择展示的网格存放的内容。

SwiftUI极简教程38:ScrollViewReader滚动视图锚点的使用_swift_03

Model准备

然后,我们构建下​​Model​​​。新建一个​​swift​​​类型的文件,命名为​​Model.swift​​。

import Foundation

struct Model: Identifiable {
var id = UUID()
var imageName: String
}

let sampleModels = (1...9).map { Model(imageName: "image0\($0)") }

SwiftUI极简教程38:ScrollViewReader滚动视图锚点的使用_swift_04

构建图片网格的Model也比较简单,我们定义了一个​​Model​​​结构体,它遵循​​Identifiable​​​协议,使用​​id​​定位到结构体中的实例。

使用​​var​​​声明了图片网格需要的元素​​imageName​​​,然后我们创建了一个​​sampleModels​​​数组,创建了一个示例数据作为​​View​​视图中展示的内容。

科普一个知识点

我们在这里使用​​Map​​​函数方法,它可以返回的是一个​​数组​​ 。

这里使用闭包表达式作为参数,集合中的每个元素调用一次该闭包函数,并返回该元素所映射的值。

这样,我们就构建好了​​Model​​部分。

网格视图

接下来,我们来做图片网格视图的部分。

首先,我们声明一个状态变量​​photoSet​​​,这样我们就可以在​​sampleModels​​图片数组被选择的时候知道它。

然后,我们使用​​LazyVGrid​​组件完成网格视图。

struct ContentView: View {

@State private var photoSet = sampleModels

var body: some View {

VStack {
ScrollView {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 80))]) {
ForEach(photoSet) { photo in
Image(photo.imageName)
.resizable()
.scaledToFill()
.frame(minWidth: 0, maxWidth: .infinity)
.frame(height: 150)
.cornerRadius(8)
}
}
}
} .padding()
}
}

SwiftUI极简教程38:ScrollViewReader滚动视图锚点的使用_数组_05

和之前的章节一样,我们使用自适应布局来完成网格图片集合的展示。

接下来,我们也完成下选择图片后放置的“暂留区”的样式。

ScrollView(.horizontal, showsIndicators: false) {
}
.frame(height: 100)
.padding()
.background(Color(.systemGray6))
.cornerRadius(8)

SwiftUI极简教程38:ScrollViewReader滚动视图锚点的使用_Swift_06

当我们选中一张照片时,我们将从照片网格中删除它,并将它插入到底部的“暂留区”中。 

为了处理照片选择,我们创建一个状态变量来保存选中的照片。

另外因为​​photoSet​​​中的每张照片都有自己的​​UUID​​​类型的​​ID​​​,我们要存储当前选中的照片,需要声明另一个​​UUID​​类型的状态变量。

@State private var selectedPhotos: [Model] = []
@State private var selectedPhotoId: UUID?

接下来,我们设计点击事件,点击网格图片集的时候,我们知道是哪一张图片,并且将它从网格图片集中删除。

.onTapGesture {
selectedPhotos.append(photo)
selectedPhotoId = photo.id
if let index = photoSet.firstIndex(where: { $0.id == photo.id }) {
photoSet.remove(at: index)
}
}

SwiftUI极简教程38:ScrollViewReader滚动视图锚点的使用_SwiftUI_07

上述代码中,我们将选中的照片添加到​​selectedPhotos​​​数组中,并更新​​selectedPhotoId​​。 

因为​​photoSet​​是一个状态变量,所以照片选中时,一旦从数组中移除,就会从网格中移除。

图片从网格图片集删除后,我们要加到下面的“暂留区”中,操作方法和上面类型,只是上面删除,下面添加,下面删除,上面添加,构建一个循环。

ScrollView(.horizontal, showsIndicators: false) {
LazyHGrid(rows: [GridItem()]) {
ForEach(selectedPhotos) { photo in
Image(photo.imageName)
.resizable()
.scaledToFill()
.frame(minWidth: 0, maxWidth: .infinity)
.frame(height: 150)
.cornerRadius(8)
.onTapGesture {
photoSet.append(photo)
if let index = selectedPhotos.firstIndex(where: { $0.id == photo.id }) {
selectedPhotos.remove(at: index)
}
}
}
}
}

SwiftUI极简教程38:ScrollViewReader滚动视图锚点的使用_SwiftUI_08

看起来不错。

交互优化

但我们发现一个问题,就是我们选中图片很多的时候,图片加到“暂留区”中是按照添加的先后顺序加的,后面加的图片要滚动才能看到。

这不是我们想要的效果。

我们希望添加图片到“​​暂留区​​”中时,“暂留区”的滚动视图能自动定位到最新添加图片的位置。

这时,我们就需要使用到​​ScrollViewReader​​滚动视图锚点组件,它可以让滚动视图移动到特定位置。

ScrollViewReader { scrollProxy in
ScrollView(.horizontal, showsIndicators: false) {
LazyHGrid(rows: [GridItem()]) {
ForEach(selectedPhotos) { photo in
Image(photo.imageName)
.resizable()
.scaledToFill()
.frame(minWidth: 0, maxWidth: .infinity)
.frame(height: 150)
.cornerRadius(8)
.id(photo.id)
.onTapGesture {
photoSet.append(photo)
if let index = selectedPhotos.firstIndex(where: { $0.id == photo.id }) {
selectedPhotos.remove(at: index)
}
}
}
}
}
.frame(height: 100)
.padding()
.background(Color(.systemGray6))
.cornerRadius(8)
.onChange(of: selectedPhotoId, perform: { id in
guard id != nil else { return }

scrollProxy.scrollTo(id)
})
}

SwiftUI极简教程38:ScrollViewReader滚动视图锚点的使用_SwiftUI_09

上述代码中,因为每张照片已经有了它唯一的标识符,我们可以使用照片​​ID​​作为视图的标识符,我们给“暂留区”的图片添加​​id​​​为​​photo的ID​​。

我们使用​​onchange​​​来监听​​selectedPhotoId​​的更新。

每当照片​​ID​​​被改变时,照片​​ID​​​就可以调用​​scrollTo​​来滚动视图到那个特定的位置。

我们预览下效果

SwiftUI极简教程38:ScrollViewReader滚动视图锚点的使用_Swift_10

本章代码

import SwiftUI

struct ContentView: View {

@State private var photoSet = sampleModels
@State private var selectedPhotos: [Model] = []
@State private var selectedPhotoId: UUID?

var body: some View {

VStack {
ScrollView {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 80))]) {
ForEach(photoSet) { photo in
Image(photo.imageName)
.resizable()
.scaledToFill()
.frame(minWidth: 0, maxWidth: .infinity)
.frame(height: 150)
.cornerRadius(8)
.onTapGesture {
selectedPhotos.append(photo)
selectedPhotoId = photo.id
if let index = photoSet.firstIndex(where: { $0.id == photo.id }) {
photoSet.remove(at: index)
}
}
}
}
}

ScrollViewReader { scrollProxy in
ScrollView(.horizontal, showsIndicators: false) {
LazyHGrid(rows: [GridItem()]) {
ForEach(selectedPhotos) { photo in
Image(photo.imageName)
.resizable()
.scaledToFill()
.frame(minWidth: 0, maxWidth: .infinity)
.frame(height: 150)
.cornerRadius(8)
.id(photo.id)
.onTapGesture {
photoSet.append(photo)
if let index = selectedPhotos.firstIndex(where: { $0.id == photo.id }) {
selectedPhotos.remove(at: index)
}
}
}
}
}
.frame(height: 100)
.padding()
.background(Color(.systemGray6))
.cornerRadius(8)
.onChange(of: selectedPhotoId, perform: { id in
guard id != nil else { return }

scrollProxy.scrollTo(id)
})
}
}
.padding()
}
}

快来动手试试吧!

如果本专栏对你有帮助,不妨点赞、评论、关注~

举报

相关推荐

0 条评论