onAppear 不允许保存/更新选择器值的更改?

Posted

技术标签:

【中文标题】onAppear 不允许保存/更新选择器值的更改?【英文标题】:onAppear not allowing saving/updating change from picker value? 【发布时间】:2020-08-18 04:39:59 【问题描述】:

我正在构建一个编辑视图,它允许用户更新列表项并将其保存回核心数据和列表。为此,我使用 .onAppear 操作将列表中的值传递到相应字段中。

所有数据都正确传递,切换和文本字段允许我轻松进行更改并将它们保存回核心数据并更新列表项。但是,如果我尝试更改选择器值 (emojiChoice) 并选择一个新值,则选择器值不会更改或保存回 Core Data。

当我注释掉选择器变量 (emojiChoice) 的 onAppear 操作时,选择器现在允许我选择我想要的值并将其保存回 Core Data。我还有另一个视图,它允许用户创建与此编辑视图几乎相同的项目,减去 onAppear 操作,它也可以工作。选择器允许用户选择和表情符号。因为有很多表情符号,所以我创建了一个数组,而只是将所选表情符号的数组索引作为 Int 传递。

如何解决此问题以允许选择器显示要编辑的值并允许对其进行更改?

这是编辑视图的代码:

struct editRemindr: View 
@Environment(\.managedObjectContext) var moc
@Environment(\.presentationMode) var presentationMode

let emojiList = EmojiList()
@ObservedObject var reminder: ReminderEntity
@State private var showingDeleteAlert = false

// Form Variables
@State var notifyOn = true
@State var emojiChoice = 0
@State var notification: String
@State var notes: String

// Delete Reminder Function
func deleteAction() 
    moc.delete(reminder)
    // try? self.moc.save()
    presentationMode.wrappedValue.dismiss()


// View Controller
var body: some View 
    Form 

        // On/Off Toggle
        Toggle(isOn: $notifyOn) 
            Text("On/Off")
        

        // Emoji Picker
        Picker(selection: $emojiChoice, label: Text("Emoji")) 
            ForEach(0 ..< emojiList.emojis.count) 
                Text(self.emojiList.emojis[$0])
            
        

        // Notification Text
        Section(header: Text("NOTIFICATION")) 
            HStack 
                TextField("Write your notification...", text: $notification)
                    .onReceive(notification.publisher.collect()) 
                        self.notification = String($0.prefix(60)) // <---- SET CHARACTER LIMIT
                
                Text("\(notification.count) / 60")
                    .font(.caption)
                    .foregroundColor(.gray)
            
        

        // Notes Text
        Section(header: Text("NOTES")) 
            VStack 
                MultiLineTextField(text: $notes).frame(numLines: 6)
                    .padding(.top, 5)
                    .onReceive(notes.publisher.collect()) 
                        self.notes = String($0.prefix(240)) // <---- SET CHARACTER LIMIT
                
                Text("\(notes.count) / 240")
                    .font(.caption)
                    .foregroundColor(.gray)
                    .frame(maxWidth: .infinity, alignment: .trailing)
            
        

        // Save Changes Button
        Section() 
            Button (action: 
                self.reminder.dateCreated = Date()
                self.reminder.notifyOn = self.notifyOn
                self.reminder.emojiChoice = Int64(self.emojiChoice)
                self.reminder.notification = self.notification
                self.reminder.notes = self.notes

                try? self.moc.save()

                self.presentationMode.wrappedValue.dismiss()
            ) 
                Text("SAVE CHANGES")
                    .fontWeight(.bold)
                    .foregroundColor(.white)
                    .font(.body)
                    .padding()
                .frame(maxWidth: .infinity)
                .background(Color.green)
                .padding(.vertical, -6)
                .padding(.horizontal, -15)

            // Cancel Button
            Button(action: 
                self.presentationMode.wrappedValue.dismiss()
            ) 
                Text("Cancel")
                    .frame(maxWidth: .infinity)
            
        
    

    // Make List Items Appear in Fields
    .onAppear(perform: 
        self.notifyOn = self.reminder.notifyOn
        self.emojiChoice = Int(self.reminder.emojiChoice)
        self.notification = self.reminder.notification ?? "unknown"
        self.notes = self.reminder.notes ?? "unknown"
    ) 
        .alert(isPresented: $showingDeleteAlert) 
            Alert(title: Text("Delete Reminder"), message: Text("Are you sure you want to delete this Reminder?"), primaryButton: .destructive(Text("Delete")) 
                self.deleteAction()
                , secondaryButton: .cancel()
            )
    
    .navigationBarTitle("Edit Reminder")
    .navigationBarItems(trailing: Button(action: self.showingDeleteAlert = true
    ) 
        Image(systemName: "trash")

    )
 

