I would like to get and show the user home directory on a NSOutlineView going deep every time the node is expandable.
So far I can get only the first level and expand it to a second level.
I imagine that it has to be appended to an index or something like that.
I can get level 1 and 2. When i click on the level 2, for example Documents, there is a directory, yourDirectoryName, that has more directories in it. I would like to show the arrow and be able to keep going further on the tree
First pic is my app. Second pic is an example from filezilla
import Cocoa
class Directories {
var name: String
var subDirectories: [String]
init(name: String, subDirectories: [String]) {
self.name = name
self.subDirectories = subDirectories
}
}
class ViewController: NSViewController {
var directories = [Directories]()
@IBOutlet weak var outlineView: NSOutlineView!
override func viewDidLoad() {
super.viewDidLoad()
getDir(path: "")
outlineView.dataSource = self
outlineView.delegate = self
}
func getDir(path: String) {
let fm = FileManager.default.homeDirectoryForCurrentUser
do {
let items = contentsOf(folder: fm)
for item in items {
let sub = getSubDir(path: item.lastPathComponent)
let re = Directories(name: item.lastPathComponent, subDirectories: sub)
directories.append(re)
}
}
}
func contentsOf(folder: URL) -> [URL] {
let fileManager = FileManager.default
do {
let contents = try fileManager.contentsOfDirectory(atPath: folder.path)
let urls = contents.map { return folder.appendingPathComponent($0) }
return urls
} catch {
return []
}
}
func getSubDir(path: String) -> [String]{
var sub = [String]()
let fm = FileManager.default
let filePath = NSString(string: path).expandingTildeInPath
do {
let items = try fm.contentsOfDirectory(atPath: filePath)
for item in items {
sub.append(item)
}
} catch {
// failed to read directory
}
return sub
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
extension ViewController: NSOutlineViewDataSource {
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
if let directories = item as? Directories {
return directories.subDirectories[index]
}
return directories[index]
}
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
if let directories = item as? Directories {
return directories.subDirectories.count > 0
}
return false
}
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
if let directories = item as? Directories {
return directories.subDirectories.count
}
return directories.count
}
}
extension ViewController: NSOutlineViewDelegate {
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
var text = ""
if let directories = item as? Directories {
text = directories.name
}
else {
text = item as! String
}
let tableCell = outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "cell"), owner: self) as! NSTableCellView
tableCell.textField!.stringValue = text
return tableCell
}
}
Expanding level 2 to 3 is the same as expanding level 1 to 2. A subdirectory is a directory and has its own subdirectories. The subDirectories
property of Directories
should be an array of Directories
. The directories
property of ViewController
points to a tree of directories and can be a Directories
.
Example:
class ViewController: NSViewController {
// the rootItem is invisible
var rootItem = DirectoryItem(url: FileManager.default.homeDirectoryForCurrentUser)
}
extension ViewController: NSOutlineViewDataSource {
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
let directoryItem = item as? DirectoryItem ?? rootItem
return directoryItem.childItems.count
}
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
let directoryItem = item as? DirectoryItem ?? rootItem
return directoryItem.childItems[index]
}
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
let directoryItem = item as? DirectoryItem ?? rootItem
return directoryItem.isExpandable
}
}
extension ViewController: NSOutlineViewDelegate {
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
let directoryItem = item as? DirectoryItem ?? rootItem
let tableCell = outlineView.makeView(withIdentifier: (tableColumn?.identifier)!, owner: self) as! NSTableCellView
tableCell.textField!.stringValue = directoryItem.name
return tableCell
}
}
class DirectoryItem {
var name: String
var url: URL
lazy var isExpandable: Bool = {
do {
return try url.resourceValues(forKeys: [.isDirectoryKey]).isDirectory ?? false
} catch let error as NSError {
return false
}
}()
lazy var childItems: [DirectoryItem] = {
do {
let urls = try FileManager.default.contentsOfDirectory(at: url,
includingPropertiesForKeys: [.isDirectoryKey],
options: [.skipsHiddenFiles])
return urls.map { DirectoryItem(url: $0) }
} catch let error as NSError {
return []
}
}()
init(url: URL) {
self.url = url
self.name = url.lastPathComponent
}
}
Disclaimer: I'm used to Objective-C and I'm struggling with Swift.