Showing text in a ForEach

I would like to show paragraphs of text in a ForEach.

This was my latest attempt:

struct ContentView: View {
    let bodyText = "This is a sample body text that will wrap naturally within the bounds of the container."

    var body: some View {
        WordWrapView(words: identifyWords(words: bodyText))
            .padding()
    }

    func identifyWords(words: String) -> [IdentifiedWord] {
        let wordsArray = words.split(separator: " ").map { String($0) }
        var idCounter = 1
        return wordsArray.reversed().map { word in
            let identifiedWord = IdentifiedWord(id: String(idCounter), word: word)
            idCounter += 1
            return identifiedWord
        }
    }
}

struct IdentifiedWord: Identifiable {
    let id: String
    let word: String
}

struct WordWrapView: View {
    let words: [IdentifiedWord]

    var body: some View {
        GeometryReader { geometry in
            self.createWrappedWords(in: geometry.size)
        }
    }

    func createWrappedWords(in size: CGSize) -> some View {
        var width: CGFloat = 0
        var height: CGFloat = 0

        return ZStack(alignment: .topLeading) {
            ForEach(words) { word in
                self.wordView(for: word.word)
                    .alignmentGuide(.leading, computeValue: { d in
                        if (abs(width - d.width) > size.width) {
                            width = 0
                            height -= d.height
                        }
                        let result = width
                        if word == self.words.last! {
                            width = 0 // last item
                        } else {
                            width -= d.width
                        }
                        return result
                    })
                    .alignmentGuide(.top, computeValue: { _ in
                        let result = height
                        if word == self.words.last! {
                            height = 0 // last item
                        }
                        return result
                    })
            }
        }
    }

    func wordView(for text: String) -> some View {
        Text(text)
            .padding([.horizontal], 4)
            .padding([.vertical], 2)
            .background(Color.gray.opacity(0.2))
            .cornerRadius(4)
    }
}

The problem is that some words are overlapping other words.

Am I on the right track here? I reversed the array because I'm showing arabic words, and the ForEach is placing the words in reverse order...

Answered by Vision Pro Engineer in 789973022

Hi @bostshakur ,

Are you splitting the bodyText variable into an array and then feeding it back into the ForEach only in an attempt to switch the order for the Arabic words? If so, this may help.

Get it right (to left) WWDC 22 is a video that shows how to flip the layout for other languages with the layoutDirection environment variable.

Accepted Answer

Hi @bostshakur ,

Are you splitting the bodyText variable into an array and then feeding it back into the ForEach only in an attempt to switch the order for the Arabic words? If so, this may help.

Get it right (to left) WWDC 22 is a video that shows how to flip the layout for other languages with the layoutDirection environment variable.

Showing text in a ForEach
 
 
Q