0

For every item in an array, I want to load a dynamic gradient of colors that are defined in the JSON. There are different colors in each item.

In the JSON, I'm storing hex values in [String]. I can use these to load Color(hex: String) using the extension from here: Use Hex color in SwiftUI

I was thinking along the following lines for implementation, but Xcode throws all sorts of errors and besides, it feels like a hack. What am I missing?

Example model:

struct Item: Codable, Identifiable, Hashable {
    let colors: [String]
}

Example Detailview:

struct DetailView: View {

    let item: Item
    let gradientColors: [Color]

    init(item: Item){
        self.item = item
        ForEach(item.colors) { color in
            gradientColors.append(Color(hex: color))
        }
    }

    var body: some View {
        Circle()
            .background(LinearGradient(gradient: Gradient(colors: gradientColors), startPoint: .leading, endPoint: .trailing))
    }
}
blu-Fox
  • 401
  • 6
  • 14

1 Answers1

0

In SwiftUI ForEach is a view that lets you pass a collection of data views similar to the subview of UIView in UIKit. so you can not use it inside the init method. For more (https://developer.apple.com/documentation/swiftui/foreach)

The correct use of code:

struct Item: Codable, Identifiable {
    var id = UUID()
    let colors: [String]
}

struct ContentView: View {
    let item: Item
    
    init(item: Item){
        self.item = item
    }
    
    var body: some View {
        Circle()
            .fill(LinearGradient(gradient: Gradient(colors: item.colors.map{Color(hex: $0)}), startPoint: .leading, endPoint: .trailing))
    }
}

If you want to iterate your array by using forEach, you need to use the same as the old way.

Example:

struct ContentView: View {
    let item: Item
    
    var gradientColors: [Color] = []
    
    init(item: Item){
        self.item = item
        item.colors.forEach { (strColor) in
            gradientColors.append(Color(hex: strColor))
        }
    }
    
    var body: some View {
        Circle()
            .fill(LinearGradient(gradient: Gradient(colors: gradientColors), startPoint: .leading, endPoint: .trailing))
    }
}

Also, one more thing. If you try to add a gradient to the circle, it's not work with .background(LinearGradient. For this you need to use .fill

var body: some View {
        Circle()
            .fill(LinearGradient(gradient: Gradient(colors: gradientColors), startPoint: .leading, endPoint: .trailing))
    }
Raja Kishan
  • 16,767
  • 2
  • 26
  • 52
  • A-ha! Once again, .map saves the day. Thank you very much! I was experimenting with having the colors optional, and solved it by first checking if colors != nil and then force unwrapping them in the Gradient modifier. I know I should be avoiding force unwraps (although in this case it is within an if statement that only executes if the value is not nil), so maybe there is a more elegant solution? – blu-Fox Jan 12 '21 at 09:46
  • Are you talking about a color array? – Raja Kishan Jan 12 '21 at 09:48
  • yes, in my actual project, the data decoded from JSON is optional. So taking your solution, I had to take an extra step and unwrap it – blu-Fox Jan 12 '21 at 16:35