Увеличение высоты строки NSTableView

У меня есть NSTableView на основе представления в приложении MacOSX, которое хорошо структурирует данные. Я хотел бы реализовать NSTableView, чтобы высота строк росла вместе с содержимым данных, введенных в один из NSTextViews. Я создал подкласс NSTextView для «роста» с пользовательским текстом, но проблема в том, что наличие поля, встроенного в TableView, приводит к обрезанию поля.

Есть ли у кого-нибудь предложения о том, как реализовать растущий размер строки?


person user3932488    schedule 11.10.2014    source источник


Ответы (2)


Вам нужно реализовать -tableView:heightOfRow: в своем делегате табличного представления и вернуть соответствующую высоту для строки. Кроме того, вам необходимо отслеживать изменения высоты текстовых представлений и вызывать -noteHeightOfRowsWithIndexesChanged: в табличном представлении при изменении любого из них. Чтобы отслеживать высоту текстовых представлений, достаточно наблюдать за NSViewFrameDidChangeNotification, которые они будут публиковать.

(Если вы обычно используете автоматическую компоновку в своем пользовательском интерфейсе, я думаю, вам придется оставить текстовые представления с включенным translatesAutoresizingMaskIntoConstraints, разместить их вручную и установить их маски автоматического изменения размера соответствующим образом. Тогда вы не будете устанавливать для них какие-либо другие ограничения. , Это потому, что вам нужно, чтобы фрейм был установлен менеджером текстового макета, а не автоматическим макетом.)

person Ken Thomases    schedule 12.10.2014
comment
Спасибо, Кен, это огромная помощь. Я реализовал -tableView:heightOfRow: но у меня проблемы с мониторингом текстовых представлений. Игра с функциями autoLayout в IB дала мне расширяющийся textView, который мне был нужен (спасибо за это - гораздо более чистое решение, чем у меня изначально), но у меня все еще есть проблемы с мониторингом текстового представления. Не могли бы вы подробнее рассказать об использовании noteHeighOfRowsWithIndexesChanged: и NSViewFrameDidChangeNotification? - person user3932488; 12.10.2014
comment
Какой аспект вызывает у вас затруднения? Каждый раз, когда вы добавляете текстовое представление в таблицу, вы добавляете свой объект контроллера в качестве наблюдателя от NSViewFrameDidChangeNotification к [NSNotificationCenter defaultCenter] с текстовым представлением в объекте публикации. Затем в коде обработки уведомлений вы ищете, какая строка соответствует представлению, фрейм которого изменился (object уведомления), используя -[NSTableView rowForView:]. Затем вы вызываете -noteHeightOfRowsWithIndexesChanged: для табличного представления, передавая набор индексов, содержащий индекс строки. Прекратите наблюдение, когда удалите вид. - person Ken Thomases; 12.10.2014
comment
Хорошо, я не думаю, что понимаю это правильно. Я реализовал -noteHeightofRowsWithIndexesChanged: но, похоже, он не вызывается. Нужно ли инициализировать NSNotificationCentre в моем AppDelegate? - person user3932488; 12.10.2014
comment
Вы не реализуете -noteHeightOfRowsWithIndexesChanged:. Это метод на NSTableView. Вы вызываете этот метод, когда вам нужно сообщить представлению таблицы, что высота некоторых строк изменилась, чтобы оно знало, что нужно запросить у вашего делегата новую высоту. - person Ken Thomases; 12.10.2014
comment
Спасибо за помощь Кен. Я продолжу играть с ним и посмотреть, смогу ли я его решить. - person user3932488; 12.10.2014
comment
Я включил следующий код в windowControllerDidLoadNib [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(testMethod) name:NSViewFrameDidChangeNotification object:nil]; Всякий раз, когда NSTextField изменяется, уведомление отправляется и запускает testMethod. Однако похоже, что отправляются несколько уведомлений. Я предполагаю, что это связано с тем, что происходит несколько изменений кадров NSView. Как мне указать, что я хочу, чтобы NSNotificationCentre прослушивал только изменения в textView в моей таблице? - person user3932488; 12.10.2014
comment
Вы передаете текстовое представление для параметра object, где вы сейчас передаете nil. Кроме того, вы обычно должны использовать метод, который принимает один параметр типа NSNotification* для вашего метода наблюдения. Предполагая, что в вашей таблице есть более одного текстового представления, вам понадобится этот объект уведомления, чтобы определить, какой из них изменил размер. Наконец, вы, вероятно, захотите отслеживать последнюю известную высоту каждого текстового представления и, когда получите уведомление, сравнить ее с новой высотой. Вызовите -noteHeightOfRowsWithIndexesChanged: только в том случае, если высота действительно изменилась. - person Ken Thomases; 12.10.2014
comment
Теперь я понимаю вызовы NSNotificationCenter, но как мне определить объект (вместо нуля), если я не знаю, каково имя объекта? Например, NSTextFields будут созданы во время выполнения и зависят, например, от количества строк в TableView. Есть ли общее имя объекта, которое я передаю в NsNotificationCenter, или я совершенно неправильно понимаю параметр объекта? - person user3932488; 13.10.2014
comment
Если вы проходите nil, вы фактически запрашиваете наблюдение за уведомлением со всех без исключения представлений! Это не очень хорошая идея. Вы должны добавить наблюдение в тот момент, когда текстовое представление добавляется к представлению структуры. Например, добавьте его в -outlineView:didAddRowView:forRow: и удалите в -outlineView:didRemoveRowView:forRow:. - person Ken Thomases; 13.10.2014
comment
Что именно я должен указать в качестве параметра объекта, если текстовое поле создается во время разработки в InterfaceBuilder? - person user3932488; 15.10.2014
comment
Текстовое представление или текстовое поле. Вы сказали оба. Они разные. В любом случае, если текстовое представление является частью представления ячейки таблицы, то вы разрабатываете только прототип в IB. В любом случае можно создать розетку и подключить ее в ИБ, чтобы получить ссылку на текстовое представление в момент его добавления в представление схемы. Только не забудьте поставить розетку на соответствующий объект. Для текстового представления внутри представления ячейки таблицы это будет представление ячейки таблицы, поскольку для каждой ячейки есть отдельное представление. Вы можете использовать подкласс NSTableCellView, который имеет выход textView (или использовать textField, если он есть). - person Ken Thomases; 15.10.2014
comment
Это потрясающе. У меня почти получилось. Спасибо, Кен. Первоначально я использовал textView, но для простоты перешел на textField. Теперь у меня есть textField, который общается с моим делегатом, но по какой-то причине Центр уведомлений продолжает вызываться много раз. Я не совсем уверен, почему это так. Вы так много помогли, хотя - это очень ценится! - person user3932488; 15.10.2014

