Запретить NSTableView от сохранения новой строки, пока пользователь не заполнит значения - PullRequest
0 голосов
/ 27 января 2019

Я работаю над окном настроек для моего приложения, где пользователь может добавлять и удалять предустановки, используя 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))

Понятия не имею, как это исправить. Я безуспешно пробовал одну вещь и придумал другую, которую я мог бы использовать, чтобы реализовать некоторые советы. Я также был бы признателен, если бы были предложены другие альтернативы, о которых я не думал.

  1. Я попытался установить sharedUserDefaultsController.appliesImmediately на false, добавив кнопку «Сохранить» и привязав ее к действию sharedUserDefaultsController save:. Когда я это делаю, мои пресеты не загружаются. С другой стороны, я могу добавить новую строку с помощью кнопки + без немедленного сбоя приложения. Пока я добавляю имя и значение в эту строку перед нажатием кнопки «Сохранить», приложение вообще не падает. Однако, если я пытаюсь сохранить без заполнения либо поля имени, либо значения, либо и того и другого, приложение все равно вылетает. Так что не очень хорошее решение. Возможно, если бы я мог выделить серым кнопку «Сохранить», если поля не были заполнены должным образом? Но с другой стороны, если это все то же самое, я бы хотел использовать для этого окна парадигму «без сохранения».

  2. Другая возможность, которую я вижу, и которая является несколько разумной, - заполнить поля «имя» и «значение» данными по умолчанию при создании новой строки. Я нашел атрибут «Заполнитель», но он не подходит: это всего лишь предложение для пользователя, и до тех пор, пока пользователь фактически не заполнит что-то, поля останутся пустыми и не будут записаны в список.

1 Ответ

0 голосов
/ 28 января 2019

Я решил эту проблему, изменив «Имя класса» в «Контроллере объектов» в Инспекторе атрибутов для NSArrayController на мой MyModuleName.Preset, теперь объявленный как класс:

class Preset: NSObject, Codable {
    let name: String
    let value: Int

    override init() {
        self.name = "A preset"
        self.setpoint = 1
        super.init()
    }

    init(name: String, value: Int) {
        self.name = name
        self.value = value
        super.init()
    }
}

Указав значения по умолчаниюзначения в init() (версия без аргументов), когда создается новая строка, она заполняется этими значениями по умолчанию, что устраняет проблему.

...