Xcode Swift: How to stop variables from changing back after each viewDidLoad, how to save and update data from different ViewControllers? - xcode

I have some vars in my Main VC and when user clicks a button in another VC the prepareForSegue passes along a new value to the Main VC and updates a label.
But when the user clicks again it's back to initial value, so it doesn't increment since the value is set back in the viewDidLoad?
MainVC:
var statsHealth:Int = 0
override func viewDidLoad() {
super.viewDidLoad()
healthLabel.text = String("Health: \(statsHealth)/10")
}
Another VC:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "startSegue") {
let startVC = segue.destinationViewController as ViewController
startVC.statsHealth += 1
}
It's displayed as 0, then 1 but then 0 again and then 1 instead of 2,3,4 etc.
Any ideas?
BR
Nils

Perhaps not the most 'Swift' way to do it, but certainly works well....
Create a file called Variables.swift which will hold all your 'universal' variables (if these are going to be on every page, I see no reason this isn't the 'best' way to do it - certainly it is the most simple to understand!)
in Variables.swift, hold all your universal variables
struct Variables {
static var statsHealth = 0
.....
}
Then, in each other page, access them at any time
healthLabel.text = String("Health: \(Variables.statsHealth)/10")
or set them
Variables.statsHealth += 1

So based on your description, I assume the view controller structure is like this:
AnotherVC -> MainVC
MainVC is presented on top of AnotherVC. When you go back to AnotherVC, did you dismiss MainVC completely? If so, then every time you go from AnotherVC to MainVC, it initiate a new ViewController, and the variables you saved before doesn't exist anymore.
If you want to keep this structure and change variables in MainVC, keep a reference of mainVC in AnotherVC. Then instead of connecting in storyboard, you may want to present it programmatically.
class AnotherVC {
var mainVC: MainVC?
func presentMainVC() {
var targetVC = UIViewController()
if self.mainVC != nil {
targetVC = self.mainVC
} else {
let storyboard = UIStoryboard(name: "Your-storyboard-name", bundle: nil)
targetVC: MainVC = storyboard.instantiateViewControllerWithIdentifier("The-main-VC-identifier") as MainVC
self.mainVC = targetVC
}
//you can change your variable here
mainVC.statsHealth += 1
self.presentViewController(self.mainVC, animated: true, completion: nil)
}
If you mainVC is on top of AnotherVC in any case, you can just revert the reference direction.

Related

How to hand over delegate to controller in different places of project

I have a UINavigationController which includes 4 screens. Each has a back button respectively. Task: by pressing the back button on Controller4 get to Controller2, which will immediately switch to Controller1. I want to do this through a delegate (connection of 2 and 4 controllers), but I don’t understand how I can access the ViewController4.delegate = self property in Controller2. There is already an action for clicking and switching to 3Controller, to do something like
let storyboard = UIStoryboard(name: "VC4", bundle: nil)
guard let vc4 = story.instantiateViewController(withIdentifier: "VC4") as? VC4 else { return }
vc4.delegate = self
}
Please, help. How to link two controllers that are in completely different places in the project through a delegate (or something else)
P.S. This is my first time asking a question, sorry in advance if it's crooked
Example
protocol Controller2Delegate: AnyObject {
func showVC1()
}
class Controller2: UIViewController, Controller2Delegate {
// need to connect between 2 and 4 controller
let story = UIStoryboard(name: "VC4", bundle: nil)
guard let vc4 = story.instantiateViewController(withIdentifier: "VC4") as? VC4 else { return }
vc4.delegate = self //this is not work, and i already have same method for push to VC3
func showVC1() {
//any logic to call VC1 from VC2
//break point never called
}
class Controller4: UIViewController {
weak var delegate: Controller2Delegate?
func GoToVC1() {
//method who's call logic for pop to VC2
delegate?.showVC1
}// breakpoint is called
}

How to detect a Pan Gesture inside a NSTouchBarView

Is it possible to detect a finger pan on a NSTouchBarView?
Sorry for the lack of code but I don't even know where to start.
MacOS is not made for finger touches but the TouchBar is but I do not see how to do it on a NSTouchBarView
I don't know specifically about using NSTouchBarView, but using a pan recognizer in a touch bar usually works like this: create a view, then create a NSPanGestureRecognizer (don't forget to set the target and action) then add the recognizer to the previously created view. Finally, create your NSCustomTouchBarItem and assign the previously created view to the item's view. Quick example in Swift:
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItemIdentifier) -> NSTouchBarItem? {
switch identifier {
case NSTouchBarItemIdentifier.yourCustomItem:
return itemWithRecognizer(identifier: identifier)
default:
return nil
}
}
func itemWithRecognizer(identifier: NSTouchBarItemIdentifier) -> NSTouchBarItem {
let customView = NSView()
customView.wantsLayer = true
let recognizer = NSPanGestureRecognizer()
recognizer.target = self
recognizer.action = #selector(doSomething)
customView.addGestureRecognizer(recognizer)
let item = NSCustomTouchBarItem(identifier: identifier)
item.view = customView
return item
}
func doSomething() {
// gesture was activated
}

