How to populate array and view it's data at the same time - Swift - IOS9 - objective-c

I am trying to retrieve data from an online database, and I do that successfully; However, after retrieving the data from the database I would like to store it in an array and then populate a listview and a mapview with it's data, but there is a problem, I am able to load the data and store it and view it, however the problem is that everytime the app loads no information appears until I go to another scene and go back, because I am populating the array though the AppDelegate. However, if I populate it through the viewdidload I get duplicate items in my table view.
Here is my code:
Approach number 1, which leads to duplicates
StoreViewController.swift
class StoreViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate, UITextFieldDelegate, UITableViewDataSource, UITableViewDelegate, StoresModelProtocoal {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
drawForm()
setUpMap()
self.hideKeyboardWhenTappedAround()
getCurrentLocation()
let hideStoreDetail: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.hideStoreDetails))
Map.addGestureRecognizer(hideStoreDetail)
//Create a nib for the custom cell and use it in the table
let nib = UINib(nibName: "CustomStoreCell", bundle: nil)
StoresListTable.registerNib(nib, forCellReuseIdentifier: "customStoreCell")
let storesModel = StoresModel()
storesModel.delegate = self
storesModel.downloadItems()
}
func itemsDownloaded(items: NSArray) {
print("Items downloaded")
for item in items
{
if let s = item as? Store
{
print(s.Address)
Globals.unsortedStoresList += [s]
Map.addAnnotation(s.Annotation)
do_table_refresh()
}
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Globals.unsortedStoresList.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:CustomStoreCell = self.StoresListTable.dequeueReusableCellWithIdentifier("customStoreCell") as! CustomStoreCell
let s = Globals.unsortedStoresList[indexPath.row]
cell.loadItem(s.Name, StoreAddress: s.Address, StoreHoursOfOperation: s.HoursOfOperation, StoreDistanceFromCurrentLocation: String(s.DistanceFromCurrentLocation))
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//tableView.deselectRowAtIndexPath(indexPath, animated: true)
let s = Globals.unsortedStoresList[indexPath.row]
print(s.Name)
print(s.Address)
print(s.HoursOfOperation)
print(s.DistanceFromCurrentLocation)
//print("You selected cell #\(indexPath.row)!")
}
func do_table_refresh()
{
dispatch_async(dispatch_get_main_queue(), {
self.StoresListTable.reloadData()
return
})
}
I know this one duplicates the items because everytime the view is loaded it re-downloads all the data again; therefore, I tried looking for a better way and then I thought about doing the downloading process in my AppDelegate and then just write couple functions that take data from the array and display it, but the problem here is that the data would be displayed on the TableView right away without duplicates but it won't be displayed on the mapview at first run, instead I have to go to another scene and go back in order for the data to be displayed on the map.
Approach number 2
StoreViewController.swift
class StoreViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate, UITextFieldDelegate, UITableViewDataSource, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
drawForm()
setUpMap()
self.hideKeyboardWhenTappedAround()
getCurrentLocation()
let hideStoreDetail: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.hideStoreDetails))
Map.addGestureRecognizer(hideStoreDetail)
//Create a nib for the custom cell and use it in the table
let nib = UINib(nibName: "CustomStoreCell", bundle: nil)
StoresListTable.registerNib(nib, forCellReuseIdentifier: "customStoreCell")
loadMapAnnotations()
}
func loadMapAnnotations(){
for item in Globals.unsortedStoresList
{
Map.addAnnotation(item.Annotation)
do_table_refresh()
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Globals.unsortedStoresList.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:CustomStoreCell = self.StoresListTable.dequeueReusableCellWithIdentifier("customStoreCell") as! CustomStoreCell
let s = Globals.unsortedStoresList[indexPath.row]
cell.loadItem(s.Name, StoreAddress: s.Address, StoreHoursOfOperation: s.HoursOfOperation, StoreDistanceFromCurrentLocation: String(s.DistanceFromCurrentLocation))
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//tableView.deselectRowAtIndexPath(indexPath, animated: true)
let s = Globals.unsortedStoresList[indexPath.row]
print(s.Name)
print(s.Address)
print(s.HoursOfOperation)
print(s.DistanceFromCurrentLocation)
//print("You selected cell #\(indexPath.row)!")
}
func do_table_refresh()
{
dispatch_async(dispatch_get_main_queue(), {
self.StoresListTable.reloadData()
return
})
}
AppDelegate.swift
class AppDelegate: UIResponder, UIApplicationDelegate, StoresModelProtocoal {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let storesModel = StoresModel()
storesModel.delegate = self
storesModel.downloadItems()
return true
}
//////////////////////////////////////
//Delegates
//////////////////////////////////////
func itemsDownloaded(items: NSArray) {
print("Items downloaded")
for item in items
{
if let s = item as? Store
{
print(s.Address)
Globals.unsortedStoresList += [s]
//Map.addAnnotation(s.Annotation)
}
}
}
Any help would be appreciated, Thanks in advance.

I was able to find a temporarily solution to the problem
I modified StoreViewController.swift to this, if anyone is having a similar problem.
class StoreViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate, UITextFieldDelegate, UITableViewDataSource, UITableViewDelegate, StoresModelProtocoal {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
drawForm()
setUpMap()
self.hideKeyboardWhenTappedAround()
getCurrentLocation()
let hideStoreDetail: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.hideStoreDetails))
Map.addGestureRecognizer(hideStoreDetail)
//Create a nib for the custom cell and use it in the table
let nib = UINib(nibName: "CustomStoreCell", bundle: nil)
StoresListTable.registerNib(nib, forCellReuseIdentifier: "customStoreCell")
Globals.unsortedStoresList.removeAll() //I added this line of code to remove the old list
let storesModel = StoresModel()
storesModel.delegate = self
storesModel.downloadItems()
}
func itemsDownloaded(items: NSArray) {
print("Items downloaded")
for item in items
{
if let s = item as? Store
{
print(s.Address)
Globals.unsortedStoresList += [s]
Map.addAnnotation(s.Annotation)
}
}
do_table_refresh()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Globals.unsortedStoresList.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:CustomStoreCell = self.StoresListTable.dequeueReusableCellWithIdentifier("customStoreCell") as! CustomStoreCell
let s = Globals.unsortedStoresList[indexPath.row]
cell.loadItem(s.Name, StoreAddress: s.Address, StoreHoursOfOperation: s.HoursOfOperation, StoreDistanceFromCurrentLocation: String(s.DistanceFromCurrentLocation))
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//tableView.deselectRowAtIndexPath(indexPath, animated: true)
let s = Globals.unsortedStoresList[indexPath.row]
print(s.Name)
print(s.Address)
print(s.HoursOfOperation)
print(s.DistanceFromCurrentLocation)
//print("You selected cell #\(indexPath.row)!")
}
func do_table_refresh()
{
dispatch_async(dispatch_get_main_queue(), {
self.StoresListTable.reloadData()
return
})
}

