自定义评论星级图像未正确显示

问题描述 投票:0回答:2

我在故事板中拍摄了 5 个图像视图并连接了这样的插座:

@IBOutlet var starRatingImageViews: [UIImageView]!

我从 JSON 中获取 rating 的字符串值,如下所示:

rating = "4.2"

例如。

rating = "4.2"
我需要显示 4 颗全星和一颗半星,但显示不正确。

o/p:这里我的评级为“4.2”,但其星星在输出中显示错误。

代码:在这里

user.rating = "4.2"
但在o/p中显示错误的星星。我的
fillStars
方法哪里错了?

 @IBOutlet var starRatingImageViews: [UIImageView]!

 if let rating = Float(user.rating ?? "0.0") {
     rating.fillStars(in: starRatingImageViews)
 }

 func fillStars(in imageViews: [UIImageView], withEmptyStar color: UIColor = .darkGray) {
    for i in (0 ..< imageViews.count) {
        imageViews[i].contentMode = .scaleAspectFit
        let review = Int(Float(self))
        if (i < review) {
            imageViews[i].image = UIImage(systemName: "star.fill")?.withRenderingMode(.alwaysOriginal).withTintColor(CommonColor.yellowColor)
        } else {
            if (self - Float(review)) > (0.5), i == review {
                imageViews[i].image = UIImage(systemName: "star.fill")?.withRenderingMode(.alwaysOriginal).withTintColor(CommonColor.yellowColor)
            } else if (self - Float(review)) <= (0.5), (self - Float(review)) > (0.0), i == review {
                imageViews[i].image = UIImage(systemName: "star.leadinghalf.fill")?.withRenderingMode(.alwaysOriginal).withTintColor(CommonColor.yellowColor)
            } else {
                imageViews[i].image = UIImage(systemName: "star")?.withRenderingMode(.alwaysOriginal).withTintColor(color)
            }
        }
    }
}
swift for-loop floating-point uiimageview
2个回答
0
投票

问题是您使用

self - Float(review)
来确定要绘制什么样的星星,但是
self
独立于您的循环,并且您甚至对此不一致,因为您使用
i < review
作为第一次测试。你可以做到这一点,但这比需要的更难。由于案例数量超出了您的需要,问题变得更加混乱:您要么需要绘制一个实心星形,一个半实心星形,要么一个空心星形。这是 3 个案例,但您的代码中有 4 个案例,因为您复制了全星案例。

在评论中您澄清了

let review = Int(Float(self))
是从
String
转换而来。那么让我们看看当
self
应该是
"4.2"
并且
i
是 3 时会发生什么,这是它错误地计算部分星的地方。在这种情况下,
review
将为4,因此
if i < review
应该
true
,因为3小于4,所以它应该将
imageViews[i]
设置为全星,但实际上并没有这样做。这告诉我
self
不是
"4.2"
,因此
review
不是
4
。打印
self
或在
if
上放置断点以查看
self
review
实际上是什么。这可能会告诉你你的错误。基于此,我认为您对
self
的假设可能在某种程度上是不正确的。

即使修复了这个问题,

fillStars
仍然有一个错误,因为当
i
为4时,即在最后一个星星上,它完全填满了它,但它应该是空的。

尽管如此,

fillStars
还可以更简单。

当我遇到这样的问题时,我更喜欢考虑计算需要绘制的当前指标(在您的情况下为星号)的百分比,然后使用该结果来决定要做什么,而不是混合计算与决策相结合。所以我会将您的代码修改为如下所示:

func clamp<T: Comparable>(_ value: T, to range: ClosedRange<T>) -> T {
    return max(range.lowerBound, min(range.upperBound, value))
}

func fillStars(
    in imageViews: [UIImageView],
    withEmptyStar color: UIColor = .darkGray)
{
    let review = Float(self) // Is this needed now?

    for i in imageViews.indices
    {
        imageViews[i].contentMode = .scaleAspectFit
        let tintColor: UIColor
        let starName: String
        
        // Calculate the percentage of the current star to draw
        let percentOfThisStar = clamp(review - Float(i), to: 0...1)

        // Now use the percentage to pick the image/tinting
        if percentOfThisStar == 0
        { // Empty star
            tintColor = color
            starName  = "star"
        }
        else
        {
            tintColor = CommonColor.yellowColor
            starName  = percentOfThisStar < 0.5
                ? "star.leadinghalf.fill" // Partial star
                : "star.fill"             // Full star
        }
        
        imageViews[i].image = UIImage(systemName: starName)?
            .withRenderingMode(.alwaysOriginal)
            .withTintColor(tintColor)

    }
}