Add completion handler to presentViewControllerAsSheet(NSViewController)?

I am attempting to present a sheet configuration view (AddSoundEffect) for my main window/view controller (I'm using storyboards), and when the configuration view controller is dismissed, take the values entered in the AddSoundEffect view and pass that back to the main view. My current code in the main view controller:
presentViewControllerAsSheet(self.storyboard!.instantiateControllerWithIdentifier("AddSoundEffect") as! AddSoundViewController
And in the AddSoundViewController.swift file, the code to dismiss it is:
self.dismissViewController(self)
To pass the data, I have a class-independent tuple that I save data to. How do I add a completion handler to presentViewControllerAsSheet, and (optionally) is there a better way to pass the data between view controllers?
Setup: Xcode version 6.4, OS X 10.10.4
Delegation pattern is the easiest way for you.
// Replace this with your tuple or whatever data represents your sound effect
struct SoundEffect {}
protocol AddSoundViewControllerDelegate: class {
func soundViewController(controller: AddSoundViewController, didAddSoundEffect: SoundEffect)
}
//
// Let's say this controller is a modal view controller for adding new sound effects
//
class AddSoundViewController: UIViewController {
weak var delegate: AddSoundViewControllerDelegate?
func done(sender: AnyObject) {
// Dummy sound effect info, replace it with your own data
let soundEffect = SoundEffect()
//
// Call it whenever you would like to inform presenting view controller
// about added sound effect (in case of Done, Add, ... button tapped, do not call it
// when user taps on Cancel to just dismiss AddSoundViewController)
//
self.delegate?.soundViewController(self, didAddSoundEffect: soundEffect)
// Dismiss self
self.dismissViewControllerAnimated(true, completion: {})
}
}
//
// Let's say this controller is main view controller, which contains list of all sound effects,
// with button to add new sound effect via AddSoundViewController
//
class SoundEffectsViewController: UIViewController, AddSoundViewControllerDelegate {
func presentAddSoundEffectController(sender: AnyObject) {
if let addSoundController = self.storyboard?.instantiateViewControllerWithIdentifier("AddSoundEffect") as? AddSoundViewController {
addSoundController.delegate = self
self.presentViewController(addSoundController, animated: true, completion: {})
}
}
func soundViewController(controller: AddSoundViewController, didAddSoundEffect: SoundEffect) {
// This method is called only when new sound effect is added
}
}
Another way is to use closures:
// Replace this with your tuple or whatever data represents your sound effect
struct SoundEffect {}
//
// Let's say this controller is a modal view controller for adding new sound effects
//
class AddSoundViewController: UIViewController {
var completionHandler: ((SoundEffect) -> ())?
func done(sender: AnyObject) {
// Dummy sound effect info, replace it with your own data
let soundEffect = SoundEffect()
//
// Call it whenever you would like to inform presenting view controller
// about added sound effect (in case of Done, Add, ... button tapped, do not call it
// when user taps on Cancel to just dismiss AddSoundViewController)
//
self.completionHandler?(soundEffect)
// Dismiss self
self.dismissViewControllerAnimated(true, completion: {})
}
}
//
// Let's say this controller is main view controller, which contains list of all sound effects,
// with button to add new sound effect via AddSoundViewController
//
class SoundEffectsViewController: UIViewController {
func presentAddSoundEffectController(sender: AnyObject) {
if let addSoundController = self.storyboard?.instantiateViewControllerWithIdentifier("AddSoundEffect") as? AddSoundViewController {
addSoundController.completionHandler = { [weak self] (soundEffect) -> () in
// Called when new sound effect is added
}
self.presentViewController(addSoundController, animated: true, completion: {})
}
}
}
Or many other ways like sending notification, ... Whatever suits your needs. But delegation pattern or closures is the best way to go in this specific case.
I missed that your question is about NSViewController. This example is for iOS, but same pattern can be used on OS X without any issues.
The easiest way to detect sheet opening or closing is to use the Sheet Notifications:
class ViewController: NSViewController, NSWindowDelegate {
override func viewDidLoad(){
NSApplication.sharedApplication().windows.first?.delegate = self
}
func windowDidEndSheet(notification: NSNotification) {
}
func windowWillBeginSheet(notification: NSNotification) {
}
}

Passing data from textfield to next view

I am trying to develop a basic app in Xcode 6.1 and Swift language.
I cant seem to find any IOS 8, Xcode6 and Swift resources yet that help with this. Everything is still xcode 5 and IOS 7.
I have the whole story board done with images buttons and segues. just need to script it.
There are 6 segues all with segue "show".
I am just trying to capture one text field per view controller. upon button press for the segue to the next view controller.
the prepareForSegue thing confuses me.
how do I capture the data from a text field save it to a variable on button press and send that variable info to the next view controller?
That text field I want saved to a variable or let that I can have the final view controller access all the data input from each view controller and then do some basic math.
Any body can help with this?
Assuming that you have two view Controllers here is how you will pass value to the second view controller.
class ViewControllerA: UIViewController {
#IBOutlet var textField: UITextField = nil
...
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "segueTest") {
var svc = segue!.destinationViewController as ViewControllerB;
svc.passedValue = textField.text
}
}
}
class ViewControllerB: UIViewController {
var passedValue: String?
...
override func viewDidLoad() {
...
if let myPassedData = passedValue {
println(myPassedData)
}
or use
let myPassedData = passedValue ?? ""
println(myPassedData)
}
}
}

