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:
Related
I have added a tableview and as well configured the cell with an identifier.
Everything works fine, the text of the cell gets displayed.
I have just one problem and don't know how to solve it.
How can I change the image of the tableview cell?
Here is my code:
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? {
let cellView = tableView.makeViewWithIdentifier("cell", owner: self) as! NSTableCellView
cellView.textField!.stringValue = self.objects.objectAtIndex(row) as! String
return cellView
}
How can I change the image which is also present inside the cell?
The standard NSTableCellView with text and image has an outlet imageView
cellView.imageView!.image = NSImage(named:"MyImage")
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
I have a feeling this table cell display problem has a simple answer but, I clearly need greater wisdom than mine to find it.
Here's my textbook controller, delegate and datasource class for the table and the enclosing view ...
import Cocoa
class Table8020ViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
var tokenText: [String] = ["alpha", "beta", "gamma", "delta", "epsilon", "zeta"]
override func viewDidLoad() {
super.viewDidLoad()
}
func numberOfRowsInTableView(aTableView: NSTableView) -> Int {
println("numberOfRowsInTableView = \(tokenText.count)")
return tokenText.count
}
func tableView(aTableView: NSTableView, objectValueForTableColumn aTableColumn: NSTableColumn?, row rowIndex: Int) -> AnyObject? {
var result = aTableView.makeViewWithIdentifier(aTableColumn!.identifier, owner: self) as! NSTableCellView
println("textField in = \(result.textField!.stringValue)")
result.textField!.stringValue = tokenText[rowIndex]
println("textField out = \(result.textField!.stringValue)")
return result
}
}
I log the updates to the .textField which seems to work ok.
numberOfRowsInTableView = 6
textField in = Table View Cell
textField out = alpha
textField in = Table View Cell
textField out = beta
textField in = Table View Cell
textField out = gamma
textField in = Table View Cell
textField out = delta
textField in = Table View Cell
textField out = epsilon
textField in = Table View Cell
textField out = zeta
But the actual table display retains the original interface builder values! 'Table View Cell' Something mysterious appears to be happening after after the 'return result'.
I'm using the latest Xcode Version 6.3 (6D570) with Swift V1.2
You're making a couple of mistakes.
Firstly you're returning the wrong kind of value from tableView:objectValueForTableColumn:row. This method isn't requesting a view instance, it's requesting the object that your view instance will be representing. In other words it wants to know what model object the view will be displaying. In each case your model object is very simple - it's one of the strings in the tokenText array.
func tableView(tableView: NSTableView,
objectValueForTableColumn tableColumn: NSTableColumn?,
row: Int) -> AnyObject? {
return tokenText[row]
}
Secondly you've failed to implement tableView:viewForTableColumn:row:. This is where you create a view for each cell in your table and tell that view which bits of the model object you want to display. In each case your model object is just a string, so you essentially tell the view to display the entire model object in its textField:
func tableView(tableView: NSTableView,
viewForTableColumn tableColumn: NSTableColumn?,
row: Int) -> NSView? {
var view = tableView.makeViewWithIdentifier(tableColumn!.identifier,
owner: self) as! NSTableCellView
view.textField!.stringValue = tokenText[row]
return view
}
Hey the original apple guide in obj-c is here,
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/TableView/PopulatingView-TablesProgrammatically/PopulatingView-TablesProgrammatically.html#//apple_ref/doc/uid/10000026i-CH14-SW6
example 3.2
I have been following through the guide and can not get the program to work, i am getting these errors:
'AnyObject?' does not have a member named 'identifier'
line: result.identifier = "HelloWorld"
and
error: cannot convert the expression's type 'AnyObject?' to type '()'
Line: return result
What am i doing wrong?
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn, row: Int){
var names: [String] = ["Anna","Alex","brain","jack","gg"]
var result: AnyObject? = tableView.makeViewWithIdentifier("HelloWorld", owner: self)
if result == nil
{
// Create the new NSTextField with a frame of the {0,0} with the width
// of the table.
result = NSTextField(frame: NSRect())
// set identifier of the NSTextField instance to HelloWorld.
result.identifier = "HelloWorld"
}
result = names[row]
return result
}
New Working code
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn, row: Int) -> NSTextField{
var names = ["Anna","Alex","Brain","Jack","Hello"]
var result: NSTextField? = tableView.makeViewWithIdentifier("HelloWorld", owner: self) as? NSTextField
if result == nil
{
// Create the new NSTextField with a frame of the {0,0} with the width
// of the table.
result = NSTextField(frame: NSRect())
// set identifier of the NSTextField instance to HelloWorld.
result?.identifier = "HelloWorld"
}
result!.bezeled = false
result?.stringValue = names[row]
return result!
}
Here:
var result: AnyObject? = tableView.makeViewWithIdentifier("HelloWorld", owner: self)
you have declared the result variable as an optional AnyObject - and this protocol doesn't have an identifier property, which is instead a property of NSTextField.
What you should do is declare that variable using the correct type:
var result: NSTextField? = tableView.makeViewWithIdentifier("HelloWorld", owner: self) as? NSTextField
which can also be shortened thanks to type inference:
var result = tableView.makeViewWithIdentifier("HelloWorld", owner: self) as? NSTextField
Side note: I think your if is checking for the opposite condition:
if result != nil
whereas I think it should be:
if result == nil
Side note 2
There's no return value declared in your function, but you are returning an instance of NSTextField.
You should change the signature to:
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn, row: Int) -> NSView
and change the return statement to:
return result!
Note that result is defined as optional, but looking at the method implementation, at the end a non-nil value is available, so I've used forced unwrapping and declared the method to return a non-optional value. Of course feel free to change that if you want.
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
}