Swift Source List

Swift Source List

Created By: Debasis Das (29-Mar-2015)

In this article we will create a sample application in Swift to display a Source List (view based NSOutlineView) with sample data.

The output of the sample code will have have the following UI

Swift Source List

Steps to create a Swift Source List implementation

In this sample app, we will use a hierarchy of information for Department, Accounts in a Department  and Employees in an Account.

There can be multiple departments in a company and each department can have multiple accounts, each account can have multiple employees. Each Employee can only belong to one account and one account can only belong to one department

Step 1: Create the required models.

//  Model.swift
//  SwiftSourceList
//  Created by Debasis Das on 3/29/15.
//  Copyright (c) 2015 Knowstack. All rights reserved.

import Cocoa

class Department: NSObject {
    let name:String
    var accounts: [Account] = []
    let icon:NSImage?
    
    init (name:String,icon:NSImage?){
        self.name = name
        self.icon = icon
    }
}

class Account: NSObject {
    let name:String
    var employees: [Employee] = []
    let icon:NSImage?
    
    init (name:String,icon:NSImage?){
        self.name = name
        self.icon = icon
    }
}

class Employee: NSObject {
    let firstName:String
    let lastName:String
    let icon:NSImage?
    let email:String
    
    init (firstName:String, lastName:String, icon:NSImage?, email:String){
        self.firstName = firstName
        self.lastName = lastName
        self.icon = icon
        self.email = email
    }
}

//  ViewController.swift
//  SwiftSourceList
//  Created by Debasis Das on 3/29/15.
//  Copyright (c) 2015 Knowstack. All rights reserved.

import Cocoa

class ViewController: NSViewController, NSOutlineViewDelegate, NSOutlineViewDataSource {

    @IBOutlet weak var firstNameTextField:NSTextField?
    @IBOutlet weak var lastNameTextField:NSTextField?
    @IBOutlet weak var emailIdTextField:NSTextField?
    @IBOutlet weak var sourceListView:NSOutlineView?
    
    var department1 = Department (name:"Department 1",icon:NSImage (named: "Department-50"))
    var department2 = Department (name:"Department 2",icon:NSImage (named: "Department-50"))
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let account1 = Account(name:"Account 1",icon:NSImage (named: "account"))
        let employee10 = Employee (firstName: "Debasis", lastName: "Das", icon: NSImage (named: "employee"), email: "debasis_das@knowstack.com")
        let employee11 = Employee (firstName: "Mary", lastName: "Jane", icon: NSImage (named: "employee"), email: "maryjane@knowstack.com")
        account1.employees.append(employee10)
        account1.employees.append(employee11)
        
        let account2 = Account(name:"Account 2",icon:NSImage (named: "account"))
        let employee20 = Employee (firstName: "John", lastName: "Doe", icon: NSImage (named: "employee"), email: "johndoe@knowstack.com")
        let employee21 = Employee (firstName: "Jane", lastName: "Doe", icon: NSImage (named: "employee"), email: "janedoe@knowstack.com")

        account2.employees.append(employee20)
        account2.employees.append(employee21)
        
        department1.accounts.append(account1)
        department1.accounts.append(account2)
        
