Какао: получить уведомление после редактирования текстовой ячейки (nstextfield) и начать редактирование текстовой ячейки после ее добавления в nstableview в swift 4?

Я сделал простую демонстрацию с использованием TableView здесь: https://github.com/deadcoder0904/TableViewDemo

Я использовал модуль По умолчанию как зависимость

Мой проект выглядит как

follow your dreams

Весь код находится в ViewController.swift следующим образом -

import Cocoa
import Defaults

extension Defaults.Keys {
    static let dreams = Defaults.Key<Array<String>>("dreams", default: [
        "Hit the gym",
        "Run daily",
        "Become a millionaire",
        "Become a better programmer",
        "Achieve your dreams"
        ])
}

class ViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {

    @IBOutlet weak var table: NSTableView!
    var dreams = defaults[.dreams]
    var selectedRow:Int = 0

    override func viewDidLoad() {
        super.viewDidLoad()
        table.dataSource = self
        table.delegate = self
    }

    override var acceptsFirstResponder : Bool {
        return true
    }

    override func keyDown(with theEvent: NSEvent) {
        if theEvent.keyCode == 51 {
            removeDream()
        }
    }

    func tableViewSelectionDidChange(_ notification: Notification) {
        let table = notification.object as! NSTableView
        selectedRow = table.selectedRow
    }

    func numberOfRows(in tableView: NSTableView) -> Int {
        return dreams.count
    }

    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
        let dream = table.makeView(withIdentifier: tableColumn!.identifier, owner: self) as! NSTableCellView
        dream.textField?.stringValue = dreams[row]

        return dream
    }

    @IBAction func addTableRow(_ sender: Any) {
        addNewDream()
    }

    @IBAction func removeTableRow(_ sender: Any) {
        removeDream()
    }

    func addNewDream() {
        dreams.append("Double Click or Press Enter to Add Item")
        table.beginUpdates()
        let last = dreams.count - 1
        table.insertRows(at: IndexSet(integer: last), withAnimation: .effectFade)
        table.scrollRowToVisible(last)
        table.selectRowIndexes([last], byExtendingSelection: false)
        table.endUpdates()
        saveDreams()
    }

    func removeDream() {
        if selectedRow >= dreams.count {
            selectedRow = dreams.count - 1
        }
        if selectedRow != -1 {
            dreams.remove(at: selectedRow)
            table.removeRows(at: IndexSet(integer: selectedRow), withAnimation: .effectFade)
        }
        saveDreams()
    }

    func saveDreams() {
        defaults[.dreams] = dreams
    }
}

Я хочу сделать 2 вещи -

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

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

Мне также нужно решение в Swift 4, а не в Objective-C. Как этого добиться?

Нет, @Willeke, это не дубликат. Мне удалось выбрать последнюю строку, но я хочу заняться чем-то другим. Прочтите, пожалуйста, вопрос :)

deadcoder0904 10.09.2018 11:53

Какой вопрос?

Willeke 10.09.2018 11:57

Мой вопрос. Вот ссылка stackoverflow.com/q/52251945/6141587. Я хочу получать уведомление, когда строка в NSTableView редактируется, чтобы я мог сохранить ее состояние и сохранить его на диск. Другой мой вопрос: после нажатия кнопки +, как я могу автоматически сфокусироваться на только что добавленной строке, которую я могу начать вводить без необходимости нажимать двойной щелчок или нажимать Enter, как то, что я делаю сейчас. Я также сделал образец репозитория Github для клонирования.

deadcoder0904 10.09.2018 12:00

Дубликат сделать NSTextField в NSTableCellView firstResponder ().

Willeke 10.09.2018 12:03

Я загрузил репозиторий Github, но не хочу устанавливать значения по умолчанию.

Willeke 10.09.2018 12:04

Конечно, я подумал, что вместо того, чтобы задавать одно и то же дважды, я спрошу один раз, но уверен, что в будущем задам только один вопрос. Если вы вернетесь к предыдущей фиксации, у нее не будет значений по умолчанию. Я рассмотрю вопрос, который вы задали выше.

