Swift NSSearchField NSArrayController No Bindings

Swift NSSearchField NSArrayController No Bindings

Created By:Debasis Das (May 2016)

In this post we will create a simple cocoa application to filter the data in a NSTableView using a NSSearchField without using Bindings

The NSArrayController will maintain the backup data in the form of its content and the NSArrayController->arrangedObjects will return the filtered contents.

//  AppDelegate.swift
//  NSSearchField_ArrayController_NoBindings
//  Created by Debasis Das on 26/05/16.
//  Copyright © 2016 Knowstack. All rights reserved.

import Cocoa

@NSApplicationMain
//App Delegate will be the delegate and datasource for the table view
class AppDelegate: NSObject, NSApplicationDelegate,NSTableViewDelegate,NSTableViewDataSource,NSSearchFieldDelegate {

    @IBOutlet weak var window: NSWindow!
    @IBOutlet weak var tableView:NSTableView!
    @IBOutlet weak var searchField:NSSearchField!
   
    var contacts:[Person] = [] //Create an empty Array to hold the person records
    var personArrayController:NSArrayController = NSArrayController()
    
    func applicationDidFinishLaunching(aNotification: NSNotification) {
        self.tableView.setDelegate(self)
        self.tableView.setDataSource(self)
        self.searchField.delegate = self
        
        for item in self.dataArray(){
            self.contacts.append(item as! Person) //Adding person objects to the contacts property
        }

        self.personArrayController.content = contacts //Setting the contents of the array controller as the recently created contacts. 
//Alternatively the array controllers content must be updated from the data fetched from db
        self.tableView.reloadData()

    }

   
    
    override func controlTextDidChange(obj: NSNotification) {
        var searchString = ""
        searchString = ((obj.object as? NSSearchField)?.stringValue)!
        let predicate = NSPredicate(format: "firstName contains %@ OR lastName contains %@ ",searchString,searchString)
        
        if searchString != ""{
            self.personArrayController.filterPredicate = predicate
        }
        else{
            self.personArrayController.filterPredicate = nil
        }
        
        self.tableView.reloadData()
    }

//Table View Delegate and datasource methods
    func numberOfRowsInTableView(tableView: NSTableView) -> Int {
        return self.personArrayController.arrangedObjects.count
    }

    func tableView(tableView: NSTableView, objectValueForTableColumn tableColumn: NSTableColumn?, row: Int) -> AnyObject? {
        if let tColIdentifier = tableColumn?.identifier{
            let str = self.personArrayController.arrangedObjects[row].valueForKey(tColIdentifier)
            return str
        }
        else{
            return ""
        }
        
    }

    func applicationWillTerminate(aNotification: NSNotification) {
        // Insert code here to tear down your application
    }

    func dataArray()->NSMutableArray{
        let arr = NSMutableArray()
        arr.addObject(Person.createPerson("Debasis", lName: "aabb"))
        arr.addObject(Person.createPerson("John", lName: "ccdd"))
        arr.addObject(Person.createPerson("Jane", lName: "eeff"))
        return arr
    }

}


class Person:NSObject{
    var firstName:String = ""
    var lastName:String = ""
    
    class func createPerson(fName:String, lName:String)->Person{
        let person = Person()
        person.firstName = fName
        person.lastName = lName
        return person
        
    }
}

On build and Run
NSSearchField NSTableView NSArrayController No Bindings 1

 

On entering a search query

NSSearchField NSTableView NSArrayController No Bindings 2

Note: The Searches are case sensitive

The working code can be downloaded here NSSearchField_ArrayController_NoBindings

