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.
Related
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
I'm very new to programming.
I am trying to update an object in my Realm database but I get always an error.
I have tried to find the issue but I can't find anyone with a similar issue.
What I'm trying to do is:
I have a Game-Score-App.
It should display the names on Tab1 and on the Tab2 I want to give the user the ability, to change the names of the players. As soon as the ViewDidDisappear I want to write the changes to Realm.
I already figured out how to update the names in the database. And it works properly the first time.
But as soon as I go a second time on the Tab2 and go back to Tab1 again, I get the message "Primary key can't be changed after an object is inserted."
Any Ideas?
class Games: Object {
#objc dynamic var game_id = UUID().uuidString
#objc dynamic var gameName: String = ""
var playerNames = List<String>()
override class func primaryKey() -> String? {
return "game_id"
}
}
class FirstPageVC: UIViewController {
#IBOutlet var playerNameLabels: [UILabel]!
#IBOutlet weak var gameNameLabel: UILabel!
let realm = try! Realm()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
let games = realm.objects(Games.self)
gameNameLabel.text = games[0].gameName
for i in 0...playerNameLabels.count - 1 {
playerNameLabels[i].text = games[0].playerNames[i]
}
}
}
class SecondPageVC: UIViewController {
#IBOutlet var playerNameTextbox: [UITextField]!
#IBOutlet weak var gameNameTextbox: UITextField!
#IBOutlet weak var numberOfIndex: UITextField!
let realm = try! Realm()
var playerNames: [String] = []
var gameName: String = ""
var game = Games()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
if realm.objects(Games.self).count != 0 {
let games = realm.objects(Games.self)
gameNameTextbox.text = games[0].gameName
for i in 0...playerNameTextbox.count - 1 {
playerNameTextbox[i].text = games[0].playerNames[i]
}
}
}
#IBAction func addButton(_ sender: UIButton) {
gameName = gameNameTextbox.text!
for i in 0...playerNameTextbox.count - 1 {
playerNames.append(playerNameTextbox[i].text!)
}
let items = realm.objects(Games.self)
let number = Int(numberOfIndex.text!)
game.game_id = items[number!].game_id
game.gameName = gameName
game.playerNames.append(objectsIn: playerNames)
try! realm.write {
realm.add(game, update: .modified)
}
}
}
The problem is your Realm object structure. Anything that could possibly ever be changed should not be used as a primary key.
Also note from the Realm Documentation
Once an object with a primary key is added to a Realm, the primary key
cannot be changed.
To expand on that, it's often best practice to disassociate an objects key (e.g. primary key) from the rest of the properties of an object.
Here's how to do that
class Games: Object{
#objc dynamic var game_id = UUID().uuidString
#objc dynamic var gameName: String = ""
var playerNames = List<String>()
override class func primaryKey() -> String? {
return "game_id"
}
}
UUID().uuidString will generate a unique string for every object that's created and will look something like this string
CDEA69EA-AC84-4465-ABE3-DDA29D31B925
Once the object is created, you can use it to load that specific object or update it's properties.
See Objects with Primary Keys
Here's how to change the game name
let item = realm.object(ofType: Game.self, forPrimaryKey: "CDEA69EA-AC84-4465-ABE3-DDA29D31B925")!
try! realm.write {
game.gameName = "Pwn You!"
}
Try this solution:
Replace the code in viewDidDisappear after the end of for loop with the following code:
if let gameInRealm = realm.objects(Game.self).first{
try! realm.write {
gameInRealm.gameName = gameName
gameInRealm.playerNames = playerNames
}
}else{
game.gameName = gameName
game.playerNames.append(objectsIn: playerNames)
realm.add(game)
}
Explanation (if needed):
The code changes the existing Game properties in case a Game object exists. Otherwise, it creates a new one with the new properties.
Therefore, the else statement should get executed the first time you leave SecondPageVC, and then the if statement will get triggered every other time you leave SecondPageVC.
Heloo, i have this problem when im using contaienerView with static tableview. i want to pass my data from my main view controller to my tableview, and im having this break where it said my data is null, but it wasnt null because i already fill that data that i want to pass in. im using firebase and dictionary. heres my code :
my main controller :
class MainController: UITableViewController, AddPatientController {
private var patientLists = [PatientList]() // empty array buat isi list yg isinya nama pasien
var Segue : String = "PatientName"
var Segue2 : String = "PatientNotes"
let user : User = Auth.auth().currentUser!
private var rootRef : DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
self.rootRef = Database.database().reference()
populateList()
}
private func populateList() {// 5. func buat fetch data dari db ke hp
self.rootRef.child(self.user.emailWithoutSpecialChar).observe(.value) { (snapshot) in
self.patientLists.removeAll()
let pasienListDict = snapshot.value as? [String:Any] ?? [:] //7. ini berarti return buat kl dict nya kosong, ini buat akses ke valuenya yg isinya itu dict[String:Any]
for (key,_) in pasienListDict {
if let pasienlistdict = pasienListDict[key] as? [String:Any]{
if let pasienlist = PatientList(pasienlistdict) {
self.patientLists.append(pasienlist)
// ini buat ngemasukin ke dalem dictionarynya, ini buat store datanya dan ngambil datanya dari firebase db
}
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
} override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == Segue {
let nc = segue.destination as! UINavigationController
let addPatientName = nc.viewControllers.first as! AddListController
addPatientName.delegate = self
}
else if segue.identifier == Segue2 {
guard let indexPath = self.tableView.indexPathForSelectedRow else {return}
let nc = segue.destination as! PasienProfileController
nc.pasien = self.patientLists[indexPath.row]
}
}
and this is my controller that should recieve the data
class NotesController: UITableViewController, AddNotesDelegate {
var pasien : PatientList!
private var rootRef : DatabaseReference!
var Segue1 : String = "AddNotes"
var Segue2 : String = "PasienNotes"
override func viewDidLoad() {
super.viewDidLoad()
self.title = pasien.name // this is the line where my code break cause it says the data is null
self.rootRef = Database.database().reference()
}
my pasienprofilecontroller and my pasienProfileTableController( the containverView one) :
class PasienTableController: UITableViewController {
#IBOutlet weak var dataKunjunganLbl: UILabel!
#IBOutlet weak var diagnosaPasienLbl: UILabel!
#IBOutlet weak var alergiPasienLbl: UILabel!
var delegete : PasienTableControllerDelegate?
var patientList = [PatientList]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
}
class PasienProfileController: UIViewController {
#IBOutlet weak var TinggiLbl: UILabel!
#IBOutlet weak var beratLbl: UILabel!
#IBOutlet weak var GolDarahLbl: UILabel!
#IBOutlet weak var NamaLbl: UILabel!
#IBOutlet weak var ImagePic: UIImageView!
var pasien : PatientList!
#IBOutlet weak var ContainerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.ImagePic.makeRounded()
self.NamaLbl.text = pasien.name
}
this is the image of my storyboard to get a clear picture of what im trying to do
so my "Patient" view controller is my main controller, which after that it will show the profile pasien controller, which is uiview with container view that contain static tableview. and when i try to hit that " Data Kunjungan Pasien" Cell, it gets the error
so is theres something wrong with my logic? why it keep saying null while it can successfully show the data from Patient view to patientProfileView?
*this is my git if you guys wanna clone and check my error https://gitlab.com/afipermanaa/skripsi.git
Thanks for the help
I dont get it
if you want to pass the data from prepareForSegue with this code
else if segue.identifier == Segue2 {
guard let indexPath = self.tableView.indexPathForSelectedRow else {return}
let nc = segue.destination as! PasienProfileController
nc.pasien = self.patientLists[indexPath.row]
}
to NotesController why do you cast "nc" as PasienProfileController ?
its not the same class
i don't see where you set pasien value.
I hope it helps you if not please explain
cannot comment due to low rep, so I tried to explain it as clear as possible.
you may try to define that variable
private var patientLists = [PatientList]()
as static like below.
private static var patientLists = [PatientList]()
when you try to pass it through segue with this code
let nc = segue.destination as! PasienProfileController
nc.pasien = self.patientLists[indexPath.row]
your class creates a new instance of patientList array when there is 'self'. so, when you define as static, there won't be any new instance of it.
private static var patientLists = [PatientList]()
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
In the app the user types in infomations as integers into two different textfields. After a action button click the user now access two other textfields which the user again types data as integers. Now after a click on a button I want a label's text to show this typed in information from the two situations earlier
However in my code, I can't access my variables (the information from the user input) because this label's text is in a new class and the user input variables are stored in two other classes).
How can I access these variables in my new class where my label is?
An example of my code:
#IBOutlet var nextStagetwo: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func check(sender: AnyObject) {
var strValue = boxShots.text
var floatValue = Double((strValue as! NSString).integerValue)
var strValue2 = boxMakes.text
var floatValue2 = Double((strValue2 as! NSString).integerValue)
var stage = "stage 2"
var procent = floatValue2 / floatValue * 100
enter code here
I now want to access my variable procent in this class:
class stagethree: UIViewController {
#IBOutlet var stats: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func check(sender: AnyObject) {
// this is where I want to write: stats.text = "\(procent)"
// however I can't, because it doesn't register the variable "procent" because it is in another class.
You can either make a global store that stores the values for you
struct Store {
static var valueOne:Int?
}
and then in your view controllers you can set those values
Store.valueOne = 1
Other option would be to send those integers to the next view controller in your
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let destinationController = segue.destinationViewController as? SecondController {
destinationController.valueOne = valueOne
}
}