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
}
Related
I am adding tableview cell label text to phNumArray, in tableview i am getting all values but when i try to pass in service call then its coming nill why?
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet weak var joinersTableView: UITableView!
var tablcellArray = [String]()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: Namescell = tableView.dequeueReusableCell(withIdentifier: "Namescell") as! Namescell
cell.empRoleLbl.text = empTeftfield.text
tablcellArray.append((cell.empRoleLbl.text!))
print("employee names array \(tablcellArray)")// here coming all appende values
return cell
}
func getPostData(params: [String:Any]) -> Data? {
return try? JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
DispatchQueue.main.async {
self.joinersTableView.reloadData()
}
}
func callPostApi() {
print("tableview emp values\(tablcellArray)")// here array values not coming
let url = URL(string: "http://itaag-env-1/")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
print("appende values \(tablcellArray)")
try? request.setMultipartFormData(["contactsList": "\(tablcellArray)"], encoding: .utf8)
DispatchQueue.main.async {
self.joinersTableView.reloadData()
}
URLSession.shared.dataTask(with: request) { data, _, _ in
if let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
}
}.resume()
}
}
in tableview cellfor row appended values coming.. but when i try to bring that array in callPostApi func then tablcellArray showing nil why?
please share your knowledge, please try to solve this error.
firstly change
var tablcellArray = [String]()
to
var tablcellArray = [String]
then in your cellForRowAt try to access your data such as
let empTeftfield = tablcellArray[indexPath.row]
if this line prints correctly
cell.empRoleLbl.text = empTeftfield.text
this should work.
if not prints your value reload tableview after fetching data from api. you shouldnt add values to your array in your conditions.
After parsing JSON I got my data in arraOfData of ViewController, but collectionView not display this. If I use static array all working.
collectionview.reloadData() is not working. In my opinion, need somehow waiting data from parsing, then try load collectionView. I tried use refresherControler for waiting data, but I don't know where put method "endRefreshing" (i guess startRefreshing need in ViewDidLoad)
If I use reloadData like didSet in arraOfData
I got error cause collectionView is nil. Use reloadData right after got data from parsing same didn't work for me
And a big request to tell a couple of resources (articles) on this topic, I feel that I have big problems with displaying data and in what sequence functions start to load. Thanks a lot.
class ViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
var arraOfData = [GettingMoney]() {
didSet {
print("Changes: \(arraOfData.count)")
}
}
// let arr = ["1", "2", "3"] - test, if i use this array, collectionView display data
override func viewDidLoad() {
super.viewDidLoad()
GettingMoney.fetchData()
collectionView.dataSource = self
collectionView.delegate = self
}
}
extension ViewController: UICollectionViewDelegate,
UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arraOfData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCellId", for: indexPath) as? CollectionViewCell
cell?.currentCurrency.text = "\(arraOfData[indexPath.row])"
return cell!
}
}
As this
GettingMoney.fetchData()
is a sign of asynchronous process that doesn't reload the collection , so do
func fetchData(completion:#escaping ([String]) -> ()) {
Api.load {
completion(arr)
}
}
Then use like
GettingMoney.fetchData { arr in
self.arraOfData = arr
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
A not direct case like yours is here Returning data from async call in Swift function
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()
}
}
Im trying to create a table which fills in its data from an online JSON file. I currently have the JSON file online and being called into the class and getting converted into an array, however when I try set my text as this array data nothing displays.
cell.textLabel?.text = "\(locationName[indexPath.row])"
However if I set the data in the array it works however I need the data to be from the JSON file.
var array: double = ["1", "2", "3"]
Here is my table class code with the array code. Any help would be great thanks
class SpotsDataClass: UITableViewController {
var locationName = [String]()
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request(.GET, "http://woamph.com/savedLocations.json")
.response { request, response, data, error in
if let data = data {
let json = JSON(data: data)
for locationLoop in json {
let usersJSON = locationLoop.1["user"].stringValue
self.locationName.append(usersJSON)
//let longitudeJSON = locationLoop.1["longitude"].doubleValue
//self.array2.append(longitudeJSON)
}
for i in 0 ..< self.locationName.count {
print("\(self.locationName[i])")
//print("\(self.array2[i])")
//self.locationNameLabel.text = "Location Name: \(self.array1[i])"
}
//self.titleLabel.text = "Spots Near You"
//self.locationNameLabel.text = "Location Name: \(spotData.locationName)"
//self.ratingLabel.text = "Rating: \(spotData.rating)"
//self.userLabel.text = "Posted By: \(spotData.user)"
//
}
}
}
//Mark: - UITableDataSource
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return locationName.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("spotTable", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel?.text = "\(locationName[indexPath.row])"
return cell
}
}
The Alamofire request executes asynchronously. You need to explicitly tell your table view to reload when the network request is done, otherwise you're populating your array from the results but the table view has already been loaded by the time your remote data has been loaded.
So, after
for locationLoop in json {
let usersJSON = locationLoop.1["user"].stringValue
self.locationName.append(usersJSON)
}
Add:
tableView.reloadData()
I have two ViewControllers. For the first ViewController, it displays my Array data on the table. I want to get the indexPath of the selected cell, and pass this data to another ViewController.
In my First ViewController
import UIKit
class ViewController: UIViewController, UITableViewDataSource {
var nameList = [NameManager]()
#IBOutlet weak var NameTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
NameTable.dataSource = self
GetData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func GetData(){
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: NSURL(string: "http://www.json-generator.com/api/json/get/bPfifKWNaq?indent=2")!)
request.HTTPMethod = "GET"
let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let data = data{
do{
let resultJSON = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions())
let resultArray = resultJSON as? NSArray
for jsonObjectString in resultArray!{
let code = jsonObjectString["code"] as! String
let name = jsonObjectString["name"] as! String
let description = jsonObjectString["description"] as! String
self.nameList.append(NameManager(code: code, name: name, description: description))
}
self.nameList.count
dispatch_async(dispatch_get_main_queue(), {
self.NameTable.reloadData()
})
}catch _{
print("Received not-well-formatted JSON")
}
}
if let response = response {
let httpResponse = response as! NSHTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
let count = nameList.count
return count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let myCell = NameTable.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath) as UITableViewCell
myCell.textLabel?.text = nameList[indexPath.row].name
myCell.detailTextLabel?.text = nameList[indexPath.row].description
return myCell
}
In my Second ViewController, which is also another class, I want to capture the indexPath of what was selected. Using the index value, I search my Array and pass that particular object to the next class. I don't have codes for this as I don't know how it works.
You could create a property outside the scope of your class and then set that within the cellForRowAtIndexPath method. That property could be accessed in your second view controller. You’ll want to look at the tableview method didSelectRowAtIndexPath as that will be where you set your property to the cell that’s been selected.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
appDelegate().index = indexPath.row
}
I’ve made a super simple project on github showing two ways of creating a property outside the scope of your view controller. One way is creating a variable within AppDelegate to be accessed via a singleton, the other is a Globals.swift file.
Hope this helps!
If you want to pass values to the controller which pops after tapping on the cell, using a singleton wouldn't be an elegant way. If you are using storyboards, then you have to use 'prepare for segue'. You implement the method in the class that handles the transfer and set all the properties in another view controller.
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "SecondVC") {
// set properties
}
}