NSTableView Delegate methods won't get called - swift

I'm currently trying to parse the reddit headlines from a specific subreddit and display these in an NSTableView. The thing is, the numberOfRows function gets called and returns the correct integer but the tableView delegate function never gets called.
As far as I can see everything is wired up correctly in the code.
ViewController:
#IBOutlet weak var tableView: NSTableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
Downloader.load(url: URL(string: "https://www.reddit.com/r/" + "gaming" + ".json")!){
(result) in
let tvc = TableViewController(data: result)
self.tableView.delegate = tvc
self.tableView.dataSource = tvc
self.tableView.reloadData()
}
}
TableViewController:
class TableViewController: NSObject{
var json: JSON!
init(data: JSON) {
super.init()
self.json = data
}
}
extension TableViewController : NSTableViewDataSource {
func numberOfRows(in tableView: NSTableView) -> Int {
return JSONFormatController.getTitlesFrom(json: json).count
}
}
extension TableViewController : NSTableViewDelegate {
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
var titles = JSONFormatController.getTitlesFrom(json: json)
if let cell = tableView.make(withIdentifier: "entry", owner: nil) as? NSTableCellView {
cell.textField?.stringValue = titles[row]
return cell
} else {
return nil
}
}
}
The result variable and getTitlesFrom method do work, I checked these.

I think your issue is that your TableViewController object is getting deallocated because you are not keeping a reference to it. Try this:
#IBOutlet weak var tableView: NSTableView!
var tvc : TableViewController!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
Downloader.load(url: URL(string: "https://www.reddit.com/r/" + "gaming" + ".json")!){
(result) in
self.tvc = TableViewController(data: result)
self.tableView.delegate = self.tvc
self.tableView.dataSource = self.tvc
self.tableView.reloadData()
}
}
Explanation: tvc is a local variable of the download block which is getting deallocated after it has executed. Presumably your assumption is that storing the tvc in delegate and/or dataSource is keeping tvc alive. But they are not, they are weak references.

Related

How can I get user to input text in a text field inside a xib cell in swift

I have created a xib cell with two textfields. The textfield are connected as IBoutlets to the xib swift file. The xib cell is registered on the view controller as a Nib. When I run the app on a simulator I cannot get the keyboard to show up. Secondly, the textfields are not editable at all, that is the cursor doesn't even show. I would like to get help with this as I have tried using a label the same thing happens. I'm just not sure how to fix this one. I'm not getting errors on build. I have installed IQKeyboardManager from cocoapods. Thanks in advance.
Heres the code:
import UIKit
class DictionaryCell: UITableViewCell {
#IBOutlet weak var DictBubble: UIView!
#IBOutlet weak var DictSymbolTextfield: UITextField!
#IBOutlet weak var SymbolMeaningTextfield: UITextView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Here is the view controller code:
import UIKit
import Firebase
class PersonalDreamDictionaryViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var dictionaryTextfield: UITextField!
let db = Firestore.firestore()
var dreamDictionary: [DreamDictionary] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
tableView.dataSource = self
tableView.delegate = self
title = K.Dictionary.appName
tableView.register(UINib(nibName: K.Dictionary.cellNibName, bundle: nil), forCellReuseIdentifier:
K.Dictionary.cellIdentifier)
loadDictionaryList()
}
func loadDictionaryList() {
db.collection(K.Fstore1.collectionName)
.order(by: K.Fstore1.date)
.addSnapshotListener{ (QuerySnapshot, error) in
self.dreamDictionary = []
if let e = error {
print("There was an issue retrieving data from Firestore. \(e)")
} else {
if let snapshotDocuments = QuerySnapshot?.documents {
for doc in snapshotDocuments {
//print(doc.data())
let data = doc.data()
if let symbolRetrieved = data[K.Fstore1.symbol] as? String, let meaningRetrieved = data[K.Fstore1.meaning] as? String {
let newDictList = DreamDictionary(mysymbol: symbolRetrieved, mymeaning: meaningRetrieved)
self.dreamDictionary.append(newDictList)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
}
}
}
}
extension PersonalDreamDictionaryViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dreamDictionary.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let dictCell = tableView.dequeueReusableCell(withIdentifier: K.Dictionary.cellIdentifier, for: indexPath)
as! DictionaryCell
if let symbolInput = dictCell.DictSymbolTextfield.text, let meaningInput = dictCell.SymbolMeaningTextfield.text { db.collection(K.Fstore1.collectionName).addDocument(data: [
K.Fstore1.symbol: symbolInput,
K.Fstore1.meaning: meaningInput,
K.Fstore1.date: Date().timeIntervalSince1970
]) { (error) in
if let e = error {
print("There was an issue saving data to firesore, \(e)")
} else {
print("Successfully saved data.")
}
}
}
return dictCell
}
}
extension PersonalDreamDictionaryViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, willBeginEditingRowAt indexPath: IndexPath) {
print(indexPath.row)
}
}

