-1

I am creating a wallpaper app & one of the views is a grid view for adding pics as you can see here: the grid view with the images

I added images to the grid itself but I noticed they are having spaces & I have to group them so I can add like 50 images in the grid view but it came in my mind to create a JSON file to the grid view & let it read it with all pics in it, but I don't know how to create the JSON and connect it to the grid view

struct GridContentView: View {


var items = Item.stubs
let data = (1...1000).map { "Item \($0)" }


let columns = [
    GridItem(.adaptive(minimum: 80)),
    GridItem(.adaptive(minimum: 80)),
    GridItem(.adaptive(minimum: 80))
    
]
let rows = [
    GridItem(.adaptive(minimum: 80)),
    GridItem(.adaptive(minimum: 80)),
    GridItem(.adaptive(minimum: 80))
    
]
var body: some View {
    ScrollView {
                Section{
        LazyVGrid(columns: columns, spacing: 30) {
                // adding images
            
                Image("joker1")
                    .resizable()
                    .frame(width: 100, height: 200)
                    Image("joker2")
                .resizable()
                .frame(width: 100, height: 200)
                 Image("joker3")
                     .resizable()
                     .frame(width: 100, height: 200)
                     Image("joker4")
                 .resizable()
                 .frame(width: 100, height: 200)
                 Image("joker5")
                     .resizable()
                     .frame(width: 100, height: 200)
                     Image("joker6")
                 .resizable()
                 .frame(width: 100, height: 200)
                 Image("joker7")
                     .resizable()
                     .frame(width: 100, height: 200)
                     Image("joker8")
                 .resizable()
                 .frame(width: 100, height: 200)
                 Image("joker9")
                     .resizable()
                     .frame(width: 100, height: 200)
                     Image("joker10")
                 .resizable()
                 .frame(width: 100, height: 200)
        }
                }
        
kaysol
  • 3
  • 3

1 Answers1

0

TL; DR

Add a new empty file to your project and call it ImageList.json. Paste the following data into the json file:

[
    {
        "name": "image1",
        "width": 100.0,
        "height": 200.0
    },
    {
        "name": "image2",
        "width": 100.0,
        "height": 200.0
    },
    {
        "name": "image3",
        "width": 100.0,
        "height": 200.0
    }
]

Use the following working example below:

import SwiftUI

struct ImageSpecification: Codable {
    let id = UUID()
    let name: String
    let width: CGFloat
    let height: CGFloat
    
    private enum CodingKeys: CodingKey {
        case name
        case width
        case height
    }
}

class DataModel: ObservableObject {
    @Published var images: [ImageSpecification] = []
    
    init() {
        DispatchQueue.global(qos: .background).async {
            if let jsonURL = Bundle.main.url(forResource: "ImageList", withExtension: "json") {
                let jsonData = try! Data(contentsOf: jsonURL)

                let jsonDecoder = JSONDecoder()
                let objects = try! jsonDecoder.decode([ImageSpecification].self, from: jsonData)

                DispatchQueue.main.async {
                    self.images.append(contentsOf: objects)
                }
            }
        }
    }
}

struct ContentView: View {
    @StateObject var model = DataModel()
    
    let columns = [
        GridItem(.adaptive(minimum: 80)),
        GridItem(.adaptive(minimum: 80)),
        GridItem(.adaptive(minimum: 80))
    ]
    
    var body: some View {
        ScrollView {
            LazyVGrid(columns: columns, spacing: 30) {
                ForEach(self.model.images, id:\.self.id) { item in
                    Image(item.name)
                        .resizable()
                        .frame(width: item.width, height: item.height)
                }
            }
        }
    }
}

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

Detailed explanation

The created json file is part of your bundle. To access this file, you can create an URL pointing to it with:

Bundle.main.url(forResource: "ImageList", withExtension: "json")

Now, create a task which runs asynchronous in background with

DispatchQueue.global(qos: .background).async { }

Inside this task, decode the raw data of the json file to an array of the struct ImageSpecification. This struct describes how a single item looks like. By making it conform to the protocol Codable you are able to use the JSONDecoder.

Any update on the screen must be done on the main thread. That's why you have to make sure to push new items into your storage on the main thread by using a new queue:

DispatchQueue.main.async { }

Inside your view, you can now use your DataModel class, which loads the data on initialization. Inside the LazyVGrid, a ForEach view is used to loop through the data. Since ForEach expects an hashable id, either make the complete struct conforming to the Hashable protocol, or point to the unique id. There's no need to provide an unqiue id in the json file, since the id is automatically initialized. You must, however, provide the coding keys to prevent compiler warnings (see here).

Nikolai
  • 659
  • 1
  • 5
  • 10
  • thank you for your help, I applied what you have said but the view is always not showing in canvas its crashes and no longer show anything, and it shows this error in the debug (020-11-22 02:33:45.471645+0200 Joker[23636:1883592] . Simulator user has requested new graphics quality: 10) ... any ideas why? – kaysol Nov 22 '20 at 01:53
  • Did you try to use a plain new project to test it? The message you posted seems not really important: https://stackoverflow.com/a/34265974/4354710 Do you have the latest macOS and XCode version? It's a bit hard to debug without proper error message ... – Nikolai Nov 22 '20 at 07:48
  • yes i do have latest for Catalina macOS and Xcode.. i have a question should I call this just in the preview ? (((struct GridContentView_Previews: PreviewProvider { static var previews: some View { GridContentView() } } )))) or I should call the decoder as well in the preview idk , I don't understand whats wrong? – kaysol Nov 22 '20 at 14:29
  • My proposal is that you create a plain new project and replace the complete content of `ContentView.swift` with the complete code from above. Remember to add the json file to this project. That works for me fine. – Nikolai Nov 22 '20 at 15:02