The following code works fine. So really, I’m good ... But I wanted to learn about ViewModifiers ... so my goal is to separate the non-changing stuff from the dynamic stuff to create a .cardify() custom modifier to call on the Shapes Views.
struct SetCard: View {
let pips: Int
let shape: SetCardShape
let color: Color
let shading: Double
let isSelected: Bool
var body: some View {
ZStack {
VStack {
ForEach( 0..<pips ) { _ in
ZStack {
getShape(self.shape).opacity(self.shading).foregroundColor(self.color)
getShape(self.shape).stroke().foregroundColor(self.color)
}
}
}
.padding() // for shape in RoundedRect
RoundedRectangle(cornerRadius: 10).stroke(lineWidth: isSelected ? 3.0 : 1.0).foregroundColor(.orange)
}
.scaleEffect(isSelected ? 0.60 : 1.0 )
.padding() // for spacing between cards
}
}
Again, for academic/learning reasons, I wanted to simplify this struct, and use a custom modifier to convert the main content into a standard card.
The below code block only works when I comment out the second content
line in the Cardify
ViewModifier struct. All cards that used filled shapes for the pips render just fine. Cards that require the Shape to be stroked (i.e. not filled) need that second content
in my Cardify
ViewModifer to work.
The second content line produces the error:
Value of type Cardify.Content (aka. _ViewModifier_Content) has no member foregroundColor
commenting out .foregroundColor() generates the error: Value of type Cardify.Content (aka. _ViewModifier_Content) has no member stroke
struct SetCardWithCardify: View {
let pips: Int
let shape: SetCardShape
let color: Color
let shading: Double
let isSelected: Bool
var body: some View {
ZStack {
getShape(shape)
.modifier(Cardify(pips: pips, shape: shape, color: color, shading: shading, isSelected: isSelected))
}
.scaleEffect(isSelected ? 0.60 : 1.0 )
.padding() // for spacing between cards
}
}
struct Cardify: ViewModifier {
var pips: Int
var shape: SetCardShape
var color: Color
var shading: Double
var isSelected: Bool
func body(content: Content) -> some View {
ZStack {
VStack {
ForEach( 0..<pips ) { _ in
ZStack {
content.opacity(self.shading).foregroundColor(self.color)
content.stroke().foregroundColor(self.color)
}
}
}
.padding() // for shape in RoundedRect
RoundedRectangle(cornerRadius: 10).stroke(lineWidth: isSelected ? 3.0 : 1.0).foregroundColor(.orange)
}
}
}
Just in case this is important, the following code is the source of getShape()
which is the source of content
in the Cardify
ViewModifier.
func getShape(_ shape: SetCardShape ) -> some Shape {
switch shape {
case .circle:
return AnyShape( Circle() )
case .diamond:
return AnyShape( SetDiamond() )
case .squiggle:
return AnyShape( SetSquiggle() )
}
}
struct AnyShape: Shape {
func path(in rect: CGRect) -> Path {
return _path(rect)
}
init<S: Shape>(_ wrapped: S) {
_path = { rect in
let path = wrapped.path(in: rect)
return path
}
}
private let _path: (CGRect) -> Path
}
Diamond() and Squiggle() are structs that conform to Shape protocol, properly returning a path from `func path(in rect: CGRect) -> Path in those structs.
I have tried downcasting the second content line with:
(content as! Shape).blah blah blah
which generates the error:
Protocol Shape can only be used as a generic constraint because it has Self or associated type requirements.
I have also tried:
(content as! Path)
This doesn't generate any compile-time errors, but when executing it fails with the error:
There was a problem encountered while running, check your code for mistakes around this line. (indicating the second content line).
What can I do to allow the compiler to know the type of content this is so that stroke() and foregroundColor() will work ?
I'm not sure that you can, but you can sort of approximate it using an extension on Shape:
extension Shape {
func modified() -> some View {
self
.foregroundColor(Color.secondary.opacity(0.5))
}
}
// Usage example
struct ContentView: View {
var body: some View {
VStack {
Rectangle().modified()
Capsule().modified()
}
}
}
However, unlike a view modifier you can't access the environment so it's somewhat limited.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With