Я работаю над окном настроек для моего приложения, где пользователь может добавлять и удалять предустановки, используя NSTableView
. Предварительная установка определяется как:
struct Preset: Codable {
var name: String
var value: Int
}
Эти предустановки должны быть сохранены до UserDefaults
. Моя цель - написать как можно меньше кода, а то, что написано, должно быть общим кодом, поэтому в моем проекте используются NSUserDefaultsController
, NSArrayController
и привязки Cocoa. Поскольку в списках свойств, используемых UserDefaults
для хранения этих настроек, доступны массивы, строковые и целочисленные типы, я решил использовать их вместо непрозрачных сериализованных типов данных.
Кроме того, чтобы облегчить доступ к этим предустановкам с помощью остальной части кода, я создал одноэлементный объект Settings со свойством presets
типа [Preset]
, которое реализует вычисленные методы получения и установки для извлечения этого из UserDefaults.
Я загрузил MCVE в GitHub . Он регистрирует значение свойства presets
каждую секунду, чтобы продемонстрировать проблемы, с которыми мне нужна помощь - кроме этих двух проблем, я считаю, что все работает нормально.
Первая проблема заключается в том, что в моем NSTableView
я бы хотел перейти в режим содержимого на основе просмотра, но когда я делаю это изменение и восстанавливаю другое, любые изменения, внесенные в записи NSTableView
, не являются сохранено в UserDefaults
. Они отлично работают в режиме контента на основе ячеек, который я использую в MCVE. Поскольку я новичок в Какао / Свифт, я бы очень хотел получить пошаговые инструкции о том, что нужно изменить в этом проекте, чтобы перейти в режим контента на основе просмотра, сохранив при этом функциональность редактирования записей NSTableView
.
РЕДАКТИРОВАТЬ : Похоже, что я только одна заметка в этом выпуске: см. этот проект, а также эту ветку в списке рассылки Cocoa.
Однако вторая проблема является самой серьезной: когда я нажимаю кнопку +
, чтобы добавить новую строку в мои NSTableView
, первоначально столбцы «имя» и «значение» остаются пустыми, пока пользователь не добавит некоторые текст / значения к нему. Однако привязки сохраняют новую пустую строку (без свойств name
и value
) в UserDefaults
. Это можно увидеть, закомментировав код в printPresets()
. Например, перед добавлением новой строки это содержание plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>presets</key>
<array>
<dict>
<key>name</key>
<string>First preset</string>
<key>value</key>
<integer>1</integer>
</dict>
<dict>
<key>name</key>
<string>Second preset</string>
<key>value</key>
<integer>2</integer>
</dict>
</array>
</dict>
</plist>
После нажатия +
это новый контент списка:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>presets</key>
<array>
<dict>
<key>name</key>
<string>First preset</string>
<key>value</key>
<integer>1</integer>
</dict>
<dict>
<key>name</key>
<string>Second preset</string>
<key>value</key>
<integer>2</integer>
</dict>
<dict/>
</array>
</dict>
</plist>
Обратите внимание на дополнительные <dict/>
после второй предустановки.
Если средство получения работает, когда настройки находятся в этом состоянии, с пустой строкой, средство получения пытается прочитать эту строку из plist и завершается ошибкой из-за отсутствия свойств name
и value
. В частности, я получаю следующую ошибку:
Thread 1: Fatal error: 'try!' expression unexpectedly raised an error:
Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "name",
intValue: nil), Swift.DecodingError.Context(codingPath:
[_PlistKey(stringValue: "Index 2", intValue: 2)], debugDescription:
"No value associated with key CodingKeys(stringValue: \"name\",
intValue: nil) (\"name\").", underlyingError: nil))
Понятия не имею, как это исправить. Я безуспешно пробовал одну вещь и придумал другую, которую я мог бы использовать, чтобы реализовать некоторые советы. Я также был бы признателен, если бы были предложены другие альтернативы, о которых я не думал.
Я попытался установить sharedUserDefaultsController.appliesImmediately
на false
, добавив кнопку «Сохранить» и привязав ее к действию sharedUserDefaultsController
save:
. Когда я это делаю, мои пресеты не загружаются. С другой стороны, я могу добавить новую строку с помощью кнопки +
без немедленного сбоя приложения. Пока я добавляю имя и значение в эту строку перед нажатием кнопки «Сохранить», приложение вообще не падает. Однако, если я пытаюсь сохранить без заполнения либо поля имени, либо значения, либо и того и другого, приложение все равно вылетает. Так что не очень хорошее решение. Возможно, если бы я мог выделить серым кнопку «Сохранить», если поля не были заполнены должным образом? Но с другой стороны, если это все то же самое, я бы хотел использовать для этого окна парадигму «без сохранения».
Другая возможность, которую я вижу, и которая является несколько разумной, - заполнить поля «имя» и «значение» данными по умолчанию при создании новой строки. Я нашел атрибут «Заполнитель», но он не подходит: это всего лишь предложение для пользователя, и до тех пор, пока пользователь фактически не заполнит что-то, поля останутся пустыми и не будут записаны в список.