Google マップには、ユーザーがお店や施設を星1から5の5段階で評価し、その平均を表示する機能があります。
そこでよく目にする左側が黄色で右側がグレーの星。
これをSwiftUIでどのように実装するかを考えました。
まず、黄色い星はSwiftUIでは以下のように実装できます。
Image(systemName: "star.fill")
.resizable()
.foregroundColor(.yellow)
.frame(width: 300, height: 300)
次に左半分が黄色で、右半分がグレーの星を実装してみます。
foregroundColor
のModifierで部分的に色をつけられればよいのですが、そのような方法は(おそらく)なさそうなので、発想を変えて、「黄色とグレーの長方形を並べ、星型で切り抜く」方針をとります。
struct HalfStarView: View {
var body: some View {
HStack(spacing: 0) {
Color.yellow
.frame(width: 150, height: 300)
Color.gray
.frame(width: 150, height: 300)
}
.mask {
Image(systemName: "star.fill")
.resizable()
.frame(width: 300, height: 300)
}
}
}
縦300、横150の黄色とグレーの長方形を用意してHStack
を使って水平方向に横並びにし、、.mask
Modifierを使って縦横300の星でHStack
を切り抜いています。
次に、星の黄色い部分の割合が0以上1以下の場合を考えます。
星を明るくする部分の割合をratioのr、星の縦横幅をそれぞれ300とすると、黄色い部分の横幅は300r、グレー部分の横幅は300(1-r)と表せます。
中学校の数学でやりましたね。
これらの横幅をそれぞれColor.yellow
とColor.gray
の横幅に決めて星型で切り抜いて上げると、ratio
が変わったときに星の黄色とグレー部分の横幅が変わります。
struct StarView: View {
let ratio: CGFloat = 0.9
let starLength: CGFloat = 300
var body: some View {
HStack(spacing: 0) {
Color.yellow
.frame(width: 300 * ratio, height: 300)
Color.gray
.frame(width: 300 * (1 - ratio), height: 300)
}
.mask {
Image(systemName: "star.fill")
.resizable()
.frame(width: 300, height: 300)
}
}
}
ratioが0.3のとき
最後に、星の縦横幅と割合をViewの外部から決められるようにして完成です。
struct StarView: View {
let ratio: CGFloat
let starLength: CGFloat
var body: some View {
HStack(spacing: 0) {
Color.yellow
.frame(width: starLength * ratio, height: starLength)
Color.gray
.frame(width: starLength * (1 - ratio), height: starLength)
}
.mask {
Image(systemName: "star.fill")
.resizable()
.frame(width: starLength, height: starLength)
}
}
}
// 呼び出し側
StarView(ratio: 0.8, starLength: 300)