what code needs to be altered to make SpeakLine or A version of the toDoList tableview work in this case?
I can’t seem to get the swift [String] Arrays to work with the NSArrays.
I also built a simple tableView project with strings of names to test this code but I run into the same problem.
in order for the tableview to send the call for the function “sortedArrayUsingDescriptors…”, In the tableViews column attributes, I assigned the sort key to “key” and the selector to caseInsensitiveCompare: is that correct?
thanks,
a bit confused here
You are on the right track with the “caseInsensitiveCompare:” selector. Can you be more descriptive about the problems you’re running into with Swift string arrays vs NSArray? What kinds of errors are you seeing?
Hello
I have the same problem
In the optional delegate, function SortDescriptorDidChange is not called till I change the table colum filling the sort descriptor:
Sort key = key // ??? What is the key for the swift array
sort description = caseInsensitiveCompare:
Order: Ascending
Making dynamic the swift array voices doesn’t solve the problem for the Kvc compliant
The problem is this:
2015-05-12 09:52:44.921 SpeakLine[1301:303015] [<__NSCFString 0x618000076700> valueForUndefinedKey:]: this class is not key value coding-compliant for the key key.
2015-05-12 09:52:44.923 SpeakLine[1301:303015] (
0 CoreFoundation 0x00007fff9188e03c __exceptionPreprocess + 172
1 libobjc.A.dylib 0x00007fff89edc76e objc_exception_throw + 43
2 CoreFoundation 0x00007fff9188dbd9 -[NSException raise] + 9
3 Foundation 0x00007fff91c8bcc5 -[NSObject(NSKeyValueCoding) valueForUndefinedKey:] + 226
4 Foundation 0x00007fff91b60c3b -[NSObject(NSKeyValueCoding) valueForKey:] + 385
5 Foundation 0x00007fff91b73951 -[NSObject(NSKeyValueCoding) valueForKeyPath:] + 324
6 Foundation 0x00007fff91b93baa _sortedObjectsUsingDescriptors + 390
7 Foundation 0x00007fff91b939cb -[NSArray(NSKeyValueSorting) sortedArrayUsingDescriptors:] + 521
8 SpeakLine 0x000000010000426c _TFC9SpeakLine20MainWindowController9tableViewfS0_FTCSo11NSTableView24sortDescriptorsDidChangeGSaPSs9AnyObject___T_ + 316
9 SpeakLine 0x00000001000043ff _TToFC9SpeakLine20MainWindowController9tableViewfS0_FTCSo11NSTableView24sortDescriptorsDidChangeGSaPSs9AnyObject___T_ + 95
10 AppKit 0x00007fff8bfb5677 -[NSTableView setSortDescriptors:] + 264
11 AppKit 0x00007fff8c3a0b78 -[NSTableView _changeSortDescriptorsForClickOnColumn:] + 388
12 AppKit 0x00007fff8c389c45 -[NSTableHeaderView _trackAndModifySelectionWithEvent:onColumn:stopOnReorderGesture:] + 1046
13 AppKit 0x00007fff8c38c9b2 -[NSTableHeaderView mouseDown:] + 434
14 AppKit 0x00007fff8c4592fc -[NSWindow _reallySendEvent:isDelayedEvent:] + 14125
15 AppKit 0x00007fff8bde8d76 -[NSWindow sendEvent:] + 470
16 AppKit 0x00007fff8bde5312 -[NSApplication sendEvent:] + 2504
17 AppKit 0x00007fff8bd0ec68 -[NSApplication run] + 711
18 AppKit 0x00007fff8bc8b354 NSApplicationMain + 1832
19 SpeakLine 0x0000000100010b9d main + 109
20 libdyld.dylib 0x00007fff971c65c9 start + 1
)
Here the list of my code
//
// MainWindowController.swift
// SpeakLine
//
// Created by Giancarlo Giaretta on 08/05/15.
// Copyright (c) 2015 Giancarlo Giaretta. All rights reserved.
//
import Cocoa
//dichiaro il controller come aderente al protocollo
class MainWindowController: NSWindowController, NSSpeechSynthesizerDelegate, NSWindowDelegate, NSTableViewDataSource, NSTableViewDelegate {
@IBOutlet weak var textField: NSTextField!
@IBOutlet weak var speakButton: NSButton!
@IBOutlet weak var stopButton: NSButton!
@IBOutlet weak var tableView: NSTableView!
let speechSynth = NSSpeechSynthesizer()
//riceve dalla classe un array delle voci in formato severse domain
//il dynamic mi serve per rendere l'assay KVC per l'ordinamento
dynamic var voices = NSSpeechSynthesizer.availableVoices() as! [String]
var isStarted:Bool = false {
didSet {
updateButtons()
}
}
override var windowNibName: String {
return "MainWindowController"
}
override func windowDidLoad() {
super.windowDidLoad()
updateButtons()
//setto l'istanza come delegato e queta è una referenza weak
speechSynth.delegate = self
println(voices)
for voice in voices {
println(voiceNameForIdentifier(voice)!)
}
let defaultVoice = NSSpeechSynthesizer.defaultVoice()
if let defaultRaw = find(voices, defaultVoice){
let indices = NSIndexSet(index: defaultRaw)
tableView.selectRowIndexes(indices, byExtendingSelection: false)
tableView.scrollRowToVisible(defaultRaw)
}
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
}
// MARK: Action Methods
@IBAction func speakIt(sender: NSButton){
let string = textField.stringValue
if string.isEmpty {
println("la string del \(textField) è vuota")
} else {
println("la string è \"\(string)")
speechSynth.startSpeakingString(string)
isStarted = true
}
}
@IBAction func stopIt(sender: NSButton){
println("cliccato il bottone Stop")
speechSynth.stopSpeaking()
}
func updateButtons(){
if isStarted {
speakButton.enabled = false
stopButton.enabled = true
}else {
stopButton.enabled = false
speakButton.enabled = true
}
}
//ritorna la voce in formato semplice di un identificativo di voce in formato reverse domain
func voiceNameForIdentifier(identifier: String) -> String?{
if let attributes = NSSpeechSynthesizer.attributesForVoice(identifier){
return attributes[NSVoiceName] as? String
} else {
return nil
}
}
// MARK: - NSSpeechSynthesizerDelegate
func speechSynthesizer(sender: NSSpeechSynthesizer, didFinishSpeaking finishedSpeaking: Bool) {
isStarted = false
println("finishedSpeaking= \(finishedSpeaking)")
}
// MARK: - nsWindowDelegate
func windowShouldClose(sender: AnyObject) -> Bool {
//impedisce la chiusura della finestra se lo speech parla
//non ha bsogno di settare il delegato in quanto la proprietà del delegato di window
//è gia dettata nel File's Owner
return !isStarted
}
// MARK: - NSTableViewDataSource
func numberOfRowsInTableView(tableView: NSTableView) -> Int {
return voices.count
}
//restituisce un oggetto stringa alla Table View per la cella
//con il binding viene ripreso il Value dal TextField
func tableView(tableView: NSTableView, objectValueForTableColumn tableColumn: NSTableColumn?, row: Int) -> AnyObject? {
let voice = voices[row]
let voiceName = voiceNameForIdentifier(voice)
return voiceName
}
func tableView(tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [AnyObject]) {
//prendo un descrittore della tabella
let sortDescriptors = tableView.sortDescriptors
println("clikkato")
//definisco l'array di tipo NSArray per avere il metodo per ordinare in base al desciptor
let objectsArray = voices as NSArray
//Uso il metodo per ordinare l'array'
let sortedObjects = objectsArray.sortedArrayUsingDescriptors(sortDescriptors)
//assegno a voices l'array ordinato
voices = sortedObjects as! [String]
//ricarico la tabella
tableView.reloadData()
}
// MARK: - NSTableViewDelegate
func tableViewSelectionDidChange(notification: NSNotification) {
let row = tableView.selectedRow
if row == -1 {
speechSynth.setVoice(nil)
return
}
let voice = voices[row]
speechSynth.setVoice(voice)
}
There is a little bit of a trick when using sort descriptors to sort an array of strings. Sort descriptors are more intended for dealing with an array of data model objects where the key corresponds to one of the properties of that object. In the case of sorting an array of strings, what you want is to compare on the object itself, so you can use an empty key or, if you prefer, “self”.
let strings = ["B", "A", "c"]
let sortDescriptor = NSSortDescriptor(key: "self", ascending: true, selector: Selector("caseInsensitiveCompare:"))
let nsArrayOfStrings = strings as NSArray
let sortedStrings = nsArrayOfStrings.sortedArrayUsingDescriptors([sortDescriptor])
Arrgh ! it was so simple …
self is the receiving tableView of the click and then it call the delegate mainWindowController for the method
It was in front of my eyes but I didn’t see it. The interface build of xib file is easy but hides the things
Thank you