NSTableView old selection - macos

How do I get the previous selected index of row in NSTableView
The methods
func tableViewSelectionIsChanging(notification: NSNotification) {
let index = (notification.object as! NSTableView).selectedRow
println(index)
}
func tableViewSelectionDidChange(notification: NSNotification)
let index = (notification.object as! NSTableView).selectedRow
println(index)
}
Both methods print same value. How do I get the index / row which is going to change due to selection ?
Or in simple terms, how do I make a statement like
println("\(Selection changed from \(tableView.oldSelectedIndex) to \(tableView.newSelectionIndex)")

Before a cell is selected, the function func tableView(tableView: NSTableView, shouldSelectRow row: Int) is called on the delegate. Here, you can see both the currently selected row, and the proposed selection.
func tableView(tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
// This is the next index
NSLog("Next index: \(row)")
// Make sure that a row is currently selected!
guard self.tableView.selectedRowIndexes.count > 0 else {
return true
}
// Get the currently selected row.
let index = self.tableView.selectedRow
NSLog("Previous index: \(index)")
return true
}
NB: This doesn't exactly answer your question, because you've asked for the change after the new selection has occurred, whereas this gives it just before. I hope nonetheless that this is sufficient.

You can get it by
tableView.selectedRow

Related

NSTableView Change cell color when selected