Мне удалось реализовать это в Swift 3 (с помощью этот отличный совет и это и < href="https://stackoverflow.com/a/32332743/372347">этот ТАК ответ):

введите здесь описание изображения

Убедитесь, что ячейка представления таблицы в NSTableView имеет подключение делегата к вашему контроллеру подкласса/представления, который использует протокол NSTextFieldDelegate.

Также дайте ему эти ограничения, чтобы убедиться, что он изменяет размер в соответствии с высотой строки:

введите здесь описание изображения

В делегате используйте этот код:

var editedString: String? = nil
var textCellForHeight: NSTextFieldCell = NSTextFieldCell.init()

func control(_ control: NSControl, textShouldBeginEditing fieldEditor: NSText) -> Bool {
    editedString = fieldEditor.string ?? ""
    return true
}

func control(_ control: NSControl, textShouldEndEditing fieldEditor: NSText) -> Bool {
    editedString = nil
    return true
}   

 func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
    if commandSelector == #selector(insertNewline(_:)) {
         textView.insertNewlineIgnoringFieldEditor(self)
         editedString = textView.string ?? ""
         //The NSAnimationContext lines get rid of the animation that normally happens when the height changes
         NSAnimationContext.beginGrouping()
         NSAnimationContext.current().duration = 0
         myTableView.noteHeightOfRows(withIndexesChanged: IndexSet.init(integer: selected))
         NSAnimationContext.endGrouping()
         myTable.needsDisplay = true
         return true
    }
    return false
}

func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
    if let temp = editedString { //we know it’s currently being edited if it’s not nil
        textCellForHeight.stringValue = temp
        //in my case, there was a specific table column that I knew would be edited
        //you might need to decide on the column width another way.
        let column = myTable.tableColumns[myTable.column(withIdentifier: “TheColumnThatsBeingEdited”)]
        let frame: NSRect = NSMakeRect(0, 0, column.width, CGFloat.greatestFiniteMagnitude)
        return textCellForHeight.cellSize(forBounds: frame).height
    }
  return yourStandardHeightCGFloat
}
person Todd    schedule 03.06.2017