0

我修改了一个自定义的 5 星评级视图 ( https://swiftuirecipes.com/blog/star-rating-view-in-swiftui ) 以满足我的需求。我在我的应用程序的多个位置使用该视图来显示结构的当前评级并通过可选列表更改评级。我遇到的问题是,当我通过 NavigationLink 为星级评分选择新值时,基础评分值会发生变化,但显示不会。我创建了一个示例应用程序来显示问题并将其包含在下面。

//
//  StarTestApp.swift
//  StarTest
//
//  Created by Ferdinand Rios on 12/20/21.
//

import SwiftUI

@main
struct StarTestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct StarHolder {
    var rating: Double = 3.5
}


struct ContentView: View {
    
    @State private var starHolder = StarHolder()

    var body: some View {
        NavigationView {
            NavigationLink {
                RatingView(starHolder: $starHolder)
            } label: {
                HStack {
                    Text("Rating: \(starHolder.rating, specifier: "%.1f")")
                    Spacer()
                    StarRatingView(rating: starHolder.rating, fontSize: 15)
                }
                .padding()
            }
            .navigationTitle("Test")
        }
    }
}

struct RatingView: View {
    @Binding var starHolder: StarHolder

    var body: some View {
        List {
            ForEach(0..<11, id: \.self) { index in
                HStack {
                    StarRatingView(rating: Double(index) * 0.5, fontSize: 15)
                    Spacer()
                    Image(systemName: starHolder.rating == Double(index) * 0.5 ? "checkmark" : "")
                }
                .contentShape(Rectangle()) //allows to tap whole area
                .onTapGesture {
                    starHolder.rating = Double(index) * 0.5
                }
            }
        }
        .navigationBarTitle(Text("Rating"))
    }
}

struct StarRatingView: View {
    private static let MAX_RATING: Double = 5 // Defines upper limit of the rating
    
    private let RATING_COLOR = Color(UIColor(red: 1.0, green: 0.714, blue: 0.0, alpha: 1.0))
    private let EMPTY_COLOR = Color(UIColor.lightGray)

    private let fullCount: Int
    private let emptyCount: Int
    private let halfFullCount: Int
    
    let rating: Double
    let fontSize: Double

    init(rating: Double, fontSize: Double) {
        self.rating = rating
        self.fontSize = fontSize
        fullCount = Int(rating)
        emptyCount = Int(StarRatingView.MAX_RATING - rating)
        halfFullCount = (Double(fullCount + emptyCount) < StarRatingView.MAX_RATING) ? 1 : 0
    }
    
    var body: some View {
        HStack (spacing: 0.5) {
            ForEach(0..<fullCount) { _ in
                self.fullStar
            }
            ForEach(0..<halfFullCount) { _ in
                self.halfFullStar
            }
            ForEach(0..<emptyCount) { _ in
                self.emptyStar
            }
        }
    }
    
    private var fullStar: some View {
        Image(systemName: "star.fill").foregroundColor(RATING_COLOR)
            .font(.system(size: fontSize))
    }
    
    private var halfFullStar: some View {
        Image(systemName: "star.lefthalf.fill").foregroundColor(RATING_COLOR)
            .font(.system(size: fontSize))
    }
    
    private var emptyStar: some View {
        Image(systemName: "star").foregroundColor(EMPTY_COLOR)
            .font(.system(size: fontSize))
    }
}

如果您运行该应用程序,初始评分将为 3.5,并且星星将显示正确的评分。当您选择星星时,RatingView 将显示并检查正确的评级。选择另一个评级并返回到 ContentView。评级文本将更新,但星级仍与以前相同。

谁能指出我在这里做错了什么?我假设 StarRatingView 会在 starHolder 评级发生变化时刷新。

4

2 回答 2

1

在您StarRatingViewForEach(0..<fullCount) {...}etc... 更改为ForEach(0..<fullCount, id: \.self) {...}.

halfFullCount和相同emptyCount

对我来说效果很好。

于 2021-12-22T00:08:57.647 回答
1

这里有几个问题。首先,在您的 中RatingView,您正在传递 a Binding<StarHolder>,但是当您更新 时rating,结构不会显示为已更改。要解决此问题,请传入 a Binding<Double>,更改将在ContentView.

第二个问题是StarRatingView无法接受更改,因此需要一些帮助。我只是在on 中插入了一个 on ,并.id(starHolder.rating)在SwiftUI 发生更改时向 SwiftUI 发出信号,因此它已更新。StarRatingViewContentViewStarRatingView

struct ContentView: View {
    @State private var starHolder = StarHolder()

    var body: some View {
        NavigationView {
            VStack {
                NavigationLink {
                    RatingView(rating: $starHolder.rating)
                } label: {
                    HStack {
                        Text("Rating: \(starHolder.rating, specifier: "%.1f")")
                        Spacer()
                        StarRatingView(rating: starHolder.rating, fontSize: 15)
                            .id(starHolder.rating)
                    }
                    .padding()
                }
                .navigationTitle("Test")
            }
        }
    }
}

struct RatingView: View {
    @Binding var rating: Double

    var body: some View {
        List {
            ForEach(0..<11, id: \.self) { index in
                HStack {
                    StarRatingView(rating: Double(index) * 0.5, fontSize: 15)
                    Spacer()
                    Image(systemName: rating == Double(index) * 0.5 ? "circle.fill" : "circle")
                }
                .contentShape(Rectangle()) //allows to tap whole area
                .onTapGesture {
                    rating = Double(index) * 0.5
                }
            }
        }
        .navigationBarTitle(Text("Rating"))
    }
}

最后一件事。SwiftUI 不喜欢 "" 作为 SF 符号,所以我将检查更改为 "circle" 和 "circle.fill"。无论如何,您应该为三元的两个部分提供实际图像。或者你可以使用“检查”并制作.opacity()三元。

于 2021-12-21T23:47:02.207 回答