SwiftUI:通过单选在 iOS 13 上创建图像的 mx2 列网格列表

SwiftUI: Create a list of mx2 column grid of images on iOS 13 with single selection

我是 SwiftUI 的新手,我正在尝试将来自 API 的图像列表显示为 iOS 13 上的 mx2 网格。我能够创建网格并且还能够下载图像。现在我有 2 个问题,所有单元格都显示最后下载的图像,我需要找出一种方法来突出显示 selected 单元格的背景,它应该作为单个 selection,即.如果我 select 仅应 selected 的任何其他单元格,而其余单元格则变为 deselected。任何帮助表示赞赏。以下是我目前的代码。

struct MyListView: View {
    @State var cardImage: Image = Image ("placeholder")
    var myCard: [ImageDataModel] = []
    let column = 2
    var body: some View {
        let itemsCount = myCard.count
        let rowCount = itemsCount % 2 == 0 ? itemsCount/2 : itemsCount/2 + 1
        ScrollView {
            VStack(spacing: 20) {
                ForEach(0..<rowCount, id: \.self) { row in
                    HStack(spacing: 20) {
                        ForEach(0..<column, id: \.self) { col in
                            if row == rowCount-1 && itemsCount%2 != 0 && col != 0 {
                                Rectangle()
                                    .fill(Color.white)
                                        .frame(width: UIScreen.main.bounds.width/2-20)
                                        .aspectRatio(CGSize(width:148, height: 84), contentMode: .fit)
                            } else {
                                cardImage
                                    .resizable()
                                    .frame(width: UIScreen.main.bounds.width/2-20)
                                
                                .cornerRadius(20)
                                .aspectRatio(CGSize(width:148, height: 84), contentMode: .fit)
                                .onTapGesture {
                                    print("Tapped on image")
                                }
                                .onAppear {
                                    getImage(cardModel: self.myCard[(row*2)+col])
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    
    fileprivate func getImage(cardModel: ImageDataModel) {
        let imageData = ImageData(withUrl: cardModel.imageUrl.medium,
                                      completion: { (response) in
            DispatchQueue.main.async {
                switch response {
                case .success(let downloadedImage, _):
                    //This image download works but fills all cell image with last downloaded data
                    cardImage = Image(uiImage: downloadedImage)
                case .failure(let error):
                    print("Image Failure \(error)")
                }
            }
        })
        ImageDownloader.shared.downloadImage(withData: imageData)
    }
}

struct ImageDataModel {
    var imageId: Int
    var imageUrl: String
}

那么对于选择部分,你可以新建一个变量

@State var selection = ""

并且在 onTapGesture

selection = "row=\(row), col=\(col)"

为了突出显示,您可以这样做:

.overlay(selection == "row=\(row), col=\(col)" ? Color.green.opacity(0.3): Color.white.opacity(0)) 

或者你可以使用背景或任何东西,只要它符合条件!

我能够使用这个库解决这个问题WaterFallGrid

struct MyListView: View {
    var myCard: [ImageDataModel] = []
    var cardImage: Image = Image("placeholder")
    @State var selection = ""
    var body: some View {
        ScrollView {
          WaterfallGrid(myCard) { card in
              CardImageView(myCard: card, cardImage: cardImage)
                  .border(selection == "\(card.id)" ? Color.blue : Color.white, width: 2.0)
                  .cornerRadius(5)
                  .onTapGesture {
                      print("Tapped on updated image = \(card.card)")
                      selection = "\(card.id)"
                  }
          }
          .gridStyle(
            columns: 2
          )
          .padding(EdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10))
        }
    }
}

struct CardImageView: View {
    @State var myCard: ImageDataModel
    @State var cardImage: Image

    var body: some View {
        VStack {
            cardImage
                .resizable()
                .frame(width: (UIScreen.main.bounds.width)/2.5)
                .aspectRatio(5/3, contentMode: .fit)
                .cornerRadius(5)
                .onAppear {
                    getImage(cardModel: myCard)
                }
        }.padding(5)
    }

struct ImageDataModel: Identifiable {
    let id = UUID()
    let imageUrl: String
}