How to control instance in another view controller - macos

I'm new to Swift and OS X programming.
I am creating a desktop application with two view controllers, which are in a split view controller. How can I get access to the instances in another controller and change its attributes?
An example:
We have a drawing application, one view is canvas, the other is tools. There is a 'clear' button in tool view, how to set the canvas clear when click on this button?

You can try to use singleton pattern in your code. When you create the view controller, put the var out of the class like:
var fooViewController = FooViewController(...)
class FooViewController{
...
}
Then you can use the fooViewController anywhere in your project.
If you are working with storyboard, you can try some code like this:
var story = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
var view:MainTabViewController = story.instantiateViewControllerWithIdentifier("MainTab") as MainTabViewController
Hope this can be helpful.

You can use delegates and protocols! Link to Official Apple Documentation
Heres a quick example:
protocol toolsProtocol {
func pressedClear()
}
var delegate : toolsProtocol
In your canvas class
class Canvas: UIViewController, toolsProtocol {
Tools.delegate = self
func pressedClear() {
// Insert stuff happens here
}
}

Related

Xcode - How can i programmatically embed/change view controller within a container view?

I am trying to programmatically set / or change the embedded View Controller inside a Controller View.
I have 3 different View Controllers, that I would like to show in the Container View, all depending on if the user is logged in or not.
I have looked around and tried a bunch of code, I found one that worked, but the code changed the self view, and not the view containers view.
A lot of the code I have tried have also not been in Swift 3, so as a new app developer, this has been quite stressful, as I tried to convert it to Swift 3.
Can anyone provide a solution for changing the embedded view controller inside a view container? Thanks.
I might have found a solution for this. I'm answering here, in case it can help anyone else in my situation.
What I did was add a new View Controller and then embed it to the View Container - This will work as a "master view" - I then use this blank view controller to decide which other view controller should be changed within the self of the blank.
Here's some code I have in the blank view controller, but I suppose the blank view controller can also be used as a master view controller (in my case for "Account"), and then it can add the login/register view as a child.
override func viewDidLoad() {
super.viewDidLoad()
updateView()
}
private lazy var loginViewController: loginViewController = {
// Load Storyboard
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
// Instantiate View Controller
var viewController = storyboard.instantiateViewController(withIdentifier: "loginViewController") as! loginViewController
// Add View Controller as Child View Controller
self.add(asChildViewController: viewController)
return viewController
}()
private lazy var registerViewController: registerViewController = {
// Load Storyboard
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
// Instantiate View Controller
var viewController = storyboard.instantiateViewController(withIdentifier: "registerViewController") as! registerViewController
// Add View Controller as Child View Controller
self.add(asChildViewController: viewController)
return viewController
}()
private func add(asChildViewController viewController: UIViewController) {
// Add Child View Controller
addChildViewController(viewController)
// Add Child View as Subview
view.addSubview(viewController.view)
// Configure Child View
viewController.view.frame = view.bounds
viewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// Notify Child View Controller
viewController.didMove(toParentViewController: self)
}
private func remove(asChildViewController viewController: UIViewController) {
// Notify Child View Controller
viewController.willMove(toParentViewController: nil)
// Remove Child View From Superview
viewController.view.removeFromSuperview()
// Notify Child View Controller
viewController.removeFromParentViewController()
}
private func updateView() {
add(asChildViewController: registerViewController)
/*
if segmentedControl.selectedSegmentIndex == 0 {
remove(asChildViewController: sessionsViewController)
add(asChildViewController: summaryViewController)
} else {
remove(asChildViewController: summaryViewController)
add(asChildViewController: sessionsViewController)
}
*/
}
Credit to this guy: https://cocoacasts.com/managing-view-controllers-with-container-view-controllers/
The easiest way to do that is to have 3 container views in Storyboard above each other, each one of them is connected to a different view controller, then in your code, make 2 of these 3 container views hidden and leave one visible based on your business needs.
This is described in detail in this blog post. Hope this helps!

