Reputation: 143
I'm having a beginner types problem in swift and the error message isn't very helpful. I get the error
The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
from the following code:
import Foundation
import SwiftUI
import CoreGraphics
struct SwiftUIView: View {
let alphabet = ["a","b","c","d","e"]
var body: some View {
GeometryReader { geometry in
let clockPadding = CGFloat(20)
let width = min(geometry.size.width, geometry.size.height)
let diam = width - 2*clockPadding
let centre = width/2
let radius = diam/2
let theta: CGFloat = 2*CGFloat.pi/CGFloat(alphabet.count)
ForEach(0..<alphabet.count) { i in
let x = centre + radius * cos(i*theta)
let y = centre + radius * sin(i*theta)
Text(alphabet[i])
.position(x: x, y: y)
}
}
}
}
In fact the error is still there even when I make the trig functions take a constant, changing the ForEach to:
ForEach(0..<alphabet.count) { i in
let x = centre + radius * cos(0*theta)
let y = centre + radius * sin(0*theta)
Text(alphabet[0])
.position(x: x, y: y)
}
What I'm trying to do is arrange the letter evenly around a circle.
Upvotes: 4
Views: 92
Reputation: 299663
The compiler is having trouble with the +
and *
in your x
and y
assignments. You need to let it know they're CGFloats, and then you need to turn i
into a CGFloat as well (which is a bug in your code, but the +
and *
overloads are just too hard on the compiler and it couldn't help you find it).
ForEach(0..<alphabet.count) { i in
let x: CGFloat = centre + radius * cos(CGFloat(i)*theta)
let y: CGFloat = centre + radius * sin(CGFloat(i)*theta)
Text(alphabet[i])
.position(x: x, y: y)
}
As with most SwiftUI compile problems, the way to debug this is to take things out until it works, and then add things in very slowly. I took out the let x
, let y
and position
lines, and it compiles, so I know the top part is fine. Then I added back in just the let x
line and I got the error (Cannot convert value of type 'Int' to expected argument type 'CGFloat'). I fixed the bug for x
and y
and it compiled (without position
). When I added the .position
, it was too complicated again, so I added CGFloat
types to x
and y
.
Another approach that often helps is to split things up into smaller functions. For example:
struct SwiftUIView: View {
let alphabet = ["a","b","c","d","e"]
private typealias Layout = (centre: CGFloat, radius: CGFloat, theta: CGFloat)
private func makeLayout(for geometry: GeometryProxy) -> Layout {
let clockPadding = CGFloat(20)
let width = min(geometry.size.width, geometry.size.height)
let diam = width - 2*clockPadding
let centre = width/2
let radius = diam/2
let theta = 2*CGFloat.pi/CGFloat(alphabet.count)
return (centre: centre, radius: radius, theta: theta)
}
private func positionedText(_ string: String, for l: Layout, at index: Int) -> some View {
let i = CGFloat(index)
return Text(string)
.position(x: l.centre + l.radius * cos(i*l.theta),
y: l.centre + l.radius * sin(i*l.theta))
}
var body: some View {
GeometryReader { geometry in
let layout = makeLayout(for: geometry)
ForEach(0..<alphabet.count) { i in
positionedText(alphabet[i], for: layout, at: i)
}
}
}
}
If you wrote it that way, the compiler would have given you a much more useful error message for the missing CGFloat conversion.
Upvotes: 3