4

I have somewhat unsuccessfully tried to figure out how to work with SwiftUI's ForEach view. By following resources such as this, this and this, it seems to me that given an array of strings:

var arrayOfStrings: [String] = ["String1", "String2", "String3"]

...each of the following implementations should work:

ForEach(arrayOfStrings, id: \.self) {
    Text(arrayOfStrings[$0])
}

ForEach(1...arrayOfStrings.count, id: \.self) {
    Text(arrayOfStrings[$0])
}

ForEach(arrayOfStrings, id: \.self) { string in
    Text(string)
}

Yet each of them gives me a compile-time error or crashes the app at runtime.

The above is just example code, but if you need the full context of what I am trying to do, here it is:

struct Infobox<Content>: View where Content: View {
    let edgeSize: CGFloat = 8
    let color: Color = .neoblå
    let content: Content

    var body: some View {
        VStack(spacing: 0) {
            BoxSide(.top, withColor: color)
                .frame(height: edgeSize)
                .padding(.horizontal, 0)

            content
                .background(color)

            BoxSide(.bottom, withColor: color)
                .frame(height: edgeSize)
                .padding(.horizontal, 0)
        }
    }

    init(@ViewBuilder _ content: @escaping () -> Content) {
        self.content = content()
    }

    init<T: View>(withHeader headerText: String, forContentArray contentArray: [(text: String, value: String)], splitInTwoColumns twoColumns: Bool, @ViewBuilder ignoreTheClosure: @escaping () -> T) where Content == VStack<TupleView<(HStack<TupleView<(Spacer, Text, Spacer)>>, HStack<TupleView<(VStack<ForEach<ClosedRange<Int>, Int, Text>>, VStack<ForEach<ClosedRange<Int>, Int, Text>>, Spacer, VStack<ForEach<ClosedRange<Int>, Int, Text>>, VStack<ForEach<ClosedRange<Int>, Int, Text>>)>>)>> {

        var column1: [String] = []
        var column2: [String] = []
        var column3: [String] = []
        var column4: [String] = []

        let halfArray = contentArray.count/2
        for (index, content) in contentArray.enumerated() {
            if index+1 <= halfArray {
                column1.append(content.text)
                column2.append(content.value)
            } else {
                column3.append(content.text)
                column4.append(content.value)
            }
        }

        self.init() {

            VStack(alignment: .leading) {
                HStack() {
                    Spacer()
                    Text(headerText)
                        .font(.headline)
                    Spacer()
                }
                HStack {
                    VStack {
                        ForEach(column1, id: \.self) {
                            Text(column1[$0])
                        }
                    }
                    VStack {
                        ForEach(column2, id: \.self) {
                            Text(column2[$0])
                        }
                    }
                    Spacer()
                    VStack {
                        ForEach(column3, id: \.self) {
                            Text(column3[$0])
                        }
                    }
                    VStack {
                        ForEach(column4, id: \.self) {
                            Text(column4[$0])
                        }
                    }
                }
            }
        }
    }
}

Summary: Given an array of (String, String), create an Infobox() consisting of Text() elements divided into four columns (two for "keys" and two for the corresponding values).

If anyone has any ideas of how to fix those ForEach views, I'll give you a cookie :)

2 Answers 2

10

This one fails because $0 is a String and you cannot index your string-array with a string

ForEach(arrayOfStrings, id: \.self) {
    Text(arrayOfStrings[$0])
}

This one fails because array indices are 0-based -> Choose a 0 base range: 0..<arrayOfStrings.count to avoid an "Index-out-of-bounds exception"

ForEach(1...arrayOfStrings.count, id: \.self) {
    Text(arrayOfStrings[$0])
}

This one is generally correct, but you may have to reference your member scope-qualified self.arrayOfStrings this applies to all SwiftUI code when your scope is a closure:

ForEach(arrayOfStrings, id: \.self) { string in
    Text(string)
}

This is a more Swifty-way of doing the array access via index:

ForEach(arrayOfStrings.indices) {
  Text(self.arrayOfStrings[$0])
}
Sign up to request clarification or add additional context in comments.

Comments

1
struct ContentView: View {
let numbers = [1, 2, 3, 4, 5]

var body: some View {
    VStack {
        ForEach(Array(numbers.enumerated()), id: \.element) { index, number in
             Text("Number \(number) is at index \(index)")
        }
        
    }
}

}

Comments

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.