簡體   English   中英

當 state 更改時,自定義 SwiftUI 視圖不會更新

[英]Custom SwiftUI view does not update when state changes

我修改了一個自定義的 5 星評級視圖 ( https://swiftuirecipes.com/blog/star-rating-view-in-swiftui ) 以滿足我的需求。 我在我的應用程序的多個位置使用該視圖來顯示結構的當前評級並通過可選列表更改評級。 我遇到的問題是,當我通過 NavigationLink 為星級評分 select 設置新值時,基礎評分值會發生變化,但顯示不會。 我創建了一個示例應用程序來顯示問題並將其包含在下面。

//
//  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,並且星星將顯示正確的評分。 當您 select 星星時,RatingView 將顯示並檢查正確的評級。 Select 另一個評級並返回到 ContentView。 評級文本將更新,但星級仍與以前相同。

誰能指出我在這里做錯了什么? 我假設 StarRatingView 會在 starHolder 評級發生變化時刷新。

這里有幾個問題。 首先,在您的RatingView中,您傳遞了一個Binding<StarHolder> ,但是當您更新rating時,該結構不會顯示為已更改。 要解決此問題,請傳入Binding<Double> ,更改將在ContentView中得到記錄。

第二個問題是StarRatingView無法接受更改,因此需要一些幫助。 我只是在ContentView .id(starHolder.rating)粘貼到StarRatingView上,當StarRatingView發生變化時,它會向 SwiftUI 發出信號,因此它已更新。

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()三元組。

在您的StarRatingView中,將ForEach(0..<fullCount) {...}等更改為ForEach(0..<fullCount, id: \.self) {...}

halfFullCountemptyCount相同。

對我來說效果很好。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM