Returning an NSTableView's Row Index By Hovering Over It in Swift 4 - swift4

I can't seem to figure out a way to return an NSTableView's row index by hovering my mouse over it. For example, when I hover over a row the index is printed out. Does anyone have any idea on how to achieve this? Thank you.
Here's my code:
import Cocoa
class ViewController: NSViewController {
let items = ["item 1","item 2","item 3","item 4","item 5"]
#IBOutlet var tableView: NSTableView!
override func viewDidLoad() {
super.viewDidLoad()
// setting up
tableView.delegate = self
tableView.dataSource = self
tableView.reloadData()
}
}
extension ViewController: NSTableViewDelegate, NSTableViewDataSource {
// tableView Data
func numberOfRows(in tableView: NSTableView) -> Int {
return items.count
}
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
return items[row]
}
}

Related

NSTableCellView not displaying its stringValue

The cells themselves appear with the amount of items in the items array (I can select them), but they don't have the title assigned to them.
...
let items: Array<String>? = // contains only strings (also without control characters)
#IBOutlet weak var tableView: NSTableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
}
func numberOfRows(in tableView: NSTableView) -> Int {
return self.items!.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
if let cell = tableView.makeView(withIdentifier: tableColumn!.identifier, owner: self) as? NSTableCellView {
cell.textField?.stringValue = self.items![row]
return cell
}
return nil
}
...
For some reason I had to select the table view on the view hierarchy and set the content mode to cell based and back to view based. Now it works.

MacOS Swift NSTableView with NSTokenFieldCell tracking which column drag started from

I have a simple test program that has a 2 column NSTableView (View based), both columns have their cells set to NSTokenFieldCell.
I also have a NSTokenField in this controller
I want to be able to drag and drop tokens from the table to the field.
When I have 1 column this is working well.
Now I have 2 columns I need to know which token to add to the field
Sample code:
class ViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate, NSTokenFieldDelegate {
#IBOutlet weak var tableView: NSTableView!
#IBOutlet weak var tokenField: NSTokenField!
let tokens = ["tok1","tok2","tok3","tok1","tok2","tok3","tok1","tok2","tok3","tok1","tok2","tok3","tok1","tok2","tok3","tok1","tok2","tok3"]
let tokens2 = ["image","date","time","folder"]
override func viewDidLoad() {
super.viewDidLoad()
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
func numberOfRows(in tableView: NSTableView) -> Int {
return max(tokens.count,tokens2.count)
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
guard let vw = tableView.makeView(withIdentifier: tableColumn!.identifier, owner: self) as? NSTableCellView else {
return nil
}
if tableColumn?.title == "Tokens" {
vw.textField?.stringValue = tokens[row]
}
else
{
if row < tokens2.count {
vw.textField?.stringValue = tokens2[row]
}
else {
vw.textField?.stringValue = ""
}
}
return vw
}
// The bit where I need help
func tableView(
_ tableView: NSTableView,
pasteboardWriterForRow row: Int)
-> NSPasteboardWriting?
{
return tokens[row] as NSString //Here I need to return tokens2[row] if a token from column 2 was dragged and dropped
}
}
What I can't figure out is which column gets the click to initiate the drag and drop
tableView.clickedColumn is always -1
None of the other functions such as this fire
func tableView(_ tableView: NSTableView, updateDraggingItemsForDrag draggingInfo: NSDraggingInfo)
Update:
I can get this function to fire as soon as I start the drag operation:
func tableView(_ tableView: NSTableView, draggingSession session: NSDraggingSession, willBeginAt screenPoint: NSPoint, forRowIndexes rowIndexes: IndexSet)
I've tried to capture mouseDown but am not having any luck with it either.
Any pointers?
The NSTokenFieldCell didn't have its property set to selectable once set the NTTokenFieldCell and the NSTokenField worked as desired.

Item Selection Event for NSTableView not triggered

I have a scrollview containing a table column.
I need to display list of added files.This is achieved as follows
#IBOutlet weak var tview: NSTableView!
self.tview.delegate = self as? NSTableViewDelegate
self.tview.dataSource = self
extension ViewController:NSTableViewDataSource{
func numberOfRows(in tableView: NSTableView) -> Int {
return fileArray.count
}
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
return fileArray[row].path
}
}
How can i hook to the event when user clicks on a filename? I have tried adding
func tableViewSelectionDidChange(_ notification: Notification)
{
}
In the extention method... but the breakpoint is not triggered when selection changes.

Can not make NSTableView work in Swift3

