View-Based NSTableView in Swift - How to - tableview

I have a NSTableView whose cells are view-based.
DataSource & Delegate are connected, but I'm not able to display the cell's textField string value.
This is the code in Objective-C, working:
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return 10;
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSTableCellView *cell = [tableView makeViewWithIdentifier:#"List" owner:self];
[cella.textField setStringValue:"Hey, this is a cell"];
return cell;
}
And here is my code in Swift, not working :
func numberOfRowsInTableView(aTableView: NSTableView!) -> Int
{
return 10 //Casual number
}
func tableView(tableView: NSTableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> NSTableCellView! {
var cell = tableView.makeViewWithIdentifier("List", owner: self) as NSTableCellView!
// setup cell without force unwrapping it
cell.textField.stringValue = "Hey, this is a cell"
println("Method called") //Never printed
return cell
}
This is the result: (table on right side of image)
Note that the comment //setup cell without force unwrapping it makes no sense, I forgot to delete it.
What I am missing ?
Edit: I tried even the following with no success:
func numberOfRowsInTableView(aTableView: NSTableView!) -> Int
{
return 10
}
func tableView(tableView: NSTableView!, objectValueForTableColumn tableColumn: NSTableColumn!, row: Int) -> AnyObject
{
var cell = tableView.makeViewWithIdentifier("List", owner: self) as NSTableCellView
cell.textField.stringValue = "Hey this is a cell"
return cell;
}
Thank you all.
Alberto

After hours of search, I discovered this method that works !
func tableView(tableView: NSTableView, viewForTableColumn: NSTableColumn, row: Int) -> NSView
{
var cell = tableView.makeViewWithIdentifier("List", owner: self) as NSTableCellView
cell.textField.stringValue = "Hey, this is a cell"
return cell;
}

I see that you found your answer your self but from what I can see your clue was in the Return Value of Objective -C delegate.
- (NSView *)tableView:...
The return value is a NSView.
But you should look at the Swift/Objective -c documentaion.
From the Docs:
Providing Views for Rows and Columns
tableView:viewForTableColumn:row:
Asks the delegate for a view to display the specified row and column.
Declaration
SWIFT
#optional func tableView(_ tableView: NSTableView!,
viewForTableColumn tableColumn: NSTableColumn!,
row row: Int) -> NSView!
OBJECTIVE-C
- (NSView *)tableView:(NSTableView *)tableView
viewForTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row
Note the -> NSView! in the swift code also.
The new docs allow you to see the code for Swift and Objective -c side by side or one or the other. You can use a selection tab at the top of the documentation to choose.
It also looks like your code should include the "!" for optionals

If you change the content mode of the table view from view based to cell based the method is called.

This beat me up for an hour. Works with Swift 2.2, probably won't work for earlier or later versions:
let cell = tableView.makeViewWithIdentifier(myid!, owner: nil) // You can't cast this with as! like they want you to
if let mycell = cell as? NSTableCellView {
mycell.textField?.stringValue = text
return mycell
}

Related

NSTableView Cell with Identifier keep giving nil

