spoax
spoax

Reputation: 589

Listing multiple arrays using a ForEach - SwiftUI

OUTLINE:

I have a data model that contains an array of video names that are attached to each ProductModel.

So for example: ProductModel 1 would contain videos video1, video2. However, ProductModel 2 would contain videos video3, video4 and video5. As well as the name of the videos I also have a thumbnail and description that link in with the video names.

My data model looks like this:

NOTE: There are other multiple ProductModels, but for the purpose of keeping the code short I have only shown one.

struct ProductModel: Identifiable {
    var id = UUID()
    var video: [String]
    var videoThumbnail: [String]
    var videoDescription: [String]
}

let productData: [ProductModel] = [
    ProductModel(
        video: ["video1", "video2"],
        videoThumbnail: ["thumbnail1", "thumbnail2"],
        videoDescription: ["description1", "description2"]
    ),
    ProductModel(
        video: ["video3", "video4", "video5"],
        videoThumbnail: ["thumbnail3", "thumbnail4", "thumbnail5"],
        videoDescription: ["description3", "description4", "description5:]
    ),
]

PROBLEM:

When trying to list the arrays using a ForEach I keep getting errors. I have tried 2 different ways but I keep getting errors and can't see where I am going wrong. Any guidance would be appreciated.

ForEach method 01:

ForEach(0..<product.video.count){ item in
    Button(action: {
        // PLAY VIDEO
    }) {
        VStack{
            Image(product.thumbnail[item]) // VIDEO THUMBNAIL
            Text(product.video[item]) // VIDEO NAME
            Text(product.videoDescription[item]) // VIDEO DESCRIPTION
        }
    }
}

ForEach method 02:

ForEach(Array(zip(product.video, product.videoThumbnail, product.videoDescription)), id: \.0) { item in
    Button(action: {
        // PLAY VIDEO
    }) {
        VStack{
            Image(item.1) // VIDEO THUMBNAIL
            Text(item.0) // VIDEO NAME
            Text(item.2) // VIDEO DESCRIPTION
        }
    }
}

Upvotes: 1

Views: 865

Answers (1)

lorem ipsum
lorem ipsum

Reputation: 29632

///This way relies on the index for the videos and related info always matching. It is an easy way to have mixmatch information
struct NestedList: View {
    var body: some View {
        List{
            ForEach(0..<productData.count, id: \.self){ productsIdx in
                ForEach(0..<productData[productsIdx].video.count, id: \.self){ videoIdx in
                    Button(action: {
                        print(
                            "playing" + productData[productsIdx].video[videoIdx])
                    }, label: {
                        HStack{
                            Text(productData[productsIdx].videoThumbnail[videoIdx])
                            Text(productData[productsIdx].videoDescription[videoIdx])
                        }
                    })
                    
                }
            }
        }
    }
}

The more concise way to do this is to isolate the video information to a model so it all sticks together regardless of the order. As mentioned in the comments.

struct ProductModel: Identifiable {
    var id = UUID()
    var videos: [VideoModel]
}
struct VideoModel: Identifiable {
    let id = UUID()
    var video: String
    var videoThumbnail: String
    var videoDescription: String
}
var productData: [ProductModel]{
    var videos : [VideoModel] = []
        for n in 1...6{
            videos.append(VideoModel(video: "video\(n)", videoThumbnail: "thumbnail\(n)", videoDescription: "description\(n)"))
        }
    return [ProductModel(videos: videos)]
}
struct NestedList2: View {
    var body: some View {
        List{
            ForEach(productData, id: \.id){ product in
                ForEach(product.videos, id: \.id){ video in
                    Button(action: {
                        print(
                            "playing" + video.video)
                    }, label: {
                        HStack{
                            Text(video.videoThumbnail)
                            Text(video.videoDescription)
                        }
                    })
                    
                    
                }
            }
        }
    }
}

The for loop is just to automate the creation of the array. The first variable below is interchangeable with your productData and the second shows the equivalent to the build format of the array with the new structure.

var productData: [ProductModel]{
    var videos: [String] = []
    var thumbnails: [String] = []
    var descriptions: [String] = []
    for n in 1...6{
        videos.append("video\(n)")
        thumbnails.append("thumbnail\(n)")
        descriptions.append("description\(n)")
    }
    
    return [ProductModel(video: videos, videoThumbnail: thumbnails, videoDescription: descriptions)]
}
    let productData: [ProductModel] = [ProductModel(videos: [
        VideoModel(video: "video1", videoThumbnail: "thumbnail1", videoDescription: "description1"),
        VideoModel(video: "video2", videoThumbnail: "thumbnail2", videoDescription: "description2"),
        VideoModel(video: "video3", videoThumbnail: "thumbnail3", videoDescription: "description3"),
        VideoModel(video: "video4", videoThumbnail: "thumbnail4", videoDescription: "description4"),
        VideoModel(video: "video5", videoThumbnail: "thumbnail5", videoDescription: "description5"),
        VideoModel(video: "video6", videoThumbnail: "thumbnail6", videoDescription: "description6")
    ])]

Different ways of accomplishing a sample array the loop has less copy and paste .

You would create your own [VideoModel] using the method that most benefits your use case.

Upvotes: 2

Related Questions