【问题标题】:How to present accurate star rating using SwiftUI?如何使用 SwiftUI 呈现准确的星级?
【发布时间】:2020-10-15 20:17:38
【问题描述】:

我需要使用 SwiftUI 提供准确的星级评分,例如 3.1、4.8。

想要的结果应该是这样的:

【问题讨论】:

  • 对于任何正在寻找半精度/全精度星级评分的人,这里有一个开源的 swiftUI 控件:github.com/dkk/StarRating
  • 实际上,StarRating 现在也允许精确评分

标签: swift swiftui


【解决方案1】:

您的一般方法很好,但我相信它可以变得更简单。

下面的代码适应它放置的任何尺寸(所以如果你想要一个特定的尺寸,把它放在一个框架中)。

请注意,内部 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 的方式相匹配。

【讨论】:

    【解决方案2】:

    花了一些时间后,我确实找到了解决方案。

    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..&lt;5) { _ in Image()... } 循环中将这 5 个 Images 替换为一个。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-21
    • 1970-01-01
    • 1970-01-01
    • 2020-04-22
    • 1970-01-01
    相关资源
    最近更新 更多