I am working on building MacOS app. I am trying to make table view that updates the cell when I press add button.
Following is my code:
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let identifier = tableColumn?.identifier as NSString?
if ( identifier == "NameCell")
{
var result: NSTableCellView
let cell = tableView.make(withIdentifier: "NameCell", owner: self) as! NSTableCellView
cell.textField?.stringValue = self.data[row].setting!
return cell
}
else if (identifier == "SettingCell")
{
if let cell = tableView.make(withIdentifier: "SettingCell", owner: self) as? NSTableCellView {
cell.textField?.stringValue = self.data[row].setting!
return cell
}
}
return nil
}
However, the line let cell = tableView.make(withIdentifier: "NameCell", owner: self) as! NSTableCellView is keep failing because it returns nil
fatal error: unexpectedly found nil while unwrapping an Optional value
NameCell is from
Can anyone please help me find a way to solve this problem?
For anyone else who comes here with this same question when trying to make an NSTableView fully programmatically: makeView(withIdentifier:owner:) WILL return nil unless a corresponding NIB exists for the given identifier:
NSTableView documentation:
If a view with the specified identifier can’t be instantiated from the nib file or found in the reuse queue, this method returns nil.
Likewise, the 'owner' param is a NIB-specific concept. In short: you cannot use this method if populating your NSTableView with cells programmatically.
In this answer, I detail the Swift code to produce an NSTableCellView programmatically: https://stackoverflow.com/a/51736468/5951226
However, if you don't want all the features of an NSTableViewCell, note that you can return any NSView in tableView(_:viewFor:row:). So you could, as per the CocoaProgrammaticHowtoCollection, simply write:
let cell = NSTextField()
cell.identifier = "my_id" // Essential! Allows re-use of the instance.
// ... Set any properties you want on the NSTextField.
return cell
You should set the "Identifier" with "NameCell" in the NSTableCellView. And your codes should simplified as follow since the column's identifier won't change for ever:
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
var result: NSTableCellView
let cell = tableView.make(withIdentifier: "NameCell", owner: self) as! NSTableCellView
cell.textField?.stringValue = self.data[row].setting!
return cell
}
references settings in XCode Interface Builder:

collect result of NStableView with checkboxes in Swift

