The question might sound complicated, so let me ease the stress*
Lets say I have 2 tab bar tabs (Home1, Home2)
Both (Home1, Home2) have navigational controllers with two view controllers each
Home1(Navigation controller -> VC11 -> VC12)
Home2 (Navigation controller -> VC21 -> VC22)
Easy right? Well, not quite
What I want to do is pass data from [Home1: VC12 to Home2: VC22].
My coding efforts:
tabBarController!.selectedIndex = 1
self.navigationController!.popToViewController(navigationController!.viewControllers[1] as! (Home2: VC22), animated: false)
let navController = self.tabBarController?.viewControllers![1] as! UINavigationController
navController.popToViewController((Home2: VC22) as! UIViewController, animated: true)
for oneView in (self.navigationController?.viewControllers)! {
if (oneView.isKind(of: (Home2: VC22).classForCoder())) {
let friendView = oneView as! (Home2: VC22)
friendView.testing = "Worked?"
}
}
ANYHELP IS WELCOMED!! Total hours spent: 8hrs
Your pass data task is going though this process
V12 --(pop)--> V11 --(switch)--> V21 --(push)--> V22
If you choose to pass data, you will need to pass it though all these three transactions and that's lots of coding and it's hard to practice.
Instead of passing data around, a better way to accomplish your goal is to store the data in V12, and then load it from V22. So now all you need to worry about is how to transfer user from V12 to V22.
You only need two lines of code to store and read data from UserDefaults
On your V12, before you are doing the above transactions, first save your data like this
UserDefaults.standard.set("yourStringData", forKey: "data")
Then when you reach VC22, from viewDidLoad, read stored info in this way
UserDefaults.standard.string(forKey: "data")
For your view transaction part, popToViewController is for going from a child view controller to it's parent. So when you switch your tab and get the UINavigationController, the topViewController must be VC21. So to go to VC22 given that you connect them though a storyboard segue, simply call it like this
let navController = self.tabBarController?.viewControllers![1] as! UINavigationController
let VC21 = navController.topViewController!
VC21.performSegue(withIdentifier: "segue", sender: nil)
You could also subclass your tabbar and create a variable to store your data there. Then both VC's would have access to the data. This way also alleviates the possible need for a singleton.
It sounds like you need to rethink your approach. It depends on the way you setup your app but it makes sense to store this data somewhere else besides directly passing it from one VC to another. Here are some options:
Singleton that stores your data and you pass that around from one VC to another so that if VC12 stores data in the Singleton, then VC22 can access it easily since it also has access to the singleton.
Have the Container View Controller store the data to be shared amongst its children view controllers. So, in this example, the TabBarController can store this data.
Use UserDefaults as a poor-mans Singleton/Persistence solution.
Use a full blown Data Persistence layer using SQLite, Core Data, or Realm for example to store and retrieve data.
... there are others but I think one of these solutions can work well for you depending on your needs.
Related
I created a string value and stored it in a UserDefault on one view controller like so:
letSwimmerOneName = "John"
UserDefaults.standard.set(String(swimmerOneName), forKey: "twoFreelayNameOne
And I'm trying to retrieve that data on a different view controller like so:
var swimmerOneName = UserDefaults.standard.string(forKey: "twoFreelayNameOne")
I'm not getting any errors or crashes, it just simply doesn't work. So I was wondering if UserDefaults transfer across the entire project or stay put in the view controller they are created in.
You are able to retrieve data stored in UserDefaults across different view controllers. My issue was created on where I had placed the UserDefaults and in the way that I declared my variables that were to be assigned this UserDefault value. TLDR: Sloppy formatting.
I made a NSTableview and a custom tablecellview-class "KSTablecellview" which defines the cells inside the tableview.
Now I'm trying to access these cells and the elements inside them at a certain time, for example triggered through an users input.
For example I wanna change the text of a textfield in one of my custom cellviews. I tried that:
Outlet_TableView.selectRowIndexes(IndexSet(integer: 0), byExtendingSelection: false) // Selecting row #1
let view = Outlet_TableView.view(atColumn: 0, row: 0, makeIfNecessary: false) as! KSTableCellView // getting cellview, at first row/ first column (-> crashes)
view.myTextField.stringvalue = "Hello World"
This fails giving me "[...] [default] Unable to load Info.plist exceptions (eGPUOverrides)"
I tried some other things and tried to work out the problems reason, but I wasn't able to do it, I'm still a beginner.
Any help would be really great.
I'm using XCode and Swift, trying to build an app for Mac OS.
You should try to avoid updating the content of your cells manually. Instead, you should reload the data from your data source.
func reloadData(forRowIndexes rowIndexes: IndexSet,
columnIndexes: IndexSet)
Which will in turn fetch the data from your data source at the given index/column.
For example, your data source would probably contain "Hello World" at the given index after the user event occurred (provided you updated your data source content), and would thus be used in the dataCellFor: delegate method.
May be you're doing these operations before table is loaded for the first time(eg. inviewDidLoad) or on tableView with numberOfRows = 0.
Until tableView loads that row, it won't create the views inside that row and tableView.view(atColumn:, row:, makeIfNecessary: false) will return nil and force unwrapping this nil will make your application crash.
I have in two views that are connected to the menu, in one view I have a text field to enter your e-mail and I want to take that textfield text (that is the email address) to the other view, but I think I need to save that data to the phone so later when the user start the app again he/she will never have to type that address again, and then use that email address in the other view that will send an email to that address. Here is the code that I have:
let configCorreo = ConfigurarCorreo()
let mail = configCorreo.textField.text!
And then I set the recipient using that mail the user typed in the other viewController:
let mc: MFMailComposeViewController = MFMailComposeViewController()
mc.mailComposeDelegate = self
mc.setToRecipients(["\(mail)"])
But I get an error saying: "fatal error: unexpectedly found nil while unwrapping an Optional value"
PrepareForSegue, NSUserDefault and Singleton
You have a few possible options to pass your data to other views depending how you want that data to be handled, I will explain each for you and you can choose which one best fit your need.
prepareForSegue: Method
I recommend this method if you want to hold your data for 1 segue transition, it's a good cause to pass this again to another view afterward you need to create another prepareForSegue within the new view. here is an example on how to do this:
First, you create 2 variables in both views, 1 to send (currentViewController.swift) and 1 to receive (toViewyourGoingController.swift).
currentViewController.swift var dataToSend: AnyObject?
ViewYourGoingController.swift var dataToReceive: AnyObject?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//Check your segue, this way you can transfer different data to different view. also make sure the identifier match your segue.
if segue.identifier == "toViewYourGoing" {
//Initial your second view data control
let ExchangeViewData = segue.destinationViewController as! toViewyourGoingController
//Send your data with segue
ExchangeViewData.dataToReceive = dataToSend
}
}
NSUserDefault
Now this method is good if you want to keep your data live as long as the app is installed, once the app is removed this will reset automatically. You also have the option to update the value of the key if you wish, here is how you do NSUserDefault:
I always like to register my NSUserDeafult to default setting, a lot of people just continue with the second step without registering.
Register NSUserDefault in AppDelgate.swift
NSUserDefaults.standardUserDefaults().registerDefaults(["valueName": AnyObject])
Set Value to your NSUserDefault, this depends on what type of data you're storing, should match the one with your registration if you did register. (Example of Boolean data type below)
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "valueName") //Bool Data Type
Available types:
Make sure you synchronize once you set the value to the NSUserDefault, this way it will update instantly, otherwise it will update when it get a chance.
NSUserDefaults.standardUserDefaults().synchronize()
Receive Value: this will receive boolean value since we set boolean and register boolean.
let Variable: Bool! = NSUserDefaults.standardUserDefaults().boolForKey("valueName")
Singleton
Now singleton is basically a global variable that you can use them in any views, but some developers experience some bugs and difficulties, use it at your own risk, I recommend this method when you're definite that you will use that data a lot (STILL RISKY), but this method is like goddess of data handling :).
Create a NSObject subclass and call it DataManager.swift (I call it data manager cause it handle data.) as following:
import UIKit
class DataManager: NSObject {
//Store Data Globally
static var someData: Boo! //This Boolean, you can choose whatever you want.
}
the static is what keep your data live.
Now you can store and receive someData from anywhere like you handle any data type like this.
//Store
DataManager.someData = true
//Receive
print(DataManager.someData)
Challenges:
You can also use
Keychain
Sergey Kargopolov will walk you through how to use a third party to use swift keychain. Otherwise, you can take even harder challenge and create one yourself :P .
Key-Value Data in iCloud
Use CoreData to save it in a database, then you can perform a fetch request to get it back out of the database.
Very simple look it up here:
https://www.youtube.com/watch?v=3IDfgATVqHw
Could anyone tell me what the way of storing a long text in a swift app is. Let's suppose I have an app that has a table view and when I chose a row I go to a new scene where I have a big page filled by text.
The question is where do I have to store the data of the table's row and the whole text? And how?
Do I have to make a model? Is it just one for both the table and the text? Or more than one. Is there any tutorial that explains this exact situation or close to it?
You could use Parse.com framework to retrieve data from a database. There is a lot of documentation on this.
In this link you can find a tutorial that will explain you how to load data from Parse and show it in your UITableView.
Storing Local (in-memory store):
For storing without a internet connection you could use Core Data Stack with NSInMemoryStoreType as storeType. This tutorial will give you a nice idea on how it works.
You can declare a model like it follows:
struct CoreDataModel {
let name: String
let bundle: NSBundle
init(name: String, bundle: NSBundle)
// other properties & methods
}
And then manage it with:
let model = CoreDataModel(name: "MyModel", bundle: myBundle)
let stack = CoreDataStack(model: model,
storeType: NSInMemoryStoreType,
concurrencyType: .MainQueueConcurrencyType)
// Use context
stack.managedObjectContext
I have a NSDocument-based app, and I'd like my window positions to be saved and restored when re-opening documents. Apple's documentation on this is pretty sparse, but what I've been able to piece together is that at some point, something in the app needs to call NSWindow.setFrameUsingName() and NSWindow.setFrameAutosaveName().
What I haven't quite figured out is what point this needs to happen at, and what things need to do this. For example, this doesn't work at all:
// In my NSDocument class
override func windowControllerDidLoadNib(aController: NSWindowController) {
super.windowControllerDidLoadNib(aController)
// Add any code here that needs to be executed once the windowController has loaded the document's window.
aController.window?.setFrameUsingName("MainWindow")
aController.window?.setFrameAutosaveName("MainWindow")
}
I've read various different pieces of documentation or forum answers that point to awakeFromNib() to be another area to do this, but I can't get that to work either.
I'm also confused / worried that this is somehow being affected by Auto Layout or something I've done wrong in Interface Builder - for example, this is how my window is set up in IB:
I don't particularly want my window centered, but the other options seem to lock it in place in fixed horizontal or fixed vertical positions, which I also don't really want. A side effect of having my window be centered is that my document windows no longer cascade, which I neither want nor can seem to stop from happening (note that windowController.shouldCascadeWindows = true isn't helping either).
So - what's going on here? I'm finding knowledge on this topic to be particularly unclear or misleading, and likely out of date for Cocoa development 2015, so a modern refresher on this would be great.
Step 1: In IB, give the window an autosave name.
Step 2: There is no step 2.
The easiest place to set the name of the autosave name for the window frame is probably in your implementation of NSDocument.
If you override makeWindowControllers() as part of the implementation, you are creating the window controller(s) manually, and thus can set the name there:
override func makeWindowControllers() {
let storyboard = NSStoryboard(name: NSStoryboard.Name("MyDocumentStoryboard"), bundle: nil)
let windowController = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("MyDocument Window Controller")) as! MyDocumentWindowController
// this assumes you have a stored property or a computed property `someUniqueIdentifier` that is as unique as possible for your document
// and that you store on disk together with the rest of the document properties
windowController.windowFrameAutosaveName = NSWindow.FrameAutosaveName(rawValue: someUniqueIdentifier)
self.addWindowController(windowController)
}
If you instantiate your document window by overriding windowNibName, you should instead override the method windowControllerDidLoadNib(_:), to set the autosave name on the window. I have not tested this code, but I assume it will work:
func windowControllerDidLoadNib(_ windowController: NSWindowController) {
// this assumes you have a stored property or a computed property `someUniqueIdentifier` that is as unique as possible for your document
// and that you store on disk together with the rest of the document properties
windowController.windowFrameAutosaveName = NSWindow.FrameAutosaveName(rawValue: someUniqueIdentifier)
}