// Creates Text Limits
class TextLimit: ObservableObject 
    @Published var text = "" 
        didSet 
            if text.count > characterLimit && oldValue.count <= characterLimit 
                text = oldValue
            
        
    
    let characterLimit: Int

    init(limit: Int = 5)
        characterLimit = limit
    


如果它也有帮助,这里是带有列表的内容视图的代码。

struct ContentView: View 

@Environment(\.managedObjectContext) var moc
@FetchRequest(entity: ReminderEntity.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \ReminderEntity.dateCreated, ascending: false)])
var reminder: FetchedResults<ReminderEntity>

@State private var showingAddScreen = false
@State var showWelcomeScreen = false

let emojiList = EmojiList()

//Toggle Control
@State var notifyOn = true

// Save Items Function
func saveItems() 
    do 
        try moc.save()
     catch 
        print(error)
    


// Delete Item Function
func deleteItem(indexSet: IndexSet) 
    let source = indexSet.first!
    let listItem = reminder[source]
    moc.delete(listItem)


// View Controller
var body: some View 
    VStack 
        NavigationView 
            ZStack (alignment: .top) 

                // List View
                List 
                    ForEach(reminder, id: \.self)  notification in
                        NavigationLink(destination: editRemindr(reminder: notification, notification: notification.notification ?? "unknown", notes: notification.notes ?? "unknown")) 
                            // Text within List View
                            HStack 
                                // MARK: TODO
                                // Toggle("NotifyOn", isOn: true)
                                // .labelsHidden() // Hides the label/title
                                Text("\(self.emojiList.emojis[Int(notification.emojiChoice)]) \(notification.notification!)")
                             
                        
                     
                    .onDelete(perform: deleteItem)
                

                    // Navigation Items
                    .navigationBarTitle("", displayMode: .inline)
                    .navigationBarItems(
                        leading:
                        HStack 

                            Button(action: 
                                self.showWelcomeScreen.toggle()
                            ) 
                                Image(systemName: "info.circle.fill")
                                    .font(.system(size: 24, weight: .regular))
                            .foregroundColor(.gray)

                            // Positioning Remindr Logo on Navigation
                            Image("remindrLogoSmall")
                                .resizable()
                                .aspectRatio(contentMode: .fit)
                                //.frame(width: 60, height: 60, alignment: .center)
                                .padding(.leading, 83)
                                .padding(.top, -10)
                        ,

                        // Global Settings Navigation Item
                        trailing: NavigationLink(destination: globalSettings())
                            Image("settings")
                                .font(Font.title.weight(.ultraLight))
                        .foregroundColor(.gray)
                )

                // Add New Reminder Button
                VStack 
                    Spacer()
                    Button(action:  self.showingAddScreen.toggle()
                    ) 
                        Image("addButton")
                            .renderingMode(.original)
                    
                    .sheet(isPresented: $showingAddScreen) 

                        newRemindr().environment(\.managedObjectContext, self.moc)

                     
                
            
          .sheet(isPresented: $showWelcomeScreen) 
            welcomeScreen()
        
    

【问题讨论】:

【参考方案1】:

试试下面的

    // Emoji Picker
    Picker(selection: $emojiChoice, label: Text("Emoji")) 
        ForEach(0 ..< emojiList.emojis.count, id: \.self)  // << added id !!
            Text(self.emojiList.emojis[$0])
        
    

【讨论】:

谢谢,我试了一下,好像没什么效果。我验证删除 onAppear 操作允许我更改选择器值并将其保存回核心数据,但没有 onAppear 值不会通过核心数据的列表传递。切换和文本字段使用 onAppear 可以正常工作,但 Picker 似乎并非如此。我怀疑这是问题所在。有什么想法吗?【参考方案2】:

我为选择器值创建了一个自定义绑定,它似乎可以解决问题。感谢Reddit user End3r117 为我指明了正确的方向!

// View Controller
var body: some View 
    
    let reminderChoice = Binding(
        get:  Int(self.reminder.emojiChoice) ,
        set:  self.reminder.emojiChoice = Int64($0) 
    )
    return
        Form 
            
            // On/Off Toggle
            Toggle(isOn: $notifyOn) 
                Text("On/Off")
            

            // Emoji Picker
            Picker(selection: reminderChoice, label: Text("Emoji")) 
                ForEach(0 ..< self.emojiList.emojis.count, id: \.self) 
                    Text(self.emojiList.emojis[$0])
                
            

【讨论】:

以上是关于onAppear 不允许保存/更新选择器值的更改?的主要内容,如果未能解决你的问题,请参考以下文章

如何更改各种微调器值的 EditText 字段

当我更改其中一个时,为啥所有日期选择器值都变为空?

Xamarin 为 UI 测试形成更改时间选择器值

绑定 xamarin 表单选择器值的正确方法

更改时发布日期选择器值

在仅年份模式下更新引导日期选择器值