user3305606
user3305606

Reputation: 33

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!

Upvotes: 1

Views: 974

Answers (2)

davidev
davidev

Reputation: 8547

vacawama has already posted the perfect solution to make it look like your example.

Just to add why you achieve the result, you are getting.

The difference between your code and the sample code is that you are using two ForEach, one for the rows and one for the columns. The array gets splitted with your extension, so you get rows and columns.

//Rows
ForEach(0..<self.profileViewModel.splitted.count) { index in
    HStack(spacing: 1) {
        // Columns
        ForEach(self.profileViewModel.splitted[index], id: \.postId) { post in

Your comments already stating how it works. If you want to have all your images in a horizontal scroller, you just need one ForEach which outputs all your images in a ScrollView.

ScrollView(.horizontal, showsIndicators: false) {
    HStack(spacing: 2) {
        ForEach(self.profileViewModel.posts, id: \.postId) { post in

Upvotes: 0

vacawama
vacawama

Reputation: 154711

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)
        }
    }
}

Upvotes: 2

Related Questions