Simple displaying of second window: OS X & Swift

I'm trying to find how to bring up a second view/window after pushing a button on my primary window. I have read about segues and I can get the first window to display the second but the second is not connected to a view controller so I can't add any code to any controls on the second view. Try as I might I cannot create a SecondViewController.swift file and connect it to a window controller or a view controller. The tutorials I have found all deal with iOS and I want OS X which means there are just enough differences to keep me from figuring this out.
Can anyone show me how to do this?
Ta,
A.
First make new file like:
After that, put these codes in your classes and that should do it.
class SecondWindowController: NSWindowController {
convenience init() {
self.init(windowNibName: "SecondWindowController")
}
}
class ViewController: NSViewController {
private var secondWindowController: SecondWindowController?
#IBAction func showSecondWindow(sender: AnyObject) {
if secondWindowController == nil {
secondWindowController = SecondWindowController()
}
secondWindowController?.showWindow(self)
}
}

QLPreviewView causing a EXC_BAD_ACCESS error at runtime when setting previewItem

PROBLEM: Getting a Thread 1: EXC_BAD_ACCESS (code=1, address=0x18) when setting the previewItem of a QLPreviewView object laid out in a window.
GOAL: Create an app with a preview of file contents and controls below for changing properties of the file being previewed. The preview must be tied to the size of the window and react to resizing of the window.
SETUP:
Using Xcode 6.1.1 I created a default OS X Cocoa Application using Swift, and Storyboards.
Using Storyboards for layout, there doesn't appear to be a QLPreviewView component in the Object Library, so I used a Custom View object and set the Class to QLPreviewView. I then set the constraints to allow the preview view to resize with the window frame.
I then linked the Custom View of class QLPreviewView into the ViewController.swift file as a member variable. In the viewDidLoad() method I added a set the previewItem of the QLPreviewView object to an implementation of the QLPreviewItem protocol. I also added an import Quartz.
Here is the source for ViewController.swift, as generated by Xcode, then modified by me to add the setup for QLPreviewView.
//
// ViewController.swift
// PreviewTest
//
// Created by Derek on 2015-03-02.
// Copyright (c) 2015 Derek. All rights reserved.
//
import Cocoa
import Quartz
class ViewController: NSViewController {
#IBOutlet var preview: QLPreviewView!
override func viewDidLoad() {
super.viewDidLoad()
preview.previewItem = {final class PreviewItem : NSObject, QLPreviewItem {
var previewItemURL: NSURL! {return NSURL(fileURLWithPath: "/Users/derek/Pictures/Scan.jpeg")}
var previewItemTitle: String! {return "Test" }
}
return PreviewItem()
} ()
// Do any additional setup after loading the view.
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
}
The line where preview.previewItem is set gets the EXC_BAD_ACCESS error described at the top when the application is run.
I have successfully created a QLPreviewView and embedded it in a window, however I did it without using the storyboards or constraints, so it's size and position is not managed by the window, which is what I want.
The inline class PreviewItem works fine. I have tried switching it to a standalone class and then created it to a variable, then set QLPreviewView.previewItem to the variable. This fails in the same way as the set to previewItem doing it inline.
The inline class PreviewItem also works fine in the non storyboard case and the file scan.jpeg is shown as expected.
Other UI objects, like Text Field work fine when I set them up in a similar way.
I'm hoping to find out what I'm doing wrong here and how to correct it. Or suggestions on how to approach this goal in a different way.
Try defining PreviewItem as a nested class instead of the inline definition. JavaScript might define and invoke it with () like your program, but I think in Swift the nested class is more general.
class ViewController: NSViewController {
private final class PreviewItem : NSObject, QLPreviewItem {
var previewItemURL: NSURL! {return NSURL(fileURLWithPath: "/Users/derek/Pictures/Scan.jpeg")}
var previewItemTitle: String! {return "Test" }
}
#IBOutlet var preview: QLPreviewView!
override func viewDidLoad() {
super.viewDidLoad()
preview.previewItem = PreviewItem()
self.view.addSubview(preview)
// Do any additional setup after loading the view.
}
...
}
Also make sure the preview property is connected to the view by Interface Builder.

Get instance of main window controller in app delegate

I know that, for iOS, you can reference the root view controller from the app delegate like so:
var rootViewController = self.window!.rootViewController
How does one reference the main window controller from the app delegate when targeting OS X? I would like to pass a variable (the managed object context) this way, as I have read that it is a good solution for referencing the moc.
You can access the NSWindowController instance of the main NSWindow like this:
NSApplication.sharedApplication().mainWindow?.windowController
The ! in earlier answers will cause a crash if the window is hidden, so I recommend caching the window either like this or using a computed property:
var mWindow: NSWindow? = nil
func mainWindowCached() -> NSWindow? {
if let window = NSApplication.shared.mainWindow {
self.mWindow = window
}
return self.mWindow
}
You can access like this:
NSApplication.shared().mainWindow?.contentViewController as! YourViewController;

Split View Controller: How to connect Master View Controller to Detail View Controller?

(Xcode6-beta3, Swift, iOS8, iPad)
In an iPad split-view controller, how do I link the Master View Controller to the Detail View Controller?
In other words, when the user taps on an item on the left, how do I change the view on the right?
I know that in didSelectRowAtIndexPath, I need to call a method... but how do I call a method in the Detail View Controller from the Master View Controller?
Example
Imagine an app to display information on different types of cheeses. We begin by dragging a split-view controller onto the storyboard. A table of items in the master view on the left is set up to read as follows.
Swiss
Cheddar
Brie
On the right, there is simply a Web View inside of the detail view controller, named cheeseViewController. Therein, HTML documents about the selected cheese will be displayed.
An IBOutlet is wired from the web view into cheeseViewController, and a method named 'changeCheese' is set up in the Detail View Controller delegate to swap out the document.
How can I make a tap on "Cheddar" change the information in the detail view?
EDIT: Do I have to modify my AppDelegate.swift file? Using a Master-Detail template, I tried the following, with no luck:
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
// Override point for customization after application launch.
let splitViewController = self.window!.rootViewController as UISplitViewController
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as UINavigationController
splitViewController.delegate = navigationController.topViewController as Paragraph
return true
}
I hope I understood your problem correctly: You would like to show the detail information of a selected cheese in your Detailview.
When you create a new Master-Detail-View application in XCode 6 Beta 3, there will be a variable called "detailItem" in your DetailViewController.Swift file:
var detailItem: AnyObject? {
didSet{
self.configureView()
}
You set this detailItem in your MasterViewController.Swift file in the following function:
override func prepareForSegue(segue: UIStoryBoardSegue, sender: AnyObject?){
if segue.identifier == "yourSegueIdentifier" {
let indexPath = self.tableView.indexPathForSelectedRow()
let cheeese = yourCheeseArrayWithDetailInformation[indexPath.row]
(segue.destinationViewController as DetailViewController).detailItem = cheeese
}
}
(Assuming, that you have linked the views with a segue with the identifier: "yourSegueIdentifier" and an array of detailinfo called "yourCheeseArrayWithDetailInformation")
The above mentioned function "configureView" in the DetailView can now access your detailItem, which contains the contents of "cheeese"
I hope this helps you.
Why don't you just post a Notification from didSelectRowAtIndexPath in your Master and add an observer in your Detail View most likely inside your viewDidLoad. You also can handle the selector within the observer method with closure.
If you didn't create a master-detail app (so you have no detailItem), you might use this:
if let
mySplitViewController = splitViewController,
detailView = mySplitViewController.childViewControllers.last as? DetailViewController {
// do something with it
}

Resources