我正在尝试将某些组件抽象为更小的部分。为此,我创建了以下清单:
struct ArticleList: View {
var fetchRequest: FetchRequest<Article>
var results: FetchedResults<Article> { fetchRequest.wrappedValue }
init() {
fetchRequest = FetchRequest<Article>(
entity: Article.entity(),
sortDescriptors: []
)
}
var body: some View {
ForEach(results) { article in
Text(article.name ?? "")
}
}
}
现在我有一个容器,它将显示列表组件,如果满足子组件中的条件,还会显示一些其他内容:
struct Container: View {
var body: some View {
let articleList = ArticleList2()
return Group {
if articleList.results.isEmpty {
Text("Add")
}
articleList
}
}
}
我的问题是,代码由于以下异常而崩溃:
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
进一步调试控制台,可为我提供以下反馈:
(lldb) po self.results
error: warning: couldn't get required object pointer (substituting NULL): Couldn't load 'self' because its value couldn't be evaluated
调试po self.fetchRequest
可行,并且包含FetchRequest<Article>
实例的一个实例。 po self.fetchRequest.wrappedValue
提供与上述self.results
相同的错误。
有人知道为什么这段代码崩溃了,有什么可能的解决方法吗?
谢谢。
答案 0 :(得分:1)
您的提取请求无效,因为在创建和使用ArticleList
视图时尚无托管对象上下文。
无论如何...在下面找到修改后的代码(我尝试将更改降到最低),您的代码有效。使用Xcode 11.4 / iOS 13.3进行了测试
struct ArticleList: View {
// always keep View memebers private to safe yourself from misconcept
private var fetchRequest: FetchRequest<Article>
private var results: FetchedResults<Article> { fetchRequest.wrappedValue }
private var reportEmpty: () -> ()
init(_ onEmpty: @escaping () -> ()) {
reportEmpty = onEmpty
// FetchRequest needs @Environment(\.managedObjectContext) which is not available (!) yet
fetchRequest = FetchRequest<Article>(
entity: Article.entity(),
sortDescriptors: []
)
}
var body: some View {
// here (!) results are valid, because before call body SwiftUI executed FetchRequest
if self.results.isEmpty {
self.reportEmpty()
}
return Group {
ForEach(results, id: \.self) { article in
Text(article.name ?? "")
}
}
}
}
struct Container: View {
@State private var isEmpty = false
var body: some View {
return Group {
if self.isEmpty { // use view state for any view's conditions
Text("Add")
}
ArticleList { // View must live only in view hierarchy !!
DispatchQueue.main.async {
self.isEmpty = true
}
}
}
}
}
答案 1 :(得分:0)
虽然@Asperi的解决方案有效,但我现在确实以不同的方式实现了它。
我将闭包传递到ArticleList
中,如果按下Button
,则会执行该回调。 Button
仅在ArticleList
为空的情况下可用,但是现在ArticleList
负责显示该按钮(这使它对我更具可重用性:
struct ArticleList: View {
var fetchRequest: FetchRequest<Article>
var results: FetchedResults<Article> { fetchRequest.wrappedValue }
let onCreate: (() -> Void)
init(onCreate: @escaping (() -> Void)) {
fetchRequest = FetchRequest<Article>(
entity: Article.entity(),
sortDescriptors: []
)
self.onCreate = onCreate
}
var body: some View {
Group {
if results.isEmpty {
Button(action: onCreate) {
Text("Add")
}
}
ForEach(results) { article in
Text(article.name ?? "")
}
}
}
}
struct Container: View {
var body: some View {
ArticleList(onCreate: onCreate)
}
func onCreate() {
// Create the article inside the container
}
}