Before someone flags this as "SO is not a code writing service"... please read my post fully and see all the possible solutions I have tried to fix my problem:
I am trying to use a ForEach
loop to create cutout circle masks, to look like a perforated edge. I get this error:
The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
I have encountered this error before, and it's usually straightforward to do as it says and break the expression up. This time, I can't figure out a way to solve it.
I believe the issue is caused by the padding on the Circle with the holePunchSize
(and various alternatives). It works fine if I use a fixed value. Originally I had the multiplier expression right in the .padding — ((geo.size.width - 40) / 17) * 2.3
. Then I thought to calculate the value once in an .onAppear
and use that (holePunchSize
). Then I thought, maybe I have to split the expression further, the result you can see in the .onAppear
below.
Still, I get the error. I have no idea how I can possibly break this down any more.
Also, I have tried to simplify this further just for testing, by using fixed values in the .frame(width..
, and all but one of the .padding
, it still complains.
Code:
.reverseMaskBottomLeading {
ForEach(0..<17) { i in
Circle()
.frame(width: (geo.size.width - 40) / 17, alignment: .bottomLeading)
.padding([.bottom], -14)
.padding([.leading], i * (geo.size.width - 40) )
.padding([.leading], CGFloat((i * (holePunchSize))))
.padding([.leading], -12)
}
.padding([.leading, .trailing])
}
.onAppear {
holePunchSize = geo.size.width - 40
holePunchSize = holePunchSize / 17
holePunchSize = holePunchSize * 2.3
}
...
@inlinable
public func reverseMaskBottomLeading<Mask: View>(
alignment: Alignment = .bottomLeading,
@ViewBuilder _ mask: () -> Mask
) -> some View {
self.mask {
Rectangle()
.overlay(alignment: alignment) {
mask()
.blendMode(.destinationOut)
}
}
}
CodePudding user response:
Swift doesn't support binary arithmetic operators like
and *
with operands of different types. The only exception is that Swift will convert between CGFloat
and Double
automatically.
For example, in the code you posted, i * (geo.size.width - 40)
is not legal, because i
is Int
while geo.size.width - 40
is CGFloat
.
Note that integer literals like 40
can be treated as a variety of types depending on context; here, if the errors are corrected, it will be treated as CGFloat
to make the -
operator work. However, when there are errors around, Swift can end up trying lots of different types for each integer literal, which means lots of different combinations, and so it can time out. Your Circle
expression has five integer literals, so if Swift can treat each as any of, let's say, 12 different types, then there are 12⁵ = 248,832 type combinations it could try. Swift has optimizations to reduce this number, but it's still exponential.
Anyway, I see at least two cases where you are trying to multiply Int
by CGFloat
:
.padding([.leading], i * (geo.size.width - 40) )
↑
here
and
.padding([.leading], CGFloat((i * (holePunchSize))))
↑
here
You need to convert i
to CGFloat
in both places:
.padding([.leading], CGFloat(i) * (geo.size.width - 40))
.padding([.leading], CGFloat(i) * holePunchSize)
Also, you might want to consider using a Canvas
to draw your reverseMaskBottomLeading
instead of that modifier-heavy ForEach
/Circle
combination.