ios UICollectionView 和 SwiftUI?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/56466306/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
UICollectionView and SwiftUI?
提问by Evgeny Mikhaylov
How to create grid of square items (for example like in iOS Photo Library) with SwiftUI?
如何使用 SwiftUI 创建方形项目的网格(例如在 iOS 照片库中)?
I tried this approach but it doesn't work:
我试过这种方法,但它不起作用:
var body: some View {
List(cellModels) { _ in
Color.orange.frame(width: 100, height: 100)
}
}
List still has UITableView style:
列表仍然具有 UITableView 样式:
采纳答案by Maciek Czarnik
One of the possible solutions is to wrap your UICollectionView
into UIViewRepresentable
. See Combining and Creating ViewsSwiftUI Tutorial, where they wrap the MKMapView
as an example.
一个可能的解决方案是你的包裹UICollectionView
到UIViewRepresentable
。参见Combining and Creating ViewsSwiftUI 教程,他们将 包装MKMapView
作为示例。
By now there isn't an equivalent of UICollectionView
in the SwiftUI and there's no plan for it yet. See a discussion under that tweet.
到目前为止UICollectionView
,SwiftUI 中还没有等效的东西,而且还没有计划。请参阅该推文下的讨论。
To get more details check the Integrating SwiftUIWWDC video (~8:08).
要获得更多详细信息,请查看集成 SwiftUIWWDC 视频 (~8:08)。
回答by Karol Kulesza
QGrid
is a small library I've created that uses the same approach as SwiftUI's List
view, by computing its cells on demand from an underlying collection of identified data:
QGrid
是我创建的一个小型库,它使用与 SwiftUI 的List
视图相同的方法,通过根据已识别数据的底层集合按需计算其单元格:
In its simplest form, QGrid
can be used with just this 1 line of code within the body of your View
, assuming you already have a custom cell view:
在最简单的形式中,假设您已经有一个自定义单元格视图,QGrid
可以在您的 主体中仅使用这 1 行代码View
:
struct PeopleView: View {
var body: some View {
QGrid(Storage.people, columns: 3) { GridCell(person: 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)
}
}
}
) }
}
}
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)
}
}
}
You can also customize the default layout configuration:
您还可以自定义默认布局配置:
struct MyGridView : View {
var body: some View {
List() {
ForEach(0..<8) { _ in
HStack {
ForEach(0..<3) { _ in
Image("orange_color")
.resizable()
.scaledToFit()
}
}
}
}
}
Please refer to demo GIF and test app within GitHub repo:
请参考 GitHub 存储库中的演示 GIF 和测试应用程序:
回答by Will
Thinking in SwiftUI, there is a easy way :
在 SwiftUI 中思考,有一个简单的方法:
import SwiftUI
import ASCollectionView
struct ExampleView: View {
@State var dataExample = (0 ..< 21).map { 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
}
}
}
}
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))
}
}
}
}
}
SwiftUI enough if you want,you need forgot such as UIColectionView sometimes..
如果你愿意,SwiftUI 就足够了,有时你需要忘记 UICollectionView 之类的..
回答by Apptek Studios
We've developed a swift package that provides a fully featured CollectionView for use in SwiftUI.
我们开发了一个 swift 包,它提供了一个在 SwiftUI 中使用的全功能 CollectionView。
Find it here: https://github.com/apptekstudios/ASCollectionView
在这里找到:https://github.com/apptekstudios/ASCollectionView
It's designed to be easy to use, but can also make full use of the new UICollectionViewCompositionalLayout for more complex layouts. It supports auto-sizing of cells.
它旨在易于使用,但也可以充分利用新的 UICollectionViewCompositionalLayout 进行更复杂的布局。它支持自动调整单元格大小。
To achieve a grid view you could use it as follows:
要实现网格视图,您可以按如下方式使用它:
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)
}
}
}
See the demo project for examples of far more complex layouts.
有关更复杂布局的示例,请参阅演示项目。
回答by Misha Stone
I've been tackling this problem myself, and by using the source posted above by @Anjali as a base, a well as @phillip, (the work of Avery Vine), I've wrapped a UICollectionView that is functional...ish? It'll display and update a grid as needed. I haven't tried the more customizable views or any other things, but for now, I think it'll do.
我自己一直在解决这个问题,并通过使用@Anjali 上面发布的源代码作为基础,以及@phillip,(Avery Vine 的工作),我包装了一个功能强大的 UICollectionView...ish ? 它将根据需要显示和更新网格。我还没有尝试过更可定制的视图或任何其他东西,但就目前而言,我认为它可以。
I commented my code below, hope it's useful to someone!
我在下面评论了我的代码,希望它对某人有用!
First, the wrapper.
首先是包装纸。
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()
}
}
Now, the DataProvider
class will allow us to access that bindable snapshot and update the UI when we want it to. This class is essentialto the collection view updating properly. The models DataSection
and DataObject
are of the same structure as the one provided by Avery Vine- so if you need those, look there.
现在,DataProvider
该类将允许我们访问该可绑定快照并在需要时更新 UI。此类对于正确更新集合视图至关重要。模型DataSection
和 与Avery VineDataObject
提供的模型具有相同的结构- 因此,如果您需要这些,请查看那里。
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)
}
}
}
And now, the CollectionView
, which is going to display our new collection. I made a simple VStack with some buttons so you can see it in action.
现在,CollectionView
将显示我们的新系列。我用一些按钮制作了一个简单的 VStack,所以你可以看到它的运行情况。
extension Array {
func chunked(into size: Int) -> [[Element]] {
return stride(from: 0, to: count, by: size).map {
Array(self[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()
}
}
..< Swift.min(Grid(0...100) { _ in
Rectangle()
.foregroundColor(.blue)
}
+ 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]]
And just for those visual referencers (ye, this is running in the Xcode Preview window):
并且仅针对那些视觉参考(是的,这是在 Xcode 预览窗口中运行的):
回答by philipp
XCode 11.0
代码 11.0
After looking for a while I decided that I wanted all the convenience and performance form the UICollectionView. So I implemented the UIViewRepresentable
protocol.
在寻找了一段时间后,我决定我想要 UICollectionView 的所有便利和性能。所以我实现了UIViewRepresentable
协议。
This example does not implement the DataSource and has a dummy data: [Int]
field on the collection view.
You would use a @Bindable var data: [YourData]
on the AlbumGridView
to automatically reload your view when the data changes.
此示例未实现 DataSource 并且data: [Int]
在集合视图上有一个虚拟字段。当数据更改时,您将使用@Bindable var data: [YourData]
onAlbumGridView
自动重新加载您的视图。
AlbumGridView
can then be used like any other view inside SwiftUI.
AlbumGridView
然后可以像 SwiftUI 中的任何其他视图一样使用。
Code
代码
GridStack(
minCellWidth: 320,
spacing: 15,
numItems: yourItems.count
) { index, cellWidth in
YourItemView(item: yourItems[index]).frame(width: cellWidth)
}
Screenshot
截屏
回答by Maba bm
For making a CollectionView without using UIKit, first of all we need an array extension. the array extension will help us chunk our array which we want to make a TableView around. Below is the code for the extension, + 3 examples. To a-little-bit-further understand how this extension works, take a look at this site, which i copied the extension from : https://www.hackingwithswift.com/example-code/language/how-to-split-an-array-into-chunks
为了在不使用 UIKit 的情况下制作 CollectionView,首先我们需要一个数组扩展。数组扩展将帮助我们分块我们想要制作 TableView 的数组。以下是扩展的代码,+ 3 个示例。为了进一步了解此扩展程序的工作原理,请查看此站点,我从该站点复制了扩展程序:https: //www.hackingwithswift.com/example-code/language/how-to-split-数组成块
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()
}
}
Now lets make our SwiftUI view:
现在让我们创建 SwiftUI 视图:
##代码##Image of Preview of the code above
Explanation:
解释:
First of all we need to make it clear how many columns do we need and put that number into our chunked extension. In my example, we have an array(ArrayOfInterest) of numbers from 1 to 18 which we want to show in our view, and i decided that i want my view to have 4 columns, so i chunked it into 4(so 4 is number of our columns).
首先,我们需要明确我们需要多少列并将该数字放入我们的分块扩展中。在我的例子中,我们有一个从 1 到 18 的数字数组(ArrayOfInterest),我们想在我们的视图中显示它,我决定我希望我的视图有 4 列,所以我把它分成 4(所以 4 是数字我们的专栏)。
To make a CollectionView, the most obvious thing is that our CollectionView is a LIST of items, so it should be in a list to make it easily scrollable (NO, DO NOT DO THAT! use a ScrollView instead. i've seen weird behaviours while those 2 foreachs are in a list). after the ScrollView we have 2 ForEach s, the first one enables us to loop as many Rows as needed, while the second one helps us make the columns.
要创建一个 CollectionView,最明显的是我们的 CollectionView 是一个项目列表,所以它应该在一个列表中以使其易于滚动(不,不要这样做!改用 ScrollView。我见过奇怪的行为而这 2 个 foreach 在列表中)。在 ScrollView 之后,我们有 2 ForEach s,第一个使我们能够根据需要循环尽可能多的行,而第二个帮助我们制作列。
I know i didn't explain the code perfectly, but i'm sure it is worth sharing with you so can make you table views easier. This Image is an example of a real app i'm making, and it looks nothing short of CollectionView, so you can be sure that this approach works great.
我知道我没有完美地解释代码,但我相信它值得与您分享,这样可以让您更轻松地查看表格。 此图像是我正在制作的真实应用程序的示例,它看起来与 CollectionView 无异,因此您可以确定这种方法非常有效。
QUESTION: whats the point of having an array and trying to let swift make those indices for foreach? its simple! if you have an array which defines its values/number-of-values in runtime, e.g. you are getting the numbers from a web api and that api tells you how many numbers are in your array, then you'll need to use my approach and let swift take care of indices of foreachs.
问题:拥有一个数组并试图让 swift 为 foreach 创建这些索引有什么意义?这很简单!如果您有一个在运行时定义其值/值数量的数组,例如您从 Web api 获取数字并且该 api 告诉您数组中有多少个数字,那么您需要使用我的方法并让 swift 处理 foreachs 的索引。
UPDATE:
更新:
More Info, reading these is optional.
更多信息,阅读这些是可选的。
LIST VS SCROLLVIEW: as some of you may not know, list works a little bit different from a scroll view. when you create a scrollview, it always calculates whole the scrollview, then shows it to us. but list doesnt do that, when using lists, swift automatically calculates only a few of the list's components which are needed to show the current view, and when you scroll down to the bottom of the list, it replaces only the changing values of those which are at the bottom of the screen, with the old values which are being scrolled out. so in general, list is always lighter, and can be much much faster when you are working with a heavy view, because it doesn't calculates all of that view at the beginning, and only calculates necessary things, while scroll view doesn't.
LIST VS SCROLLVIEW:有些人可能不知道,列表的工作方式与滚动视图略有不同。当您创建滚动视图时,它总是计算整个滚动视图,然后将其显示给我们。但是 list 不会这样做,当使用列表时,swift 会自动仅计算显示当前视图所需的少数列表组件,当您向下滚动到列表底部时,它只会替换那些正在更改的值位于屏幕底部,旧值被卷出。所以一般来说,列表总是更轻,当你处理一个重视图时可以快得多,因为它在开始时不会计算所有的视图,只计算必要的东西,而滚动视图不会.
WHY DID YOU SAY WE SHOULD USE SCROLLVIEW INSTEAD OF LIST?as i said before, there are some interactions with list that you probably dont like. for example when creating a list, every row is tappable, which is fine, but what is not fine is that ONLY the whole row is tappable! that means you cant set a tap action for the left side of a row, and a different one for the right side! this is just one of the weird interactions of a List() this either needs some knowledge i dont have! or is a big xcode-ios issue, or maybe its just fine and as intended! what i think is that its an apple issue and i hope it'll get fixed till at most the next WWDC.
你为什么说我们应该使用滚动视图而不是列表?正如我之前所说,您可能不喜欢与列表的一些交互。例如在创建列表时,每一行都是可点击的,这很好,但不好的是只有整行是可点击的!这意味着您不能为一行的左侧设置点击动作,而为右侧设置不同的动作!这只是 List() 的奇怪交互之一,这需要一些我不具备的知识!或者是一个很大的 xcode-ios 问题,或者它可能很好并且符合预期!我认为这是一个苹果问题,我希望它最多能在下一个 WWDC 上得到修复。
ANY WAYS TO OVERCOME THIS PROBLEM?as far as i know, the only way is to use UIKit. I've tried many many ways with SwiftUI, probably weeks worth of time, and although i've found out that you can get help from ActionSheet and ContextMenu to make lists better in terms of options when you tap them, i was unable to get the optimal intended functionality out of a SwiftUI List. so from my POV, SwiftUI devs can only wait for now.
有什么办法可以解决这个问题?据我所知,唯一的方法是使用 UIKit。我已经尝试了很多使用 SwiftUI 的方法,可能需要数周的时间,尽管我发现您可以从 ActionSheet 和 ContextMenu 获得帮助,以便在您点击列表时在选项方面做得更好,但我无法获得SwiftUI 列表中的最佳预期功能。所以从我的观点来看,SwiftUI 开发人员只能等待。
回答by Peter Minarik
I've written a small component called GridStack that makes a grid that adjusts to the available width. Even when that changes dynamically like when you rotate an iPad.
我编写了一个名为 GridStack 的小组件,它可以制作一个调整到可用宽度的网格。即使像旋转 iPad 那样动态变化。
https://github.com/pietropizzi/GridStack
https://github.com/pietropizzi/GridStack
The essentials of that implementation are similar to what others have replied here (so HStack
s inside a VStack
) with the difference that it figures out the width depending on the available width and a configuration you pass it.
该实现的要点与其他人在此处回复的内容相似(所以HStack
s 位于 a 内VStack
),不同之处在于它根据可用宽度和您传递的配置计算出宽度。
- With
minCellWidth
you define the smallest width you want your item in the grid should have - With
spacing
you define the space between the items in the grid.
- 随着
minCellWidth
您定义的最小宽度你想你的网格项目应该有 - 随着
spacing
您定义网格中项目之间的空间。
e.g.
例如
##代码##回答by iGhost
Tired of finding many complicated solutions or Github libraries, I have decided to do my own, easy and beautiful Mathematicalsolution.
厌倦了寻找许多复杂的解决方案或 Github 库,我决定做我自己的,简单而漂亮的数学解决方案。
- Think you have an array of items
var items : [ITEM] = [...YOUR_ITEMS...]
- You want to display an grid of Nx2
- 认为你有一系列项目
var items : [ITEM] = [...YOUR_ITEMS...]
- 你想显示一个Nx2的网格
When Nis the number of ROWSand 2is the number of COLUMNS
当Ñ是数量ROWS和2是数量 COLUMNS
- To show all items you need to use two
ForEach
statements, one for columns and one for rows.
- 要显示所有项目,您需要使用两个
ForEach
语句,一个用于列,一个用于行。
Into both
ForEach
: (i)current index of ROWS, and (j)current index of COLUMNS
分为
ForEach
:(i)ROWS 的当前索引,和(j)COLUMNS 的当前索引
- Display the current item in the index [(i* 2) + j]
- Now let's go to the code:
- 显示索引中的当前项 [( i* 2) + j]
- 现在让我们来看看代码:
Note: Xcode 11.3.1
##代码##注意:Xcode 11.3.1