I am making a app with many Views. When I am navigating from one View to another I want the data to be sent to a third view without entering the view. I know that it is a way to do this by using this code:
let nextView = self.storyboard?.instantiateViewControllerWithIdentifier("view3") as lastViewController
nextView.data = 1103
But if I do it this way and don't move to the view the data won't be saved. How can I save the data without entering it? Do I need to open it?
In the first view:
var chosenItem:Int = 0
#IBAction func buttonClicked(sender: AnyObject) {
if sender.tag == 0 {
chosenItem = 1
} else if sender.tag == 1 {
chosenItem = 2
} else if sender.tag == 2 {
chosenItem = 3
}
}
This is how my code looks now:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "toView2" {
let nextView = segue.destinationViewController as questionAgeViewController
nextView.firstAnswer = chosenItem
}
}
In the next view I send both the first item and the second. And as you understand this is not the best way to get the information. Is there a better?
I think you might be looking for a global variable. Use this code to make the variables:
struct globalVars {
static var firstVar = "Your text"
static var secondVar = Some number
static var thirdVar = Some array
}
In a other view you can access the information like this:
let globalText = globalVars.firstVar
println(globalText)
To edit the variables just use this code:
globalVars.firstVar = "The new text"
Create a singleton class and add properties into it, as per your requirement.
Now you can set its properties from your firstViewController and access the same from SecondViewController.
class Singleton {
class var sharedInstance: Singleton {
struct Static {
static var instance: Singleton?
static var token: dispatch_once_t = 0
}
dispatch_once(&Static.token) {
Static.instance = Singleton()
}
return Static.instance!
}
}
Singleton tutorials for Swift
http://thatthinginswift.com/singletons/
http://code.martinrue.com/posts/the-singleton-pattern-in-swift
http://www.raywenderlich.com/86477/introducing-ios-design-patterns-in-swift-part-1
Related
I am using a segue to go from View Controller 1 to View Controller 2. View Controller 1 has a button that sets the persistent data when it is clicked on:
I declare a global var for user default:
let userDefault = UserDefaults()
Here is my button to set the user default to a string with text values from labels:
#IBAction func saving(_ sender: UIButton) {
let savedText = "Gallon \(gallonTextFieldOutlet.text) is equal to Litre \(litreTextFieldOutlet.text) is equal to Pint \(pintTextFieldOutlet.text)"
userDefault.setValue(savedText, forKey: "SavedConversion")
}
I then get a reference to View Controller 2 and pass this user default when the user goes from View Controller 1 to View Controller 2 via a segue:
// in view controller 2: reference to get persistent data
var volumeDataOne:String?
// in view controller 2: instantiation of my queue class to use methods
var queue = Queue<String>()
// segue action in view controller 1
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "savedVolumeData"
{
let historyVC:VolumeHistoryViewController = segue.destination as! VolumeHistoryViewController
if let value = userDefault.value(forKey: "SavedConversion") as? String {
historyVC.volumeDataOne = value
}
}
I get this in the View Controller 2 and I am trying to set this to three labels that I have in this View Controller:
func DisplayVolumeHistory() {
let labelArray = [volumeDataLabelOutlet, volumeDataLabelTwoOutlet, volumeDataLabelThreeOutlet]
if let bindingOptional = volumeDataOne
{
for index in 0..<labelArray.count
{
queue.enqueue(val: bindingOptional)
labelArray[index]?.text = queue.arr[index]
}
}
}
In my specification, I have been told that the data needs to be persistent and that only the last five data can be stored at one time. So I have a class that I have called Queue which is referenced in this function. This function gets called on the viewDidLoad of the View Controller 2.
override func viewDidLoad() {
super.viewDidLoad()
//Debug ...
volumeDataLabelOutlet.text = "na"
volumeDataLabelTwoOutlet.text = "na 2"
volumeDataLabelThreeOutlet.text = "na 3"
//...
DisplayVolumeHistory()
// Do any additional setup after loading the view.
}
I have tested my Queue class in a Playground and it works as expected. The Queue class can be seen here:
class Queue {
var arr = [T]()
func enqueue(val: T){
if(arr.count < 3) {
arr.append(val)
} else {
for i in 0..<arr.count-1 {
arr[i] = arr[i + 1]
}
arr[arr.count - 1] = val
}
}
func dequeue() -> (T?){
if (arr.isEmpty){
return nil
} else {
return arr.remove(at: 0)
}
}
}
Here is my issue that I cannot seem to figure out. In View Controller 2, all of the three labels will have persistent data, but they will all be of the same data,
For example, if I have data as follows:
DATA 1: 555
DATA 2: 700
DATA 3: 62
I would want:
LABEL 1 --> 555
LABEL 2 --> 700
LABEL 3 --> 62
However, currently it will be:
LABEL 1 --> 62
LABEL 2 --> 62
LABEL 3 --> 62
I am unsure as to why debugging. I believe it is because my persistent data in my View Controller 1 is only taking a string, which the Dictionary is overriding as I use the same key.
However, I looked at the documentation and trying to use a user default array did not solve my issue and I am unsure as to what is causing this problem.
I appreciate any guidance and help to try to solve this issue.
Thanks
You are right on your comment,
which the Dictionary is overriding as I use the same key
Each time you tap that button, you are overriding the value with the new one. So you should be seeing always on your v2, the last one.
So probably you should store an array instead of a String.
//on v1
var values = [String]()
#IBAction func saving(_ sender: UIButton) {
let savedText = "Gallon \(gallonTextFieldOutlet.text) is equal to Litre \(litreTextFieldOutlet.text) is equal to Pint \(pintTextFieldOutlet.text)"
values.append(savedText)
UserDefaults.standard.setValue(values, forKey: "SavedConversion")
}
You will pass it to v2 as you are doing now
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "savedVolumeData"
{
let historyVC:VolumeHistoryViewController = segue.destination as! VolumeHistoryViewController
if let values = userDefault.value(forKey: "SavedConversion") as? [String] {
historyVC.volumeDataOne = values //volumeDataOne now needs to be an [String]
}
}
Then on V2, go through your array and labels.
I have a view controller that shows detail for my "SkillGroup" entity. I want to use this for both viewing and editing/creating a "SkillGroup". Thus I have an optional variable skillGroup which is either unset - and nil when you are first creating the SkillGroup and before you save it, OR it is set and you will be simply viewing the SkillGroup. Here is my code
class GroupViewController:UIViewController {
var skillGroup: SkillGroup?
override func viewDidLoad() {
super.viewDidLoad()
if let skillGroup = skillGroup {
self.title = skillGroup.name
}
}
}
and in the previous view controller:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "createGroupSegue" {
let destination = segue.destination as? GroupViewController
destination?.createOrEdit = true
}
if segue.identifier == "showGroupSegue" {
if let selectedGroupPath = self.groupsTableView.indexPathForSelectedRow {
let destination = segue.destination as? GroupViewController
destination?.createOrEdit = false
let group = groups[selectedGroupPath.row]
destination?.skillGroup = group
}
}
}
If I set a breakpoint right after the call to super, and I inspect skillGroup, it says its <uninitialized>. I don't think this because its "nil" like a normal optional variable that hasn't been set. Additionally I did set the skillGroup variable in the prepare for segue code.
I really can't find much information on what means. Can someone help me out here?
I think you are inspecting the wrong variable in the debugger (the local variable instead of the instance variable).
I made a quick sample:
class ViewController: UIViewController {
public class TestClass {
var foo: String
var bar: Int
init() {
foo = "foo"
bar = 4711
}
}
public var test: TestClass?
override func viewDidLoad() {
super.viewDidLoad()
test = TestClass()
if let test = test {
print(test.foo)
}
}
}
When I set a breakpoint on the line if let test = test { I will see the following in the debugger:
Here you can see that it will print <uninitialized> when I print the description of the local test variable because the let statement on that line was not executed yet.
Please note that there is a local variable (only living inside the if scope) and an instance variable. In the debugger you will see the instance variables when you expand the self node.
How can i insert a value of textfield texts into an array of type class?
Basically, i have two view controllers one shows the list of items and code created using the following class
class Courses {
var courseName : String
var courseCode : Int
init(courseName : String, courseCode : Int){
self.courseName = courseName
self.courseCode = courseCode
}
}
//created array from that class
var courses : [Courses] = [Courses(courseName: "Unix Linux", courseCode: 101),
Courses(courseName: "ASP.Net", courseCode: 202),
Courses(courseName: "CISCO", courseCode: 203),
Courses(courseName: "Photoshop Editing", courseCode: 306)
]
Second View Controller has two text field which will allow someone to add new courses to the existing array. How can i achieve that and reload the table view with new items from a different view controller
i added var courses : [Courses] = [] on the second view controller.swift file. not sure what else to do.
I tried something like this
#IBAction func addCourseButton(_ sender: Any) {
if courseName.text != "" {
let courseCodeString = Int(courseCode.text!)
let item = Courses(courseName: "\(courseName.text)", courseCode: courseCodeString!)
courses.append(item)
for i in courses {
print(i.courseName)
}
}
}
There are two possible solutions to your problem.
Using Protocols
Using Notification Observers
In 1st case,
Add protocol in Second ViewController.swift, just above the class definition
protocol UpdateCourseAfterAddProtocol:class {
func classListUpdated(with course: Courses)
}
class SecondViewController:UIViewController {
weak var dele:UpdateCourseAfterAddProtocol? = nil
#IBAction func addCourseButton(_ sender: Any) {
if courseName.text != "" {
let courseCodeString = Int(courseCode.text!)
let item = Courses(courseName: "\(courseName.text)", courseCode: courseCodeString!)
dele?.classListUpdated(with: item)
}
}
}
Add Segue name in Main.storyboard to "firstToSecond" or any name
In first View Controller where your table View Exists (Suppose name is FirstViewController)
class FirstViewController {
// Bar button Action
#IBAction func addCoursePressed(_ send:UIBarButton) {
self.performsegue(with:"firstToSecond")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "firstToSecond" {
let toViewController = segue.destination as! SecondViewController
toViewController.dele = self
}
}
}
extension FirstViewController: UpdateCourseAfterAddProtocol {
func classListUpdated(with course: Courses) {
self.courses.append(course)
self.tableView.reloadData()
}
}
I want to pass data between two view controllers, but don't want the view to change when the users presses my save data button.
The users needs to fill in multiple data fields, and when finish can press another button to go to the second view controller.
I found many tutorials how to pass data using segue, but they all change view as soon as the 'save button is pressed'.
Any one can explain to me how to alter the code?
#Phillip Mills: here is how I used your code. (what am I doing wrong?)
code:
//////// declaring classes on FirstViewController (trying it first on only one ViewController)
class FakeVC1 {
func userInput() {
DataModel.shared.username = outbj14u.text
}
class FakeVC2 {
func viewAppears() {
if let name = DataModel.shared.username {
outbj14p.text = name
print("I have nothing to say")
}
}
}
class DataModel {
static let shared = DataModel()
var username: String?
}
////till here
//// here is where i call the functions
override func viewDidAppear(_ animated: Bool) {
FakeVC1().userInput()
FakeVC2().viewAppears()
if let xbj14p = UserDefaults.standard.object(forKey: "outbj14p") as? String
{
outbj14p.text = xbj14p
}
if let xbj14u = UserDefaults.standard.object(forKey: "outbj14u") as? String
{
outbj14u.text = xbj14u
}
////
#Phillip Mills: Below is what I have know. I think I got the code on the FirstViewController right, but the code on the Second View controller must be wrong. I don't get any errors, but the text field on the SecondViewController remains unchanged after putting input on in the FirstViewController
//// Code on the FirstViewController
class DataModel {
static let shared = DataModel()
var username: String?
}
#IBAction func savebj14p(_ sender: Any) {
outbj14p.text = inbj14p.text
DataModel.shared.username = outbj14p.text
UserDefaults.standard.set(inbj14p.text, forKey: "namebj14p")
}
//and on the SecondViewController
#IBOutlet weak var bj14u: UILabel! // connected to a label
//and
class DataModel {
static let shared = DataModel()
var username: String?
}
override func viewDidAppear(_ animated: Bool) {
if let name = DataModel.shared.username {
bj14u.text = name
}
}
In your case, don't pass data.
Create a shared object to act as your data model. When users fill in the fields, update the data model.
When the user moves to the second controller/view, that controller uses the data model object to show what it needs to.
class FakeVC1 {
func userInput() {
DataModel.shared.username = "Me"
}
}
class FakeVC2 {
func viewAppears() {
if let name = DataModel.shared.username {
print(name)
} else {
print("I have nothing to say")
}
}
}
class DataModel {
static let shared = DataModel()
var username: String?
}
FakeVC1().userInput()
FakeVC2().viewAppears()
If you need to pass value to another viewcontroller without changing the view , you can user NSNotificationCenter class
Refer this link for more details
NSNotificationCenter addObserver in Swift
what i will recommend is to use a global variable or array, you will have the info in all view controllers and you will be able to call it in your new view controller.
I'm having a menuViewController and a ContentViewController using https://github.com/romaonthego/RESideMenu. The MenuViewController contain a list of different leagues retrieved from local database. This contain a league object with following vars leagueId and name. When a league is selected it should send the data to the ContentViewController. However the problem is that the MenuViewController is not presenting the viewController it is just hiding the menuViwController and therefore i can't pass data from menuViewController to contentViewController when a cell with a league is selected. i've therefore tried to save the leagueId to a NSUserDefault key, however this creates a problem when the app is exited, since it wont reset the NSUserDefaults. What is the best approach for such issue? should i rethink it?
pressing cell in menuViewController
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
NSUserDefaults.standardUserDefaults().setInteger(menuArray![indexPath.row].id, forKey: "leagueId")
self.sideMenuViewController.hideMenuViewController()
}
You can achieve this by creating a global class, First create a global class, :-
import Foundation
class User {
class var sharedInstance: User {
struct Static {
static var instance: User?
static var token: dispatch_once_t = 0
}
dispatch_once(&Static.token) {
Static.instance = User()
}
return Static.instance!
}
var leagueId: Int?
var name:String?
}
then store data in the class that you want
class ViewController: UIViewController {
let user = User.sharedInstance
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetails" {
self.user.leagueId = Id
self.user.name = Name
}
}
}
Then retrieve data :-
class ViewController: UIViewController {
let user = User.sharedInstance
mylabel.text = self.user.leagueId
mylabael2.text = self.user.name
}