![](/img/trans.png)
[英]How to show half star rating using cosmos rating controller in swift?
[英]How to present accurate star rating using SwiftUI?
您的一般方法很好,但我相信它可以變得更簡單。
下面的代碼適應它放置的任何尺寸(所以如果你想要一個特定的尺寸,把它放在一個框架中)。
請注意, 在 iOS 14 中不需要內部 ZStack ,但 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)
另一個簡單的解決方案,通過剪裁實現星形填充水平。 您可以將它與以下 API 一起使用:
/// Pass in rating as a Binding or provide a constant
StarRating(rating: .constant(1.6), maxRating: 5)
.font(.title2) // Provide font for sizing
看起來像:
執行:
struct StarRating: View {
struct ClipShape: Shape {
let width: Double
func path(in rect: CGRect) -> Path {
Path(CGRect(x: rect.minX, y: rect.minY, width: width, height: rect.height))
}
}
@Binding var rating: Double
let maxRating: Int
init(rating: Binding<Double>, maxRating: Int) {
self.maxRating = maxRating
self._rating = rating
}
var body: some View {
HStack(spacing: 0) {
ForEach(0..<maxRating, id: \.self) { _ in
Text(Image(systemName: "star"))
.foregroundColor(.blue)
.aspectRatio(contentMode: .fill)
}
}.overlay(
GeometryReader { reader in
HStack(spacing: 0) {
ForEach(0..<maxRating, id: \.self) { _ in
Image(systemName: "star.fill")
.foregroundColor(.blue)
.aspectRatio(contentMode: .fit)
}
}
.clipShape(
ClipShape(width: (reader.size.width / CGFloat(maxRating)) * CGFloat(rating))
)
}
)
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.