I am currently trying to use Permanent Data and Prepare For Segue together. This is what I have so far: (VC1)
override func viewDidAppear(_ animated: Bool) {
let itemsObject = UserDefaults.standard.object(forKey: "items")
if let tempItems = itemsObject as? [String] {
items = tempItems
}
(VC2):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toSecondViewController" {
let itemsObject = UserDefaults.standard.object(forKey: "items")
var items:[String]
if let tempItems = itemsObject as? [String] {
items = tempItems
items.append(textField.text!)
print(items)
} else {
items = [textField.text!]
}
UserDefaults.standard.set(items, forKey: "items")
}
}
I am trying to add an item to an array on VC2. Then I want to transfer the array to VC1 whilst storing it permanently. I am using Xcode 8.0 and Swift 3.0.
Its the other way around; when your source view controller initiates a segue from either a storyboard or performSegue, the source view controller's prepareForSegue method gets invoked. With in that method you should determine the type of view controller you are segueing to and set the properties accordingly. Note that you don't really need the identifier unless you have more than one segue to the same destination in which case its sometimes useful to know which one is being invoked.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let notificationsViewController = segue.destination as? NotificationsViewController {
NotificationsViewController.notification = notification
}
}
Your question only explains.how to pass and store data permanently. But, you haven't mentioned about where you want to save data. I presume,you using tableView to store data and retrieving it.
In AppDelegate applicationDidFinishLaunching register an empty array as default value for the key "NewArray".
let defaultValues = ["NewArray": [String]()]
UserDefaults.standard.register(defaults: defaultValues)
In Second VC define newArray (variable names are supposed to start with a lowercase letter) also as empty String array
var newArray = [String]()
In viewDidLoad retrieve the array from user defaults, append the new item, save the array back to user defaults and reload the table view
override func viewDidLoad() {
super.viewDidLoad()
let defaults = UserDefaults.standard
newArray = defaults.array(forKey: "NewArray") as! [String]
newArray.append(newItem) //newItem is the data.Which you passing from First VC
defaults.set(newArray, forKey: "NewArray")
self.tableView?.reloadData()
}
and you don't need to use segue on your second VC unless you passing data from it...
Related
I have a array like this in a tableView:
var array: [[String]] = [["Apples", "Bananas", "Oranges"], ["Round", "Curved", "Round"]]
I would like to pass on the name of the cell when the cell is pressed. With a standard array I would do this:
let InfoSegueIdentifier = "ToInfoSegue"
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == InfoSegueIdentifier
{
let destination = segue.destination as! InfoViewController
let arrayIndex = tableView.indexPathForSelectedRow?.row
destination.name = nameArray[arrayIndex!]
}
}
And in the next ViewController (InfoViewController)
var name = String()
override func viewDidLoad() {
super.viewDidLoad()
nameLabel.text = name
}
Error: "Cannot assign value of type '[String]' to type 'String'"
Change this part of code
if segue.identifier == InfoSegueIdentifier
{
let destination = segue.destination as! InfoViewController
let arrayIndex = tableView.indexPathForSelectedRow?.row
destination.name = nameArray[arrayIndex!]
}
To
if segue.identifier == InfoSegueIdentifier
{
let destination = segue.destination as! InfoViewController
let arrayIndexRow = tableView.indexPathForSelectedRow?.row
let arrayIndexSection = tableView.indexPathForSelectedRow?.section
destination.name = nameArray[arrayIndexSection!][arrayIndexRow!]
}
Try and share the results.
Reason For crash: In your first viewController you have [[String]] which is the datasource for your section. Now, when you try to get the object from this array it will returns you [String] and in your destination viewController you have the object of type String. and while assigning [String] to String it cause the crash with type mismatch. So, what above code does is, it will take first [String] from the arrayIndexSection and then the String from arrayIndexRow and thus pass a String object to destination.
Hope it clears.
You are getting this error because you are passing an array to second view controller and there is a variable of type string. So, replace this method like this.
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == InfoSegueIdentifier
{
let destination = segue.destination as! InfoViewController
if let indexPath = tableView.indexPathForSelectedRow{
destination.name = nameArray[indexPath.section][indexPath.row]
}
}
}
which I add or remove from another ViewController
Im display this array.count in tableView
How in swift I can auto updates cell for array.count?
without Timer and Pull to refresh
Or how can I make refresh when loadedCart.count haw change?
Thanks
class TestTABLEVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableViewT: UITableView!
var CellT = TestTableViewCell()
var def = UserDefaults.standard
var loadedCart = [[String:Any]]()
override func viewDidLoad() {
super.viewDidLoad()
tableViewT.estimatedRowHeight = 100
tableViewT.dataSource = self
tableViewT.delegate = self
loadedCart = UserDefaults.standard.array(forKey: "cartt") as? [[String: Any]] ?? []
//Here need add auto update
}
cell for row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TestTableViewCell
let item = loadedCart[indexPath.row]
cell.nameLbl?.text = item["name"] as? String
cell.priceLbl.text = item["price"] as? String
cell.qntLbl.text = item["qty"] as? String
let im = item["image"] as? NSData
cell.PhoroUmage.image = UIImage(data: im! as Data)
return cell
}
Btn, when click - remove arr and reload cells
#IBAction func Btn(_ sender: UIButton) {
def.removeObject(forKey: "cartt")
print("remove is OK")
//Here need add auto update when btn pressed
}
If I understand you correctly, you can use reactive programming for this. For example Bond framework can be used like this:
let todoItems: SafeSignal<[TodoItem]> = ....
let tableView: UITableView = ...
class TodoItemCell: UITableView Cell { ... }
...
todoItems.bind(to: tableView, cellType: TodoItemCell.self) { (cell, item) in
cell.titleLabel.text = item.name
}
Using this framework, your table view will automatically reload when there are any changes to the source array. You will find more about usage on table views at this link.
you probably need to use notifications. i.e. send a notification when something new gets added to your cart.
Then set up an observer and, every time the observer notices a change, update the tableview with reload data.
A similar use case is listed here I think but would need to be adapted for your needs (if you need more help someone more expert than me will be able to help probably with the specifics!) Automatically Reload TableViewController On Rewind
Add
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let carts = UserDefaults.standard.array(forKey: "cartt") as? [[String: Any]] {
loadedCart = carts
}
DispatchQueue.main.async{
self.tableViewT.reloadData()
}
}
I am trying to use Permanent Data and Prepare For Segue together. This is what I have so far in: (View Controller 1)
override func viewDidAppear(_ animated: Bool) {
let itemsObject = UserDefaults.standard.object(forKey: "items")
if let tempItems = itemsObject as? [String] {
items = tempItems
}
in (View Controller 2):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toSecondViewController" {
let itemsObject = UserDefaults.standard.object(forKey: "items")
var items:[String]
if let tempItems = itemsObject as? [String] {
items = tempItems
items.append(textField.text!)
print(items)
} else {
items = [textField.text!]
}
UserDefaults.standard.set(items, forKey: "items")
}
}
I am attempting to add an item to an array on VC2. Then I want to transfer the array to VC1 whilst also storing it permanently. Then every time I shut down and reload the app I can print the array. The error message states "Use of unresolved identifier items". I am using Xcode 8.0 and Swift 3.0.
Ok so because you said you want to persist the data over app starts you will need the User Defaults to store your items. As Dan already suggested you are basically doing it right. You just want to set a variable that was not declared before. But I will show you this in the following code. I will also attach a second approach in which the items are passed to next view controller while performing the segue.
First example: Imagine we have two view controllers like in your example. The first view controller contains a UITextField to do user text input. Whenever we switch from the first view controller to the second view controller with the help of a storyboard segue (e.g. when pressing a button) we take the existing texts from previous segues from the User Defaults and add the current user input and then persist it back to the User Defaults. This happens in the first view controller:
class ViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
if segue.identifier == "toSecondViewController" {
let itemsFromDefaults = UserDefaults.standard.object(forKey: "items")
var items: [String]
if let tempItems = itemsFromDefaults as? [String]
{
items = tempItems
items.append(textField.text!)
}
else
{
items = [textField.text!]
}
print(items)
UserDefaults.standard.set(items, forKey: "items")
}
}
}
This first view controller looks pretty similar to your code but I wanted to add it for completeness.
Then in the second view controller we just grab the items from the user defaults and store it directly in a instance variable of this view controller. With the help of this we can do what we want in other methods within the view controller and process the items further. As I said what you were missing was the instance variable declaration to store the items in.
class ViewController2: UIViewController {
private var items: [String]? // This is only accessible privately
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.items = UserDefaults.standard.object(forKey: "items") as? [String]
}
}
Second Example: You could also declare a internal/public variable in ViewController2 so that you can set it directly from the first view controller in perform segue. Than you wouldn´t need to grab the items from the User Defaults in ViewController2. For that you can access the destination view controller of the segue then cast it to ViewController2 and directly set the items of it.
class ViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
if segue.identifier == "toSecondViewController" {
let itemsFromDefaults = UserDefaults.standard.object(forKey: "items")
var items: [String]
// [...] Do the stuff to get the items and add current input like before [...]
let destinationViewController = segue.destination as! ViewController2
destinationViewController.items = items
}
}
}
class ViewController2: UIViewController {
var items: [String]? // This is accessible from outside now
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print(items) // We can now print this because it is set in prepareForSegue
}
}
I really hope I could help you with that and explained it understandable. Feel free to leave a comment if you have any questions.
You forget to write "let" or "var" in viewDidAppear. Try this:
override func viewDidAppear(_ animated: Bool) {
let itemsObject = UserDefaults.standard.object(forKey: "items")
if let tempItems = itemsObject as? [String] {
let items = tempItems
}
}
If you want to use items after if statement, then you must declare variable before if statement:
override func viewDidAppear(_ animated: Bool) {
let itemsObject = UserDefaults.standard.object(forKey: "items")
var items: [String]
if let tempItems = itemsObject as? [String] {
items = tempItems
}
}
I am fairly new to Swift and I am having a few issues with getting understanding how to do what I want to do.
I am currently testing some stuff with json.
What I am trying to do is to append the data I get from my json data into an array. And when my array contains the data I wish to present it to my UICollectionView. I am assuming that I should be using an array.
import UIKit
import Foundation
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func getData() {
let path = "http://myurl/test.json"
let url = URL(string: path)
let session = URLSession.shared
let task = session.dataTask(with: url!) { (data: Data?, response: URLResponse?, error: Error?) in
let json = JSON(data: data!)
for result in json["dokumentstatus"]["dokutskott"]["utskott"].array! {
let punkter = result["punkt"].string!
print("punkt: \(punkt)")
let rubrik = result["rubrik"].string
print("rubrik: \(rubrik)")
let forslag = result["forslag"].string
print("förslag: \(forslag)")
}
}
task.resume()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return //someArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CustomCollectionViewCell
cell.customLabel.text = //Put the data form rubrik variable
cell.customTW.text = //Put the data from foreleg variable
return cell
}
}
the function getData() gets the correct json data I just need help understanding how to put this data to an array.
(Also, I know I probably shouldn't be getting the data in the ViewController, but this is only a test.)
import Foundation
class Information: NSObject {
var punkter: String?
var rubrik: String?
var forslag: String?
}
I'm thinking that maybe I should be using an array that looks something like this:var someArray = [Information]()
But I do not know how to then use my getData()
Or maybe I should be using three different arrays, one for each of my json variables.
Since you're still using a custom class (well done!, three different arrays are horrible) it's correct to declare a data source array like you suggested
var someArray = [Information]()
Most likely a struct is sufficient, I recommend to use non-optional strings
struct Information {
var punkter : String
var rubrik : String
var forslag : String
}
If the properties won't change you could even use let to make the properties constants.
To populate the array use the nil coalescing operator to check for nil,create the Information instance with the memberwise initializer and append the instance to the datasource array. Then reload the collection view on the main thread.
...
for result in json["dokumentstatus"]["dokutskott"]["utskott"].array! {
let punkter = result["punkt"].string ?? ""
let rubrik = result["rubrik"].string ?? ""
let forslag = result["forslag"].string ?? ""
let information = Information(punkter:punkter, rubrik:rubrik, forslag:forslag)
self.someArray.append(information)
}
DispatchQueue.main.async {
self.collectionView.reloadData()
}
...
Edit:
To display the data in cellForItemAtIndexPath use
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CustomCollectionViewCell
let information = someArray[indexPath.row]
cell.customLabel.text = information.rubrik
cell.customTW.text = information.foreleg
return cell
}
var routes:[PFObject] = [PFObject]()
func getRoutes() {
let query = PFQuery(className: "PoolRoute")
query.includeKey("selectedCustomer")
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if let objects = objects as? [PFObject] {
self.routes = objects
self.tableView.reloadData()
these objects have a column named "selectedCustomer" that is full of pointer objects.....I would like to segue to another tableview controller and display the pointer objects from the selected cell...
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue == "toRoutesDetailVC" {
let nav = segue.destinationViewController as! PoolRouteDetailVC
if let indexPath = self.tableView?.indexPathForSelectedRow {
nav.currentObject = (self.routes[indexPath.row]["selectedCustomer"] as [PFObject])
// '() -> NSIndexPath?' does not have a member named 'row'
//i guess its not the right syntax...not exactly sure how to write the code to get it to send to the array to the detailViewController. thanks in advance!