Set the outlet of the item in TableCellView within the .xib file to the custom NSTableCellView subclass

I want to fill my NSTableView with content. Per table-cell-row are 3 items (2 NSTextFields and 1 NSImageView). For that I created a custom NSTableCellView where I want to set the #IBOutlets of the 3 Items, to set there the value for them. But when I try to set the referencing outlets, the only option is to create an action.
When I try to write #IBOutlet weak var personName: NSTextfield and then set the references, I can't because "xcode cannot locate the class in the current workspace"
When I create the NSTableViewinside a main.storyboard, I'm able to set the outlet references. So what is the different behavior between .storyboard and .xib?
When I try to connect the #IBOutlet with the Item "Person Name"
My NSViewController (owner of the .xib)
class ViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {
#IBOutlet weak var tableView: NSTableView! //ref to tableView in xib
var persons:[Person] = [] //content to fill tableview
override func viewDidLoad() {
super.viewDidLoad()
persons.append(Person(name: "John", age: 23, piRef: "/Users/xy/Desktop/profilePic.png"))
persons.append(Person(name: "Marie", age: 26, piRef: "/Users/xy/Desktop/profilePic.png"))
tableView.delegate = self
tableView.dataSource = self
}
func numberOfRows(in tableView: NSTableView) -> Int {
return persons.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let tableCellView:personTableCell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "defaultRow"), owner: self) as! personTableCell
//NSTableColumn in xib has id "defaulRow"
if let person:Person = persons[row] {
tableCellView.setPerson(person: person) //call method inside NSTableCellView-subclass to set item values
}
return tableCellView
}
}
The custom NSTableCellView subclass ("personTableCell")
class personTableCell: NSTableCellView {
var person:Person! = nil
//here should be:
//#IBOutlet weak var personName: NSTextField!
//#IBOutlet weak var personAge: NSTextField!
//#IBOutlet weak var personImg: NSImageView!
func setPerson(person: Person) {
self.person = person
self.personName = person.name
self.personAge = person.age
self.personImg = NSImage(byReferencingFile: person.profileImgRef)
}
}
I want to be able to add the item outlet references to my NSTableCellView-subclass.
It appears to me you're making this harder than it needs to be. makeView is giving you a reference to the cell. Therefore you can access its members directly. No need for outlets (which is why Xcode won't make them for you.)
I can't read your screenshots well enough to tell how the textfields are defined (old eyes), so I can only give you a generic example from a working demo of a custom cell class:
class DIYTableViewDelegate: NSObject, NSTableViewDelegate {
var count = 0 // counts the number of views actually created
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let id = tableColumn!.identifier
var view = tableView.makeView(withIdentifier: id, owner: nil) as? CustomTableCellView
if view == nil {
view = createCell(id)
count += 1
}
view!.textField!.stringValue = "\(id.rawValue) \(row) \(view!.count) \(count)"
view!.count += 1
return view
}
}
Also, it's customary in Swift to capitalize the first letter of types (classes, structures, enums, protocols) and lowercase methods & properties. Doesn't affect how your code compiles, but it helps other Swifties read it.
Here's another example that may help:
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
guard let vw = tableView.makeView(withIdentifier: tableColumn!.identifier, owner: self) as? CustomTableCellView else { return nil }
vw.textField?.stringValue = String(pictures[row].dropLast(4))
vw.imageView?.image = NSImage(named: pictures[row])
return vw
}

NSTableView Doesn't work when in ViewController

