0

I'm using a 4-block grid for my Widget Extension.

I want to populate these grids with an array of elements.

let exampleArray = ["test1","test2","test3","test4"]

When I use two nested ForEach loops, I can't get the element in the 4th index.

VStack {
    ForEach((0..<2)) { column in
        HStack {
            ForEach((0..<2)) { index in
                ZStack (alignment: .bottomLeading) {
                    Text(newsList[(index+column)])  // 0+0, 1+0, 0+1, 1+1
                    // Text = test1, test2, test2, test3
                }
            }
        }
    }
}

Any way I can work around this? Because it's in View, I can't use any operations.

Full code of the View:

struct AppWidgetEntryView : View {
    var entry: Provider.Entry
    
    @Environment(\.widgetFamily)
    var widgetFamily

    var body: some View {
        VStack(alignment: .trailing, spacing: 6) {
            Image("logo")
                .frame(maxWidth: .infinity, alignment: .leading)

            if widgetFamily == .systemLarge {
                VStack {
                ForEach((0..<2)) { column in
                    HStack {
                        ForEach((0..<2)) { index in
                            ZStack (alignment: .bottomLeading) {
                                if let url = URL(string: imageList[(index+column)]), let imageData = try? Data(contentsOf: url),
                                let uiImage = UIImage(data: imageData) {

                                Image(uiImage: uiImage)
                                    .centerCropped()
                                    .frame(maxHeight: 150, alignment: .center)
                                    .cornerRadius(10)
                                            .overlay(RoundedRectangle(cornerRadius: 10)
                                            .stroke(Color.gray, lineWidth: 1))
                                            .shadow(radius: 10)
                                } else {
                                    Image("ph_background")
                                        .centerCropped()
                                        .frame(maxHeight: 150, alignment: .center)
                                        .cornerRadius(10)
                                                .overlay(RoundedRectangle(cornerRadius: 10)
                                                .stroke(Color.gray, lineWidth: 1))
                                                .shadow(radius: 10)
                                }
                                
                                Text(newsList[(index+column)])
                                    .font(.system(size: 12))
                                    .foregroundColor(.white)
                                    .fontWeight(.light)
                                    // .frame(maxHeight: 50)
                                    .background(Rectangle().fill(Color.black).blur(radius: 20))
                                    .padding(.bottom, 5)
                                    .padding(.leading, 5)
                                    .padding(.trailing, 5)
                                    .padding(.top, 5)
                            }
                        }
                    }
                }
            }
        }
    }
}

Solution Please check the accepted answer.

struct AppWidgetEntryView : View {
    var entry: Provider.Entry
    
    @Environment(\.widgetFamily)
    var widgetFamily

    var body: some View {
        var columns: [GridItem] =
                 Array(repeating: .init(.fixed(100)), count: 2)

        if widgetFamily == .systemLarge {
        LazyVGrid(columns: columns) {
            ForEach((0..<4)) { index in
                ZStack (alignment: .bottomLeading) {
                    if let url = URL(string: imageList[(index)]), let imageData = try? Data(contentsOf: url),
                    let uiImage = UIImage(data: imageData) {

                    Image(uiImage: uiImage)
                        .centerCropped()
                        .frame(maxHeight: 150, alignment: .center)
                        .cornerRadius(10)
                                .overlay(RoundedRectangle(cornerRadius: 10)
                                .stroke(Color.gray, lineWidth: 1))
                                .shadow(radius: 10)
                    } else {
                        Image("ph_background")
                            .centerCropped()
                            .frame(maxHeight: 150, alignment: .center)
                            .cornerRadius(10)
                                    .overlay(RoundedRectangle(cornerRadius: 10)
                                    .stroke(Color.gray, lineWidth: 1))
                                    .shadow(radius: 10)
                    }
                    
                    Text(newsList[(index)])
                        .font(.system(size: 12))
                        .foregroundColor(.white)
                        .fontWeight(.light)
                        // .frame(maxHeight: 50)
                        .background(Rectangle().fill(Color.black).blur(radius: 20))
                        .padding(.bottom, 5)
                        .padding(.leading, 5)
                        .padding(.trailing, 5)
                        .padding(.top, 5)
                }
            }
        }
        }
    }
}
2
  • This question is already answered. You should not edit it after the fact to ask a second question. Commented Oct 1, 2021 at 11:44
  • Asked as a separate question, thanks. Commented Oct 1, 2021 at 13:20

1 Answer 1

2

Use LazyVGrid

I feel like you really should be using LazyVGrid for this...

https://developer.apple.com/documentation/swiftui/lazyvgrid

It works like a List but it places each item in a grid of columns.

Using this will solve this problem for you.

Without LazyVGrid

If you really don't want to use a LazyVGrid (please use this though). Then the calculation you are using is not correct. The calculation is something like...

index = row*numberOfCols + column

You can't just use the row as that will only increase the index by 1 per row. But you need to increase the index by the number of columns per row (2 in your case).

Sign up to request clarification or add additional context in comments.

6 Comments

Thank you for the answer! I used LazyVGrid and it is working as expected. The only issue is now the grid sizes are not automatically set. The default VStack + HStack cell sizes were great, I need to set them but other than that everything is fine!
@OğuzhanVarsak Great! You should be able to configure the size of the columns using the GridItems that you define for the columns. You can read more about GridItem here... developer.apple.com/documentation/swiftui/griditem
Hello again! I'm having some issues with LazyVGrid. My LazyVGrid cells push the top view (which is in the same VStack with LazyVGrid) out of the frame. Is there a workaround for this? I couldn't find how can I anchor the top of my view to the frame.
@OğuzhanVarsak hmm... without seeing the code I'm not sure why that would be the case. Would you be able to add a new question on StackOverflow to show your code and then link it here. I'll take a look. Thanks
I'll just update the code in here if that's fine? There are not many changes already.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.