First, I'm not pass data between view controllers. I need pass data from a View Controller to another class.
Situation likes this:
I have one View Controller, it has slider, then I have a Camera class. I defined a camera the Scene Class, once I change the slider, the value of the camera in the Scene class needs to be changed.
VC:
class ViewController: {
#IBAction func moveSlider(_ sender: NSSlider) {
// here I want to pass the sender.value to the Camera class
}
}
Camera class:
class Camera {
var cameraLocationX ; // I want this value updated once the the slider moved, however the camera instance is defined in the Scene
}
However, this Camera class are not defined in the VC. So I can't use the static var method...
class Scene {
let camera = Camera()
..
camera.cameraLocationX; // here it needs to be updated.
}
How to achieve this? I Googled it seemed that I should use delegate or notification , but can someone give me a little more instructions?
Use the delegation pattern. This is a core concept described https://developer.apple.com/library/content/documentation/General/Conceptual/DevPedia-CocoaCore/Delegation.html
If you want to update property of class then you can set new value from your VC:
class ViewController: {
let camera = Camera()
#IBAction func moveSlider(_ sender: NSSlider) {
// here I want to pass the sender.value to the Camera class
camera.cameraLocationX = sender.value
}
}
Related
I am trying to use a variable from my GameScene.swift file on my GameViewController.swift file to time my interstitial ads appropriately. It's a boolean value that determines if my player is dead or not.
var died = Bool()
That's all I did to create the variable in my GameScene.
When died == true in my GameScene, I want to send that to my GameViewController and then show an interstitial ad. I really just need to know how to pass a boolean between scenes. Thanks in advanced for your help.
You can follow these steps.
Do this in your GameScene:
protocol PlayerDeadDelegate {
func didPlayerDeath(player:SKSpriteNode)
}
class GameScene: SKScene {
var playerDeadDelegate:PlayerDeadDelegate?
...
// during your game flow the player dead and you do:
playerDeadDelegate.didPlayerDeath(player)
...
}
In the GameViewController you do:
class GameViewController: UIViewController,PlayerDeadDelegate {
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene(fileNamed:"GameScene") {
...
scene.playerDeadDelegate = self
}
}
func didPlayerDeath(player:SKSpriteNode) {
print("GameViewController: the player is dead now!!!")
// do whatever you want with the property player..
}
}
Your GameScene should have a reference object as delegate (e.g conforms to GameSceneDelegate protocol) which is actually pointing to GameViewController object. Then when died becomes true, inform your delegate object (GameViewController object) about this event via a delegate-method and implement that method by conforming to the above protocol in your GameViewController class.
I read a lot about the delegates but in practice I cannot use it properly.
Description: I have A: UIViewController, B: UIView, C: UIViewController. I want to run segue from A: UIViewController to the C: UIViewController from the inside of B: UIView.
I've tried:
protocol SegueDelegate {
func runSegue(identifier: String)
}
class B: UIView { ... }
where in my A: UIViewController:
override func viewDidLoad() {
B().delegate = self
}
func runSegue(identifier: String) {
self.performSegueWithIdentifier(identifier, sender: self)
}
and trying to call it via:
#IBAction func send(sender: AnyObject) {
let a: SegueDelegate? = nil
a!.runSegue("goToMainPage")
}
but I'm sure that I do not use it properly. Can anyone help me with it? I do not want just an answer. Please describe me it concept shortly
Delegates are just a Design Pattern that you can use in a number of ways. You can look at the Apple Frameworks to see how and where to use delegates as examples. A table view delegate is probably the best known delegate in UIKit.
Delegates serve as a callback mechanism for code to communicate with an instance of an unknown class without knowing more than that that instance will respond to the methods of the delegate protocol.
An alternative to a delegate is to use a closure (what we used to call a block in Objective-C). When to use one vs. the other is a matter of taste. There are a couple of rules of thumb, like for instance outlined here.
What you are doing is, IMO, the proper way to use delegates. You separate the view functionality from the View Controller's functionalities via a delegate, and so the contract for your view is clear: the user needs to respond to the delegate method.
Your code works and is correct. I made a quick implementation here: https://github.com/kristofvanlandschoot/DelegateUsage/tree/master
The main difference from your example, and maybe that's the place where you made a mistake is the third part of your code where you should write something like:
#IBAction func send(sender: AnyObject) {
delegate?.runSegue("segueAB")
}
There are multiple errors in your code, for example:
Here you are creating a new B, and setting A as a delegate of that new instance, no the one you actually want
override func viewDidLoad() {
«B()».delegate = self
}
And here you are creating force unwrapping a nil value
#IBAction func send(sender: AnyObject) {
let a: SegueDelegate? = «nil»
«a!».runSegue("goToMainPage")
}
If what you want to do is tell A to perform a segue to C, from inside B, all you need to do is to call performSegueWithIdentifier on A
For example:
class B: UIView {
weak var referenceToA: UIViewController? = nil // set this somewhere
#IBAction func send(sender: AnyObject) {
guard let a = referenceToA else {
fatalError("you didn't set the reference to a view controller of class A")
}
a.performSegueWithIdentifier("goToMainPage", sender: self)
}
}
In my GameScene.swift file, I have the following member variable "chosenBall" that I want to assign in another file called ChooseBall.swift.
class GameScene: SKScene, SKPhysicsContactDelegate {
internal var chosenBall: BallType!
}
My ChooseBall.swift file looks like this.
class ChooseBall: UIViewController {
#IBAction func chooseBeachBall(sender: UIButton) {
chosenBall = BallType.BeachBall
}
}
I am getting a compiler error saying:
Use of unresolved identifier 'chosenBall'
How can I fix it ?
I haven't used SpriteKit so I do not know the basics on where and when to instantiate Scenes.
However, the error you are getting is because you have not created an instance of GameScene and made that accessible from your view controller.
A possible solution could be the following code, but again, I'm not sure when or where the scene should be instantiated.
class ChooseBall: UIViewController {
var scene = GameScene()
#IBAction func chooseBeachBall(sender: UIButton) {
scene.chosenBall = BallType.BeachBall
}
}
I have two scenes where I would like to pass a single variable to another scene using a segue. I have tried but unfortunately all tutorials I have seen are dealing with the storyboard. I am not using the storyboard. I am doing all of this programatically.
Here is the segue i am trying to initialize:
func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "segueTest") {
var lose = segue.destinationViewController as! loserScene;
lose.toPass = scoreLabelNode.text
}
}
The Loser Scene is my second scene.
scoreLabelNode.text is the text of an NSTimer which i'm using as a score.
I want to move the scoreLabelNode.text into another scene which is my loserScene.
My loserScene is set up like this:
import SpriteKit
import Foundation
let GameOverLabelCategoryName = "gameOverLabel"
class loserScene: SKScene {
var toPass: String!
var scoreLabel = SKLabelNode(fontNamed:"[z] Arista Light")
override func didMoveToView(view: SKView) {
var gameOver = SKSpriteNode(imageNamed: "gameover.png")
gameOver.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2)
self.addChild(gameOver)
println(toPass)
}
}
I am trying to print 'toPass' to test the string segue, but I just get nil.
OK! So instead of using a segue. WHICH YOU CANNOT DO WITH AN SKSCENE. I used a struct. I put the struct outside of one of my scenes that I wanted data to be passed from, and made sure the data was being fed into the struct. I then accessed the struct from another scene and it works perfectly!
struct Variables {
static var aVariable = 0
}
this is the struct^ I have it set to 0 but it will be updated automatically when the score is recorded at the end of the run of my game.
I then accessed it like so in another scene:
print(Variables.aVariable)
This prints the variable from the struct in my new scene. I can also turn this struct into a string and use it for a labelnode.
Your toPass String returns nil because it is not initialized when you send the value from your prepareForSegue method.
Create a some function for example;
// Do whatever you need with the toPass variable.
func printToPassValue(){
print(toPass)
}
And add the property observers to your toPass variable. like this;
var toPass: String? {
willSet {
printToPassValue() // Call your function
}
}
And add this function to your viewDidLoad method.
override func viewDidLoad() {
super.viewDidLoad()
printToPassValue()
}
All of the searches I've done focus on passing data between view controllers. That's not really what I'm trying to do. I have a ViewController that has multiple Views in it. The ViewController has a slider which works fine:
var throttleSetting = Float()
#IBAction func changeThrottleSetting(sender: UISlider)
{
throttleSetting = sender.value
}
Then, in one of the Views contained in that same ViewController, I have a basic line that (for now) sets an initial value which is used later in the DrawRect portion of the code:
var RPMPointerAngle: CGFloat {
var angle: CGFloat = 2.0
return angle
}
What I want to do is have the slider's value from the ViewController be passed to the View contained in the ViewController to allow the drawRect to be dynamic.
Thanks for your help!
EDIT: Sorry, when I created this answer I was having ViewControllers in mind. A much easier way would be to create a method in SomeView and talk directly to it.
Example:
class MainViewController: UIViewController {
var view1: SomeView!
var view2: SomeView!
override func viewDidLoad() {
super.viewDidLoad()
// Create the views here
view1 = SomeView()
view2 = SomeView()
view.addSubview(view1)
view.addSubview(view2)
}
#IBAction func someAction(sender: UIButton) {
view1.changeString("blabla")
}
}
class SomeView: UIView {
var someString: String?
func changeString(someText: String) {
someString = someText
}
}
Delegate:
First you create a protocol:
protocol NameOfDelegate: class { // ": class" isn't mandatory, but it is when you want to set the delegate property to weak
func someFunction() // this function has to be implemented in your MainViewController so it can access the properties and other methods in there
}
In your Views you have to add:
class SomeView: UIView, NameOfDelegate {
// your code
func someFunction() {
// change your slider settings
}
}
And the last step, you'll have to add a property of the delegate, so you can "talk" to it. Personally I imagine this property to be a gate of some sort, between the two classes so they can talk to each other.
class MainViewController: UIViewController {
weak var delegate: NameOfDelegate?
#IBAction func button(sender: UIButton) {
if delegate != nil {
let someString = delegate.someFunction()
}
}
}
I used a button here just to show how you could use the delegate. Just replace it with your slider to change the properties of your Views
EDIT: One thing I forgot to mention is, you'll somehow need to assign SomeView as the delegate. But like I said, I don't know how you're creating the views etc so I can't help you with that.
In the MVC model views can't communicate directly with each other.
There is always a view controller who manages the views. The views are just like the controllers minions.
All communication goes via a view controller.
If you want to react to some view changing, you can setup an IBAction. In the method you can then change your other view to which you might have an IBOutlet.
So in your example you might have an IBAction for the slider changing it's value (as in your original question) from which you could set some public properties on the view you would like to change. If necessary you could also call setNeedsDisplay() on the target view to make it redraw itself.