【发布时间】:2020-10-15 20:17:38
【问题描述】:
【问题讨论】:
-
对于任何正在寻找半精度/全精度星级评分的人,这里有一个开源的 swiftUI 控件:github.com/dkk/StarRating
-
实际上,StarRating 现在也允许精确评分
【问题讨论】:
您的一般方法很好,但我相信它可以变得更简单。
下面的代码适应它放置的任何尺寸(所以如果你想要一个特定的尺寸,把它放在一个框架中)。
请注意,内部 ZStack isn't required in iOS 14,但 GeometryReader 仍然没有记录其布局行为(Xcode 12 发行说明中除外),因此这使其明确。
struct StarsView: View {
var rating: CGFloat
var maxRating: Int
var body: some View {
let stars = HStack(spacing: 0) {
ForEach(0..<maxRating) { _ in
Image(systemName: "star.fill")
.resizable()
.aspectRatio(contentMode: .fit)
}
}
stars.overlay(
GeometryReader { g in
let width = rating / CGFloat(maxRating) * g.size.width
ZStack(alignment: .leading) {
Rectangle()
.frame(width: width)
.foregroundColor(.yellow)
}
}
.mask(stars)
)
.foregroundColor(.gray)
}
}
这会以灰色绘制所有星星,然后创建一个正确宽度的黄色矩形,将其遮盖住星星,并将其绘制在顶部作为叠加层。叠加层自动与它们附加到的视图大小相同,因此您不需要所有框架来使大小与使用 ZStack 的方式相匹配。
【讨论】:
花了一些时间后,我确实找到了解决方案。
struct StarsView: View {
let rating: CGFloat
let maxRating: CGFloat
private let size: CGFloat = 12
var body: some View {
let text = HStack(spacing: 0) {
Image(systemName: "star.fill")
.resizable()
.frame(width: size, height: size, alignment: .center)
Image(systemName: "star.fill")
.resizable()
.frame(width: size, height: size, alignment: .center)
Image(systemName: "star.fill")
.resizable()
.frame(width: size, height: size, alignment: .center)
Image(systemName: "star.fill")
.resizable()
.frame(width: size, height: size, alignment: .center)
Image(systemName: "star.fill")
.resizable()
.frame(width: size, height: size, alignment: .center)
}
ZStack {
text
HStack(content: {
GeometryReader(content: { geometry in
HStack(spacing: 0, content: {
let width1 = self.valueForWidth(geometry.size.width, value: rating)
let width2 = self.valueForWidth(geometry.size.width, value: (maxRating - rating))
Rectangle()
.frame(width: width1, height: geometry.size.height, alignment: .center)
.foregroundColor(.yellow)
Rectangle()
.frame(width: width2, height: geometry.size.height, alignment: .center)
.foregroundColor(.gray)
})
})
.frame(width: size * maxRating, height: size, alignment: .trailing)
})
.mask(
text
)
}
.frame(width: size * maxRating, height: size, alignment: .leading)
}
func valueForWidth(_ width: CGFloat, value: CGFloat) -> CGFloat {
value * width / maxRating
}
}
用法:
StarsView(rating: 2.4, maxRating: 5)
【讨论】:
ForEach(0..<5) { _ in Image()... } 循环中将这 5 个 Images 替换为一个。