Implementing Photos to Gallery App

Hi,

I just finished the gallery app tutorial and want to implement my photos instead of the orange rectangles grid for practice but I am having some trouble. My app is able to load but it’s showing me a black screen after the second onboarding screen though I was able to get the menu screen to load. Any advice I could use to fix this?

Codes and screenshots are below.

Thank you

import SwiftUI

@main
struct GalleryAppApp: App {
    var body: some Scene {
        WindowGroup {
            Onboarding_1()
        }
    }
}


import SwiftUI

struct Onboarding_1: View {
    @State var isActive:Bool = false
    var body: some View {
        NavigationView{
            ZStack {
                
                Color.black.edgesIgnoringSafeArea(.all)
                VStack{
                    Spacer()
                    Text("Thank you for joining my soap bubbles odyssey.").foregroundColor(.white)
                        .font(.title).multilineTextAlignment(.center)
                    Spacer()
                    
                    NavigationLink(destination: Onboarding_2(),isActive:$isActive){
                        Button(action:{
                            isActive = true
                        }){
                            Image(systemName: "chevron.right").font(.largeTitle).foregroundColor(.white).padding(24)
                        }
                    }
                        Spacer()
                    }
                }
                .navigationBarHidden(true)
            }
            
        }
    }
    
    
    struct Onboarding_1_Previews: PreviewProvider {
        static var previews: some View {
            Onboarding_1()
        }
    }


import SwiftUI

struct Onboarding_2: View {
    @State var isActive:Bool = false
    var body: some View {
            ZStack {
                
                Color.black.edgesIgnoringSafeArea(.all)
                VStack{
                    Spacer()
                    Text("To my brother, Mihai, protector of the unprolific.").foregroundColor(.white)
                        .font(.title).multilineTextAlignment(.center)
                    Spacer()
                    NavigationLink(destination: ContentView(), isActive: $isActive ){
                        Button(action:{
                            isActive = true
                        }){
                            Image(systemName: "chevron.right").font(.largeTitle).foregroundColor(.white).padding(24)
                        }
                    }
                    Spacer()
                }
            }
            .navigationBarHidden(true)
        }
    
}

struct Onboarding_2_Previews: PreviewProvider {
    static var previews: some View {
        Onboarding_2()
    }
}



import SwiftUI
import WebKit

struct ContentView: View {
    let columns = [GridItem(.flexible()), GridItem(.flexible())]
    @State var openMenu:Bool = false
    @State var aboutLink:Bool = false
    @State var licenseLink:Bool = false
    init(){
        (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.windows.first!.overrideUserInterfaceStyle = .dark
    }
    var body: some View {
        GeometryReader{ geo in
            ScrollView{
                LazyVGrid(columns: columns) {
                    ForEach(1...10, id: \.self){ i in
                        Image("image\(i)")
                                                    .resizable()
                                                    .aspectRatio(contentMode: .fill)
                                                    .frame(height: 200)
                                                    .cornerRadius(10)
                                                    .padding(5)
                        
                        
                    }
                }
                HStack {
                    VStack {
                        Text("50 Soap Bubbles").foregroundColor(.white).font(.title2)
                        Text("See whats coming in the next update").foregroundColor(.white)
                        Link("Follow Irisdesfera on Instagram >", destination: URL(string:"https:/www.codewithchris.com")!)
                            .foregroundColor(.blue)
                    }
                }
                /*NavigationLink(destination: AboutPage(), isActive: $aboutLink) { EmptyView()
                 } */
            }
        }
        .navigationBarBackButtonHidden(true)
        /*.navigationBarItems(trailing:
         Button(action: {
         openMenu = true
         print("triggered")
         },
         label: { Image(systemName: "chevron.down").font(.title)
         }))
         */
        .navigationBarTitleDisplayMode(.inline)
        .overlay(alignment: .topTrailing, content: {
            Button(action: {
                openMenu.toggle()
                
            }, label: {
                Image(systemName: "chevron.down").font(.largeTitle)
            })
        })
        //.fullScreenCover(isPresented: $openMenu, content: {
        .sheet(isPresented: $openMenu, content: {
            NavigationView {
            ZStack {
                Color.white.opacity(0).edgesIgnoringSafeArea(.all)
                VStack(spacing: 30) {
                    Text("").padding(30)
                    
                    NavigationLink(destination: AboutPage(), isActive: $aboutLink) {
                        Button("About", action: {
                            aboutLink.toggle()
                        }).font(.title)
                    }
                    
                    Button("App Icon", action: {
                        openMenu.toggle()
                    }).font(.title)
                    
                    NavigationLink(destination: WebView(request: URLRequest(url: URL(string: "https:iridisfera.app/legal/ios/")!)), isActive: $licenseLink) {
                        Button("License Agreement", action: {
                            licenseLink.toggle()
                        }).font(.title)
                    }
                    
                    Link("Privacy Policy", destination: URL(string:"https://iridisfera.app/privacy/")!).font(.title)
                    
                    
                    
                    
                    Spacer()
                    Text("App Version 1.0").font(.subheadline).padding(20)
                }
                
                
                
            }
            .navigationBarTitleDisplayMode(.inline)
            .navigationBarHidden(true)
            .background(BackgroundBlurView())
        }
            .background(BackgroundBlurView())
        })
    }
}
    
