状态更改时自定义 SwiftUI 视图不会更新
Posted
技术标签:
【中文标题】状态更改时自定义 SwiftUI 视图不会更新【英文标题】:Custom SwiftUI view does not update when state changes 【发布时间】:2021-12-21 17:03:35 【问题描述】:我修改了一个自定义的 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 评级发生变化时刷新。
【问题讨论】:
【参考方案1】:这里有几个问题。首先,在您的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 Symbol,所以我将检查更改为 "circle" 和 "circle.fill"。无论如何,您应该为三元的两个部分提供实际图像。或者您可以使用“检查”并将.opacity()
设为三元组。
【讨论】:
您添加 .id(starHolder.rating) 的建议与上面@dudette 的回答相结合,为我提供了所需的解决方案。谢谢!【参考方案2】:在您的StarRatingView
中更改ForEach(0..<fullCount) ...
等...
到ForEach(0..<fullCount, id: \.self) ...
。
halfFullCount
和 emptyCount
相同。
很适合我。
【讨论】:
这是部分解决方案。我将它与下面的 Yrb 解决方案结合起来得到我需要的答案。谢谢!以上是关于状态更改时自定义 SwiftUI 视图不会更新的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI 不会将状态更新为 @ObservedObject cameraViewModel 对象