Search code examples
imagefirebaseswiftuiscrollview

SwiftUI URLImage Horizontal ScrollView


So I've been going through a SwiftUI instagram tutorial and learnt how to load images uploaded by user to firebase in the standard 3x3 instagram view but am now wanting to expand my knowledge and practice doing it in horizontal scrollview.

Here's what I have to create grid view:

    import SwiftUI
    import URLImage
    import FirebaseAuth
    
    struct Photo: Identifiable {
        let id = UUID()
        var photo = ""
    }
    
    struct PhotoView: View {
        @Environment(\.presentationMode) var mode: Binding<PresentationMode>
        @EnvironmentObject var session: SessionStore
        @ObservedObject var profileViewModel = ProfileViewModel()

var body: some View {
        
        return
                ScrollView {
                    if !profileViewModel.isLoading {
                        VStack(alignment: .leading, spacing: 1) {
                            // rows
                            ForEach(0..<self.profileViewModel.splitted.count) { index in
                                HStack(spacing: 1) {
                                    // Columns
                                    ForEach(self.profileViewModel.splitted[index], id: \.postId) { post in
                                        
                                        URLImage(URL(string: post.mediaUrl)!,
                                        content: {
                                            $0.image
                                                .resizable()
                                                .aspectRatio(contentMode: .fill)
                                            }).frame(width: UIScreen.main.bounds.width / 3, height: UIScreen.main.bounds.width / 3).clipped().cornerRadius(5)
                                        
                                    }
                                }
                            }
                        }.frame(width: UIScreen.main.bounds.width, alignment: .leading).padding(.top, 2)
                    }
 
                }.navigationBarTitle(Text("Photos"), displayMode: .inline).navigationBarBackButtonHidden(true).navigationBarItems(leading: Button(action : {
                    self.mode.wrappedValue.dismiss()
                }) {
                    Image(systemName: "arrow.left")
                }).onAppear {
                    self.profileViewModel.loadUserPosts(userId: Auth.auth().currentUser!.uid)
        }
    }
}

extension Array {
    
    func splitted(into size:Int) -> [[Element]] {
        var splittedArray = [[Element]]()
        if self.count >= size {
            for index in 0...self.count {
                if index % size == 0 && index != 0 {
                    splittedArray.append(Array(self[(index - size)..<index]))
                } else if (index == self.count) {
                    splittedArray.append(Array(self[index - 1..<index]))
                }
            }
            
        } else {
            splittedArray.append(Array(self[0..<self.count]))
        }
        return splittedArray
    }
}

class ProfileViewModel: ObservableObject {
    @Published var posts: [Post] = []
    @Published var isLoading = false
    var splitted: [[Post]] = []
    
    func loadUserPosts(userId: String) {
        isLoading = true
        Api.User.loadPosts(userId: userId) { (posts) in
            self.isLoading = false
            self.posts = posts
            self.splitted = self.posts.splitted(into: 3)
        }
    }
}

And this is what it looks like:

enter image description here

This is the sample code for what I am trying to achieve:

import SwiftUI
import URLImage
import FirebaseAuth

struct TestView: View {
    @Environment(\.presentationMode) var mode: Binding<PresentationMode>
    var body: some View {
        VStack {
            
            ScrollView(.horizontal, showsIndicators: false) {
                HStack(spacing: 2) {
                    ForEach(1..<5) { _ in
                        Image("photo3").resizable()
                            .clipShape(Rectangle())
                            .aspectRatio(contentMode: ContentMode.fill)
                            .frame(width: 100, height: 100).cornerRadius(10).opacity(1).shadow(radius: 4)
                    }
                    
                }
            }.navigationBarTitle(Text("Photos"), displayMode: .inline).navigationBarBackButtonHidden(true).navigationBarItems(leading: Button(action : {
                self.mode.wrappedValue.dismiss()
            }) {
                Image(systemName: "arrow.left")
            })
            Spacer()
        }.padding()
    }
}

and here is the sample image of what I want it to look like:

enter image description here

I'm really struggling to understand the ForLoop part and how I can retrieve the image to just be in a simple scrollView.

Any help would be much appreciated!

Thanks!


Solution

  • You want to loop over the posts in your model. Borrowing from your earlier code, you need something like this:

    ScrollView(.horizontal, showsIndicators: false) {
        HStack(spacing: 2) {
    
            ForEach(self.profileViewModel.posts, id: \.postId) { post in
                                            
                URLImage(URL(string: post.mediaUrl)!,
                    content: {
                        $0.image
                        .resizable()
                        .aspectRatio(contentMode: .fill)
                    }
                )
                .frame(width: 100, height: 100)
                .clipped()
                .cornerRadius(10)
                .shadow(radius: 4)
            }
        }
    }