Swiching Between 2 Diferent Nsviewcontrollers with Data

Swiching between 2 diferent NSViewControllers with data

You need to use segues to perform this action.

First make the segues connections between the ViewControllers using to storyboard editor. After that you need to give the segues an identifier on the storyboard's attributes inspector. Then in your code you can call the new ViewController by the segue like the code below.

With this code you can pass the data using the button:

class ViewController: NSViewController {

var dataToPass: String = "DataToPass"

override func viewDidLoad() {
super.viewDidLoad()

}

@IBAction func loginButton(_ sender: Any) {

performSegue(withIdentifier: NSStoryboardSegue.Identifier(rawValue: "segueIdentifier"), sender: self)
}

override func prepare(for segue: NSStoryboardSegue, sender: Any?) {

if segue.identifier!.rawValue == "segueIdentifier" {
let destinationViewController = segue.destinationController as! ViewController2

destinationViewController.dataToReceive = dataToPass
}
}
}

class ViewController2: NSViewController {

var dataToReceive: String

override func viewDidLoad() {
super.viewDidLoad()

}
}

And with this code you will use the override viewWillAppear

class ViewController: NSViewController {

var dataToPass: String = "DataToPass"

override func viewDidLoad() {
super.viewDidLoad()

}

override func viewWillAppear() {

performSegue(withIdentifier: NSStoryboardSegue.Identifier(rawValue: "segueIdentifier"), sender: self)
}

override func prepare(for segue: NSStoryboardSegue, sender: Any?) {

if segue.identifier!.rawValue == "segueIdentifier" {
let destinationViewController = segue.destinationController as! ViewController2

destinationViewController.dataToReceive = dataToPass
}
}
}

class ViewController2: NSViewController {

var dataToReceive: String

override func viewDidLoad() {
super.viewDidLoad()

}
}

In both cases you need to assure the data you want to pass to the other view controller is not null.

Switching between NSViewControllers

Two considerations about your problem:

  • Keep model data in model classes. This means that you can always recreate a view controller and set its represented object provided the model classes have kept the changes made via the view controller. When you need to instantiate a view controller, set its represented object to (a representation of) a model class.

  • When removing a view from its superview, you do not necessarily need to release its corresponding view controller. Instead, you can keep strong references to all view controllers in your window controller/application delegate, so no state is actually lost.

Swift: Switch between NSViewController inside Container View / NSView

Just note that in order for this to work you have to add storyboard identifiers to your view controllers, which can by going to your storyboard then selecting the Identity Inspector in the right hand pane and then entering the Storyboard ID in the Identity subcategory.

Then this implementation of ViewController would achieve what you are looking for.

import Cocoa

class ViewController: NSViewController {

// link to the NSView Container
@IBOutlet weak var container : NSView!

var vc1 : ViewController1!
var vc2 : ViewController2!

var vc1Active : Bool = false

override func viewDidLoad() {
super.viewDidLoad()

// Make sure to set your storyboard identiefiers on ViewController1 and ViewController2
vc1 = NSStoryboard(name: "name", bundle: nil).instantiateController(withIdentifier: "ViewController1") as! ViewController1
vc2 = NSStoryboard(name: "name", bundle: nil).instantiateController(withIdentifier: "ViewController2") as! ViewController2

self.addChild(vc1)
self.addChild(vc2)
vc1.view.frame = self.container.bounds
self.container.addSubview(vc1.view)
vc1Active = true

}

// You can link this action to both buttons
@IBAction func switchViews(sender: NSButton) {

for sView in self.container.subviews {
sView.removeFromSuperview()
}

if vc1Active == true {

vc1Active = false
vc2.view.frame = self.container.bounds
self.container.addSubview(vc2.view)

} else {

vc1Active = true
vc1.view.frame = self.container.bounds
self.container.addSubview(vc1.view)
}

}
}

Switching between Views using NSViewControllers in OS X

presentingViewController is a property of a UIViewController. It holds a pointer to the viewController that presented the current viewController (or nil if this viewController was not presented by a presenting viewController).

What you meant to do was send a presentViewController:animated:completion message to self:

[self presentViewController:second animated:YES completion:nil]; 

