I am working on a basic alarm app. This is how the main storyboard looks like
I have added a custom view controller as a separate xib file which looks like this
And this is how the interface looks like when it runs. (The main ViewController in background and the CustomAlertController in the foreground)
It contains a date picker. What I mean to do is that when a user clicks on the addButton in the Main story board the customAlertViewController will come up and the user can choose a date and time to add as an alarm. When the user taps on Add in the customAlertViewController the date and time are supposed to be passed back into an array and added to the tableView in the Main storyboard view controller.
This is the code I have written so far:
Code for TableView
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
let adate = alarmDate[indexPath.row].date
print(adate)
cell.textLabel?.text = String(adate)
return cell
}
Code in the Alarm class
import Foundation
class Alarm{
var date : NSDate
init (date : NSDate){
self.date = date
}
}
Code in CustomAlertViewController
You don't have to go through the entire code. I have tried using prepare for segue, but I guess that's not a doable solution when the CustomAlertviewcontroller is in a different storyboard(?)
The next approach I took was to somehow pass the date to an instance of Alarm class in the viewDidDisappear method, and subsequently append it to alarmDate array (declared in ViewController).
This is where I am getting stuck. The print statement in the viewDidDisappear outputs 1 to the console, obviously because the date has been appended. But once the CustomAlertViewController exits and the viewController is back, the alarmDate array resets and no value shows up in the table view. I have tried to work around this but to no avail.
I realise that if I had used a new view controller in the storyboard in place of a separate xib file, I could have achieved the same result easily.
class CustomAlertViewController: UIViewController {
//MARK: - Properties
#IBOutlet weak var customAlertView: UIView!
#IBOutlet weak var alarmPicker: UIDatePicker!
var destinationDate = NSDate()
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName : nibNameOrNil, bundle : nibBundleOrNil)
self.modalPresentationStyle = .OverCurrentContext
}
convenience init(){
self.init(nibName : "CustomAlertViewController", bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
//MARK: - Methods
override func viewDidLoad() {
super.viewDidLoad()
print ("View loaded")
self.customAlertView.layer.borderColor = UIColor.darkGrayColor().CGColor
self.customAlertView.layer.borderWidth = 2
self.customAlertView.layer.cornerRadius = 8
view.backgroundColor = UIColor(white: 1, alpha: 0.7)
view.opaque = false
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func viewDidDisappear(animated: Bool) {
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("table") as! ViewController
let alarm = Alarm( date : destinationDate)
vc.alarmDate.append(alarm)
// vc.alarmData.reloadData()
print(vc.alarmDate.count)
}
// MARK: - Navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destinationVC = segue.destinationViewController as! ViewController
let alarm = Alarm( date : destinationDate)
destinationVC.alarmDate.append(alarm)
destinationVC.alarmData.reloadData()
print(destinationVC.alarmDate.count)
}
//MARK: - Actions
#IBAction func cancelButton(sender: AnyObject) {
self.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func addButton(sender: AnyObject) {
//Code to correct the time zone difference
let sourceDate = alarmPicker.date
let sourceTmeZone = NSTimeZone(abbreviation: "GMT")
let destinationTimeZone = NSTimeZone.systemTimeZone()
let sourceOffset = sourceTmeZone!.secondsFromGMTForDate(sourceDate)
let destinationOffset = destinationTimeZone.secondsFromGMTForDate(sourceDate)
let interval : Double
interval = Double(destinationOffset - sourceOffset)
destinationDate = NSDate.init(timeInterval: interval, sinceDate: sourceDate)
self.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}
}
I lack experience with Swift and would gladly appreciate any help.
PS I'm using Swift 2.3
You could use a protocol:
protocol DateShare {
func share(date: NSDate)
}
which you declare wherever you want outside of any controller scope.
then you add a
delegate: DateShare!
property in your CustomAlertVC. In the VC, when you init the CustomAlert, you add this line:
customAlert.delegate = self
And of course you add an extension that you conform to the DateShare protocol in your VC, as so:
extension ViewController: DateShare {
func share(date: NSDate) {
// do whatever you can for that the date can be displayed in table view
alarmDates.append(date) // if I understood it all ?
}
}
And finally you add this line in addButton(sender: _) scope:
#IBOutlet func addButton(sender: UIButton?) {
// generate inputDate here
delegate.share(date: inputDate)
}
This should make the trick.
Related
In FourthViewController, I have a slider, which has values ranging from 1 to 1000. The value that is set gets sent via the delegate to PatternViewController, where it should be used to do sth (I put the print for testing purposes).
I've worked with delegates before and it was all ok, checked the code multiple times and multiple answers here on stack, I can't seem to find the issue. Any help would be much appreciated
update: I have added a button so that it would be easier to track along. It turns out that by pressing first time the button, nothing happens. but if I first checkout the PatternViewController, then I go back to FourthViewController and press the button, the delegate gets triggered. anyone got any idea on why is this happening?
FourthViewController
import UIKit
class FourthViewController: UIViewController {
//MARK: Outlets
#IBOutlet var persistenceButton: UIButton!
#IBOutlet var persistenceSlider: UISlider!
#IBOutlet var persistenceLabel: UILabel!
weak var delegate: FourthViewControllerDelegate?
//MARK: Stored Properties - Constants
let userDefaults = UserDefaults.standard
let keyName = "sliderValue"
//MARK: Initializer
override func viewDidLoad() {
super.viewDidLoad()
loadSliderValue()
initialSetUp()
}
//MARK: Actions
#IBAction func handleValueChanged(_ sender: UISlider) {
updateLabel()
persistSliderValue(value: persistenceSlider.value, key: keyName)
}
//MARK: Methods
func updateLabel() {
persistenceLabel.text = String(format: "%.2f", persistenceSlider.value)
}
func persistSliderValue(value: Float, key: String) {
userDefaults.set(value, forKey: key)
}
func loadSliderValue() {
let persistedValue = userDefaults.float(forKey: keyName)
persistenceSlider.value = persistedValue
updateLabel()
}
}
func initialSetUp() {
persistenceButton.addTarget(self, action: #selector(handleButtonPressed), for: .touchUpInside)
}
#objc func handleButtonPressed() {
delegate?.valueChanged(value: persistenceSlider.value)
}
}
PatternViewController
import UIKit
class PatternViewController: UIViewController, FourthViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setUp()
}
func setUp() {
if let tabBar = self.tabBarController, let viewController = tabBar.viewControllers, let fourthViewController = viewController[3] as? FourthViewController {
fourthViewController.delegate = self
}
}
func valueChanged(value: Float) {
print(value)
}
}
It depends upon how you instantiated the tab view controller. If you do it with storyboards, for example, the view controllers for the respective tabs are instantiated lazily, only instantiated as the user taps on them. (This helps reduce latency resulting from instantiating all four of the tabs’ view controllers.)
While you theoretically could go ahead and have the tab bar controller instantiate the four view controllers programmatically up front, rather than just-in-time via the storyboard, I might instead consider specifying a UITabBarControllerDelegate for the tab bar controller. Have the tab bar controller’s delegate method update the relevant tab’s view controller’s model.
Here is an example with two tabs, the first has a slider and the second has a label that displays the slider’s value. In this simplified example, I’ve moved the model object (the value associated with the slider) into the tab bar controller, and it passes it to the second view controller when you select the associated tab.
// TabViewController.swift
import UIKit
class TabBarController: UITabBarController {
var value: Float = 0.5
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}
}
// MARK: - UITabBarControllerDelegate
extension TabViewController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
guard let viewController = viewController as? SecondViewController else { return }
viewController.value = value
}
}
And
// FirstViewController.swift
import UIKit
class FirstViewController: UIViewController {
#IBOutlet weak var slider: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
guard let tabBarController = tabBarController as? TabViewController else { return }
slider.value = tabBarController.value
}
#IBAction func didAdjustSlider(_ sender: UISlider) {
guard let tabBarController = tabBarController as? TabViewController else { return }
tabBarController.value = sender.value
}
}
And
// SecondViewController.swift
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var label: UILabel!
var value: Float = 0 { didSet { updateLabel() } }
let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .percent
return formatter
}()
override func viewDidLoad() {
super.viewDidLoad()
updateLabel()
}
func updateLabel() {
label?.text = formatter.string(for: value)
}
}
Probably needless to say, I not only set the base view controller class for the two tab’s view controllers, but also set the base class for the tab bar controller’s storyboard scene to the above TabBarController.
Now Using the Delegate Pattern to transfer the data:
I have implemented this function:
var overviewController = addOverview()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
overviewController.delegate = self
}
func setValues(){
overview.name = buildingName.text!
overview.city = city.text!
overview.contact = contactName.text!
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM-dd-yyyy"
overview.date = dateFormatter.string(from: datePicker.date)
overview.email = email.text!
overview.building = buildingTypes[buildingPicker.selectedRow(inComponent: 0)]
overview.freq = cleaningFreqs[cleaningFreqPicker.selectedRow(inComponent: 0)]
overview.phone = phoneNumber.text!
overview.sft = sqFt.text!
overview.state = State.text!
overview.street = street.text!
}
// MARK: - Navigation
override func willMove(toParent parent: UIViewController?) {
super.willMove(toParent:parent)
if parent == nil {
setValues()
print("Hello")
delegate?.setValues(obj: overview)
}
}
And here i the setValues from the protocol I wrote:
func setValues(obj: overviewProps){
overview = obj
}
However after printing one of the properties of the object, the data has not been transferred.
You can use Delegate pattern or use Observer. See these examples for help.
Delegate: https://www.appcoda.com/swift-delegate/
Observer: NSNotificationCenter addObserver in Swift
Edit: using delegate based on your code
Protocol
protocol SetValueDelegate {
func didFinishSetValue(obj: Overview)
}
First view controller
class FirstViewController: UIViewController, SetValueDelegate {
var secondViewController = SecondViewContrller()
override func viewDidLoad() {
super.viewDidLoad()
secondViewController?.delegate = self
}
func didFinishSetValue(obj: Overview) {
// when it comes back to the first screen you can use ->> obj data
}
}
Second view controller
class SecondViewController: UIViewController {
var delegate: SetValueDelegate?
override func viewDidLoad() {
super.viewDidLoad()
…
}
override func willMove(toParent parent: UIViewController?) {
super.willMove(toParent: parent)
if parent == nil {
setValues()
…
self.delegate?.didFinishSetValue(obj: overview)
}
}
}
There are many ways to pass/exchange data between two viewControllers. I do not know how your implementation look like. But you can use delegate pattern to achieve your target. Use a delegate and trigger the methods when you want to pass data.
As I said there are other solutions, too. But it is up to you, which one you want to choose.
The proper way to pass data between the current view and the root view with your navigation design is to trigger an unwind segue.
Have a look at the follow article. It is in Swift 3, but it should give you a good start in translating that into Swift 4.2
https://link.medium.com/b4eFIx7LaV
I am setting a variable in another viewcontroller and it is working inside didSet but in viewdidload it prints out nil
ViewController 1
let methodView = MethodViewController()
methodView.items = itemsSelected
presentDetail(newVC)
ViewController 2
class MethodViewController: UIViewController {
var items: [[String: Any]]? {
didSet {
print(items) // PRINTS OUT ITEMS NORMALLY
}
}
override func viewDidLoad() {
super.viewDidLoad()
print(items) // PRINTS OUT NIL
}
}
it is returning nil because you are setting the items by using one instance and when you present the screen and display your methodViewController. then you pass a new instance for same. instead set the items by using the same instance which you are using to present your controller
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let methodView = StackProgramViewController() //instance 1 //your case methodView
methodView.items = itemsSelected //value is set and printed
}
#IBAction func present(_ sender: Any) {
let newVc = self.storyboard?.instantiateViewController(withIdentifier: "stack") as! StackProgramViewController //instance 2 while presenting //your case newVc
newVc.items = itemsSelected //set value here with second instance nstaead of creating methodView
present(newVc, animated: true, completion: nil)
}
hope it helps
Why not use an initializer method on your class, and then pass it explicitly to the present method like so:
MethodViewController
class MethodViewController: UIViewController {
var items: [[String: Any]]?
init(withItems items:[[String: Any]]?) {
self.items = items
super.init(nibName: "MethodViewController", bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
print(items);
}
}
Previous View Controller
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let items = [["Stack":"Overflow"]]
let methodView = MethodViewController(withItems: items)
self.present(methodView, animated: true, completion: nil)
}
}
I have tested it and it will print out the items passed, also note the require init() method which the compiler forces you to have. You will also need to specify the bundle and nibName depending on how you set up your views.
I should note this approach is if you aren't trying to load from the storyboard, but rather from a separate .xib file with the same name.
So I have the following layout for my iOS app.
What I'm intending to do is put a table view in the purpleVC to control the Green viewcontroller...the top peachVC will have text in it which will need to change. I'm just not sure how to control one view controller from another. This includes having the purple slide in and out when a button on the GreenVC is clicked. I know there are classes out there to do this however I want to learn as well.
TESTING DELEGATES:
MAINVIEW CONTROLER
import UIKit
protocol Purpleprotocol {
func buttonpressed()
}
protocol Greenprotocol {
}
extension UIViewController {
func alert(message: String, title: String = "") {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let OKAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(OKAction)
self.present(alertController, animated: true, completion: nil)
}
}
class MainViewController: UIViewController,Purpleprotocol,Greenprotocol {
weak var infoNav : UINavigationController?
weak var greenVC: GreenVC?
weak var purpleVC: PurpleVC?
weak var peachVC: PeachVC?
func buttonpressed() {
alert(message: "This is message")
print("buttonpressed")
let date = Date()
let calendar = Calendar.current
let hour = calendar.component(.hour, from: date)
let minutes = calendar.component(.minute, from: date)
greenVC?.greenlabel.text = String(hour) + ":" + String(minutes)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "contentSegue" {
let infoNav = segue.destination as! UINavigationController
}
}
}
PURPLEVIEW CONTROLER
class PurpleVC: UIViewController {
var delegate: Purpleprotocol?
#IBAction func butclick(_ sender: UIButton) {
alert(message: "infunction")
delegate?.buttonpressed()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
Thanks
R
It does depend on the case but to see a few examples:
A) You can connect it through the delegates. Your main view controller has 3 child view controllers to which it should report changes. It should also assign itself as a delegate to all 3 child controllers where it will get all notifications for events. This would look like
func purpleViewController(sender: PVC, selectedItem: Item) {
self.greenViewController.showItem(item: selectedItem)
self.peachVC.showItem(item: selectedItem)
}
func purpleViewController(sender: PVC, shouldSetMenuClosed closed: Bool) {
self.menuConstraint.constant = closed ? targetWidth : 0.0
}
B) You may have a data model which controls the whole screen and has a delegate for each of the children. The model will report any changes to its delegates so they may react accordingly. Main view controller would create an instance of this model when it loads and pass it to all of the children view controllers. The children would then directly manipulate the model:
In green controller:
func onTap() {
mode.menuShown = !mode.menuShown
}
In model:
var menuShown: Bool = true {
didSet {
self.menuDelegate.model(self, changedMenuShownStateTo: menuShown)
}
}
In main view controller:
func model(_ sender: Model, changedMenuShownStateTo shown:Bool) {
self.menuConstraint.constant = shown ? 0.0 : targetWidth
}
C) You can use notifications where any of the controllers may post to notification center a custom notification and other controllers may observe the notifications and act accordingly.
There are many other ways in doing so but these probably most popular. See if any of them fits you...
Delegation.
Your MainViewController will become a delegate to each of the embedded VC's that want to pass back information. From your description you'll need two delegate relationships:
protocol PurpleProtocol {
func selected(row: Int, text: String)
}
protocol GreenProtocol {
func slideButtonPressed()
}
Have MainViewController implement these protocols. Give identifiers to the embed segues. You can find them in the Document Outline view. In prepareForSegue, retain pointers to the embedded VC's and pass your self as the delegate:
class MainViewController: UIViewController, PurpleProtocol, GreenProtocol {
weak var greenVC: GreenViewController?
weak var purpleVC: PurpleViewController?
weak var peachVC: PeachViewController?
func selectedRow(row: Int, text: String) {
// do something useful
}
func slideButtonPressed() {
// slide purple view in or out depending on current state
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "EmbedPurple" {
let dvc = segue.destination as! PurpleViewController
purpleVC = dvc
dvc.delegate = self
}
else if segue.identifier = "EmbedGreen" {
let nav = segue.destination as! UINavigationController
let dvc = nav.topViewController as! GreenViewController
greenVC = dvc
dvc.delegate = self
} else if segue.identifier = "EmbedPeach" {
peachVC = segue.destination as! PeachViewController
}
}
}
In your embedded VC's, add a delegate pointer and call the delegate with the protocol method when it is time:
class GreenViewController: UIViewController {
weak var delegate: GreenProtocol?
#IBAction slideButtonPressed(sender: UIButton) {
delegate?.slideButtonPressed()
}
}
class PurpleViewController: UITableViewController {
weak var delegate: PurpleProtocol?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
delegate?.selected(row: indexPath.row, text: modelArray[indexPath.row])
}
}
I am working on a settings view for a basic app. Basic, in there is just one switch in the settings view for the user. The switch setting is saved with NSUserDefault. I use a delegate to send the switch signal from the settings view to the main view. The delegation works properly.
The UI is basic. On the main view, a label will read On in green (if the switch is on) and Off in red (if the switch is off.) There is a setting button in the top right that will segue (settingsSegue) to the settings UITableViewController, where the UISwitch is located.
The problem is loading up the NSUserDefault once the app loads. In viewDidLoad, I check to see if there's a value saved for the switch key. If there is, load it up. If not, set it to false (in the storyboard, the switch is set to false as default.) The Switch Status loads up as Off every time. Even if the default value is On. This shouldn't be happening.
ViewController.swift:
import UIKit
var nsDefaults = NSUserDefaults.standardUserDefaults()
class ViewController: UIViewController, SettingsViewControllerDelegate {
var onFromMain = Bool()
#IBOutlet weak var switchStateLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
if let mySavedKey = nsDefaults.objectForKey("savedSwitchSettingDefault") {
// A value exists. Load it up.
nsDefaults.objectForKey("savedSwitchSettingDefault")
print("The switch is set! \(mySavedKey)")
checkSwitchState()
}
else {
// Nothing stored in NSUserDefaults yet. Set switch to False.
nsDefaults.setBool(false, forKey: "savedSwitchSettingDefault")
checkSwitchState()
}
}
func myVCDidFinish(controller: SettingsViewController, switchState: Bool) {
onFromMain = switchState.boolValue
checkSwitchState()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "settingsSegue" {
let nav = segue.destinationViewController as! UINavigationController
let secondVC = nav.topViewController as! SettingsViewController
secondVC.delegate = self
}
}
func checkSwitchState() {
if onFromMain {
switchStateLabel.text = "On"
switchStateLabel.textColor = UIColor.greenColor()
}
else {
switchStateLabel.text = "Off"
switchStateLabel.textColor = UIColor.redColor()
}
}
}
SettingsViewController.swift:
import UIKit
protocol SettingsViewControllerDelegate {
func myVCDidFinish(controller: SettingsViewController, switchState: Bool)
}
class SettingsViewController: UITableViewController {
var delegate: SettingsViewControllerDelegate? = nil
#IBOutlet weak var switchOutlet: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
switchOutlet.on = nsDefaults.boolForKey("savedSwitchSettingDefault")
}
#IBAction func closeSettingsPageBarButtonItemPressed(sender: UIBarButtonItem) {
if (delegate != nil) {
delegate!.myVCDidFinish(self, switchState: switchOutlet.on)
self.dismissViewControllerAnimated(true, completion: nil)
}
}
#IBAction func switchPressed(sender: UISwitch) {
// Tap the switch to change the setting.
nsDefaults.setBool(switchOutlet.on, forKey: "savedSwitchSettingDefault")
}
}
I believe my problem lies somewhere in loading up the default key for "savedSwitchSettingDefault". Is this correct? Or does the issue lie elsewhere in the code?
You can tidy things up quite a bit by relying on the fact that the default you want is false and that boolForKey gives you false when the key isn't present.
Also, by accessing the setting in viewWillAppear you can avoid the need for the delegate callback.
ViewController.swift
import UIKit
class ViewController: UIViewController {
let nsDefaults = NSUserDefaults.standardUserDefaults()
var onFromMain = false
#IBOutlet weak var switchStateLabel: UILabel!
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.onFromMain = self.nsDefaults.boolForKey("savedSwitchSettingDefault")
self.checkSwitchState()
}
func checkSwitchState() {
if self.onFromMain {
switchStateLabel.text = "On"
switchStateLabel.textColor = UIColor.greenColor()
}
else {
switchStateLabel.text = "Off"
switchStateLabel.textColor = UIColor.redColor()
}
}
}
SettingsViewController.swift:
import UIKit
class SettingsViewController: UITableViewController {
let nsDefaults = NSUserDefaults.standardUserDefaults()
#IBOutlet weak var switchOutlet: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.switchOutlet.on = self.nsDefaults.boolForKey("savedSwitchSettingDefault")
}
#IBAction func closeSettingsPageBarButtonItemPressed(sender: UIBarButtonItem) {
self.dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func switchPressed(sender: UISwitch) {
// Tap the switch to change the setting.
self.nsDefaults.setBool(self.switchOutlet.on, forKey: "savedSwitchSettingDefault")
}
}
When retrieving the bool value from User Defaults, boolForKey will return false if the value doesn't exist. So in this case there's no need for unwrapping. From the documentation:
If a boolean value is associated with defaultName in the user defaults, that value is returned. Otherwise, false is returned.
If the value is getting set (you are sure of it), and the behavior of the app is not working correctly your problem might lie elsewhere.
I would recommend using another approach, declare your "onFromMain" as an optional boolean, then unwrap it when you need it.
var onFromMain: Bool?
...
func checkSwitchState() {
//- This will unwrap your optional or set false if its nil
let switchSate = onFromMain ?? false
//- Then you can set the values based on the value (or the default false)
switchStateLabel.text = switchState ? "On" : "Off"
switchStateLabel.textColor = switchState ? UIColor.greenColor() : UIColor.redColor()
}
Then attach the debugger with a breakpoint and see if the value is being unwrapped or if its defaulting to false.
Also, you are setting your delegate only when the segue is called, depends of the scenario, and if i understand you correctly, you migt not get the value until you have actually navigated to the settings view. So when opening the app (without navigating to the settings view) the onFromMain will never get populated.
Alternatively you can fetch the value on the view did load method to get it straight away when you load the app.