I'm trying to craft a view based tableView delegate method to alter a column's font IFF its identifier is 'name' and its value matches some global; otherwise the the nib should prevail. I think I have either a noob setup issue or my logic is omitting something.
I have two tables, one view based (tag=0), one cell (tag=1), and both have the same view controller as their delegate. Each tableView's has a unique tag and identifier, an array controller for column value bindings, and each of fits columns has an explicit unique identifier - matching its binding; I had tried omitting this - "the automatic setting" - but this yielded no data!?
So, I added this method, but both tableViews call into it; I would expect only the first. The 2nd table is a detail of the 1st, having its 1st array controller selection.
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let item = ((tableView.dataSource as! NSArrayController).arrangedObjects as! [AnyObject])[row]
var view = tableView.makeView(withIdentifier: tableColumn!.identifier, owner: self)
if view == nil {
Swift.print("tag: \(tableView.tag) ident: \(tableColumn!.identifier.rawValue)")
view = NSTableCellView.init()
view?.identifier = tableColumn?.identifier
}
guard tableView.tag == 0 else { return view }
guard isGlobalPlist, item.name == '<some-global-string>',
let identifier = tableColumn?.identifier,
identifier.rawValue == "name" else { return view }
if let cellView = view as? NSTableCellView {
if tableView.selectedRowIndexes.intersection(IndexSet.init(integer: row)).count > 0 {
cellView.textField?.font = .boldSystemFont(ofSize: -1)
cellView.textField?.textColor = .white
}
else
{
cellView.textField?.font = .systemFont(ofSize: -1)
cellView.textField?.textColor = .blue
}
}
return view
}
Firstly I notice that the method is called for the 2nd table when I wouldn't have expect it, it being a cell based tableView.
Anyway, I do create the cell when nil return, the docs cite that you need just need to matchup the identifiers:
Note that a cell view’s identifier must be the same as its table column’s identifier for bindings to work. If you’re using bindings, it’s recommended that you use the Automatic identifier setting in Interface Builder.
which I do; both tableView's columns are bound (view, or cell) to their respective array controller using column identifier explicitly entered.
Anyway the original intent I was after was for the view based (1st) tableView:
the view controller was controlling a global resource (isGlobalPlist is true)
the row's 'name' value matches a global value value
to the affect 'name' cell shown in distinct color (blue/yellow - or something) depending on whether the row was selected due to selection highlighting, dark mode etc conditions.
So there would be 1 of 4 possibilities
selected row, matching global -> fore:yellow, back:default
selected row, not matching global -> fore:default, back:default
non-selected row, matching global -> fore:blue, back:default
non-selected row, not matching global -> fore:default, back:default
If the target row matched requirements, the coloring needs to vary depending on its inclusion in the tableView's selected indexes.
The setting seems to properly affect initially - selected row and matching value, but then settings seem to bleed across other rows navigated to - so the selection change. At most there should be 1 row's name column with this affect.
I can't tell if my logic or setup is wrong; I suspect I probably need to refresh as I move about ?
Well I gave up on a view based solution but this fills my need; a cell based solution:
func tableView(_ tableView: NSTableView, dataCellFor tableColumn: NSTableColumn?, row: Int) -> NSCell? {
guard let column = tableColumn else { return nil }
let item : AnyObject = ([table0ArrayController,table1ArrayController][tableView.tag]?.arrangedObjects as! [AnyObject])[row]
let cell : NSTextFieldCell = column.dataCell(forRow: row) as! NSTextFieldCell
cell.textColor = .textColor
guard tableView.tag == 0, isGlobalPlist, item.name == '<some-global-string>' else { return cell }
if tableView.selectedRowIndexes.intersection(IndexSet.init(integer: row)).count > 0 {
cell.textColor = .white
}
else
{
cell.textColor = .blue
}
return cell
}
I wanted to call attention to specific row(s) with color, unless row was selected for the 'name' column only.
Related
I'm working on an app with NSOutlineView.
The outline view is view-based and is initialized programmatically like:
internal lazy var outlineView: NSOutlineView = {
let outlineView = NSOutlineView()
outlineView.usesAutomaticRowHeights = true
let column = NSTableColumn(identifier: .listOutlineColumn)
outlineView.addTableColumn(column)
outlineView.outlineTableColumn = column
outlineView.headerView = nil
outlineView.backgroundColor = .clear
outlineView.indentationPerLevel = 16
outlineView.allowsEmptySelection = false
outlineView.refusesFirstResponder = true
outlineView.style = .sourceList
return outlineView
}()
The problem is when I'm editing a row like List 11, if I tap the row New List 2 the new row is in editing mode immediately (and all text is selected).
What I want is just to end the editing and select the new row.
Could somebody tell me how to do this?
Found the answer, if the refusesFirstResponder of outline view is set to false, this property of NSTextField inside the NSTableCellView should also be set to false.
And if a customized cell of NSTextField is used, this refusesFirstResponder property should also be set.
I have a search field (NSTextField) called searchField and when you type in it, it refreshes the data shown in a NSTableView. The problem is that this refresh also triggers the selection of a table row, and that takes the focus out of the NSTextField.
The user types in the search field:
func controlTextDidChange(_ obj: Notification) {
if let field = obj.object as? NSTextField, field == searchField{
refreshData()
}
}
Then the NSTableView gets reloaded here:
func refreshData(){
//Process search term and filter data array
//...
//Restore previously selected row (if available)
let index = tableView.selectedRow
tableView.reloadData()
//Select the previously selected row
tableView.selectRowIndexes(NSIndexSet(index: index) as IndexSet, byExtendingSelection: false)
}
If I comment-out both the reloadData() and the selectRowIndexes then the search field behaves as intended (I can keep typing and it keeps the focus in the field). But if include either or both of those methods, the search field loses focus after I type the first character and refreshData() is called.
How can I keep focus in my search field and not let the table reload hijack the focus?
Ugh... it turns out I could never get the NSTableView to let go of the focus because it wasn't set to allow an Empty selection state. Checking a box in Interface Builder fixed it.
I have subclassed NSTableView and NSTableColumn, and I want to control processus of edition of the cells of the table.
I have 2 states for my table class: edition and nonedition.
whatever the state of the table, when the user select a row, the row has to be overlighted.
if the table is in state edition, the controls corresponding to the selected row must be visible.
I use the function rowview to make this possible, but I have no success.
1 when I click on the text cell of a cell, the control appears always.
2 when the rowview function is called, I don't understand the value of the row parameter. Sometimes it is correct, sometimes it is incorrect.
is it someone who could hep me?
I want to make precision: to simplyfy, in my subclassed NSTableView, I write this code:
override func rowView(atRow row: Int, makeIfNecessary: Bool) -> NSTableRowView? {
let aRow = super.rowView (atRow: row, makeIfNecessary: makeIfNecessary)
if aRow != nil {
Swift.print("rowview is called for row (row)")
}
return aRow
}
When I click on row 0, I have the following result: "rowview is called for row 1"
Then when I click on row 1: "rowview is called for row 1 rowview is called for row 0 rowview is called for row 2"
i have two table views where you can drag a row from table A to table B.
this works fine.
but i need to know, which row number will dragged from table A.
This is my code of table A and the problem, too:
func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool {
pboard.declareTypes([NSStringPboardType],owner: nil)
pboard.setString(persons[0].objectID.uriRepresentation().absoluteString, forType: NSStringPboardType)
return true
}
at the moment i always dragged the first element of "persons"
i need a solution where i can set a row number (or something like this) instead of the static "persons[0]".
is there an way for this?
Have you tried persons[rowindexes.first] ?
I'm having trouble accessing an optional string in swift from a table view cell. Each cell has a title label as well as a detail text label and the amount of cells at any given time in the table view is dependent on the user (the table view is used to display saved values). Since the strings saved as the detail text label's text in each cell are way too long to read in the table view, I wish to load them in another view that is segued to when a cell is pressed. This is my prepare for segue method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "cellSegue"
{
let cell = tableView.cellForRowAtIndexPath(tableView.indexPathForSelectedRow()!)
var transfer : ExplanationView = segue.destinationViewController as ExplanationView
if let unwrapped = cell?.textLabel?.text!
{
transfer.infoText.text = unwrapped
}
}
}
When a segue is performed, I get a fatal crash, the offending line is "transfer.infoText.text = unwrapped" and Xcode tells me it found nil when unwrapping an optional. During my debuggingg efforts, I've tried "println(cell?.textLabel?.text!)" and Xcode prints "Optional(String)" where "String" is the actual string I'm trying to access so it seems like I'm on the right track, but obviously there's something I'm missing. I've also tried "cell?.textLabel?.text" but I get the same error. Any help is appreciated
You shouldn't ever extract data from a view. You should extract data from a model. The fact that you have a tableView with cells implies you have a model for the data that goes in those cells. It might be an array of strings or whatever. Instead of trying to extract the data from the cells, just get the data from the data model you used to populate the cells. Something like:
let selectedIndexPath = tableView.indexPathForSelectedRow()
let selectedData = data[selectedIndexPath.row]
Your problem is not unwrapped but instead transfer.infoText.text. Your IBOutlets are not set up at the time of the prepareForSegue. You need add a property (var) to your destination view controller to hold the unwrapped value. Then in viewDidLoad when the IBOutlets are set up, copy the string to your text field.