deadcoder0904 10.09.2018 12:10

Используйте привязки какао. Это обеспечивает вещи без добавления дополнительного кода, и вы даже можете удалить методы источника данных табличного представления.

vadian 10.09.2018 12:12

editColumn(_:row:with:select:) не делает ячейку сфокусированной и редактируемой?

Willeke 10.09.2018 12:14

@vadian Я проверил привязки какао, но не нашел легкой статьи, чтобы понять это. Я проверил статью Рэя Вендерлиха, но, поскольку я новичок, я, к сожалению, не мог понять всего этого. Не могли бы вы дать ссылку для новичков?

deadcoder0904 10.09.2018 12:14

@Willeke Можете указать полный код для editColumn(_:row:with:select:)? Я попытаюсь найти его, но это потребует времени.

deadcoder0904 10.09.2018 12:18

@Willeke Итак, мой второй вопрос сработал. Я использовал вашу ссылку SO, и теперь после нажатия + она фокусируется на последнем элементе и начинает редактирование. Сейчас проверю 1-й, работает ли.

deadcoder0904 10.09.2018 12:29

@Willeke Я пробовал editColumn, но он не работает. Может я неправильно реализовал. Можешь проверить здесь?

deadcoder0904 10.09.2018 12:49

Я написал ответ.

vadian 10.09.2018 13:05

Большое спасибо. Дайте мне посмотреть, работает ли это, чтобы я мог проголосовать и принять его :)

deadcoder0904 10.09.2018 13:13
0
15
580
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Используйте привязки какао, это очень мощный инструмент, позволяющий сэкономить много стандартного кода.

Краткое руководство:

Редактировать: Чтобы в полной мере использовать KVC, источником данных должен быть подкласс NSObject со свойствами dynamic.

  • Создайте простой класс Dream (свойство description необязательно)

    class Dream : NSObject {
        @objc dynamic var name : String
        init(name : String) { self.name = name }
        override var description : String { return "Dream " + name }
    }
    
  • В контроллере представления объявите массив источника данных

    var dreams = [Dream]()
    
  • и замените var selectedRow:Int = 0 на

    @objc dynamic var selectedIndexes = IndexSet()
    
  • Перейти к Интерфейсному Разработчику

    • Выберите представление таблицы, нажмите ⌥⌘7, чтобы перейти к Инспектору привязок. Свяжите Selection Indexes с View ControllerПуть к ключу моделиselectedIndexes.
      Нажмите ⌥⌘6 и подключите dataSource (перетаскиванием) к контроллеру представления (enter image description here).

    • Выберите текстовое поле File 1 в Table Cell View в столбце таблицы. Самый простой способ - набрать ⌃⇧click в области текстового поля. Нажмите ⌥⌘7 и привяжите Value к Table Cell ViewПуть к ключу моделиobjectValue.name (!)

  • В контроллере представления заполните массив источника данных в viewDidLoad (я не знаю эту структуру, поэтому я оставлю ее) и перезагрузите представление таблицы.

    override func viewDidLoad() {
        super.viewDidLoad()
        let dreamNames = ["Hit the gym", "Run daily", "Become a millionaire", "Become a better programmer", "Achieve your dreams"]
        dreams = dreamNames.map{Dream(name: $0)}
        table.reloadData()         
    }
    
  • Удалить acceptsFirstResponder

  • Удалить tableViewSelectionDidChange
  • Удалить tableView:viewFor:row:
  • Добавлять

    func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
        return dreams[row]
    }
    
  • Заменить addNewDream на

    func addNewDream() {
         let last = dreams.count
         dreams.append(Dream(name: "Double Click or Press Enter to Add Item"))
         table.insertRows(at: IndexSet(integer: last), withAnimation: .effectGap)
         table.scrollRowToVisible(last)
         table.selectRowIndexes([last], byExtendingSelection: false)
    
         saveDreams()
     }
    
  • Заменить removeDream() на

    func removeDream() {
         guard let selectedRow = selectedIndexes.first else { return }
         dreams.remove(at: selectedRow)
         table.removeRows(at: IndexSet(integer: selectedRow), withAnimation: .effectFade)
    
         saveDreams()
     }
    

Чтобы сохранить массив при последующем редактировании текста, необходимо реализовать метод делегата controlTextDidEndEditing(_:)

override func controlTextDidEndEditing(_ obj: Notification) {
    saveDreams()
}

и в Интерфейсном Разработчике подключите delegate текстового поля в табличном представлении к контроллеру представления.

Большое спасибо @vadian. Это работает отлично, и это сильно уменьшило мой шаблонный код. Я загрузил его здесь, если кто-нибудь когда-нибудь столкнется с такой же проблемой :) Еще раз спасибо

deadcoder0904 10.09.2018 13:45

В связанном проекте tableView(:viewFor:row:) все еще присутствует. Удалить это избыточно. Просмотр и присвоение значений управляется привязками какао.

vadian 10.09.2018 14:45

Как получить новый текст при изменении строки?

deadcoder0904 10.09.2018 14:47

Я не понимаю. Что вы имеете в виду под новый текст и строка изменена?

vadian 10.09.2018 14:48

Я удалил его в новом коммите и добавил модуль Defaults, но последнее, что осталось - после редактирования я не могу получить новые данные. Как мне это получить?

deadcoder0904 10.09.2018 14:51

См. Последний раздел моего ответа. Вам необходимо реализовать метод делегата NSTextField

vadian 10.09.2018 14:52

Позволь мне объяснить. После того, как вы нажмете +, вы можете ввести новый текст, и данные будут добавлены. Но предположим, что я хочу отредактировать первый элемент и поменять его с «Попробуй спортзал» на «Прыгай с парашютом», я нажму «Ввод» и изменю его на «Иди с парашютом», и он изменился. Но после того, как я закрою приложение и снова запустил его, я не вижу его, потому что я не сохранял его. Итак, как я получу "Перейти к прыжкам с парашютом" после его редактирования из старого текста (это отличается от нажатия кнопки "плюс"). Надеюсь, вы понимаете. Если нет, я буду рад предоставить GIF.

deadcoder0904 10.09.2018 14:54

Я проверил это, но настоящая проблема, с которой я столкнулся, заключается в том, как мне теперь продолжать использовать модуль Defaults. Это очень маленький модуль, но он очень полезен, чем встроенный UserDefaults. Но я не могу понять, как его использовать, когда занимаюсь KVC. Вы можете взглянуть на это? Вам не потребуется более 2 минут, чтобы понять это. Вот ссылка github.com/sindresorhus/Defaults, если вы можете помочь с этим. Спасибо за ответ. Вы действительно очень помогли :)

deadcoder0904 10.09.2018 18:56

Честно говоря, я не понимаю смысла этих библиотек. UserDefaults прост, и вы можете легко расширить его под свои нужды. Например, вы можете написать расширение для Dream (около 8 строк кода). У вас есть несколько вариантов: 1) сохранить только имена let dreamNames = dreams.map{$0} и загрузить их, как описано в viewDidLoad. 2) Примените Codable к Dream и сохраните массив как Список свойств или как JSON. 3) Присоедините NSCoding к Dream и сохраните массив как архив. Рекомендую первый вариант.

vadian 10.09.2018 19:05

Круто попробую сейчас. Библиотека уменьшила для меня много кода. У меня было 80 LOC настроек, и мне нужно было хранить 5-6 свойств. С помощью этой библиотеки я сократил код до <10 LOC. Эта библиотека также очень мала - <100 LOC, но зачем изобретать велосипед снова и снова, когда мы можем просто выполнить работу с ним.

deadcoder0904 11.09.2018 05:30

Большое спасибо @vadian. Первый метод сработал, и я выложу код на Github github.com/deadcoder0904/TableViewDemoUsingCocoaBindings

deadcoder0904 11.09.2018 05:35

Другие вопросы по теме