Related

index not being transferred from uicollectionviewcell to view controller

My code below is not working and is causing a runtime error. Stating Unexpectedly found nil while unwrapping an Optional value. I don't know what is causing this. I don't know how to fix the optional issue in this. The code for cellforitemat has also been added.
class antion : UICollectionViewCell {
#IBOutlet var sam : UILabel!
var deleagete : DataCollectionProtocoe?
var index : IndexPath?
#IBAction func press(){
deleagete?.passData(indx: (index?.row)!)
}
#IBAction func delete(){
deleagete?.deleteData(indx: (index?.row)!)
}
}
extension ViewController : DataCollectionProtocoe {
func passData(indx: Int) {
let vc = storyboard?.instantiateViewController(withIdentifier: "DetailVDC") as? DetailVDC
vc?.name = people[indx].name!
self.navigationController?.pushViewController(vc!, animated: true)
}
func deleteData(indx: Int) {
people.remove(at: indx)
cc.reloadData()
}
}
protocol DataCollectionProtocoe {
func passData(indx:Int)
func deleteData(indx:Int)
}
class DetailVDC: UIViewController {
var name = ""
#IBOutlet var lbl : UILabel!
override func viewDidLoad() {
lbl.text = name
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! antion
cell.index = indexPath
cell.deleagete = self
return cell
}
Based on what little i could understand from your code, it looks like you are creating the UICollectionViewCell instance but not setting any value to the delegate property of the cell.
When you create an instance of the collection view cell in the controller , assign the controller to the delegate property of the cell as follows:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = antion()
cell.delegate = self // assuming the controller is the data source of the collection view
}

