這是“主題”結構專案的簡單串列視圖。目標是在點擊串列的一行時呈現一個編輯器視圖。在此代碼中,點擊一行預計會導致所選主題在 @State var 中存盤為“tappedTopic”,并設定一個布爾型 @State var 以顯示 EditorV。
當運行所示代碼并點擊一行時,其主題名稱在 Button 操作的 Print 陳述句中正確列印,但隨后應用程式崩潰,因為 self.tappedTopic! 在 EditTopicV(...) 行中發現 tappedTopic 為 nil。
如果未注釋“tlVM.objectWillChange.send()”行,則代碼運行良好。為什么需要這個?
第二個難題:在代碼運行良好的情況下,未注釋 objectWillChange.send() 時,EditTopicV init() 中的列印陳述句顯示它運行了兩次。為什么?
任何幫助將不勝感激。我正在使用 Xcode 13.2.1,我的部署目標設定為 iOS 15.1。
主題.swift:
struct Topic: Identifiable {
var name: String = "Default"
var iconName: String = "circle"
var id: String { name }
}
主題串列V.swift:
struct TopicListV: View {
@ObservedObject var tlVM: TopicListVM
@State var tappedTopic: Topic? = nil
@State var doEditTappedTopic = false
var body: some View {
VStack(alignment: .leading) {
List {
ForEach(tlVM.topics) { topic in
Button(action: {
tappedTopic = topic
// why is the following line needed?
tlVM.objectWillChange.send()
doEditTappedTopic = true
print("Tapped topic = \(tappedTopic!.name)")
}) {
Label(topic.name, systemImage: topic.iconName)
.padding(10)
}
}
}
Spacer()
}
.sheet(isPresented: $doEditTappedTopic) {
EditTopicV(tlVM: tlVM, originalTopic: self.tappedTopic!)
}
}
}
EditTopicV.swift(編輯器視圖):
struct EditTopicV: View {
@ObservedObject var tlVM: TopicListVM
@Environment(\.presentationMode) var presentationMode
let originalTopic: Topic
@State private var editTopic: Topic
@State private var ic = "circle"
let iconList = ["circle", "leaf", "photo"]
init(tlVM: TopicListVM, originalTopic: Topic) {
print("DBG: EditTopicV: originalTopic = \(originalTopic)")
self.tlVM = tlVM
self.originalTopic = originalTopic
self._editTopic = .init(initialValue: originalTopic)
}
var body: some View {
VStack(alignment: .leading) {
HStack {
Button("Cancel") {
presentationMode.wrappedValue.dismiss()
}
Spacer()
Button("Save") {
editTopic.iconName = editTopic.iconName.lowercased()
tlVM.change(topic: originalTopic, to: editTopic)
presentationMode.wrappedValue.dismiss()
}
}
HStack {
Text("Name:")
TextField("name", text: $editTopic.name)
Spacer()
}
Picker("Color Theme", selection: $editTopic.iconName) {
ForEach(iconList, id: \.self) { icon in
Text(icon).tag(icon)
}
}
.pickerStyle(.segmented)
Spacer()
}
.padding()
}
}
TopicListVM.swift(可觀察物件視圖模型):
class TopicListVM: ObservableObject {
@Published var topics = [Topic]()
func append(topic: Topic) {
topics.append(topic)
}
func change(topic: Topic, to newTopic: Topic) {
if let index = topics.firstIndex(where: { $0.name == topic.name }) {
topics[index] = newTopic
}
}
static func ex1() -> TopicListVM {
let tvm = TopicListVM()
tvm.append(topic: Topic(name: "leaves", iconName: "leaf"))
tvm.append(topic: Topic(name: "photos", iconName: "photo"))
tvm.append(topic: Topic(name: "shapes", iconName: "circle"))
return tvm
}
}
串列如下所示:

uj5u.com熱心網友回復:
使用sheet(isPresented:)傾向于導致這樣的問題,因為 SwiftUI 以似乎并不總是有意義的順序計算目標視圖。在您的情況下,objectWillSend在視圖模型上使用,即使它不應該有任何影響,似乎會延遲您的強制展開變數的計算并避免崩潰。
要解決此問題,請使用以下sheet(item:)形式:
.sheet(item: $tappedTopic) { item in
EditTopicV(tlVM: tlVM, originalTopic: item)
}
然后,您item可以安全地通過閉包,并且沒有理由強制解開。
您也可以捕獲tappedTopic類似的結果,但您仍然必須強制打開它,這通常是我們想要避免的:
.sheet(isPresented: $doEditTappedTopic) { [tappedTopic] in
EditTopicV(tlVM: tlVM, originalTopic: tappedTopic!)
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/416281.html
標籤:
