Я не уверен, что я делаю неправильно. Поскольку я не мог найти никаких других вопросов (или даже документации) об этом, кажется, нормально работать без проблем для других людей.
Я просто пытаюсь получить NSTableView, основанный на представлении, для поддержки редактирования его содержимого. То есть приложение отображает NSTableView с одним столбцом и несколькими строками, содержащими NSTextField с некоторым контентом. Я хочу, чтобы (дважды) нажимал на ячейку и редактировал содержимое ячейки. Таким образом, в основном нормальное поведение NSTableView на основе ячейки, где реализован метод tableView:setObjectValue:forTableColumn:row:
.
Я проанализировал пример Complex TableView в примере кода TableViewPlayground от Apple (который поддерживает редактирование содержимого ячеек), но я не могу найти параметр/код/переключатель, который позволяет редактировать.
Источник данных и делегат представления таблицы установлены в контроллер представления.
При запуске этого приложения в представлении таблицы отображаются 10 тестовых строк, но невозможно редактировать одну из строк.
Я дважды проверял все атрибуты NSTableView (и его содержимое) таким же, как в примере TableViewPlayground от Apple. И после нескольких часов поиска документации и Интернета для полезных советов без каких-либо успехов, я немного расстроен. Все, что вы можете найти на основе NSTableViews, являются незаменимыми образцами или очень неопределенной информацией о редактируемом контенте. И, конечно, есть тонны информации, документации и образцов для редактируемых, основанных на ячейках NSTableViews...
Ответ 2
Несмотря на то, что все части для редактирования NSTableView, основанного на представлении, присутствуют в вопросе и ответе, мне все еще не удалось собрать все это вместе. Следующая демонстрация находится в Swift, используя Xcode 6.3.2, но для пешеходов/женщин objective-C должно быть легко следовать. Полный список кодов находится в конце.
Начните здесь:
Справочник по протоколу NSTableViewDataSource
Значения настройки
- tableView: setObjectValue: forTableColumn: строка:
Swift:
optional func tableView(_ aTableView: NSTableView,
setObjectValue anObject: AnyObject?,
forTableColumn aTableColumn: NSTableColumn?,
row rowIndex: Int)
Objective-C:
- (void)tableView:(NSTableView *)aTableView
setObjectValue:(id)anObject
forTableColumn:(NSTableColumn *)aTableColumn
row:(NSInteger)rowIndex
Обсуждение. Этот метод предназначен для использования с табличными представлениями в ячейках, он не должен использоваться с представлениями на основе представления. В таблицы, основанные на представлении, используйте цель/действие для установки каждого элемента в представлении клетка.
Если вы похожи на меня, вы просматривали протоколы NSTableViewDelegate и NSTableViewDataSource, которые искали какой-то метод редактирования. Однако обсуждение в приведенной выше цитате говорит вам, что вещи намного проще.
1) Если вы посмотрите в схеме документа для вашего TableView в Xcode, вы увидите что-то вроде этого:
![enter image description here]()
Ячейка в TableView представлена Table Cell View
. Ячейка содержит несколько элементов, и по умолчанию одним из элементов является NSTextField. Но где NSTextField в контуре документа?! Ну, элементы управления в контуре документа имеют значок, который выглядит как слайдер рядом с их именами. Взглянуть. Внутри ячейки вы увидите что-то, у которого есть значок слайдера рядом с ним. И, если вы выберете эту строку в контуре документа, затем откройте Identity Inspector, вы увидите, что это NSTextField:
![enter image description here]()
Вы можете считать это просто обычным старым NSTextField.
При реализации методов протокола NSTableViewDataSource:
import Cocoa
class MainWindowController: NSWindowController,
NSTableViewDataSource {
...
...
var items: [String] = [] //The data source: an array of String's
...
...
// MARK: NSTableViewDataSource protocol methods
func numberOfRowsInTableView(tableView: NSTableView) -> Int {
return items.count
}
func tableView(tableView: NSTableView,
objectValueForTableColumn tableColumn: NSTableColumn?,
row: Int) -> AnyObject? {
return items[row]
}
}
.. TableView принимает значение, возвращаемое вторым методом, и присваивает ему свойство с именем objectValue
во внешнем Table Cell View
- другими словами, TableView не использует возвращаемое значение для установки NSTextField (т.е. внутренний Table View Cell
). Это означает, что ваши элементы источника данных не будут отображаться в TableView, потому что NSTextField отображает элемент. Чтобы установить значение NSTextField, вам необходимо связать или привязать NSTextField value
к свойству objectValue. Вы делаете это в инспекторе Bindings:
Предупреждение.. Не устанавливайте флажок Bind to
до тех пор, пока не выберете объект, к которому вы хотите привязать. Если вы проверите флажок сначала, объект будет вставлен в схему вашего документа, и если вы этого не заметите, вы получите ошибки при запуске программа. Если вы случайно проверите флажок Bind to
, сделайте обязательно удалите автоматически добавленный объект в документе контур. Xcode создает отдельный раздел для добавленного объекта, поэтому его легко обнаружить в контуре вашего документа.
![enter image description here]()
2) Отказываясь на мгновение, вы, вероятно, знакомы с подключением кнопки к методу действий, а после этого, если вы нажмете на кнопку, действие будет выполнено. С другой стороны, с помощью NSTextField вы обычно объявляете IBOutlet, который затем используется для получения или установки NSTextField stringValue
.
Однако NSTextField также может инициировать выполнение метода действия. Wha??! Но вы не можете нажать на поле NSText, как вы можете кнопку! Тем не менее, у NSTextField есть триггер, как нажатие кнопки, что приведет к выполнению метода действия, а триггер: закончить редактирование NSTextField. Как NSTextField знает, когда вы закончите его редактирование? Существует два способа:
Вы можете выбрать триггер в Инспекторе атрибутов:
![enter image description here]()
3) Как показал в своем ответе @Paul Patterson, следующее, что вам нужно сделать, - установить NSTextField Behavior
в Editable в Attributes Inspector.
4) Затем подключите NSTextField к методу действий, который вы хотите выполнить. Если вы не использовали следующий трюк для подключения элемента управления к методу действий, вы должны попробовать его некоторое время:
Выберите файл .xib в Навигаторе проектов, чтобы окно и отображаются его элементы управления. Затем нажмите "Редактор помощника" ( two_interlocking_rings в верхней части окна Xcode справа) - отобразит ваш файл контроллера (если будет показан другой файл, затем используйте панель перехода, чтобы перейти к файлу вашего контроллера). Затем Control + drag от NSTextField (в контуре документа) до места в вашем Файл контроллера, в котором вы хотите создать свой метод действий:
![enter image description here]()
Когда вы отпустите, вы увидите это всплывающее окно:
![enter image description here]()
Если вы введете ту же информацию, что и показано, в файл будет введен следующий код:
@IBAction func onEnterInTextField(sender: NSTextField) {
}
И... связь между NSTextField и действием будет уже выполнена. (Вы также можете использовать эти шаги для создания и подключения IBOutlet.)
5) Внутри метода действий вы можете получить текущую выбранную строку, то есть только что отредактированную, из TableView:
@IBAction func onEnterInTextField(sender: NSTextField) {
let selectedRowNumber = tableView.selectedRow //tableView is an IBOutlet connected to the NSTableView
}
Затем я был озадачен тем, как получить текст выбранной строки в TableView, и обратно в документы, которые я просматривал, просматривая методы TableView и протокола. Но мы все знаем, как получить stringValue из NSTextField, не так ли? Отправитель - это NSTextField, который вы редактировали:
@IBAction func onEnterInTextField(sender: NSTextField) {
let selectedRowNumber = tableView.selectedRow //My Controller has an IBOutlet property named tableView which is connected to the TableView
if selectedRowNumber != -1 { //-1 is returned when no row is selected in the TableView
items[selectedRowNumber] = sender.stringValue //items is the data source, which is an array of Strings to be displayed in the TableView
}
}
Если вы не введете новое значение в источник данных, то в следующий раз, когда TableView должен отобразить строки, исходное значение снова отобразится, перезаписав отредактированные изменения. Помните, что TableView извлекает значения из источника данных - не NSTextField. Затем NSTextField отображает любое значение, которое TableView присваивает свойству cellValue ячейки.
Последняя вещь. У меня появилось предупреждение о том, что я не мог подключить NSTextField к действию внутри класса, если класс не был делегатом TableView.... так что я подключил выход делегата TableView к файловому владельцу:
![enter image description here]()
Ранее я установил File Owner как My Controller (= MainWindowController), поэтому после того, как я сделал это соединение, MainWindowController, содержащий метод действия для NSTextField, стал делегатом TableView, а предупреждение ушел.
Случайные советы:
1) Я нашел самый простой способ начать редактирование NSTextField - выбрать строку в TableView, а затем нажать Return.
2) NSTableView поставляется с двумя столбцами по умолчанию. Если вы выберете один из столбцов в контуре документа, нажмите "Удалить" на клавиатуре, вы можете создать одну таблицу столбцов, однако TableView по-прежнему показывает разделитель столбцов, поэтому похоже, что еще есть два столбца. Чтобы избавиться от разделителя столбцов, выберите Bordered Scroll View - Table View
в контуре документа, а затем перетяните один из углов, чтобы изменить размер TableView - один столбец мгновенно изменит размер, чтобы охватить все свободное пространство.
Кредит для шагов №1 и №2 и Случайный совет №2: Cocoa Программирование для OS X (5-е издание, 2015).
Полный список кодов:
//
// MainWindowController.swift
// ToDo
//
//import Foundation
import Cocoa
class MainWindowController: NSWindowController,
NSTableViewDataSource {
//@IBOutlet var window: NSWindow? -- inherited from NSWindowController
@IBOutlet weak var textField: NSTextField!
@IBOutlet weak var tableView: NSTableView!
var items: [String] = [] //The data source: an array of String's
override var windowNibName: String {
return "MainWindow"
}
@IBAction func onclickAddButton(sender: NSButton) {
items.append(textField.stringValue)
tableView.reloadData() //Displays the new item in the TableView
}
@IBAction func onEnterInTextField(sender: NSTextField) {
let selectedRowNumber = tableView.selectedRow
if selectedRowNumber != -1 {
items[selectedRowNumber] = sender.stringValue
}
}
// MARK: NSTableViewDataSource protocol methods
func numberOfRowsInTableView(tableView: NSTableView) -> Int {
return items.count
}
func tableView(tableView: NSTableView,
objectValueForTableColumn tableColumn: NSTableColumn?,
row: Int) -> AnyObject? {
return items[row]
}
}
Инспектор подключений, показывающий все подключения для File Owner (= MainWindowController):
![enter image description here]()