Communication between view controllers, not able to pass on the data to the outlet of second view controller

Two view controllers:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (sender as! cell1).name.text == "ww"{
let s = segue.destination as! DetailViewController
s.detail?.text = "wwwwwwwwwww"
}
}
I am new to coding. What I am trying here is to communicate between view controllers. For some reason, I am not able to show the wwwwwwwwwww in the label of the second view controller
At the prepare function the view hasn't been loaded yet from storyboard, so the outlets of the DetailViewController are still nil.
Create a public variable in the DetailViewController which will be available at prepare, then in the viewDidLoad method of DetailViewController you can set the label's text to be the content of that variable.
class DetailViewController: UIViewController {
var text: String?
override function viewDidLoad() {
super.viewDidLoad()
label.text = text
}
}
Then in your list viewcontroller set the text property of detail vc:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard (sender as! cell1).name.text == "ww",
let detailVc = segue.destination as? DetailViewController
else { return }
detailVc.text = "wwwwwwwwwww"
}

TableView is not counting exactly

I pass data(value) from a ViewController to a TableView (see below), but the tableview fill always only the first row. It donĀ“t count. How can I fix it?
The tableview should show every passing data in a new row.
#IBAction func a(sender: UIButton) {
txtBalkenbewehrung = ausgabe.text
performSegueWithIdentifier("transferfile", sender: sender)
}
import UIKit
var txtBalkenbewehrung: String?
class EBTableViewController: UITableViewController, UITableViewDelegate, UITableViewDataSource {
var rowData = [txtBalkenbewehrung]
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.editing = true
self.tableView.reloadData()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return rowData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("textcell",
forIndexPath: indexPath) as! UITableViewCell
// let data = rowData[indexPath.row]
cell.textLabel!.text = rowData[indexPath.row]
return cell
}
`
First, please make sure that you correctly format your code!
The behavior seems coherent, as the data is a an array with a single element, txtBalkenbewehrung
You need to fill the array with more element if you want to display more than one

Segue on DidSelectRowAtIndexPath from Custom DataSource/Delegate Swift

My setup:
`UITableViewController` (ComboViewController)
-> Several Static Cells
-> One Static Cell contains a dynamic `tableView`
I need to use a custom Delegate/DataSource because the dynamic tableView is embedded in the Static TableView within the TableViewController
This custom Delegate/DataSource looks like this:
class DataSource: NSObject, UITableViewDataSource, UITableViewDelegate {
// class variables
override init() {
super.init()
// initialize variables
}
//some data source/ delegate methods like number of rows, cellForRowAtIndexPath
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var indexedCombos: NSDictionary?
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let comboVC: ComboInfoViewController = storyboard.instantiateViewControllerWithIdentifier("ComboInfo") as! ComboInfoViewController
comboVC.doSegue()
}
}
Within ComboViewController I have this:
class ComboInfoViewController: UITableViewController {
func doSegue() {
self.performSegueWithIdentifier("tosingle", sender: combListTable)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "tosingle" {
//do stuff
}
}
}
If the segue is modal I get this error:
Warning: Attempt to present SingleProductViewController on ComboInfoViewController whose view is not in the window hierarchy!
If the segue is push, then the prepareForSegue method gets called, but the viewController does not push! What is happening?
I've searched and searched. But I have no idea what could be resulting in this behavior.
When you create the ComboInfoViewController instance with this line,
let comboVC: ComboInfoViewController = storyboard.instantiateViewControllerWithIdentifier("ComboInfo") as! ComboInfoViewController
You're creating a new instance that is not the one you have on screen, and never will be, so that's why you get the error. It is very important that you understand this concept; understanding how view controllers are created, and how to get pointers to ones that already exist is fundamental to iOS programming.
However, in this case you don't even need to get a pointer to the one on screen, because you should connect the segue directly from the cell (the dynamic prototype), which means you won't need any code to execute it. You can delete the didSelectRowAtIndexPath method, and the doSegue method. You only need to implement prepareForSegue. If you need to pass information to the next controller based one which row was touched, you can do it like below. The table view controller code should now look like this (this is an update of the code in my answer to this question, Swift: TableView within Static UITableViewCell),
class ComboInfoViewController: UITableViewController {
#IBOutlet weak var staticTableView: UITableView!
#IBOutlet weak var dynamicTableView: UITableView!
var dataSource = DataSource()
override func viewDidLoad() {
super.viewDidLoad()
dynamicTableView.dataSource = dataSource
dynamicTableView.delegate = dataSource
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.row != 1 {
return 44
}else{
return 250 // the second cell has the dynamic table view in it
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "tosingle" {
var cell = sender as! UITableViewCell
var indexPath = dynamicTableView.indexPathForCell(cell)
var dataPoint = dataSource.theData[indexPath!.row] // theData is the array used to populate the dynamic table view in the DataSource class
// pass dataPoint to the next view controller which you get from segue.destinationviewController
println(dataPoint)
}
}
}

SWIFT TableView not loading text

I am making a tableView programatically. For some reason the table view is always empty. Please help me find the bug. Here is my code
// Set all view we will need
func setupTableView() {
self.tableView = UITableView(frame: CGRectMake(0, 20, 320, self.heighTableView))
self.tableView!.tableHeaderView = UIView(frame: CGRectMake(0, 0, self.view.frame.size.width, CGFloat(self.heighTableViewHeader)))
self.tableView!.backgroundColor = UIColor.clearColor()
self.tapMapViewGesture = UITapGestureRecognizer(target: self, action: "handleTapMapView:")
self.tapTableViewGesture = UITapGestureRecognizer(target: self, action: "handleTapTableView:")
self.tapTableViewGesture!.delegate = self
self.tableView!.tableHeaderView?.addGestureRecognizer(self.tapMapViewGesture!)
self.tableView!.addGestureRecognizer(self.tapTableViewGesture!)
// Init selt as default tableview's delegate & datasource
//self.tableView!.dataSource = self
self.tableView!.delegate = self
self.view.addSubview(self.tableView!)
}
// UITableViewDataSource Methods
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(tableView:UITableView!, heightForRowAtIndexPath indexPath:NSIndexPath)->CGFloat {
return 80
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var identifier = "Cell"
var cell = tableView.dequeueReusableCellWithIdentifier(identifier) as UITableViewCell?
cell!.textLabel!.text = "Hello World !"
// }
return cell!
}
Try this :
class ViewController: UIViewController, UITableViewDelegate, MKMapViewDelegate, UIGestureRecognizerDelegate,UITableViewDataSource
then replace your function by copying this two functions by command + click on UITableViewDataSource:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
}
and other one is :
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
}
and put all the code into this two function and try again becouse in your code this two functions are not getting called but by doing this your function will called and you have put all the required code into this function.
and also remove comment from
self.tableView!.dataSource = self
May be this can help you.
Edit For this Answer:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
var identifier : NSString = "Cell"
var cell = tableView.dequeueReusableCellWithIdentifier(identifier) as? UITableViewCell
if !(cell != nil) {
cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: identifier)
}
cell?.textLabel?.text = "Hello World"
return cell!
}