Cannot get value of switch from different VC Swift - swift

I want to access switch value with the code below. I get nil value. From the second View controller I get settings value to display content according to the value - englishRef.
SetConViewController is my settings view controller from where I want to get my value.
let languageSettingsRef = SetConvViewController()
var englishRef = ""
// Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
if languageSettingsRef.swithEnglish.isOn{
englishRef = "yes"
}
In the View Controller below, there are switches for the settings
class SetConvViewController: UIViewController {
var conversationSettingsReference: DatabaseReference!
let userID = Auth.auth().currentUser?.uid
var engS = "engS"
var engGoster = ""
#IBOutlet weak var swithEnglish: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
if let eng2 = defaults.value(forKey: engS) {
swithEnglish.isOn = eng2 as! Bool
}
}
let defaults = UserDefaults.standard
#IBAction func switchEng(_ sender: UISwitch) {
defaults.set(sender.isOn, forKey: engS)
}
}
I get nil when I want to access value from this class.

First It's nil because you don't load the VC from storyboard or xib
Second this line
let languageSettingsRef = SetConvViewController()
is not the presented one , so use delegate or any other observing technique to get current value of the presented VC
//
class SettingsViewController: UIViewController {
var setVc:SetConvViewController?
}
when you present SettingsViewController with with segue / push , set
settInstance.setVc = self
then ask the var about the switch value
if self.setVc.swithEnglish.isOn{
englishRef = "yes"
}
Making the var as Bool is another better option

Related

how to append data to struct on another viewcontroller whenever i click a button and keep the prev data persistent

I have this button whenever I click this button, I want to append new data to a struct on another viewcontroller and show it on tableview and when I go back to add more data the prev data wont gone. In my case, I can only add 1 data, after I go back and add more data, the previous one is gone.
ViewController segue code and storyboard:
override
viewcontroller
confirmviewcontroller code and storyboard:
class ConfirmViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
struct menu {
let menuImages : String
let menuPrice : Int
}
#IBOutlet weak var totalLbl: UILabel!
#IBOutlet weak var confirmBtn: UIButton!
#IBOutlet weak var cartTable: UITableView!
var data: [menu] = [
**confirmcontroller storyboard**
when you go back to previous controller your VC is removed from navigation controller so the object of ConfirmViewController removed from memory so when you again pushed ConfirmViewController in navigation stack that new object for that class again initialised and at that time array of data is Empty and before pushing you again add one item in data which you are adding while moving your segue. This is why your previous items are removing .SUGGESTED SOLUTION: If you want to hold all items data than you can define another class for Holding Data and that will not be clear from memory and when your ConfirmViewController appears get all list from that class and populate that data
class DataRack : NSObject{
static let sharedManager : DataRack = DataRack()
var itemsData : [menu] = []
}
One more thing when you are done with these items you have to clear it from that DataRack Class
You have option for this
if you want to store data permanently then store into UserDefaults like this.
First screen :---
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "main1btn") {
let vc = segue.destination as! ConfirmViewController
}
if (segue.identifier == "main2btn") {
let vc = segue.destination as! ConfirmViewController
}
if (segue.identifier == "main3btn") {
let vc = segue.destination as! ConfirmViewController
}
var newData = [menu]()
if let data = UserDefaults.standard.object(forKey: "yourKey") as? [menu] {
newData.append(data)
} else {
newData.append(menu(menuImages: "main3",menuPrice: 120000))
}
UserDefaults.standard.setValue(value: newData, forKey: "yourKey")
}
Second Screen :--
class ConfirmViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var data = [menu]()
override func viewDidLoad() {
super.viewDidLoad()
if let data = UserDefaults.standard.object(forKey: "yourKey") as? [menu]{
data = data
}
}
}
OR
if you do not want to store data then take
struct menu {
let menuImages : String
let menuPrice : Int
}
and
var data: [menu] = [
]
variable globally
removeAll while you not need data

swift delegate variable is always nil

