I’ve had to take three weeks off from studying this book for training with my current job and am still trying to get back in the swing of this book. I’ve made some progress since my break, but I’m uncertain what is needed for this Challenge.
I’ve created the MainWindowController as directed, but can’t figure out how to "move the code for instantiating the view controller within it. I’ve deleted the ImageViewContoller.swift and .xib. I can get the flowViewTemplate or the columnViewTemplate to display by changing the code from flowWindowController.showWindow(self) to columnWindowController…, but can’t figure out the tabs, or if that was even the intent. I’ve made no changes to the SpeakLineViewController yet, so I won’t include it here.
AppDelegate.swift:
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
let flowWindowController = MainWindowController()
flowWindowController.contentViewController?.title = "Flow"
flowWindowController.image = NSImage(named: NSImage.Name.flowViewTemplate)
let columnWindowController = MainWindowController()
columnWindowController.contentViewController?.title = "Column"
columnWindowController.image = NSImage(named: NSImage.Name.columnViewTemplate)
let speakLineViewController = SpeakLineViewController()
speakLineViewController.title = "SpeakLine"
// let tabViewController = NSTabViewController()
// tabViewController.addChildViewController(flowViewController.contentViewController!)
// tabViewController.addChildViewController(columnViewController.contentViewController!)
// tabViewController.addChildViewController(speakLineViewController)
flowWindowController.showWindow(self)
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
MainWindowController:
import Cocoa
class MainWindowController: NSWindowController {
@objc dynamic var image: NSImage?
override var windowNibName: NSNib.Name? {
return NSNib.Name(rawValue: "MainWindowController")
}
override func windowDidLoad() {
super.windowDidLoad()
}
}
Is this all there is to the Challenge? Or am I way off here and missing the point?
So, I managed to getting it working this way, with ViewController code moved into MainWindowController (includes the SpeakLine ViewController) (Swift 4.2, Xcode 10.1):
//
// AppDelegate.swift
// ViewControl
//
// Created by Frank on 01/04/18
//
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate
{
var mainWindowController: MainWindowController?
func applicationDidFinishLaunching(_ aNotification: Notification)
{
// Create a window controller
let mainWindowController = MainWindowController()
// Put the window of the window controller on screen
mainWindowController.showWindow(self)
// Set the property to point to the window controller
self.mainWindowController = mainWindowController
}
}
//
// MainWindowController.swift
// ViewControl
//
// Created by Frank on 01/04/18
//
import Cocoa
class MainWindowController: NSWindowController
{
override var windowNibName: NSNib.Name?
{
return "MainWindowController"
}
override func windowDidLoad()
{
super.windowDidLoad()
let flowViewController = ImageViewController()
flowViewController.title = "Flow"
flowViewController.image = NSImage(named: NSImage.flowViewTemplateName)
let columnViewController = ImageViewController()
columnViewController.title = "Column"
columnViewController.image = NSImage(named: NSImage.columnViewTemplateName)
let speakLineViewController = SpeakLineViewController()
speakLineViewController.title = "SpeakLine"
let tabViewController = NSTabViewController()
tabViewController.addChild(flowViewController)
tabViewController.addChild(columnViewController)
tabViewController.addChild(speakLineViewController)
self.contentViewController = tabViewController
self.window!.makeKeyAndOrderFront(self)
}
}
//
// ImageViewController.swift
// ViewControl
//
// Created by Frank on 01/04/18
//
import Cocoa
class ImageViewController: NSViewController
{
@objc dynamic var image: NSImage?
override var nibName: NSNib.Name?
{
return "ImageViewController"
}
override func viewDidLoad()
{
super.viewDidLoad()
// Do view setup here.
}
}
//
// SpeakLineViewController.swift
// ViewControl
//
// Created by Frank on 01/04/18
//
// NB: ...You will need to connect the DataSource and Delegate by control-clickings on the TableView first,
// and then dragging the dataSource/delegate connections to File Owner.
import Cocoa
class SpeakLineViewController: NSViewController, 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()
let voices = NSSpeechSynthesizer.availableVoices // array of strings representing available voices
var isStarted: Bool = false
{
didSet
{
updateButtons() // when "isStarted" changes, updateButtons() will be called
}
}
override var nibName: String
{
return "SpeakLineViewController"
}
override func viewDidLoad()
{
super.viewDidLoad()
updateButtons() // call buttons directly here, to set the inital state of buttons
speechSynth.delegate = self
for voice in voices
{
print(voiceNameForIdentifier(identifier: voice.rawValue)!)
}
let defaultVoice = NSSpeechSynthesizer.defaultVoice
if let defaultRow = voices.index(of: defaultVoice)
{
let indices = IndexSet(integer: defaultRow)
tableView.selectRowIndexes(indices, byExtendingSelection: false)
tableView.scrollRowToVisible(defaultRow)
}
}
// MARK: - Action methods
@IBAction func speakIt(sender: NSButton)
{
// Get typed-in text as a string
let string = textField.stringValue
if string.isEmpty
{
print("string is empty")
}
else
{
print("string is \"\(textField.stringValue)\"")
speechSynth.startSpeaking(string)
isStarted = true
}
}
@IBAction func stopIt(sender: NSButton)
{
print("stop button clicked")
speechSynth.stopSpeaking()
}
func updateButtons()
{
if isStarted
{
speakButton.isEnabled = false
stopButton.isEnabled = true
}
else
{
stopButton.isEnabled = false
speakButton.isEnabled = true
}
}
func voiceNameForIdentifier(identifier: String) -> String?
{
let attributes: [NSSpeechSynthesizer.VoiceAttributeKey : Any] = NSSpeechSynthesizer.attributes(forVoice: NSSpeechSynthesizer.VoiceName(rawValue: identifier))
return attributes[NSSpeechSynthesizer.VoiceAttributeKey.name] as? String
}
// MARK: - NSSpeechSynthesizerDelegate
func speechSynthesizer(_ sender: NSSpeechSynthesizer, didFinishSpeaking finishedSpeaking: Bool)
{
isStarted = false
print("finishedSpeaking = \(finishedSpeaking)")
}
// MARK: - NSWindowDelegate
func windowShouldClose(_ sender: NSWindow) -> Bool
{
return !isStarted // prevents window from closing when speech is started
}
// MARK: - NSTableViewDataSource Methods
func numberOfRows(in tableView: NSTableView) -> Int
{
return voices.count
}
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any?
{
let voice = voices[row]
let voiceName = voiceNameForIdentifier(identifier: voice.rawValue)
return voiceName
}
// MARK: - NSTableViewDelegate Methods
func tableViewSelectionDidChange(_ notification: Notification)
{
let row = tableView.selectedRow
// Set the voice back to the default if the user has deselected all rows
if row == -1
{
speechSynth.setVoice(nil)
return
}
let voice = voices[row]
speechSynth.setVoice(voice)
}
}