UICollectionView 和 SwiftUI?
Posted
技术标签:
【中文标题】UICollectionView 和 SwiftUI?【英文标题】:UICollectionView and SwiftUI? 【发布时间】:2019-06-05 18:43:17 【问题描述】:如何使用 SwiftUI 创建方形项目的网格(例如在 ios 照片库中)?
我尝试了这种方法,但它不起作用:
var body: some View
List(cellModels) _ in
Color.orange.frame(width: 100, height: 100)
List 仍然有 UITableView 样式:
【问题讨论】:
和你要找的差不多averyvine.com/blog/programming/2019/06/07/… 【参考方案1】:iOS 14 和 XCode 12
iOS 14 的 SwiftUI 带来了一个易于使用的全新原生网格视图,名为 LazyVGrid: https://developer.apple.com/documentation/swiftui/lazyvgrid
您可以从定义 GridItem 的 数组开始。 GridItems 用于指定每一列的布局属性。在这种情况下,所有 GridItem 都是灵活的。
LazyVGrid 将 GridItem 的数组作为其参数,并根据定义的 GridItem 显示包含的视图。
import SwiftUI
struct ContentView: View
let columns = [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())
]
var body: some View
ScrollView
LazyVGrid(columns: columns)
ForEach(0...100, id: \.self) _ in
Color.orange.frame(width: 100, height: 100)
【讨论】:
在 iOS 15 中,与 UICollectionView 相比,LazyVGrid 的性能非常糟糕(即使是最简单的示例),它不会随着项目的数量而扩展。他们在 WWDC 上开设了关于避免故障的讲座,而且他们甚至没有解决自己组件中的性能问题,这真是太荒谬了。【参考方案2】:一种可能的解决方案是将您的UICollectionView
包装到UIViewRepresentable
中。请参阅 Combining and Creating Views SwiftUI 教程,其中他们以包装 MKMapView
为例。
到目前为止,SwiftUI 中还没有 UICollectionView
的等价物,而且还没有计划。请参阅tweet 下的讨论。
要了解更多详情,请查看Integrating SwiftUI WWDC 视频 (~8:08)。
更新:
从 iOS 14 (beta) 开始,我们可以使用Lazy*Stack
至少达到 SwiftUI 中集合视图的性能。当涉及到单元格的布局时,我认为我们仍然必须在每行/每列的基础上手动管理它。
【讨论】:
当与多行一起使用时,请注意 bad performance 和Lazy*Stack
。【参考方案3】:
QGrid
是我创建的一个小型库,它使用与 SwiftUI 的 List
视图相同的方法,通过从基础数据集合中按需计算其单元格:
假设您已经有一个自定义单元格视图,QGrid
最简单的形式可以与 View
正文中的这 1 行代码一起使用:
struct PeopleView: View
var body: some View
QGrid(Storage.people, columns: 3) GridCell(person: $0)
struct GridCell: View
var person: Person
var body: some View
VStack()
Image(person.imageName).resizable().scaledToFit()
Text(person.firstName).font(.headline).color(.white)
Text(person.lastName).font(.headline).color(.white)
您还可以自定义默认布局配置:
struct PeopleView: View
var body: some View
QGrid(Storage.people,
columns: 3,
columnsInLandscape: 4,
vSpacing: 50,
hSpacing: 20,
vPadding: 100,
hPadding: 20) person in
GridCell(person: person)
请参考 GitHub repo 中的演示 GIF 和测试应用:
https://github.com/Q-Mobile/QGrid
【讨论】:
我还没有测试过你的库,但它看起来令人印象深刻。很棒的工作。我认为这不是水平滚动的,对吧? @Imthath :此功能在路线图/待办事项列表中;-) 不会花费太长时间,所以如果您愿意,请随时贡献! @GOR:请使用最新版本(v.0.1.3)重试 我使用这个库,我很感谢创建者。之前的评论提到了对点击项目的操作。只需传递一个“按钮”或任何附加了“tapGesture”的视图。 QGrid(viewModel.colorSelectionNames, columns: columnCount) colorName in Circle() .onTapGesture self.viewModel.selectedColorName = colorName DUUUDE!你救了我的命!这是令人难以置信的工作!【参考方案4】:在 SwiftUI 中思考,有一个简单的方法:
struct MyGridView : View
var body: some View
List()
ForEach(0..<8) _ in
HStack
ForEach(0..<3) _ in
Image("orange_color")
.resizable()
.scaledToFit()
SwiftUI 够用了,有时你需要忘记 UIColectionView 之类的......
【讨论】:
这种方法的问题是它不会根据屏幕宽度调整列。有人想出解决办法吗? 通过计算UIScreen.main.bounds.width / (itemSize + spacing)
,您可以获得适合一行的项目数。 (在所有水平间距都相同的情况下)通过将其放入计算属性中,您可以在上面的代码示例中轻松使用它。
这个解决方案的问题在于它不适用于水平方向的集合视图。也就是说,它可以工作,但在这种情况下,我们完全失去了可重用性。
@ManeManero 删除你的图像框架并设置 .edgesIgnoringSafeArea(.all)【参考方案5】:
XCode 11.0
寻找了一段时间后,我决定我想要 UICollectionView 的所有便利和性能。所以我实现了UIViewRepresentable
协议。
此示例未实现 DataSource,并且在集合视图上有一个虚拟的 data: [Int]
字段。
您可以在AlbumGridView
上使用@Bindable var data: [YourData]
,以便在数据更改时自动重新加载您的视图。
AlbumGridView
然后可以像 SwiftUI 中的任何其他视图一样使用。
代码
class AlbumPrivateCell: UICollectionViewCell
private static let reuseId = "AlbumPrivateCell"
static func registerWithCollectionView(collectionView: UICollectionView)
collectionView.register(AlbumPrivateCell.self, forCellWithReuseIdentifier: reuseId)
static func getReusedCellFrom(collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> AlbumPrivateCell
return collectionView.dequeueReusableCell(withReuseIdentifier: reuseId, for: indexPath) as! AlbumPrivateCell
var albumView: UILabel =
let label = UILabel()
return label
()
override init(frame: CGRect)
super.init(frame: frame)
contentView.addSubview(self.albumView)
albumView.translatesAutoresizingMaskIntoConstraints = false
albumView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
albumView.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
albumView.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
albumView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
required init?(coder: NSCoder)
fatalError("init?(coder: NSCoder) has not been implemented")
struct AlbumGridView: UIViewRepresentable
var data = [1,2,3,4,5,6,7,8,9]
func makeUIView(context: Context) -> UICollectionView
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
collectionView.backgroundColor = .blue
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.dataSource = context.coordinator
collectionView.delegate = context.coordinator
AlbumPrivateCell.registerWithCollectionView(collectionView: collectionView)
return collectionView
func updateUIView(_ uiView: UICollectionView, context: Context)
//
func makeCoordinator() -> Coordinator
Coordinator(self)
class Coordinator: NSObject, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
private let parent: AlbumGridView
init(_ albumGridView: AlbumGridView)
self.parent = albumGridView
// MARK: UICollectionViewDataSource
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
self.parent.data.count
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let albumCell = AlbumPrivateCell.getReusedCellFrom(collectionView: collectionView, cellForItemAt: indexPath)
albumCell.backgroundColor = .red
return albumCell
// MARK: UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
let width = collectionView.frame.width / 3
return CGSize(width: width, height: width)
截图
【讨论】:
对于 >= iOS 14,请使用 SwiftUI LazyGridView 而不是这个 如果我从视图中添加 AlbumGridView,cellForItemAt 方法不会调用我。 @GauravThummar 我会认真研究 WWDC 2020 中引入的惰性视图,而不是尝试这个集合视图的东西。 感谢 philipp,一旦我将它添加到主视图中并给出特定框架,它就对我有用。 @philipp LazyVGrid 即使在 iOS 15 中,大量项目的性能也很糟糕【参考方案6】:我们开发了一个 swift 包,它提供了一个功能齐全的 CollectionView 以在 SwiftUI 中使用。
Find it here: https://github.com/apptekstudios/ASCollectionView
它被设计为易于使用,但也可以充分利用新的 UICollectionViewCompositionalLayout 进行更复杂的布局。它支持单元格的自动调整大小。
要实现网格视图,您可以按如下方式使用它:
import SwiftUI
import ASCollectionView
struct ExampleView: View
@State var dataExample = (0 ..< 21).map $0
var body: some View
ASCollectionView(data: dataExample, dataID: \.self) item, _ in
Color.blue
.overlay(Text("\(item)"))
.layout
.grid(layoutMode: .adaptive(withMinItemSize: 100),
itemSpacing: 5,
lineSpacing: 5,
itemSize: .absolute(50))
有关更复杂的布局示例,请参阅演示项目。
【讨论】:
这是一件令人难以置信的作品。 这真是令人印象深刻。 这个包超级有问题...【参考方案7】:我自己一直在解决这个问题,并以@Anjali 上面发布的源代码为基础,以及@phillip (the work of Avery Vine),我已经包装了一个功能性的 UICollectionView...是吗?它将根据需要显示和更新网格。我还没有尝试过更可定制的视图或任何其他东西,但现在,我认为它会做。
我在下面评论了我的代码,希望对某人有用!
首先,包装器。
struct UIKitCollectionView: UIViewRepresentable
typealias UIViewType = UICollectionView
//This is where the magic happens! This binding allows the UI to update.
@Binding var snapshot: NSDiffableDataSourceSnapshot<DataSection, DataObject>
func makeCoordinator() -> Coordinator
Coordinator(self)
func makeUIView(context: UIViewRepresentableContext<UIKitCollectionView>) -> UICollectionView
//Create and configure your layout flow seperately
let flowLayout = UICollectionViewFlowLayout()
flowLayout.sectionInsets = UIEdgeInsets(top: 25, left: 0, bottom: 25, right: 0)
//And create the UICollection View
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
//Create your cells seperately, and populate as needed.
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "customCell")
//And set your datasource - referenced from Avery
let dataSource = UICollectionViewDiffableDataSource<DataSection, DataObject>(collectionView: collectionView) (collectionView, indexPath, object) -> UICollectionViewCell? in
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "customCell", for: indexPath)
//Do cell customization here
if object.id.uuidString.contains("D")
cell.backgroundColor = .red
else
cell.backgroundColor = .green
return cell
context.coordinator.dataSource = dataSource
populate(load: [DataObject(), DataObject()], dataSource: dataSource)
return collectionView
func populate(load: [DataObject], dataSource: UICollectionViewDiffableDataSource<DataSection, DataObject>)
//Load the 'empty' state here!
//Or any default data. You also don't even have to call this function - I just thought it might be useful, and Avery uses it in their example.
snapshot.appendItems(load)
dataSource.apply(snapshot, animatingDifferences: true)
//Whatever other actions you need to do here.
func updateUIView(_ uiView: UICollectionView, context: UIViewRepresentableContext<UIKitCollectionView>)
let dataSource = context.coordinator.dataSource
//This is where updates happen - when snapshot is changed, this function is called automatically.
dataSource?.apply(snapshot, animatingDifferences: true, completion:
//Any other things you need to do here.
)
class Coordinator: NSObject
var parent: UIKitCollectionView
var dataSource: UICollectionViewDiffableDataSource<DataSection, DataObject>?
var snapshot = NSDiffableDataSourceSnapshot<DataSection, DataObject>()
init(_ collectionView: UIKitCollectionView)
self.parent = collectionView
现在,DataProvider
类将允许我们访问该可绑定快照并在需要时更新 UI。此类对于正确更新集合视图是必不可少的。 DataSection
和 DataObject
模型与 Avery Vine 提供的模型具有相同的结构 - 所以如果您需要这些模型,请看那里。
class DataProvider: ObservableObject //This HAS to be an ObservableObject, or our UpdateUIView function won't fire!
var data = [DataObject]()
@Published var snapshot : NSDiffableDataSourceSnapshot<DataSection, DataObject> =
//Set all of your sections here, or at least your main section.
var snap = NSDiffableDataSourceSnapshot<DataSection, DataObject>()
snap.appendSections([.main, .second])
return snap
()
didSet
self.data = self.snapshot.itemIdentifiers
//I set the 'data' to be equal to the snapshot here, in the event I just want a list of the data. Not necessary.
//Create any snapshot editing functions here! You can also simply call snapshot functions directly, append, delete, but I have this addItem function to prevent an exception crash.
func addItems(items: [DataObject], to section: DataSection)
if snapshot.sectionIdentifiers.contains(section)
snapshot.appendItems(items, toSection: section)
else
snapshot.appendSections([section])
snapshot.appendItems(items, toSection: section)
现在,CollectionView
将展示我们的新系列。我用一些按钮制作了一个简单的 VStack,以便您可以看到它的实际效果。
struct CollectionView: View
@ObservedObject var dataProvider = DataProvider()
var body: some View
VStack
UIKitCollectionView(snapshot: $dataProvider.snapshot)
Button("Add a box")
self.dataProvider.addItems(items: [DataObject(), DataObject()], to: .main)
Button("Append a Box in Section Two")
self.dataProvider.addItems(items: [DataObject(), DataObject()], to: .second)
Button("Remove all Boxes in Section Two")
self.dataProvider.snapshot.deleteSections([.second])
struct CollectionView_Previews: PreviewProvider
static var previews: some View
CollectionView()
仅针对那些可视化引用(是的,这是在 Xcode 预览窗口中运行的):
【讨论】:
【参考方案8】:更新:此答案与 iOS 13 有关。对于 iOS 14,我们有 LazyGrids + 更多内容,遵循此答案将无济于事。
为了在不使用 UIKit 的情况下制作 CollectionView,首先我们需要一个数组扩展。数组扩展将帮助我们将我们想要制作 TableView 的数组分块。下面是扩展的代码,+ 3 个示例。要进一步了解此扩展程序的工作原理,请查看此站点,我从该站点复制了扩展程序:https://www.hackingwithswift.com/example-code/language/how-to-split-an-array-into-chunks
extension Array
func chunked(into size: Int) -> [[Element]]
return stride(from: 0, to: count, by: size).map
Array(self[$0 ..< Swift.min($0 + size, count)])
let exampleArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
print(exampleArray.chunked(into: 2)) // prints [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]]
print(exampleArray.chunked(into: 3)) // prints [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
print(exampleArray.chunked(into: 5)) // prints [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12]]
现在让我们制作 SwiftUI 视图:
struct TestView: View
let arrayOfInterest = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18].chunked(into: 4)
// = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16], [17, 18]]
var body: some View
return VStack
ScrollView
VStack(spacing: 16)
ForEach(self.arrayOfInterest.indices, id:\.self) idx in
HStack
ForEach(self.arrayOfInterest[idx].indices, id:\.self) index in
HStack
Spacer()
Text("\(self.arrayOfInterest[idx][index])")
.font(.system(size: 50))
.padding(4)
.background(Color.blue)
.cornerRadius(8)
Spacer()
struct TestView_Preview : PreviewProvider
static var previews: some View
TestView()
Image of Preview of the code above
解释:
首先,我们需要明确我们需要多少列,并将该数字放入我们的分块扩展中。在我的示例中,我们有一个从 1 到 18 的数字数组(arrayOfInterest),我们想在视图中显示它,我决定我希望我的视图有 4 列,所以我将它分成 4 列(所以 4 是数字我们的专栏)。
要制作一个 CollectionView,最明显的是我们的 CollectionView 是一个项目列表,所以它应该在一个列表中以使其易于滚动(不,不要这样做!改用 ScrollView。我已经看到奇怪的行为,而这 2 个 foreachs 在列表中)。 在 ScrollView 之后我们有 2 个 ForEach ,第一个使我们能够根据需要循环尽可能多的 Rows,而第二个帮助我们制作列。
我知道我没有完美地解释代码,但我相信它值得与您分享,这样可以让您更轻松地查看表格。 This Image is an early example of a real app i'm making,它看起来简直就是 CollectionView,所以你可以确定这种方法效果很好。
问题:拥有一个数组并试图让 swift 为 foreach 创建这些索引有什么意义? 这很简单!如果您有一个在运行时定义其值/值数的数组,例如您从 web api 获取数字,该 api 告诉您数组中有多少个数字,然后您需要使用类似这样的方法并让 swift 处理 foreachs 的索引。
更新:
更多信息,阅读这些是可选的。
LIST VS SCROLLVIEW:有些人可能不知道,列表的工作方式与滚动视图有点不同。当你创建一个滚动视图时,它总是计算整个滚动视图,然后显示给我们。但是 list 不会这样做,当使用列表时,swift 会自动计算仅显示当前视图所需的列表组件中的几个,并且当您向下滚动到列表底部时,它只会替换正在执行的旧值滚动出屏幕底部的新值。所以一般来说, list 总是更轻,当你使用重视图时可以更快,因为它不会在开始时计算你的所有视图,而只计算必要的东西,而 ScrollView 没有。
您为什么说我们应该使用滚动视图而不是列表? 正如我之前所说,列表中有一些你可能不喜欢的交互。例如,在创建列表时,每一行都是可点击的,这很好,但不好的是只有整行是可点击的!这意味着您不能为行的左侧设置点击动作,而为右侧设置不同的点击动作!这只是 List() 的奇怪交互之一 这要么需要一些我没有的知识!或者是一个很大的 xcode-ios 问题,或者它可能很好并且符合预期!我认为这是一个苹果问题,我希望它最多能在下一个 WWDC 之前得到修复。 (更新:当然,它通过引入所有的东西得到修复,比如用于 iOS14-SwiftUI 的 LazyGrids)
有什么办法可以解决这个问题? 据我所知,唯一的方法是使用 UIKit。我用 SwiftUI 尝试了很多方法,虽然我发现你可以从 ActionSheet 和 ContextMenu 获得帮助,以便在你点击它们时在选项方面更好地制作列表,但我无法从中获得最佳预期功能一个 SwiftUI 列表。所以从我的 POV 来看,SwiftUI 开发者只能等待。
【讨论】:
【参考方案9】:签出基于 ZStack 的示例 here
Grid(0...100) _ in
Rectangle()
.foregroundColor(.blue)
【讨论】:
【参考方案10】:厌倦了寻找许多复杂的解决方案或 Github 库,我决定自己做一个简单而漂亮的数学解决方案。
-
认为您有一系列项目
var items : [ITEM] = [...YOUR_ITEMS...]
您想显示一个 Nx2 的网格
当N是ROWS的个数,2是个数 列
-
要显示所有项目,您需要使用两个
ForEach
语句,一个用于列,一个用于行。
进入
的当前索引ForEach
:(i) ROWS 的当前索引和 (j) COLUMNS
-
在索引中显示当前项 [(i * 2) + j]
现在让我们进入代码:
注意:Xcode 11.3.1
var items : [ITEM] = [...YOUR_ITEMS...]
var body: some View
VStack
// items.count/2 represent the number of rows
ForEach(0..< items.count/2) i in
HStack(alignment: .center,spacing: 20)
//2 columns
ForEach(0..<2) j in
//Show your custom view here
// [(i*2) + j] represent the index of the current item
ProductThumbnailView(product: self.items[(i*2) + j])
.padding(.horizontal)
Spacer()
【讨论】:
您不能将它与大量对象一起使用 :) 内存问题。只有 List 里面有可重复使用的项目 我是新手,我可以理解大型 m x n 网格如何耗尽内存,但这是我正在考虑滚动到 m x n 网格中的解决方案类型,其中 m,n 的范围为 1..64。带有滚动和缩放功能。我在想只是映射像素。【参考方案11】:尝试使用 VStack 和 HStack
var body: some View
GeometryReader geometry in
VStack
ForEach(1...3) _ in
HStack
Color.orange.frame(width: 100, height: 100)
Color.orange.frame(width: 100, height: 100)
Color.orange.frame(width: 100, height: 100)
.frame(width: geometry.size.width, height: 100)
如果你想滚动,你可以包裹在一个 ScrollView 中
【讨论】:
唯一的问题是这不是根据显示器的宽度动态更新的...... 但这不是一个列表而是堆栈——没有可重用性。如果我有 10000 件商品怎么办? 这个答案与 CollectionView 无关。单元格不会被重复使用,性能很差 重用在这里不是问题——它是一个声明性代码,所以在这里你只是创建了符合View
类型的超轻量级struct
s。实际演示实例的重用在后台处理,无需您参与。这就是 SwiftUI 的美妙之处 ? 虽然关于 SwiftUI 中集合视图的最佳实践的问题我认为仍然有效。这里似乎没有 UICollectionViewLayout
的等价物,而这正是 UIKit 中优雅地解决问题的原因。
@MaciekCzarnik 很好的答案,但它与 CollectionView 不太一样,因为 HStacks 和 VStacks 无法处理任意动态布局;例如,流式布局。【参考方案12】:
我认为你可以像这样使用滚动视图
struct MovieItemView : View
var body: some View
VStack
Image("sky")
.resizable()
.frame(width: 150, height: 200)
VStack
Text("Movie Title")
.font(.headline)
.fontWeight(.bold)
Text("Category")
.font(.subheadline)
struct MoviesView : View
var body: some View
VStack(alignment: .leading, spacing: 10)
Text("Now Playing")
.font(.title)
.padding(.leading)
ScrollView
HStack(spacing: 10)
MovieItemView()
MovieItemView()
MovieItemView()
MovieItemView()
MovieItemView()
MovieItemView()
.padding(.leading, 20)
【讨论】:
【参考方案13】:我编写了一个名为 ?GridStack 的小组件,它可以创建一个可以调整到可用宽度的网格。即使当你旋转 iPad 时这种动态变化也是如此。
https://github.com/pietropizzi/GridStack
该实现的要点与其他人在此处回答的内容相似(因此 HStack
s 在 VStack
中),不同之处在于它根据可用宽度和您传递的配置来计算宽度。
minCellWidth
,您可以定义您希望网格中的项目应具有的最小宽度
使用spacing
,您可以定义网格中项目之间的空间。
例如
GridStack(
minCellWidth: 320,
spacing: 15,
numItems: yourItems.count
) index, cellWidth in
YourItemView(item: yourItems[index]).frame(width: cellWidth)
【讨论】:
虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review @DerkJanSpeelman 我更新了答案以包括内联的基本部分。干杯【参考方案14】:由于我没有使用 Catalina Beta,因此我在此处编写了我的代码,您可以在 Xcode 11 (Mojave) 上运行作为一个游乐场,以利用运行时编译和预览
基本上,当您寻找网格方法时,您应该记住 SwiftUI 子视图从父视图获取理想的大小参数,以便它们可以根据自己的内容自动适应,这种行为可以被覆盖(不要与 swift覆盖指令)通过 .frame(...) 方法将视图强制为特定大小。
在我看来,这使得 View 行为稳定,并且 Apple SwiftUI 框架已经过正确测试。
import PlaygroundSupport
import SwiftUI
struct ContentView: View
var body: some View
VStack
ForEach(0..<5) _ in
HStack(spacing: 0)
ForEach(0..<5) _ in
Button(action: )
Text("Ok")
.frame(minWidth: nil, idealWidth: nil, maxWidth: .infinity, minHeight: nil, idealHeight: nil, maxHeight: .infinity, alignment: .center)
.border(Color.red)
let contentView = ContentView()
PlaygroundPage.current.liveView = UIHostingController(rootView: contentView)
【讨论】:
【参考方案15】:尽管下一个 WWDC 即将到来,但我们仍然缺少 SwiftUI
中的集合视图。我试图提供一个不错的例子来说明如何使用UIViewControllerRepresentable
和Combine
创建自己的SwiftUI collection view。我选择创建自己的集合视图,而不是使用开源库,因为我觉得它们缺少一些关键功能,或者因为太多东西而臃肿。在我的示例中,我使用了UICollectionViewDiffableDataSource
和UICollectionViewCompositionalLayout
来创建集合视图。它同时支持pullToRefresh
和pagination
功能。
完整实现见:https://github.com/shabib87/SwiftUICollectionView。
【讨论】:
【参考方案16】:根据 Will 的回答,我将其全部封装在 SwiftUI ScrollView 中。 所以你可以实现水平(在这种情况下)或垂直滚动。
它还使用 GeometryReader,因此可以使用屏幕大小进行计算。
GeometryReader geometry in
.....
Rectangle()
.fill(Color.blue)
.frame(width: geometry.size.width/6, height: geometry.size.width/6, alignment: .center)
这是一个工作示例:
import SwiftUI
struct MaterialView: View
var body: some View
GeometryReader geometry in
ScrollView(Axis.Set.horizontal, showsIndicators: true)
ForEach(0..<2) _ in
HStack
ForEach(0..<30) index in
ZStack
Rectangle()
.fill(Color.blue)
.frame(width: geometry.size.width/6, height: geometry.size.width/6, alignment: .center)
Text("\(index)")
.background(Color.red)
.background(Color.black)
struct MaterialView_Previews: PreviewProvider
static var previews: some View
MaterialView()
【讨论】:
以上是关于UICollectionView 和 SwiftUI?的主要内容,如果未能解决你的问题,请参考以下文章
UICollectionView:contentSize和bounds有啥区别?
UICollectionView - 分离 scrollViewDidScroll 和 scrollToItemAtIndexPath