This is a macOS question
In
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?
I'm using
func tableViewSelectionDidChange(_ notification: Notification) {
cell.layer?.backgroundColor = mintGreen.cgColor
to change the cell color.
Is it possible to change the color when the cell is selected?
Thanks
Did you ever get anywhere with this?
Realise this is an old question now, but I couldn't find a solution to this problem documented anywhere, so this is what I've come up with:
This code assumes an NSTableView with one column and therefore that the cell you are interested is in the zero indexed column. It also assumes a reference to the NSTableView on the delegate of 'theTableView'.
func tableViewSelectionDidChange(_ notification: Notification) {
// You'd probably want to handle this else statement more elegantly
guard let selected = theTableView?.selectedRow else { return }
if (selected != -1) {
if let numberOfRows = theTableView?.numberOfRows {
let sequenceOfRows = 0..<numberOfRows
for row in sequenceOfRows.reversed() {
if row == selected {
if let cell = theTableView?.view(atColumn: 0, row: selected, makeIfNecessary: false) {
cell.layer?.backgroundColor = NSColor.green.cgColor
}
} else {
if let cell = theTableView?.view(atColumn: 0, row: Int(row), makeIfNecessary: false) {
cell.layer?.backgroundColor = NSColor.clear.cgColor
}
}
}
}
} else {
// No row is selected, so iterate over all and revert background to .clear
if let numberOfRows = theTableView?.numberOfRows {
let sequenceOfRows = 0..<numberOfRows
for row in sequenceOfRows {
if let cell = theTableView?.view(atColumn: 0, row: Int(row), makeIfNecessary: false) {
cell.layer?.backgroundColor = NSColor.clear.cgColor
}
}
}
}
}
This works, and technically answers the question, but feels really horrible, having to iterate over all the rows to assess which background colour to change, and to change all the rows back again when the row selection changes or the row is deselected. I can't help feel there must be many more elegant or efficient ways to do this, but as I say can't find any documented myself.
Would be grateful if anyone else could update if there is another more succinct way of doing this, or if there is another pattern aside from the NSTableView delegates to achieve this.
Have you tried setting the row view using a subclass of NSTableRowView. You can override drawing in the subclass. or simply set the rows background colour based on the item.
func outlineView(_ outlineView: NSOutlineView, rowViewForItem item: Any) -> NSTableRowView? {
switch (item as! NSTreeNode).representedObject {
case _ as FixtureNode:
return OSFixtureTableHeaderRowView()
default:
return nil
}
}
It took me a long time to figure this out so for anyone interested here is a short clip showing a custom outline view that shows grouped items in a box in the list view. I haven't figured out how to determine when the outline view loses focus to change the selection colours accordingly but for this application it's better that the user can easily see the current selection so there is no immediate requirement but if anyone figures that out let us know. Happy to post more details if anyone need help.
Custom NSOutlineView

Confused about NSTableView Identifier on Table Cell View

I am able to create simple view based NSTableViews but there's one point I don't understand about identifiers.
In an NSTableView you typically give a column an identifier and then implement the delegate method:
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?
And then you switch on the column to do what you need, something like:
switch tableColumn!.identifier {
case "firstColumn":
//do something...
case "secondColumn":
//do something else...
default:
return nil
}
However additionally you can give each Table Cell View an identifier as well. So in the above example, say I didn't give the identifier to the column, and instead gave the identifier to the Table Cell View itself.
I presumed that then I could do something like this in the delegate method:
if let firstColumnCellView = tableView.make(withIdentifier: "firstColumnCell", owner: self) as? NSTableCellView {
view.textField?.stringValue = "Hi! I'm in the first column"
return view
} else if let secondColumnCellView = tableView.make(withIdentifier: "secondColumnCell", owner: self) as? NSTableCellView {
view.textField?.stringValue = "Hi! I'm in the second column"
return view
} else {
return nil
}
This works, but never makes it past the first if let statement, and so all my cells say "Hi! I'm in the first column"
More Info:
Something else I don't understand: it seems that the Table Cell View identifier overrides the identifier to the column.
If I go to the document outline and assign identifiers something like this:
tableColumn: "firstColumn"
tableViewCell: "firstColumnCell"
tableColumn: "secondColumn"
tableViewCell: "secondColumnCell"
and then supply both the column identifier and the cell identifier, it works!
switch tableColumn!.identifier {
case "firstColumn":
if let firstColumnCellView = tableView.make(withIdentifier: "firstColumnCell", owner: self) as? NSTableCellView {
view.textField?.stringValue = "Hi! I'm in the first column"
return view
} else {
return nil
}
case "secondColumn":
if let secondColumnCellView = tableView.make(withIdentifier: "secondColumnCell", owner: self) as? NSTableCellView {
view.textField?.stringValue = "Hi! I'm in the second column"
return view
} else {
return nil
}
default:
return nil
}
But it crashes if I allow the switch statement to ignore the cell identifier for the second column, and fall through to trying to use the column identifier.
switch tableColumn!.identifier {
case "firstColumn":
if let firstColumnCellView = tableView.make(withIdentifier: "firstColumnCell", owner: self) as? NSTableCellView {
view.textField?.stringValue = "Hi! I'm in the first column"
return view
} else {
return nil
}
default:
break
}
let cellView = tableView.make(withIdentifier: tableColumn!.identifier, owner: self) as! NSTableCellView
cellView.textField?.stringValue = "hello"
return cellView
//CRASH: Unexpectedly found nil when unwrapping tableColumn!.identifier
// The column both exists and has an identifier of "secondColumn", so how could
//this be nil?
And it seems I can confirm this overriding behavior by renaming the secondColumnCell to the same name as the secondColumn:
tableColumn: "firstColumn"
tableViewCell: "firstColumnCell"
tableColumn: "secondColumn" <--- Same Name
tableViewCell: "secondColumn" <-- Same Name
And now the code runs as expected and doesn't crash.
If I read your last chunk of code correctly, you instantiate (or retrieve from a used-views-no-longer-on-screen pool - see here) a view with the identifier firstColumnCell.
As long as the identifier is valid (you have somewhere a nib defining the view) the method will always return a non-nil view so the first if let ... will always succeed.
So the view.textField?.stringValue = "Hi! I'm in the first column" will execute thus showing the message in the cell and then it will return the view to be used by the NSTableView and exit your method.
The next if let ... statements will never have a chance to execute.

Select and return selection of multiple rows in a single column Swift OSX table [duplicate]

I have a NSTableView with one column. I would like to print the row number of the row that the user has clicked on. I am not sure where I should start with this. Is there a method for this?
You can use the selectedRowIndexes property from the tableView in the tableViewSelectionDidChange method in your NSTableView delegate.
In this example, the tableView allows multiple selection.
Swift 3
func tableViewSelectionDidChange(_ notification: Notification) {
if let myTable = notification.object as? NSTableView {
// we create an [Int] array from the index set
let selected = myTable.selectedRowIndexes.map { Int($0) }
print(selected)
}
}
Swift 2
func tableViewSelectionDidChange(notification: NSNotification) {
var mySelectedRows = [Int]()
let myTableViewFromNotification = notification.object as! NSTableView
let indexes = myTableViewFromNotification.selectedRowIndexes
// we iterate over the indexes using `.indexGreaterThanIndex`
var index = indexes.firstIndex
while index != NSNotFound {
mySelectedRows.append(index)
index = indexes.indexGreaterThanIndex(index)
}
print(mySelectedRows)
}
Use -selectedRowIndexes
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSTableView_Class/#//apple_ref/occ/instp/NSTableView/selectedRowIndexes
Then you can use those indexes to grab the data from your dataSource
(typically an array)

Present dictionary in several NSTableviews

I'm a beginner to cocoa and I've been trying to make a simple app for Mac using swift programming language, but I'm stuck and can't find a solution.
I want to present a data from dictionary in two or more tableViews, where the first table will show key, and the second table will show value.
For example, I have a dictionary
var worldDict:NSDictionary = ["Africa":["Egypt", "Togo"],"Europe": ["Austria", "Spain"]]
I can present all continents in the first table, but I can't find out how to make second table to display countries from continent I choose in the first table.
My ViewController is a DataSource and Delegate for both tables.
extension ViewController: NSTableViewDataSource {
func numberOfRowsInTableView(tableView: NSTableView) -> Int {
if tableView == continentTable {
return self.worldDict.valueForKey("Continent")!.count
} else if tableView == countryTable {
return self.worldDict.valueForKey("Continent")!.allKeys.count
}
return 0
}
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? {
var cell = tableView.makeViewWithIdentifier(tableColumn!.identifier, owner: self) as! NSTableCellView
if tableView == self.continentTable {
let continent: AnyObject? = wordlDict.valueForKey("Continent")
var keys = continent!.allKeys
cell.textField?.stringValue = keys[row] as! String
} else if tableView == self.countryTable {
var countriesOfContinent: AnyObject? = worldDict.valueForKey("Continent")?.valueForKey("Africa")!
cell.textField?.stringValue = countriesOfContinent?.allKeys[row] as! String
}
return cell
}
}
Here I present data from dictionary in tables, but separately, and can't figure out how to make them work together.
Also I know how to get the number of row that has been selected
extension ViewController: NSTableViewDelegate {
func tableViewSelectionDidChange(notification: NSNotification) {
let continentSelected = rowSelected()
}}
func rowSelected() -> Int? {
let selectedRow = self.continentTable.selectedRow
if selectedRow >= 0 && selectedRow < self.worldDict.valueForKey("Continent")!.count {
return selectedRow
}
return nil
}
Part of the problem is that you're relying on the ordering of the keys returned by allKeys() to be reliable, which it's not. You need to keep a separate array of continents. It can basically be a copy of whatever allKeys() returned on one occasion, but you should not keep calling allKeys() each time.
In numberOfRowsInTableView(), for the countries table, you want to return the number of countries in the selected continent:
} else if tableView == countryTable {
if let selectedContinentRow = rowSelected() {
let selectedContinent = continentsArray[selectedContinentRow]
return self.worldDict[selectedContinent].count
}
return 0
}
For tableView(_:viewForTableColumn:row:), you want to return an element from the selected continent's array of countries:
} else if tableView == self.countryTable {
if let selectedContinentRow = rowSelected() {
let selectedContinent = continentsArray[selectedContinentRow]
return self.worldDict[selectedContinent][row]
}
}
Also, whenever the selected continent changes, you need to tell the countries table to reload its data:
func tableViewSelectionDidChange(notification: NSNotification) {
// ... whatever else ...
let tableView = notification.object as! NSTableView
if tableView == continentTable {
countryTable.reloadData()
}
}