作为建议,我可能会将

fillStars
移至代码库的另一部分。我不太确定
self
是什么(
Float
Double
Decimal
?),除了它是某种数字类型,而且
fillStars
似乎是任何该数字类型的扩展中的方法是。我可能只是让它成为免费函数,或者可能是包含星星的任何视图中的方法,并将
review
作为参数传递。我的推理是,填充星星与
self
可能属于的任何数字类型没有太大关系。这只是您的应用程序的一种特殊情况用法,可能仅适用于您应用程序中的一个特定视图,那么为什么不将其放在该视图中呢?或者作为顶级应用程序特定功能?显然这是一个判断。编译器并不关心,所以这只是什么对您和您的团队最有意义的问题。

为了让测试变得更容易,而无需创建完整的应用程序(并避免必须使用模拟器),我在命令行工具中这样编写:

enum StarType: String
{
    case empty   = "[  ]"
    case partial = "[* ]"
    case filled  = "[**]"
}

func clamp<T: Comparable>(_ value: T, to range: ClosedRange<T>) -> T {
    return max(range.lowerBound, min(range.upperBound, value))
}


func fillStars(
    in stars: inout [StarType],
    from rating: Float)
{
    assert(Float(stars.count) >= rating)
    
    for i in stars.indices
    {
        // Calculate the percentage of the current star to draw
        let percentOfThisStar = clamp(rating - Float(i), to: 0...1)

        // Now use the percentage to set the star type
        let starType: StarType
        if percentOfThisStar == 0
        { // Empty star
            starType = .empty
        }
        else
        {
            starType  = percentOfThisStar < 0.5
                ? .partial
                : .filled
        }
        
        // This could have been done in the `if`, but doing it here allows
        // easier refactoring to UIImageView, if that's desired.
        stars[i] = starType
    }
}

我将

review
重命名为
rating
,并将其作为参数传入。我打印
[**]
,而不是实心星形,对于部分星形,我打印
[* ]
,对于空星形,我打印
[  ]

我用这个代码驱动它:

var stars: [StarType] = .init(repeating: .empty, count: 5)
let rating: Float = 4.2
fillStars(in: &stars, from: rating)
let starStr = stars.map { $0.rawValue }.joined()

print("rating = \(rating), stars = \(starStr)")

这些是结果:

评分 = 4.2,星星 = [**][**][**][**][* ]

这似乎是正确的结果。

如上所述,根据您的描述,我认为在调用 fillStars之前

至少发生了一个错误。


0
投票
我正在使用以下代码。 基本上我正在为每个起始位置创建图标

import SwiftUI private func startIcon(rate: Float, position: Int) -> String { let rate10 = Int(rate * 10) let position10 = position * 10 if rate10 > position10 || rate10 == position10 { return "star.fill" } if rate10 < position10 && rate10 > position10 - 10 { return "star.leadinghalf.filled" } return "star" /// rate10 < position10 } struct StarsRating: View { var rate: Float var body: some View { HStack { Label("", systemImage: startIcon(rate: rate, position: 1)) Label("", systemImage: startIcon(rate: rate, position: 2)) Label("", systemImage: startIcon(rate: rate, position: 3)) Label("", systemImage: startIcon(rate: rate, position: 4)) Label("", systemImage: startIcon(rate: rate, position: 5)) } } } #Preview { StarsRating(rate: 3.5).font(.title) }
如果你想以你正在做的方式循环,我会:

struct StarsRating: View { var rate: Float var body: some View { HStack { ForEach(1 ... 5, id: \.self) { index in Label("", systemImage: startIcon(rate: rate, position: index)) } } } }
    
© www.soinside.com 2019 - 2024. All rights reserved.