Увеличаване на височината на реда на 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. Във всеки случай можете да създадете изход и да го свържете в IB, за да получите препратка към текстовия изглед в момента, в който бъде добавен към изгледа на схемата. Просто не забравяйте да поставите изхода на съответния обект. За текстов изглед в изглед на клетка на таблица, това би бил изгледът на клетка на таблица, тъй като има отделен изглед на клетка. Можете да използвате подклас на NSTableCellView, който има textView изход (или да използвате textField, ако има). - person Ken Thomases; 15.10.2014
comment
Това е страхотно. До голяма степен успях. Благодаря Кен. Първоначално използвах textView, но за простота го промених на textField. Сега имам textField, който комуникира с моя делегат, но по някаква причина Центърът за уведомяване продължава да се извиква многократно. Не съм съвсем сигурен защо е така. Все пак бяхте толкова много полезни - това е високо оценено! - person user3932488; 15.10.2014

Успях да внедря това в Swift 3 (с помощта на този страхотен съвет и това и този SO отговор):

въведете описание на изображението тук

Уверете се, че клетката за изглед на таблица в 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