Ios 使用SwiftUI创建网格/UICollectionView
我正在使用SwiftUI开发iOS应用程序。转到SwiftUI,我很快注意到缺少UICollectionView,这使得类似网格的视图很容易实现。我的手机项目代码如下:Ios 使用SwiftUI创建网格/UICollectionView,ios,swift,swiftui,Ios,Swift,Swiftui,我正在使用SwiftUI开发iOS应用程序。转到SwiftUI,我很快注意到缺少UICollectionView,这使得类似网格的视图很容易实现。我的手机项目代码如下: struct ListingView : View{ var body: some View{ VStack (alignment: .leading) { VStack{ HStack(spacing: 5){
struct ListingView : View{
var body: some View{
VStack (alignment: .leading) {
VStack{
HStack(spacing: 5){
KFImage(URL(string: Global.imageStoreListings + image1)!).resizable().scaledToFill()
.frame(height:140).frame(minWidth: 0, maxWidth: .infinity)
.cornerRadius(4)
KFImage(URL(string: Global.imageStoreListings + image2)!).resizable()
.scaledToFill()
.frame(height:140).frame(minWidth: 0, maxWidth: .infinity)
.cornerRadius(4)
}.frame(minWidth: 0, maxWidth: .infinity)
HStack(spacing: 5){
KFImage(URL(string: Global.imageStoreListings + image3)!).resizable()
.scaledToFill()
.frame(height:140).frame(minWidth: 0, maxWidth: .infinity)
.cornerRadius(4)
KFImage(URL(string: Global.imageStoreListings + image4)!).resizable()
.scaledToFill()
.frame(height:140).frame(minWidth: 0, maxWidth: .infinity)
.cornerRadius(4)
}.frame(minWidth: 0, maxWidth: .infinity)
}.frame(minWidth: 0, maxWidth: .infinity)
Text(listing.tags! + " • " + listing.location!).font(.custom("Nolasco Sans", size: 14))
Text(listing.title).font(.custom("Google Sans", size: 16))
}
}
}
我在ContentView中调用此选项:
var body: some View{
List {
ForEach(self.list, id: \.bid_id){ item in
ListingView()
}
}
}
结果如下所示(这是一行):
相反,我希望它看起来像这样(这是一行)
我已经尝试过嵌套循环,正如我从其他示例中看到的,但我对它非常着迷,所以我决定为我的特定场景寻求帮助。非常感谢您提供的任何帮助解决此问题的方法是使用数据结构。您需要清单的2D数组 编辑:我正在使用数组转换器添加完整代码。我还没有调整风格,使其画面完美。但我认为一切都是可行的
import SwiftUI
struct ContentView: View {
@ObservedObject var model = DataLoader()
var body: some View {
VStack {
List {
ForEach(self.model.data, id: \.self){ item in
HStack(spacing: 25) {
ForEach(item, id: \.self) { listing in
ListingView(listing: listing)
}
}
}
}
}.padding()
}
}
struct ListingView: View {
@State var listing: Listing
var body: some View {
VStack (alignment: .leading) {
VStack{
HStack(spacing: 5){
Image(listing.images[0]).resizable().scaledToFill()
.frame(height:70).frame(minWidth: 0, maxWidth: .infinity)
.cornerRadius(4)
Image(listing.images[1]).resizable()
.scaledToFill()
.frame(height:70).frame(minWidth: 0, maxWidth: .infinity)
.cornerRadius(4)
}.frame(minWidth: 0, maxWidth: .infinity)
HStack(spacing: 5){
Image(listing.images[2]).resizable()
.scaledToFill()
.frame(height:70).frame(minWidth: 0, maxWidth: .infinity)
.cornerRadius(4)
Image(listing.images[3]).resizable()
.scaledToFill()
.frame(height:70).frame(minWidth: 0, maxWidth: .infinity)
.cornerRadius(4)
}.frame(minWidth: 0, maxWidth: .infinity)
}.frame(minWidth: 0, maxWidth: .infinity)
Text(listing.tags + " • " + listing.location).font(.custom("Nolasco Sans", size: 14))
Text(listing.title).font(.custom("Google Sans", size: 16))
}
}
}
struct Listing: Hashable, Decodable {
var id: Int
var title: String
var location: String
var tags: String
var images: [String]
}
class DataLoader: ObservableObject {
@Published var data: [[Listing]] = [[Listing]]()
func loader() {
if let url = URL(string: "https://nikz.in/data.json") {
let req = URLRequest(url: url)
URLSession.shared.dataTask(with: req) { data, res, err in
if let data = data {
do {
let response = try JSONDecoder().decode([Listing].self, from: data)
DispatchQueue.main.async {
self.data = self.to2DArray(input: response, rows: 2)
}
return
} catch {
print("Unable to decode")
}
} else {
print("Data not loading")
}
}.resume()
}
}
func to2DArray<T>(input: [T], rows: Int) -> [[T]] {
let columns = Int(ceil(Double(input.count)/Double(rows)))
print(Double(input.count)/Double(rows))
var output = [[T]]()
for column in 0..<columns {
var temp: [T] = [T]()
for row in 0..<rows {
let id = (column*rows)+row
if id>input.count-1 { break }
temp.append(input[id])
}
output.append(temp)
}
return output
}
init() {
loader()
}
}
导入快捷界面
结构ContentView:View{
@ObservedObject变量模型=数据加载器()
var body:一些观点{
VStack{
名单{
ForEach(self.model.data,id:\.self){item in
HStack(间距:25){
ForEach(item,id:\.self){正在
ListingView(列表:列表)
}
}
}
}
}.padding()
}
}
结构列表视图:视图{
@状态变量清单:清单
var body:一些观点{
VStack(对齐:。前导){
VStack{
HStack(间距:5){
Image(listing.images[0]).resizable().scaledToFill()
.frame(高度:70)。frame(最小宽度:0,最大宽度:。无穷大)
.转弯半径(4)
Image(清单.images[1]).resizeable()
.scaledToFill()
.frame(高度:70)。frame(最小宽度:0,最大宽度:。无穷大)
.转弯半径(4)
}.frame(最小宽度:0,最大宽度:无穷大)
HStack(间距:5){
Image(listing.images[2]).resizeable()
.scaledToFill()
.frame(高度:70)。frame(最小宽度:0,最大宽度:。无穷大)
.转弯半径(4)
Image(listing.images[3]).resizeable()
.scaledToFill()
.frame(高度:70)。frame(最小宽度:0,最大宽度:。无穷大)
.转弯半径(4)
}.frame(最小宽度:0,最大宽度:无穷大)
}.frame(最小宽度:0,最大宽度:无穷大)
文本(listing.tags+“•”+listing.location).font(.custom(“Nolasco Sans”,大小:14))
Text(listing.title).font(.custom(“googlesans”,大小:16))
}
}
}
结构列表:可散列,可解码{
变量id:Int
变量标题:字符串
变量位置:字符串
变量标记:字符串
变量图像:[字符串]
}
类数据加载器:ObserveObject{
@已发布的var数据:[[Listing]]=[[Listing]]()
func加载器(){
如果让url=url(字符串):https://nikz.in/data.json") {
let req=URLRequest(url:url)
URLSession.shared.dataTask(with:req){data,res,err in
如果let data=data{
做{
让response=try JSONDecoder().decode([Listing].self,from:data)
DispatchQueue.main.async{
self.data=self.to2DArray(输入:响应,行:2)
}
回来
}抓住{
打印(“无法解码”)
}
}否则{
打印(“数据未加载”)
}
}1.简历()
}
}
func to2DArray(输入:[T],行:Int)->[[T]]{
让columns=Int(ceil(Double(input.count)/Double(rows)))
打印(双精度(输入.计数)/双精度(行))
变量输出=[[T]]()
对于0中的列..感谢您的回复-但是考虑到我从API获取并拥有一个可解码的模型-我如何将其转换为上面建议的数组?@Niks Jon这太棒了,非常感谢,教会了我很多,并为我节省了很多时间headache@tendai其中有一个小的逻辑修正。to2DArray()的第二个参数“rows”
实际上是指一行中的项目数,在您的情况下是2。是的@Nikz Jon,我注意到当我单击每一行时,NavigationLink会打开两个项目。它实际上是一个包含2个项目的行,单击它不会处理每个项目individually@tendai这就是列表视图的局限性。但是有一个解决方法可以使每个列表视图em作为按钮视图。