Posted in Cocoa, Swift Tagged with: , ,
One comment on “Swift NSSearchField NSArrayController No Bindings
  1. Bob Soule says:

    I would like to get your example working with Xcode 11 and swift 5. So far I have updated your code to the following:

    //
    // AppDelegate.swift
    // NSSearchField_NSTableView_Programatically
    //
    // Created by Debasis Das on 27/05/16.
    // Copyright © 2016 Knowstack. All rights reserved.
    //

    import Cocoa

    @NSApplicationMain
    class AppDelegate: NSObject, NSApplicationDelegate, NSTableViewDelegate , NSTableViewDataSource {

    @IBOutlet weak var window: NSWindow!
    @IBOutlet weak var tableView: NSTableView!
    @IBOutlet weak var searchField: NSSearchField!

    var contacts:[Person] = []
    var backUpContacts:[Person] = []

    func applicationDidFinishLaunching(aNotification: NSNotification) {
    // Insert code here to initialize your application
    tableView.delegate = self
    tableView.dataSource = self

    for item in self.dataArray(){
    self.contacts.append(item as! Person)
    }
    self.backUpContacts = self.contacts //Creating a backup of the contacts, else we can also us the
    self.tableView.reloadData()

    self.createMenuForSearchField()
    }

    func createMenuForSearchField(){
    let menu = NSMenu()
    menu.title = “Menu”

    let allMenuItem = NSMenuItem()
    allMenuItem.title = “All”
    allMenuItem.target = self
    allMenuItem.action = #selector(AppDelegate.changeSearchFieldItem(sender:))

    let fNameMenuItem = NSMenuItem()
    fNameMenuItem.title = “First Name”
    fNameMenuItem.target = self
    fNameMenuItem.action = #selector(AppDelegate.changeSearchFieldItem(sender:))

    let lNameMenuItem = NSMenuItem()
    lNameMenuItem.title = “Last Name”
    lNameMenuItem.target = self
    lNameMenuItem.action = #selector(AppDelegate.changeSearchFieldItem(sender:))

    menu.addItem(allMenuItem)
    menu.addItem(fNameMenuItem)
    menu.addItem(lNameMenuItem)

    self.searchField.searchMenuTemplate = menu
    self.changeSearchFieldItem(sender: allMenuItem)

    }

    @objc func changeSearchFieldItem(sender:AnyObject){
    (self.searchField.cell as? NSSearchFieldCell)?.placeholderString = sender.title

    }

    func numberOfRowsInTableView(tableView: NSTableView) -> Int {
    return self.contacts.count
    }

    private func tableView(tableView: NSTableView, objectValueForTableColumn tableColumn: NSTableColumn?, row: Int) -> AnyObject? {
    let identifier = tableColumn?.identifier
    let str = self.contacts[row].value(forKey: identifier!.rawValue)
    return str as AnyObject?
    }

    private func tableView(tableView: NSTableView, setObjectValue object: AnyObject?, forTableColumn tableColumn: NSTableColumn?, row: Int) {
    self.contacts[row].setValue(object, forKey: (tableColumn?.identifier)!.rawValue)
    }

    func dataArray()->NSMutableArray{
    let arr = NSMutableArray()
    arr.add(Person.createPerson(fName: “Debasis”, lName: “Das”))
    arr.add(Person.createPerson(fName: “John”, lName: “Doe”))
    arr.add(Person.createPerson(fName: “Jane”, lName: “Doe”))
    return arr
    }

    func applicationWillTerminate(aNotification: NSNotification) {
    // Insert code here to tear down your application
    }

    override func textDidChange(obj: NSNotification) {
    if obj.object as AnyObject? === self.searchField {

    let searchString = self.searchField.stringValue
    var predicate:NSPredicate = NSPredicate()
    if searchString.isEmpty{
    self.contacts = self.backUpContacts
    }
    else{
    if (self.searchField.cell as? NSSearchFieldCell)?.placeholderString == “All”{
    predicate = NSPredicate(format: “firstName contains %@ OR lastName contains %@ “,searchString,searchString)
    }
    else if (self.searchField.cell as? NSSearchFieldCell)?.placeholderString == “First Name”{
    predicate = NSPredicate(format: “firstName contains %@”,searchString)
    }
    else if (self.searchField.cell as? NSSearchFieldCell)?.placeholderString == “Last Name”{
    predicate = NSPredicate(format: “lastName contains %@”,searchString)
    }
    self.contacts = (self.backUpContacts as NSArray).filtered(using: predicate) as! [Person]
    }
    self.tableView.reloadData()
    }
    }

    }

    class Person:NSObject{
    var firstName:String = “”
    var lastName:String = “”

    class func createPerson(fName:String, lName:String)->Person{
    let person = Person()
    person.firstName = fName
    person.lastName = lName
    return person

    }
    }
    At this point I only have one error on this line:
    override func textDidChange(obj: NSNotification) that I changed from original:
    override func controlTextDidChange(obj: NSNotification)
    and I get the message method does not override any method in its super class .. I see that controlTextDidChange is a method of a text field object. So, that is why I changed it to textDidChange.

    How do I get this example going?

Leave a Reply

Your email address will not be published. Required fields are marked *

*