I'm a relatively new developer and have used protocols/delegates fairly regularly but I am stuck on getting this one to implement properly. I have a view within my view controller that is assigned a class for a StarRatingView. I need to capture within the viewcontroller class the float value that the StarRatingView generates.
The star rating is working fine. I'm just not able to set the delegate variable for some reason.
here's where I define the protocol and the delegate variable.
protocol StarRatingProtocol {
func receiveStarRating(_ touchedStarRating: Float)
}
#IBDesignable
class StarRatingView: UIView {
var delegate: StarRatingProtocol? //THIS IS ALWAYS NIL NOT GETTING SET
Here's where I call the delegate function
fileprivate func touched(touch: UITouch, moveTouch: Bool) {
guard !moveTouch || lastTouch == nil || lastTouch!.timeIntervalSince(Date()) < -0.1 else { return }
print("processing touch")
guard let hs = self.hstack else { return }
let touchX = touch.location(in: hs).x
let ratingFromTouch = 5*touchX/hs.frame.width
var roundedRatingFromTouch: Float!
switch starRounding {
case .roundToHalfStar, .ceilToHalfStar, .floorToHalfStar:
roundedRatingFromTouch = Float(round(2*ratingFromTouch)/2)
case .roundToFullStar, .ceilToFullStar, .floorToFullStar:
roundedRatingFromTouch = Float(round(ratingFromTouch))
}
self.rating = roundedRatingFromTouch
lastTouch = Date()
guard delegate != nil else {return}
delegate!.receiveStarRating(roundedRatingFromTouch)
}
This is my view controller class where I set the protocol and try to set the delegate value . Should I be setting the delegate value somewhere other than viewdidload?
class LogController: UIViewController, StarRatingProtocol {
#IBOutlet weak var starRatingView: StarRatingView!
#IBOutlet weak var labelStarRating: UILabel!
let starRating = StarRatingView()
var testRating: Float?
override func viewDidLoad() {
super.viewDidLoad()
starRating.delegate = self
}
The problem is that when you say let starRating = StarRatingView(), that is a new and different StarRatingView that you are creating right now. That's not what you want; you want the StarRatingView that already exists in the interface. Presumably, if the outlet is hooked up properly, that would be self.starRatingView. That is the StarRatingView whose delegate you need to set.
Another (unrelated) problem is that the delegate needs to be weak. Otherwise there will be a retain cycle and a memory leak.

Swift OSX - Delegate protocol function returns nil, crashes when unwrapping textfield value

I'm working on an OSX app with Swift which makes use of an NSSplitView which holds two view controllers: "TableViewController" and "EntryViewController". I'm using delegates in order to transmit a custom NSObject ("Entry") on click from TableViewController up to the SplitViewController, then back down to the EntryViewController.
My problem is this: When the Entry object is received in the EntryViewController, any attempt to assign its properties to a text field value result in an unexpectedly found nil type error, never mind that the IBOutlets are properly linked, and that it can both print the Entry.property and the textfield string value (provided it is in a different, unrelated function).
I have tried many arrangements to solve this problem, which is why the current configuration might be a bit over-complicated. A delegate relation straight from Table VC to Entry VC caused the same issues.
Is there some way that the IBOutlets are not connecting, even though the view has loaded before the delegate is called? I've read many many articles on delegation—mostly for iOS—and yet can't seem to find the root of my problems. I'll be the first to admit that my grasp of Swift is a little bit piecemeal, so I am open to the possibility that what I am trying to do is simply bad/hacky coding and that I should try something completely different.
Thanks for your help!
TableViewController:
protocol SplitViewSelectionDelegate: class {
func sendSelection(_ entrySelection: NSObject)
}
class TableViewController: NSViewController {
#IBOutlet weak var searchField: NSSearchField!
#IBOutlet var tableArrayController: NSArrayController!
#IBOutlet weak var tableView: NSTableView!
var sendDelegate: SplitViewSelectionDelegate?
dynamic var dataArray = [Entry]()
// load array from .plist array of dictionaries
func getItems(){
let home = FileManager.default.homeDirectoryForCurrentUser
let path = "Documents/resources.plist"
let urlUse = home.appendingPathComponent(path)
let referenceArray = NSArray(contentsOf: urlUse)
dataArray = [Entry]()
for item in referenceArray! {
let headwordValue = (item as AnyObject).value(forKey: "headword") as! String
let defValue = (item as AnyObject).value(forKey: "definition") as! String
let notesValue = (item as AnyObject).value(forKey: "notes") as! String
dataArray.append(Entry(headword: headwordValue, definition: defValue, notes: notesValue))
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.sendDelegate = SplitViewController()
getItems()
print("TVC loaded")
// Do any additional setup after loading the view.
}
// send selection forward to entryviewcontroller
#IBAction func tableViewSelection(_ sender: Any) {
let index = tableArrayController.selectionIndex
let array = tableArrayController.arrangedObjects as! Array<Any>
let obj: Entry
let arraySize = array.count
if index <= arraySize {
obj = array[index] as! Entry
print(index)
print(obj)
sendDelegate?.sendSelection(obj)
}
else {
print("index unassigned")
}
}
}
SplitViewController:
protocol EntryViewSelectionDelegate: class {
func sendSecondSelection(_ entrySelection: NSObject)
}
class SplitViewController: NSSplitViewController, SplitViewSelectionDelegate {
var delegate: EntryViewSelectionDelegate?
#IBOutlet weak var mySplitView: NSSplitView!
var leftPane: NSViewController?
var contentView: NSViewController?
var entrySelectionObject: NSObject!
override func viewDidLoad() {
super.viewDidLoad()
// assign tableview and entryview as child view controllers
let story = self.storyboard
leftPane = story?.instantiateController(withIdentifier: "TableViewController") as! TableViewController?
contentView = story?.instantiateController(withIdentifier: "EntryViewController") as! EntryViewController?
self.addChildViewController(leftPane!)
self.addChildViewController(contentView!)
print("SVC loaded")
}
func sendSelection(_ entrySelection: NSObject) {
self.delegate = EntryViewController() //if this goes in viewDidLoad, then delegate is never called/assigned
entrySelectionObject = entrySelection
print("SVC:", entrySelectionObject!)
let obj = entrySelectionObject!
delegate?.sendSecondSelection(obj)
}
}
And Finally, EntryViewController:
class EntryViewController: NSViewController, EntryViewSelectionDelegate {
#IBOutlet weak var definitionField: NSTextField!
#IBOutlet weak var notesField: NSTextField!
#IBOutlet weak var entryField: NSTextField!
var entryObject: Entry!
override func viewDidLoad() {
super.viewDidLoad()
print("EVC loaded")
}
func sendSecondSelection(_ entrySelection: NSObject) {
self.entryObject = entrySelection as! Entry
print("EVC:", entryObject)
print(entryObject.headword)
// The Error gets thrown here:
entryField.stringValue = entryObject.headword
}
}
You don't need a delegate / protocol since there is a reference to EntryViewController (contentView) – by the way the instance created with EntryViewController() is not the instantiated instance in viewDidLoad.
Just use the contentView reference:
func sendSelection(_ entrySelection: NSObject) {
contentView?.sendSecondSelection(entrySelection)
}

Call a func from another class in swift

I would like to call a function which is coded on another class.
So far I have made a struct on the file structs.swift for my data:
struct defValues {
let defCityName: String
let loadImages: Bool
init(defCity: String, loadImgs: Bool){
self.defCityName = defCity
self.loadImages = loadImgs
}
}
I have made the file Defaults.swift containing:
import Foundation
class DefaultsSet {
let cityKey: String = "default_city"
let loadKey: String = "load_imgs"
func read() -> defValues {
let defaults = NSUserDefaults.standardUserDefaults()
if let name = defaults.stringForKey(cityKey){
print(name)
let valuesToReturn = defValues(defCity: name, loadImgs: true)
return valuesToReturn
}
else {
let valuesToReturn = defValues(defCity: "No default city set", loadImgs: true)
return valuesToReturn
}
}
func write(city: String, load: Bool){
let def = NSUserDefaults.standardUserDefaults()
def.setObject(city, forKey: cityKey)
def.setBool(load, forKey: loadKey)
}
}
in which I have the two functions read, write to read and write data with NSUsersDefault respectively.
On my main ViewController I can read data with:
let loadeddata: defValues = DefaultsSet().read()
if loadeddata.defCityName == "No default city set" {
defaultCity = "London"
}
else {
defaultCity = loadeddata.defCityName
defaultLoad = loadeddata.loadImages
}
But when I try to write data it gives me error. I use this code:
#IBOutlet var settingsTable: UITableView!
#IBOutlet var defaultCityName: UITextField!
#IBOutlet var loadImgs: UISwitch!
var switchState: Bool = true
#IBAction func switchChanged(sender: UISwitch) {
if sender.on{
switchState = true
print(switchState)
}else {
switchState = false
print(switchState)
}
}
#IBAction func saveSettings(sender: UIButton) {
DefaultsSet.write(defaultCityName.text, switchState)
}
You need an instance of the DefaultsSet class
In the view controller add this line on the class level
var setOfDefaults = DefaultsSet()
Then read
let loadeddata = setOfDefaults.read()
and write
setOfDefaults.write(defaultCityName.text, switchState)
The variable name setOfDefaults is on purpose to see the difference.
Or make the functions class functions and the variables static variables and call the functions on the class (without parentheses)
From the code you posted, it seems you either need to make the write method a class method (just prefix it with class) or you need to call it on an instance of DefaultsSet: DefaultsSet().write(defaultCityName.text, switchState).
Another issue I found is that you also need to unwrapp the value of the textField. Your write method takes as parameters a String and a Bool, but the value of defaultCityName.text is an optional, so String?. This results in a compiler error.
You can try something like this:
#IBAction func saveSettings(sender: UIButton) {
guard let text = defaultCityName.text else {
// the text is empty - nothing to save
return
}
DefaultsSet.write(text, switchState)
}
This code should now compile and let you call your method.
Let me know if it helped you solve the problem