Setting NSTableView cell text

I have a NSTableView that I want to populate with 20 cells, each of them will say "Test". I'm fluent with UITableView's but not so much with NSTableViews, so I went hunting online to figure this out. Oh, how confused that made me! I understand that I need to use the numberOfRowsInTableView function, but how do I set the text of the cell? Each source I find seems to do everything in a different way. For instance, this site uses:
func tableView(tableView: NSTableView!, viewForTableColumn tableColumn: NSTableColumn!, row: Int) -> NSView! {
// 1
var cellView: NSTableCellView = tableView.makeViewWithIdentifier(tableColumn.identifier, owner: self) as NSTableCellView
// 2
if tableColumn.identifier == "BugColumn" {
// 3
let bugDoc = self.bugs[row]
cellView.imageView!.image = bugDoc.thumbImage
cellView.textField!.stringValue = bugDoc.data.title
return cellView
}
return cellView
}
I tried that but I got an error - the code found nil while unwrapping an optional. Then I tried what I found here:
func tableView(tableView: NSTableView, dataCellForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSCell? {
if tableColumn == nil { return nil }
if tableColumn!.identifier != "right" { return nil }
let cell = NSPopUpButtonCell()
cell.bordered = false
cell.menu!.addItemWithTitle("one", action: nil, keyEquivalent: "")
cell.menu!.addItemWithTitle("two", action: nil, keyEquivalent: "")
cell.menu!.addItemWithTitle("three", action: nil, keyEquivalent: "")
cell.selectItemAtIndex(1) // <--- obviously ignored ?!
return cell
}
So my question is, how do I set the cell text? How do the two examples I've inserted above differ in what they do? Please, make some sense of this - cause I sure can't!
-Thanks,
A confused CodeIt
P.S.
I've looked at several other sources except the two I named above. I'm just plain confused..
Edit:
The found nil while unwrapping an Optional error I mentioned in the first example is found on this line:
var cellView: NSTableCellView = tableView.makeViewWithIdentifier(tableColumn.identifier, owner: self) as NSTableCellView
// Get an existing cell with the MyView identifier if it exists
var cellView?: NSTableCellView = tableView.makeViewWithIdentifier("someIdentifier", owner: self) as NSTableCellView
// There is no existing cell to reuse so create a new one
if cellView == nil {
cellView = NSTableCellView(frame: NSRect())
// The identifier of the NSTextField instance is set to someIdentifier.
// This allows the cell to be reused.
cellView.identifier = "someIdentifier
}
This should give you the cell, and you can proceed.
See Apple Doc for more

Resources