For some reason when I put my code for my NSTableView in a ViewController, none of the cells appear, but if I put the code in the AppDelegate, everything works great.
Any ideas as to why this is happening? I'm working with a .xib file if that helps at all.
class ViewController: NSViewController{
var delegate: AppDelegate? = nil
#IBOutlet weak var tableView: NSTableView!
override func viewDidLoad() {
super.viewDidLoad()
delegate = NSApplication.shared.delegate as? AppDelegate
tableView.dataSource = self
tableView.delegate = self
}
}
extension ViewController: NSTableViewDataSource{
func numberOfRows(in tableView: NSTableView) -> Int {
print(delegate?.FlightList.count ?? 0)
return delegate?.FlightList.count ?? 0
}
}
extension ViewController: NSTableViewDelegate{
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
var text: String = ""
var cellIdentifier: String = ""
guard let item = delegate?.FlightList[row] else {
return nil
}
if tableColumn == tableView.tableColumns[0] {
text = item.flightName
cellIdentifier = "flightID"
}
if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(cellIdentifier), owner: nil) as? NSTableCellView {
cell.textField?.stringValue = text
return cell
}
return nil
}
}
you need to implement the NSTableViewDataSource, NSTableViewDelegate protocols to tell the table this is the class its getting data from else the
tableView.dataSource = self
tableView.delegate = self
wont work and then you wont need to use the app delegate

Cannot set delegate to reload table view

I am trying to reload a table view with the help of a delegate. I found tons of examples here on stack overflow, but I always end up with an error.
My first controller which should update the table view:
import Cocoa
class ViewController: NSViewController {
#IBOutlet weak var tableView: NSTableView!
var tableViewData: [[String:String]] = []
override func viewDidLoad() {
super.viewDidLoad()
...
self.tableView.delegate = self as NSTableViewDelegate
self.tableView.dataSource = self
self.tableView.reloadData()
}
override var representedObject: Any? {
didSet {
}
}
func reloadTableData(_ notification: Notification) {
tableView.reloadData()
}
}
extension ViewController: NSTableViewDataSource, NSTableViewDelegate {
func numberOfRows(in tableView: NSTableView) -> Int {
return tableViewData.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?{
var result:CustomTableCellview
result = tableView.make(withIdentifier: (tableColumn?.identifier)!, owner: self) as! CustomTableCellview
result.textField?.stringValue = tableViewData[row][(result.textField?.identifier!)!]!
result.secondTextField?.stringValue = tableViewData[row][result.secondTextField.identifier!]!
return result
}
}
extension ViewController: PageControllerDelegate {
func updateTableData() {
tableView.reloadData()
}
}
My second controller, which should tell the first one it can update the table view:
import Cocoa
protocol PageControllerDelegate {
func updateTableData()
}
class PageController: NSPageController {
var delegate: PageControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func saveData(sender: NSButton) {
...
delegate?.updateTableData()
self.dismiss(self)
}
}
Within the PageController I get the following error:
Property 'delegate' with type 'PageControllerDelegate?' cannot override a property with type 'NSPageControllerDelegate?' (aka 'Optional<NSPageControllerDelegate>')
Rename the delegate protocol name to something like MyPageControllerDelegate. It seems like there is already something called PageControllerDelegate that is defined either by you, or Apple
NSPageController already has a property delegate of type NSPageControllerDelegate. Remove var delegate: PageControllerDelegate?.

NSWorkspace: runningApplications is not returning all user processes unless I use an NSTimer

I am trying to use NSWorkspace to get all the currently running user applications/processes. When I run the code below, it only returns a subset of all the currently running user applications and displays them in the tableView. Yet, when I uncomment the timer line, all the applications are returned and displayed in the table. I'm not sure why the first call to NSWorkspace.runningApplications doesn't immediately fetch all the currently running user applications? Thank you!
import Cocoa
class ViewController: NSViewController {
// MARK: - Properties
#IBOutlet weak var tableView: NSTableView!
var runningApplications = [NSRunningApplication]()
// MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
//NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: #selector(ViewController.refresh(_:)), userInfo: nil, repeats: true)
runningApplications = NSWorkspace.sharedWorkspace().runningApplications
print(runningApplications)
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
func refresh(timer: NSTimer){
runningApplications = NSWorkspace.sharedWorkspace().runningApplications
tableView.reloadData()
}
}
// MARK: - NSTableViewDataSource
extension ViewController: NSTableViewDataSource {
func numberOfRowsInTableView(tableView: NSTableView) -> Int {
return runningApplications.count
}
}
// MARK: - NSTableViewDelegate
extension ViewController: NSTableViewDelegate {
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? {
let cellView: NSTableCellView = tableView.makeViewWithIdentifier(tableColumn!.identifier, owner: self) as! NSTableCellView
cellView.textField?.stringValue = runningApplications[row].localizedName!
return cellView
}
}