Best temporary storage measure

I am working on an app where i needed to move between view controllers a lot of time mostly making a loop but now i have to store some data(simple variable's and array's).
I am currently storing them in the app delegate but i don't know if this is a great idea. i have looked online but i couldn't really tell what is the best solution for me.
This is how i have it set up:
Appdelegate:
var aantalSpelers: Int!
var namenSpelers = [String]()
var youself = KaartenSpeler()
var player2 = KaartenSpeler()
var player3 = KaartenSpeler()
var player4 = KaartenSpeler()
var player5 = KaartenSpeler()
var player6 = KaartenSpeler()
var Vragen = [[0,0,0,0,0,5]]
var VragenOnbekend = [[6,0,0,0,0,0]]
var VragenInformatie = [[["Spelletjeskamer",""],["Keuken",""],["Garage",""],["Binnenplaats",""],["Zitkamer",""],["Slaapkamer",""],["Studeerkamer",""],["Eetkamer",""],["Badkamer",""]], [["De Wit",""],["Pimpel",""],["Blaauw van Draet",""],["Roodhart",""],["Groenewoud",""],["Van Geelen",""]], [["Loden pijp",""],["Pistool",""],["Engelse sleutel",""],["Dolk",""],["Touw",""],["Kandelaar",""]]]
var EersteKeerMainScreen = true
and in the VC:
func Appdelegate() -> AppDelegate {
return UIApplication.sharedApplication().delegate as! AppDelegate
}
let sections = ["Locaties","Personages","Wapens"]
var aantalSpelers: Int!
var namenSpelers = [String]()
var eersteKaarten = [[Int(),Int()]]
var youself: KaartenSpeler!
var player2: KaartenSpeler!
var player3: KaartenSpeler!
var player4: KaartenSpeler!
var player5: KaartenSpeler!
var player6: KaartenSpeler!
//vraag is: [numberVrager,numerGevraagde,numerLocatie,numerPersonage,numerWapen,0=lietgeenkaartzien - 1=locatie, - 2=personage - 3=wapen - 4=onbekend]
var Vragen = [[]]
var VragenOnbekend = [[]]
var VragenInformatie = []
var EersteKeerMainScreen = false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//Get information back from appdelegate
aantalSpelers = Appdelegate().aantalSpelers
namenSpelers = Appdelegate().namenSpelers
youself = Appdelegate().youself
player2 = Appdelegate().player2
player3 = Appdelegate().player3
player4 = Appdelegate().player4
player5 = Appdelegate().player5
player6 = Appdelegate().player6
Vragen = Appdelegate().Vragen
VragenOnbekend = Appdelegate().VragenOnbekend
VragenInformatie = Appdelegate().VragenInformatie
EersteKeerMainScreen = Appdelegate().EersteKeerMainScreen
And is this actually a viable option?
P.S. sorry for my bad english
The approach I would use (may not be the best) in this case is to pass the variables with you using segues through the use of self.performSegueWithIdentifier("segueTitle", sender).
Then you can in a prepareForSegue actually handle the objects you moving from this VC to another VC.
e.g.
/*Global Variable is numbersArray*/
var numbersArray: [String] = []
and whenever you finish writing your logic for the view controller, use:
self.performSegueWithIdentifier("segueTitle", sender: self)
Now to handle what you want to pass to the next VC, add this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "segueTitle") {
// pass data to next view
let destinationVC = segue.destinationViewController as! YourNextViewController
destinationVC.numbersArray = self.numbersArray;
}
}
Things to keep in mind is that you must have the same global variable in next VC as you can see we are assigning current to next in the above example. Also make sure that segue is created and connected via Storyboard and replace segueTitle with title given to it.
Update: E.g. TransferClass
class TranferClass {
var numbersArray:[String] = []
}
then instantiate this class and send it to next VC.Eg.:
in VC 1:
/*in global scope*/
var transferObject = TransferClass()
in the same VC, set the array for example:
self.transferObject.numbersArray[0] = "Hello"
then trigger the segue:
self.performSegueWithIdentifier(...)
then handle the passing:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "segueTitle") {
// pass data to next view
let destinationVC = segue.destinationViewController as! YourNextViewController
destinationVC.transferObject = self.transferObject;
}
}
Your initial thought in that you probably shouldn't store these in the AppDelegate. I recommend either creating a new class or a struct that can hold these values.
But the other thing I noticed is that you have a separate class already for KaartenSpeler so you definitely want to look out for retain cycles and make sure that these classes don't hold strong references to one another.