I am having problems getting NSTableView to work with Swift 3 and XCode 8. I have an app with one table and one column in it and trying to add items from NSMutableArray. Here is my code :
import Cocoa
class ViewController: NSViewController {
#IBOutlet weak var tableView: NSTableView!
var objects: NSMutableArray! = NSMutableArray()
override func viewDidLoad() {
super.viewDidLoad()
self.objects.add("one")
self.objects.add("two")
self.objects.add("three")
self.objects.add("four")
self.tableView.reloadData()
}
func numberOfRowsInTableView(tableView: NSTableView) -> Int
{
return self.objects.count
}
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any?
{
let cellView = tableView.make(withIdentifier: "cell", owner: self) as! NSTableCellView
cellView.textField!.stringValue = self.objects.object(at: row) as! String
return cellView
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
However this gives me an error:
*** Illegal NSTableView data source (<NSView: 0x610000120820>). Must implement numberOfRowsInTableView: and tableView:objectValueForTableColumn:row
It looks like boths methods are implemented so I have no idea why I'm getting this error.
The dataSource and the delegate for the table view are both set to the man View.
Your numberOfRows method signature is incorrect for the current macOS SDK. It should be:
func numberOfRows(in tableView: NSTableView) -> Int
As pbush25 recommended I had to set the delegate and datasource in the code. Also, I switched the TableView mode to 'cell based' and then the final code looks like this:
import Cocoa
class ViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
#IBOutlet weak var tableView: NSTableView!
var objects: NSMutableArray! = NSMutableArray()
override func viewDidLoad() {
super.viewDidLoad()
self.objects.add("one")
self.objects.add("two")
self.objects.add("three")
self.objects.add("four")
tableView.delegate = self
tableView.dataSource = self
}
func numberOfRows(in tableView: NSTableView) -> Int
{
return self.objects.count
}
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any?
{
return self.objects.object(at: row)
}
override var representedObject: Any? {
didSet {
}
}
}

Multiple NSTableViews on one View Controller

I want to have multiple NSTableViews on one ViewController. I have implemented the first TableView with the following code:
class MainViewController: NSViewController
{
#IBOutlet weak var tableView: NSTableView!
override func viewDidLoad()
{
super.viewDidLoad()
let nib = NSNib(nibNamed: "MyCellView", bundle: NSBundle.mainBundle())
tableView.registerNib(nib!, forIdentifier: "MyCellView")
let appDelegate = NSApplication.sharedApplication().delegate as! AppDelegate
appDelegate.view = self
}
}
extension MainViewController: NSTableViewDataSource, NSTableViewDelegate
{
func numberOfRowsInTableView(tableView: NSTableView) -> Int
{
return 5
}
func tableView(tableView: NSTableView, heightOfRow row: Int) -> CGFloat
{
return 25
}
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView?
{
let cell = tableView.makeViewWithIdentifier("MyCellView", owner: self) as! MyCellView
cell.itemName.stringValue = "row text"
return cell
}
}
This NSTableView is working fine but it is obviously tied closely with the view controller. What is the best way of adding a second NSTableView?
Finally found out how to do this. This is a much nicer approach than putting the NSTableView in the main controller in my opinion.
Each table is defined with its own class:
import Cocoa
class Table1: NSTableView, NSTableViewDataSource, NSTableViewDelegate
{
override func drawRect(dirtyRect: NSRect)
{
super.drawRect(dirtyRect)
}
func numberOfRowsInTableView(tableView: NSTableView) -> Int
{
return 10
}
func tableView(tableView: NSTableView, heightOfRow row: Int) -> CGFloat
{
return 50
}
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView?
{
let cell = tableView.makeViewWithIdentifier("MyCellView", owner: self) as! MyCellView!
cell.itemName.stringValue = "hello"
cell.itemDescription.stringValue = "This is some more text"
return cell
}
}
Table 2 is the same, but can obviously have different data etc.
My tables were View Based so here is the cell view class definition:
import Cocoa
class MyCellView: NSTableCellView
{
#IBOutlet weak var itemName: NSTextField!
#IBOutlet weak var itemDescription: NSTextField!
}
Here is the View Controller method:
import Cocoa
class ViewController: NSViewController
{
var dataSource1 : Table1!
var dataSource2 : Table2!
#IBOutlet weak var table1: NSTableView!
#IBOutlet weak var table2: NSTableView!
override func viewDidLoad()
{
super.viewDidLoad()
self.dataSource1 = Table1()
table1.setDataSource(self.dataSource1)
table1.setDelegate(self.dataSource1)
self.dataSource2 = Table2()
table2.setDataSource(self.dataSource2)
table2.setDelegate(self.dataSource2)
let nib = NSNib(nibNamed: "MyCellView", bundle: NSBundle.mainBundle())
table1.registerNib(nib!, forIdentifier: "MyCellView")
table2.registerNib(nib!, forIdentifier: "MyCellView")
}
}
That depends on what do you mean with "best". The easiest is to just reason based on the tableView parameter in the callbacks, i.e.
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView?
{
if(tableView == self.tableView1)
{
}
else if(tableView == self.tableView2)
{
}
}
But I'd suggest to create separate classes to be used as datasources. That classes would implement UITableViewDataSource protocol. You create two instances of that class and tie your table views accordingly.