问题描述
我似乎陷入了第 22 条军规的境地。也许我的方法在这里完全错误。我希望有人能帮帮忙。我想使用访问特定现实世界地标的用户的反馈来创建基于星级的评级显示。 在 CoreData 中,我有一个名为 rating 的实体,具有名为 rating (Int32) 和地标 (String) 的属性。我想获得与给定地标相关的所有评分的平均值,以便在视图中为每个评分显示星星。 这是视图的代码:
struct TitleImageView: View {
@Environment(\.managedobjectContext) var viewContext : NSManagedobjectContext
let landmark: Landmark
var body: some View {
Image(landmark.imageName)
.resizable()
.shadow(radius: 10 )
.border(Color.white)
.scaledToFit()
.padding([.leading,.trailing],40)
.layoutPriority(1)
.overlay(TextOverlay(landmark: landmark))
.overlay(ratingsOverlay(rating: stars))
}
}
let fetchRequest = rating.fetchRequestForLandmark(landmark: landmark.name)
var ratings: FetchedResults<rating> {
fetchRequest.wrappedValue
}
var sum: Int32 {
ratings.map { $0.rating }.reduce(0,+)
}
var stars : Int32 {
sum / Int32(ratings.count)
}
问题是这样的:当我在视图主体之前插入 fetch 时,我收到警告
“不能在属性初始化器中使用实例成员‘landmark’;属性初始化器在‘self’可用之前运行”
当我把 fetch 放在 body 之后,我得到:
“包含声明的闭包不能与函数构建器‘viewbuilder’一起使用”(参考 var 评级)
是否有解决这个难题的简单方法,还是我必须回到众所周知的绘图板?谢谢。
解决方法
如何将 fetch 包装在一个计算属性中,该属性返回一个 sum 和 stars 元组?形状略有不同,结果相同。计算出来的属性可以引用其他属性,这样就扫除了 swift-init 的障碍!
var metrics : (sum: Int,stars: Int) {
let fetchRequest = Rating.fetchRequestForLandmark(landmark: landmark.name)
var ratings: FetchedResults<Rating> {
fetchRequest.wrappedValue
}
let sum = ratings.map { $0.rating }.reduce(0,+)
let stars = sum / ratings.count
return (sum: sum,stars: stars)
}
,感谢提供建议的人。经过长时间的中断,我回到了这个问题并得出了这个解决方案(基于 HackingWithSwift 教程:https://www.hackingwithswift.com/books/ios-swiftui/dynamically-filtering-fetchrequest-with-swiftui) 代码如下:
struct RatingsView: View {
var fetchRequest: FetchRequest<Rating>
var body: some View {
HStack(spacing: 0.4){
ForEach(1...5) { number in
if number > stars {
//Image(systemName: "star")
} else {
Image(systemName: "star.fill")
}
}
}
}
init(filter: String) {
fetchRequest = FetchRequest<Rating>(entity: Rating.entity(),sortDescriptors: [],predicate: NSPredicate(format: "%K == %@","landmark",filter))
}
var sum: Int16 {
fetchRequest.wrappedValue.reduce(0) { $0 + $1.rating }
}
var stars : Int {
var starCount:Int
if fetchRequest.wrappedValue.count > 0 {
starCount = Int(sum) / fetchRequest.wrappedValue.count
} else {
starCount = 0
}
return starCount
}
}
我不确定这是否是绝对的最佳解决方案,但它按预期工作。 希望这可以帮助其他人。