I have hard time trying to collect the number of checkboxes checked inside the second column of a NStableView.
I composed a NSTableView with 2 column (via IB),
the first is named : BugColumn (it contains textfiled)
the second is named : CheckedColumn (it contains checkboxes)
Here is the code used to display strings in the first column :
var objets: NSMutableArray! = NSMutableArray()
...
extension MasterViewController: NSTableViewDataSource
{
func numberOfRowsInTableView(aTableView: NSTableView) -> Int
{
return self.objets.count
}
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView?
{
var cellView: NSTableCellView = tableView.makeViewWithIdentifier(tableColumn!.identifier, owner: self) as! NSTableCellView
if tableColumn!.identifier == "BugColumn"
{
cellView.textField!.stringValue = self.objets.objectAtIndex(row) as! String
}
return cellView
}
The second column is made of checkboxes appearing for each element of the first column.
I would like to know what is the corresponding text in the first column for each checkboxes enabled (checked).
I red a few exemples about NSTableView but, or they do differents things, or they are in Objective-C.
Could someone explain how to do that using swift?
Thanks

NSTableView: detecting a mouse click together with the row and column

I'm trying to detect when a mouse click occurs in an NSTableView, and when it does, to determine the row and column of the cell that was clicked.
So far I've tried to use NSTableViewSelectionDidChangeNotification, but there are two problems:
It only triggers when the selection changes, whereas I want every mouse click, even if it is on the currently selected row.
The clickedRow and clickedColumn properties of NSTableView are both -1 when my delegate is called.
Is there a better (and correct) way of doing this?
There is a simple way.
Tested with Swift 3.0.2 on macOS 10.12.2 and Xcode 8.2.1
Let
tableView.action = #selector(onItemClicked)
Then
#objc private func onItemClicked() {
print("row \(tableView.clickedRow), col \(tableView.clickedColumn) clicked")
}
To catch the user clicking a row (only, when the user clicks a row, not when it is selected programmatically) :
Subclass your NSTableView and declare a protocol
MyTableView.h
#protocol ExtendedTableViewDelegate <NSObject>
- (void)tableView:(NSTableView *)tableView didClickedRow:(NSInteger)row;
#end
#interface MyTableView : NSTableView
#property (nonatomic, weak) id<ExtendedTableViewDelegate> extendedDelegate;
#end
MyTableView.m
Handle the mouse down event (note, the delegate callback is not called when the user clicks outside, maybe you want to handle that too, in that case, just comment out the condition "if (clickedRow != -1)")
- (void)mouseDown:(NSEvent *)theEvent {
NSPoint globalLocation = [theEvent locationInWindow];
NSPoint localLocation = [self convertPoint:globalLocation fromView:nil];
NSInteger clickedRow = [self rowAtPoint:localLocation];
[super mouseDown:theEvent];
if (clickedRow != -1) {
[self.extendedDelegate tableView:self didClickedRow:clickedRow];
}
}
Make your WC, VC conform to ExtendedTableViewDelegate.
#interface MyViewController : DocumentBaseViewController<ExtendedTableViewDelegate, NSTableViewDelegate, NSTableViewDataSource>
set the extendedDelegate of the MyTableView to your WC, VC (MyViewController)
somewhere in MyTableView.m
self.myTableView.extendedDelegate = self
Implement the callback in delegate (MyViewController.m)
- (void)tableView:(NSTableView *)tableView didClickedRow:(NSInteger)row {
// have fun
}
I would prefer doing as follows.
Override
-(BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row;
Provide super implementation;
RequiredRow = row;
RequiredColumn = [tableView clickedColumn];
Hope this helps.
If someone is looking for a Swift 3/4/5 version of Peter Lapisu's answer:
Add an extension for the NSTableView (NSTableView+Clickable.swift):
import Foundation
import Cocoa
extension NSTableView {
open override func mouseDown(with event: NSEvent) {
let globalLocation = event.locationInWindow
let localLocation = self.convert(globalLocation, from: nil)
let clickedRow = self.row(at: localLocation)
super.mouseDown(with: event)
if (clickedRow != -1) {
(self.delegate as? NSTableViewClickableDelegate)?.tableView(self, didClickRow: clickedRow)
}
}
}
protocol NSTableViewClickableDelegate: NSTableViewDelegate {
func tableView(_ tableView: NSTableView, didClickRow row: Int)
}
Then to use it, make sure you implement the new delegate protocol:
extension MyViewController: NSTableViewClickableDelegate {
#nonobjc func tableView(_ tableView: NSTableView, didClickRow row: Int) {
Swift.print("Clicked row \(row)")
}
}
The #nonobjc attribute silences the warning about it being close to didClick.
Just in case someone was looking for it in SWIFT and / or for NSOutlineView.
Based on #Peter Lapisu instructions.
class MYOutlineViewDelegate: NSOutlineView, NSOutlineViewDelegate,NSOutlineViewDataSource{
//....
}
extension MYOutlineViewDelegate{
func outlineView(outlineView: NSOutlineView, didClickTableRow item: AnyObject?) {
//Click stuff
}
override func mouseDown(theEvent: NSEvent) {
let globalLocation:NSPoint = theEvent.locationInWindow
let localLocation:NSPoint = self.convertPoint(globalLocation, fromView: nil)
let clickedRow:Int = self.rowAtPoint(localLocation)
super.mouseDown(theEvent)
if (clickedRow != -1) {
self.outlineView(self, didClickTableRow: self.itemAtRow(clickedRow))
}
}}
see the tableViewSelectionIsChanging notification, here are the the comments from NSTableView.h
/*
Optional - Called when the selection is about to be changed, but note, tableViewSelectionIsChanging: is only called when mouse events are changing the selection and not keyboard events.
*/
I concede that this might not be the surest way to correlate your mouse clicks, but it is another area to investigate, seeing that you are interested in mouse clicks.

which method is called when selecting a cell in the NSTableView in Cocoa OS X?