        self.sourceListView?.expandItem(nil, expandChildren: true)
    }

    override var representedObject: AnyObject? {
        didSet {
        // Update the view, if already loaded.
        }
    }
   
    func outlineView(outlineView: NSOutlineView, child index: Int, ofItem item: AnyObject?) -> AnyObject {
        if let item: AnyObject = item {
            switch item {
            case let department as Department: 
                return department.accounts[index]
            case let account as Account:
                return account.employees[index]
            default:
                return self
            }
        } else {
            switch index {
            case 0:
                return department1
            default:
                return department2
            }
        }
    }
    
    func outlineView(outlineView: NSOutlineView, isItemExpandable item: AnyObject) -> Bool {
        switch item {
        case let department as Department:
            return (department.accounts.count > 0) ? true : false
        case let account as Account:
            return (account.employees.count > 0) ? true : false
        default:
            return false
        }
    }
    
    func outlineView(outlineView: NSOutlineView, numberOfChildrenOfItem item: AnyObject?) -> Int {
        if let item: AnyObject = item {
            switch item {
            case let department as Department:
                return department.accounts.count
            case let account as Account:
                return account.employees.count
            default:
                return 0
            }
        } else {
            return 2 //Department1 , Department 2
        }
    }
    
    
    // NSOutlineViewDelegate
    func outlineView(outlineView: NSOutlineView, viewForTableColumn: NSTableColumn?, item: AnyObject) -> NSView? {
        switch item {
        case let department as Department:
            let view = outlineView.makeViewWithIdentifier("HeaderCell", owner: self) as NSTableCellView
            if let textField = view.textField {
                textField.stringValue = department.name
            }
            return view
        case let account as Account:
            let view = outlineView.makeViewWithIdentifier("DataCell", owner: self) as NSTableCellView
            if let textField = view.textField {
                textField.stringValue = account.name
            }
            if let image = account.icon {
                view.imageView!.image = image
            }
            return view
        case let employee as Employee:
            let view = outlineView.makeViewWithIdentifier("DataCell", owner: self) as NSTableCellView
            if let textField = view.textField {
                textField.stringValue = employee.firstName + " " + employee.lastName
            }
            if let image = employee.icon {
                view.imageView!.image = image
            }
            return view
        default:
            return nil
        }
        
    }
    
    func outlineView(outlineView: NSOutlineView, isGroupItem item: AnyObject) -> Bool {
        switch item {
        case let department as Department:
            return true
        default:
            return false
        }
    }
    
    func outlineViewSelectionDidChange(notification: NSNotification){
        println(notification)
        var selectedIndex = notification.object?.selectedRow
        var object:AnyObject? = notification.object?.itemAtRow(selectedIndex!)
        println(object)
        
        if (object is Employee){
            println("selected Object is a Employee " +  (object as Employee).firstName);
            self.firstNameTextField?.stringValue = (object as Employee).firstName
            self.lastNameTextField?.stringValue = (object as Employee).lastName
            self.emailIdTextField?.stringValue = (object as Employee).email
        }
        else{
            println("Do nothing on Department or Account Selection")
        }
        
    }

}

Download the Source Code here SwiftSourceList

Posted in Swift Tagged with: , , , ,
4 comments on “Swift Source List
  1. Tina says:

    Great tutorial. Thanks for the help. Any chance you have a tutorial on how to add items to outline view using a button and not code?

    • Debasis Das says:

      In this sample
      Depending on whether you want to add an account to a selected department or an employee to a selected account,
      In the button click action (For adding a new account to a selected department)
      1. Create a new account object
      2. Get hold of the department object by outlineViewSelection – index
      3. Add the account object created from step 1 to the department.accounts array
      4. reload the outline view.

      Similarly for adding an employee to an account
      1. Create a new employee object
      2. get hold of the selected account
      3. add the employee object to the account.employees array
      4. reload the outline view

      The above approach should work

  2. David Collin says:

    This article explains a little bit, but lacks in two points. This uses a predefined hierarchy (Department->Account->Employee). What if we want all levels to be the same object type? i.e. a Category object could have a subcategory of the same type as the parent, and go deeper (even though it’s probably not a good idea to use outline view for a 10+ level deep hierarchy). Can’t we have a single object type?

    Second big problem is, this is rarely real-world compliant. Many programs will actually have to build up the hierarchy at runtime, gathering data from Core Data or iCloud user data, or an online database. It’s really seldom that programmers will actually be able to build the tree at development time.

    • Debasis Das says:

      Thanks David for the feedback. I agree this doestnt mimic a real world problem one to one. True that a real life scenario would not have the tree all layered out perfectly.
      This post was for beginners to get started with a Source List in Swift.
      I will be working on couple of sample code where the data is loaded at runtime and thus the source list will be created at runtime.
      Thanks again for the feedback

1 Pings/Trackbacks for "Swift Source List"
  1. […] Also read Source List in Swift […]

Leave a Reply

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

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Recent Posts


Hit Counter provided by technology news