Home > Blockchain >  Can I generate a sequence of Views programatically in SwiftUI?
Can I generate a sequence of Views programatically in SwiftUI?

Time:10-02

Here is a fragment of code that works for me...

    VStack(spacing: pad/2) { 
        getRgbColor(i:0)
            .onTapGesture { editColor(i:0) }
            .frame(width: pad, height: pad)
            .border(.black.opacity(0.5), width: 1)
                
        getRgbColor(i:1)
            .onTapGesture { editColor(i:1) }
            .frame(width: pad, height: pad)
            .border(.black.opacity(0.5), width: 1)
 
        getRgbColor(i:2)
            .onTapGesture { editColor(i:2) }
            .frame(width: pad, height: pad)
            .border(.black.opacity(0.5), width: 1)
    }

The original had more entries. It worked but you can see how it repeats. I may want to make a 6x4 set of patches that looks like a Macbeth target, so I wondered whether there was a better way that did not repeat so much. I tried moving the calls into the getRgbColor() call..

    func getRgbColor(i:Int) -> SwiftUI.Color {
        return SwiftUI.Color(red:   R   rgbDel[i][0] * step,
                             green: G   rgbDel[i][1] * step,
                             blue:  B   rgbDel[i][2] * step)
            .onTapGesture { editColor(i:0) }
            .frame(width: pad, height: pad)
            .border(.black.opacity(0.5), width: 1) as! Color 
    }

This compiled but gave a SIGABRT when I ran it...

Could not cast value of type 'SwiftUI.ModifiedContent<SwiftUI.ModifiedContent<SwiftUI.ModifiedContent<SwiftUI.Color, SwiftUI.AddGestureModifier<SwiftUI._EndedGesture<SwiftUI.TapGesture>>>, SwiftUI._FrameLayout>, SwiftUI._OverlayModifier<SwiftUI._ShapeView<SwiftUI._StrokedShape<SwiftUI.Rectangle._Inset>, SwiftUI.Color>>>' (0x109735e90) to 'SwiftUI.Color' (0x1ec3b9eb8). 2022-10-01 12:38:49.468445 0100 ByEye[2810:493288] Could not cast value of type 'SwiftUI.ModifiedContent<SwiftUI.ModifiedContent<SwiftUI.ModifiedContent<SwiftUI.Color, SwiftUI.AddGestureModifier<SwiftUI._EndedGesture<SwiftUI.TapGesture>>>, SwiftUI._FrameLayout>, SwiftUI._OverlayModifier<SwiftUI._ShapeView<SwiftUI._StrokedShape<SwiftUI.Rectangle._Inset>, SwiftUI.Color>>>' (0x109735e90) to 'SwiftUI.Color' (0x1ec3b9eb8).

Is there a neat solution?

Yes there is. I hadn't imagined that calling the modifier changed the underlying type, because most of what I write is in cpp, where this is unthinkable. Following the first comment, I wrote and re-named the getRgbColor() function...

    func rgbButton(i:Int) -> some View {
        return SwiftUI.Color(red:   R   rgbDel[i][0] * step,
                             green: G   rgbDel[i][1] * step,
                             blue:  B   rgbDel[i][2] * step)
                .onTapGesture { editColor(i:i) }
                .frame(width: pad, height: pad)
    }

CodePudding user response:

Modifiers in SwiftUI don't actually modify the view. Instead, they create a new view (e.g. ModifiedContent) with the property changed/applied.

Whenever we apply a modifier to a SwiftUI view, we actually create a new view with that change applied – we don’t just modify the existing view in place.

- Hacking With Swift

So when you add all the modifiers to the Color, you are making it a different type of view. That's why as! Color fails.

To fix that, you could make a View for the rgb color.

struct RgbColor: View {
    let i: Int
    // Add any other info the view needs as constants

    var body: some View {
       // ...
    }
}

To use it:

RgbColor(i: 1, /* other parameters */)

I hope this helps!

  • Related