(that's 'present', not 'presented')

When second is presented, it's presentingViewController property will be set to this self, and self's presentedViewController property will be set to second

edit
as Jef has pointed out, I have mistakenly assumed you are dealing with iOS. On OSX, the approach is similar. An NSViewController has a presentingViewController property, and a presentedViewControllers array. The various presenting methods available to you differ:

– presentViewController:animator:
– presentViewController:asPopoverRelativeToRect:ofView:preferredEdge:behavior:
– presentViewControllerAsModalWindow:
– presentViewControllerAsSheet:

IN any case I think you need to consider the NSViewController documentation carefully to be sure you are using the correct approach for the correct platform (your erroneous method seems to be derived from iOS, not OSX).

edit 2
I have made a small demo app showing the various presentation behaviours for NSViewController on OSX, including a very basic custom animator object.

Passing Data Indirectly to an Unrelated NSViewController

With the caveat that "globals and singletons should be avoided," it sounds like they are a good fit for what you're trying to do. As you get more comfortable in Cocoa you can move into more sophisticated means of accomplishing this (dependency injection). Look into this as you get more comfortable with Cocoa.

Create a simple singleton type:

// AppState.swift

class AppState {
// Basic singleton setup:
static let shared = AppState()
private init() {} // private prevents instantiating it elsewhere

// Shared state:
var carrierArray: [String] = []
}

Access it from your view controllers:

// YourViewController.swift:

@IBAction func doSomething(_ sender: Any) {
AppState.shared.carrierArray = ...
}

If you need to update the other view controllers when this shared state changes, notifications are a good tool for that. You could do this with a didSet on carrierArray, or simply trigger the notification manually.

View Controllers: How to switch between views programmatically?

You can begin from the simplest removeFromSuperview/insertSubview and add code to it little by little.


//SwitchViewController.h
#import
@class BlueViewController;
@class YellowViewController;

@interface SwitchViewController : UIViewController {
IBOutlet BlueViewController *blueViewController;
IBOutlet YellowViewController *yellowViewController;
}
- (IBAction)switchViews:(id)sender;
@property (nonatomic, retain) BlueViewController *blueViewController;
@property (nonatomic, retain) YellowViewController *yellowViewController;
@end

//1. remove yellow view and insert blue view
- (IBAction)switchViews:(id)sender {
if(self.blueViewController.view.superview == nil)
{
[yellowViewController.view removeFromSuperview];
[self.view insertSubview:blueViewController.view atIndex:0];
}
}

//2. appear=insert, disappear=remove
if(blueViewController.view.superview == nil)
{
[blueViewController viewWillAppear:YES];
[yellowViewController viewWillDisappear:YES];

[yellowViewController.view removeFromSuperview];
[self.view insertSubview:self.blueViewController.view atIndex:0];

[yellowViewController viewDidDisappear:YES];
[blueViewController viewDidAppear:YES];
}

//3. now add animation
[UIView beginAnimations:@"View Flip" context:nil];
[UIView setAnimationDuration:1.25];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
//blue view will appear by flipping from right
if(blueViewController.view.superview == nil)
{
[UIView setAnimationTransition: UIViewAnimationTransitionFlipFromRight
forView:self.view cache:YES];

[blueViewController viewWillAppear:YES];
[yellowViewController viewWillDisappear:YES];

[yellowViewController.view removeFromSuperview];
[self.view insertSubview:self.blueViewController.view atIndex:0];

[yellowViewController viewDidDisappear:YES];
[blueViewController viewDidAppear:YES];
}
[UIView commitAnimations];

How to pass data from NSWindowController to its NSViewController?

How about like this using delegate? This example will change your button's title.

@objc protocol SomeDelegate {
func changeTitle(title: String)
}

class ViewController: NSViewController {

weak var delegate: SomeDelegate?

@IBAction func myAction(sender: AnyObject) {
delegate?.changeTitle("NewTitle")
}

}

class MainWindowController: NSWindowController, SomeDelegate {

@IBOutlet weak var myButton: NSButton!

override func windowDidLoad() {
super.windowDidLoad()

// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
let myVc = window!.contentViewController as! ViewController
myVc.delegate = self

}

func changeTitle(title: String) {
myButton.title = title
}

}

Displaying and switching between ViewControllers in another ViewController

Here I created sample project:
CustomTabBarViewController


  • You should have container view for child ViewControllers
  • Then you should have array with embed ViewControllers
  • You should call method in
    CustomTabBarViewController which change ViewController inside
    container view to ViewController from array of VCs at index which you pass as parameter of this method

Start with declaring outlet collection for your TabBar buttons and also get reference for container view where your ViewControllers will be showed

@IBOutlet var tabBarButtons: [UIButton]!
@IBOutlet weak var container: UIView!

then create array for your tab bar items

var items: [UIViewController]?

next create lazy variables for your controllers

private lazy var aVC: A = {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
return storyboard.instantiateViewController(withIdentifier: "a") as! A
}()

private lazy var bVC: B = {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
return storyboard.instantiateViewController(withIdentifier: "b") as! B
}()

.... this can be simplified by creating method which returns ViewController depending on VC’s identifier

After that append ViewControllers to your items array and also each add as child of your TabBarViewController

override func viewDidLoad() {
super.viewDidLoad()
items = [aVC, bVC]
items!.forEach { addChild($0) }
}

continue with declaring method for setting ViewController

private func setViewController(_ viewController: UIViewController) {
items!.forEach { $0.view.removeFromSuperview(); $0.willMove(toParent: nil) }
container.addSubview(viewController.view)
viewController.view.frame = container.bounds
viewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
viewController.didMove(toParent: self)
}

now add action for your tab bar buttons and get index of button. Then with this index call your tabSelected method

@IBAction func buttonPressed(_ sender: UIButton) {
if let index = tabBarButtons.index(of: sender) {
tabSelected(index)
}
}

inside tabSelected set VC from items depending on index of sender tab bar button

func tabSelected(_ index: Int) {
if let item = items?[index] {
setViewController(item)
}
}

finally in viewDidLoad set first item

override func viewDidLoad() {
...
tabSelected(0)
}

Now you can fully customize your ViewController and make other epic stuff which you know from UITabBarController



Related Topics



Leave a reply



Submit