How to create global variable in Swift?

I am trying to set a global variable. In my case, just a boolean flag that indicates if a view is being presented for the first time:
var initialLoadFlag: Bool = true
After the view is presented, I want to set this flag to false:
var initialLoadFlag: Bool = false
And then check for it thenceforth:
if initialLoadFlag {
showWelcomeMessage()
}
So, I would like to create initialLoadFlag as a global variable. Where and how? I've tried:
In the viewDidLoad area of my view controller
In the application() method in my AppDelegate.swift file
In the AppDelegate class
No luck. I'm getting a Use of unresolved identifier 'initialLoadFlag' error message
(Note: I realize that in this question I betray my ignorance of how scope is handled in Swift. Please forgive me... I'm on a deadline, and still new to the language.)
Thanks for your help.
You can define a struct with static field:
struct MyViewState {
static var initialLoadFlag = false
}
Usage:
// set
MyViewState.initialLoadFlag = true
// get
let state = MyViewState.initialLoadFlag
println("My view state:\(state)")
Remarks:
Such hacks as singletons and global vars are usually needed in case of bad design. Maybe you can store your state in NSUserDefaults? Or store it in some session object that can be injected in any ViewController that needs to be aware about context.
You could store a flag in the master controller and set it to true when you perform the segue to the details controller. E.g.
class MasterViewController: UIViewController {
var firstTimePresenting = true
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if firstTimePresenting {
println("First time!")
firstTimePresenting = false
}
}
}
}

Resources