I have a NSTableView , i want to get the value present in the cell. I am having only one column so , i just need the row number
i can use this [tableView selectedRow]- but where do i put this i want to put this in a method that gets called on selection of any of the rows.
-(void)tableViewSelectionDidChange:(NSNotification *)notification{
NSLog(#"%d",[tableViewController selectedRow]);
}
The above method also does not work i am getting the error
-[NSScrollView selectedRow]: unrecognized selector sent to instance 0x100438ef0]
i want something like the method available in the iPhone tableview-
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
}
What is tableViewController object? Only NSTableView instances respond to selectedRow. You can get current table view (the one that sent the notification) from notification's object property:
Objective-C:
-(void)tableViewSelectionDidChange:(NSNotification *)notification{
NSLog(#"%d",[[notification object] selectedRow]);
}
Swift:
func tableViewSelectionDidChange(notification: NSNotification) {
let table = notification.object as! NSTableView
print(table.selectedRow);
}
my 2 cents for Xcode 10/swift 4.2
func tableViewSelectionDidChange(_ notification: Notification) {
guard let table = notification.object as? NSTableView else {
return
}
let row = table.selectedRow
print(row)
}
In Swift 5.4:
Just use tableView.action = #selector(YOUR_METHOD), then in your method, try to do something with tableView.selectedRow.
This would simply do the trick. Take a look at the demo below:
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
tableView.action = #selector(didSelectRow)
}
#objc private func didSelectRow() {
print("Selected row at \(tableView.selectedRow)")
}
But please be aware that if nothing is selected, or you clicked outside of a cell, then you'll get a tableView.selectedRow of -1. So you might wanna check if the index is out of range before you use it. :)
Swift 3 (from Eimantas' answer):
func tableViewSelectionDidChange(_ notification: NSNotification) {
let table = notification.object as! NSTableView
print(table.selectedRow);
}
You should addObserver Notification like this
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(tableViewSelectionDidChangeNotification)
name:NSTableViewSelectionDidChangeNotification object:nil];
It will action when tableView selected row
good luck
full guide In Swift 5:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.didSelectRow(_:)), name: NSTableView.selectionDidChangeNotification, object: tableView)
}
#objc
func didSelectRow(_ noti: Notification){
guard let table = noti.object as? NSTableView else {
return
}
let row = table.selectedRow
print(row)
}
deinit {
NotificationCenter.default.removeObserver(self)
}

Change color of NSTableViewCell

How can I change the color of a cell in my NSTableView?
In your NSTableViewDelegate for the NSTableView, implement this method:
- (void)tableView:(NSTableView *)tableView
willDisplayCell:(id)cell
forTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row
The NSTableView calls this on its delegate before displaying each cell so that you can affect its appearance. Assuming you're using NSTextFieldCells, for the cell that you want to change call:
[cell setBackgroundColor:...];
Or, if you want to change the text color:
[cell setTextColor:...];
If you want columns to have different appearances, or if all of the columns aren't NSTextFieldCells, use [tableColumn identifier] to, er, identify the column. You can set the identifier in Interface Builder by selecting the table column.
// TESTED - Swift 3 solution...for changing color of cell text in a single column. All columns in my tableview have a unique identifier
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let myCell:NSTableCellView = tableView.make(withIdentifier: (tableColumn?.identifier)!, owner: self) as! NSTableCellView
if tableColumn?.identifier == "MyColumn" {
let results = arrayController.arrangedObjects as! [ProjectData]
let result = results[row]
if result.ebit < 0.0 {
myCell.textField?.textColor = NSColor.red
} else {
myCell.textField?.textColor = NSColor.black
}
}
return myCell
}
//for VIEW based TableViews using Objective C
//in your NSTableViewDelegate, implement the following
//this customization makes the column numbers red if negative.
- (NSView *)tableView:(NSTableView *)inTableView
viewForTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row
{
NSTableCellView *result = nil;
if ([tableColumn.title isEqualToString: #"Amount"]) {
//pick one of the following methods to identify the NSTableCellView
//in .xib file creation, leave identifier "blank" (default)
result = [inTableView makeViewWithIdentifier:[tableColumn identifier] owner:self];
//or set the Amount column's NSTableCellView's identifier to "Amount"
result = [inTableView makeViewWithIdentifier:#"Amount" owner:self];
id aRecord = [[arrayController arrangedObjects] objectAtIndex:row];
//test the relevant field's value
if ( aRecord.amount < 0.0 )
[[result textField] setTextColor:[NSColor colorWithSRGBRed:1.0 green:0.0 blue:0.0 alpha:1.0]];
} else {
//allow the defaults to handle the rest of the columns
result = [inTableView makeViewWithIdentifier:[tableColumn identifier] owner:self];
}
return result;
}
Try using a custom NSView for that, or NSTableView's -setBackgroundColor: method.

Resources