    struct AboutPage:View {
        var body: some View {
            HStack (alignment: .top) {
                VStack {
                    Text ("About Iridisfere").font(.largeTitle).foregroundColor(.white).padding()
                    Text("After two years of global pandemic, we all worked out ways of maintaining balance while social distancing. The soap film is my window; an extension of vision over great expanses. My name is Bogdan Chesaru. I made Iridisfera so you can wander with me.\n\nA soap film is a thin layer of water bounded by two layers of surfactant molecules. The iridescent colors arise from the interference of light waves reflecting off the front and back surfaces of the film. This natural phenomenon is known as thin-film interference.\n\nThank you for joining my soap bubble odyssey. If you want to make my day, leave a review.").font(.body).foregroundColor(.gray).multilineTextAlignment(.leading).padding()
                    Spacer()
                }
            }
        }
    }
struct WebView: UIViewRepresentable{
    let request:URLRequest
func makeUIView(context: Context) ->  WKWebView {
    return WKWebView()
}
    func updateUIView(_ uiView: WKWebView, context: Context) {
        uiView.load(request)
    }

}
    
    struct BackgroundBlurView:UIViewRepresentable{
        func makeUIView(context: Context) -> some UIView {
            let view = UIVisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterialDark))
            
            DispatchQueue.main.async{
                view.superview?.superview?.backgroundColor = .clear
            }
            return view
        }
        func updateUIView(_ uiView: UIViewType, context: Context) {
         
        }
    }

                                            

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


The images you have added to the Assets folder have a space between the word image and the number. If you re-name each of them to remove the space, it should probably work.

Hi Chris,

I tried as you say but it didn’t work.

OK let’s check something else that might be causing it. Check that each image has its Target Membership set.

To do that, select each image in turn and look for that setting as per the attached screenshot.

Hi Chris,

Yes, each of my photos has its Target Membership checked.

@ellieN1235

This is really strange. Can you compress your project into a zip file at the top level and post that to either DropBox or to Google Drive and created a share link in whichever one you choose and paste that share link in a reply.

If you use Google Drive and create a share link, make sure that the General Access option is set to “anyone with the link”.

Hi Chris,

This is the Google Drive link to my Gallery App project: GalleryApp.zip - Google Drive

Thank you

@ellieN1235

Hi Ellie,

I was as equally as puzzled as you were when nothing showed up on the screen but then I checked the images in the Asset catalogue and it was then that I noticed that the images had been named with a capital I for Image1 which meant that all I needed to do was make a small change in ContentView inside the ForEach like this.
Image("Image\(i)") and it fixed that problem.

So the LazyVGrid block of code now looks like:

LazyVGrid(columns: columns) {
    ForEach(1...10, id: \.self){ i in
        Image("Image\(i)")
            .resizable()
            .aspectRatio(contentMode: .fill)
            .frame(height: 200)
            .cornerRadius(10)
            .padding(5)
    }
}

Your button the opens the menu should not be implemented as an .overlay but as a .toolbar as per the original lesson. This is because the initial View when the App launches creates a NavigationView (inside OnBoarding1) which stays in effect when ContentView comes into effect when the Onboarding views are done.

.toolbar {
    ToolbarItem(placement: .navigationBarTrailing) {
        Button(action: {
            openMenu.toggle()
        }, label: {
            Image(systemName: "chevron.down").font(.title)
        })
    }
}

Did you have another reason for placing the button inside an overlay?

I hope you don’t mind if I say that the photographs in the Asset catalogue are absolutely beautiful.

Hi Chris,

The only reason why I put it as an overlay is because of the tutorial.

The image is from the course’s materials.

Thanks! I don’t mind it at all.

Vi

Ah yes, he did show that. I opted to get the menu to show up by using a .toolbar.