我知道有很多关于Type of expression is ambiguous without more context
编译时错误的问题,但我读了很多,似乎不明白为什么会发生我的错误。
首先,我有
protocol ExpensePeriod: AnyObject, Identifiable, Hashable {
associatedtype Period: ExpensePeriod
var type: Calendar.Component { get set }
var interval: DateInterval { get set }
var start: Date { get }
var end: Date { get }
var expenses: FetchRequest<Expense> { get }
static func array(from startDate: Date, to endDate: Date) -> [Period]
init(from date: Date)
}
广告:
extension ExpensePeriod {
var start: Date { interval.start }
var end: Date { interval.end }
var expenses: FetchRequest<Expense> {
FetchRequest<Expense>(
entity: Expense.entity(),
sortDescriptors: [NSSortDescriptor(key: "datetime", ascending: false)],
predicate: NSPredicate(
format: "datetime > %@ AND datetime < %@",
argumentArray: [start, end]
)
)
}
static func array(of timeComponent: Calendar.Component, from startDate: Date, to endDate: Date) -> [Self] {
var currentDate = startDate
var array = [Self(from: currentDate)]
while !Calendar.current.dateInterval(of: timeComponent, for: currentDate)!.contains(endDate) {
currentDate = Calendar.current.date(byAdding: timeComponent, value: 1, to: currentDate)!
array.append(Self(from: currentDate))
}
return array
}
static func == (lhs: Self, rhs: Self) -> Bool {
lhs.interval == rhs.interval
}
func hash(into hasher: inout Hasher) {
hasher.combine(interval)
}
}
然后:
final class ExpenseYear: ExpensePeriod {
typealias Period = ExpenseYear
var type: Calendar.Component
var interval: DateInterval
var year: Int { Calendar.current.component(.year, from: interval.start) }
var expenseMonths: [ExpenseMonth] {
return ExpenseMonth.array(from: start, to: end)
}
static func array(from startDate: Date, to endDate: Date) -> [ExpenseYear] {
array(of: .year, from: startDate, to: endDate)
}
init(from date: Date) {
self.type = .year
self.interval = Calendar.current.dateInterval(of: type, for: date)!
}
}
现在,主要的SwiftUI视图:
struct ListView: View {
@Environment(\.managedObjectContext) private var managedObjectContext
@FetchRequest(
entity: Expense.entity(),
sortDescriptors: [NSSortDescriptor(key: "datetime", ascending: false)]
) var expenses: FetchedResults<Expense>
@State private var showingNewExpenseSheet = false
@State private var showingPreferencesSheet = false
private var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = "YYYY MMM"
return formatter
}
var body: some View {
NavigationView {
List {
ForEach(ExpenseYear.array(from: expenses.last!, to: expenses.first!)) { expenseYear in
ForEach(expenseYear.expenseMonths) { expenseMonth in
MonthlyListView(expenseMonth)
}
Text("\(0)")
}.onDelete(perform: deleteExpenseItem)
}
.navigationBarTitle("Expenses")
.navigationBarHidden(true)
}
}
func deleteExpenseItem(at offsets: IndexSet) {
for index in offsets {
let expense = expenses[index]
managedObjectContext.delete(expense)
}
do {
try managedObjectContext.save()
} catch {
print("Wasn't able to save after delete due to \(error)")
}
}
}
struct MonthlyListView: View {
@Environment(\.managedObjectContext) private var managedObjectContext
var expenseFetchRequest: FetchRequest<Expense>
var expenses: FetchedResults<Expense> {
expenseFetchRequest.wrappedValue
}
let expenseMonth: ExpenseMonth
init(_ month: ExpenseMonth) {
self.expenseMonth = month
self.expenseFetchRequest = month.expenses
}
var body: some View {
Section(header: Text("\(expenseMonth.month)")) {
ForEach(expenses) { expense in
ExpenseRowItemView(expense)
}
}
}
}
ExpenseRowItemView
只显示各种日期时间/备注项。
费用实体看起来如下:
Type of expression is ambiguous without more context
似乎发生在ForEach(ExpenseYear.array(from: expenses.last!, to: expenses.first!))
。我在Xcode 12测试版上。谢谢你的帮助。
(如果您想知道为什么我要经历所有这些麻烦,只是为了提供一个费用列表:我以前有一个函数,它贯穿每个费用的datetime
,并构建一个嵌套的SwiftUI呈现的年数和月份结构(使用节,等等)。但是,我不认为这是一种可伸缩/高性能的方法,因为每次呈现视图时都会调用此函数,从而触及核心数据中的每个条目,所以我认为每个月列表都会处理自己的FetchRequest,并有自己的日期边界,这也会使动态视图更容易,比如“选择一个月来查看本月之前的事务列表”。如果还有更好的方法,请告诉我。)
发布于 2020-07-14 23:33:00
在ExpensePeriod
中,您似乎没有遵循ExpenseYear
--您缺少start
和end
变量(很可能是错误的来源,但很难说)
符合后,如果错误仍然存在,我将在循环中用Text
替换Text
视图,并且在找到错误源之前继续替换。
当您缺少关键字或格式化循环时,通常会发生此错误。大多数情况下,它只是意味着编译器无法解释您所写的内容。
我会解决这个问题,但是上面的代码缺少一些东西,可以通过复制和粘贴来运行它。
编辑:
所以您的问题在于forEach
,因为如果您仔细注意,您的代码看起来像这个ForEach(ExpenseYear.array(from: expenses.last!, to: expenses.first!))
,但是,开销定义为var expenses: FetchedResults<Expense>
,其中这个数组中的每个项都是ExpenseYear
数组中的Expense
类型,您的头看起来像这个tatic func array(from startDate: Date, to endDate: Date) -> [ExpenseYear]
,第一个和第二个参数是Date
类型的,但是您要传递一个Expense
类型的项。expenses.last!
返回一个Expense
对象,这不是一个Date
!因此,要解决这个问题,首先必须做一些类似于这个expenses.last!.datetime!
的事情
所以将您的代码更改为
ForEach(ExpenseYear.array(from: expenses.last!.datetime!, to: expenses.first!.datetime!), id: \.id) { expense in
应该能解决你的问题。请记住以下几点
而且,我知道您评论说我不需要在start
和end
中实现ExpenseYear
,但不幸的是,我无法在没有实现它们的情况下编译,所以我不得不这样做。
或者,您可以更改.array
协议以接受Expense
而不是Date
,然后处理如何从对象Expense
返回数组,以便您的协议看起来如下所示
static func array(from startExpense: Expense, to endExpense: Expense) -> [Period]
实现可以是这样的
static func array(from startExpense: Expense, to endExpense: Expense) -> [ExpenseYear] {
guard let startDate = startExpense.datetime, let endDate = endExpense.datetime else {
return []
}
return array(of: .year, from: startDate, to: endDate)
}
在这里,您已经处理了防范零日期的问题,除了实现之外,您不需要更改任何东西(老实说,我更喜欢这种方法)。
我知道要实现第二个方法,您必须改变设置协议和其他东西的方式,所以您可以做的是将一个可选的Date
传递给array
,类似于这个static func array(from startExpense: Date?, to endExpense: Date?) -> [Period]
。
然后保护,打开它们,否则返回空数组。但是在您的.last
循环中仍然存在解包装.first
和ForEach
开销的问题。
祝其他人好运!
https://stackoverflow.com/questions/62907643
复制