I put three table view controllers on pageviewcontroller.
The pageviewcontroller loads middle vc.
When I go to the left vc, and pull table in order to reload - I have problem that on function cellForRowAtIndexPath the indexPath.row starts from 4, not 0.
Why does the indexPath.row start from 4 and not 0?
I think, it is not about code issue! I have error:
Cannot index empty buffer
I ve found the error. I dont know why does this happens but when I reload table (pulling it), for some reason, it takes the last row (in my case I have 4 rows on screen counting from 0) and uses it in cellAtIndex array. Every time before pulling data from internet I remove all elements from array. I changed that code, i did remove all elements before reloading and the error didnt appear. What's interesting I am using the same function on other vc and everything works.
I am making two request to download data:
class func JSONRequest2(urlInput: String, tableName: UITableView, action: (NSArray)->Void, refresh:UIRefreshControl, viewContr: UIViewController, hideLoadingViewAndStopAnimating: ()->Void) {
let urlPath = urlInput
let url = NSURL(string: urlPath)
let session = NSURLSession.sharedSession()
println("started first json request")
let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
if error != nil {
println(error)
let stringError = error.localizedDescription
CommonFunctions.showAlert("Ошибка", alertText: stringError, alertButtonText: "Закрыть", viewController: viewContr)
}
else{
var err: NSError?
if data != nil {
if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as? NSArray {
println("the number of news in json :\(jsonResult.count)")
if (err == nil) {
dispatch_async(dispatch_get_main_queue(), {
action(jsonResult)
//tableName.reloadData()
})
}else{
let stringError = err?.localizedDescription
CommonFunctions.showAlert("Ошибка", alertText: stringError!, alertButtonText: "Закрыть", viewController: viewContr)
}
}else {
hideLoadingViewAndStopAnimating()
println("json is not valid")
var dic = dictForErrors()
CommonFunctions.showAlert("Ошибка", alertText: dic.alertText, alertButtonText: "Закрыть", viewController: viewContr)
}
}else {
CommonFunctions.showAlert("Ошибка", alertText: "data is nil",alertButtonText: "Закрыть", viewController: viewContr)
println("json data is nil")
}
}
})
task.resume()
}
class func JSONRequest(urlInput: String, tableName: UITableView, action: (NSArray)->Void, refresh:UIRefreshControl, category: Int, viewContr: UIViewController, hideLoadingViewAndStopAnimating: ()->Void) {
//refresh.beginRefreshing()
let urlPath = urlInput
let url = NSURL(string: urlPath)
let session = NSURLSession.sharedSession()
println("started second request")
let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
if error != nil {
println(error)
let stringError = error.localizedDescription
CommonFunctions.showAlert("Ошибка", alertText: stringError, alertButtonText: "Закрыть", viewController: viewContr)
}
else{
var err: NSError?
if data != nil{
if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as? NSArray {
println("the number of news in json :\(jsonResult.count)")
if (err == nil) {
dispatch_async(dispatch_get_main_queue(), {
action(jsonResult)
hideLoadingViewAndStopAnimating()
tableName.reloadData()
refresh.endRefreshing()
})
}else{
let stringError = err?.localizedDescription
hideLoadingViewAndStopAnimating()
CommonFunctions.showAlert("Ошибка", alertText: stringError!, alertButtonText: "Закрыть", viewController: viewContr)
}
}else {
hideLoadingViewAndStopAnimating()
println("json is not valid")
var dic = dictForErrors()
CommonFunctions.showAlert("Ошибка", alertText: dic.alertText, alertButtonText: "Закрыть", viewController: viewContr)
}
}else{
CommonFunctions.showAlert("Ошибка", alertText: "data is nil",alertButtonText: "Закрыть", viewController: viewContr)
println("json data is nil")
}
}
})
task.resume()
}
This is how I am making request in order to download two request
self.refreshControl = self.refreshController
self.refreshControl?.addTarget(self, action: "loadDataNewsLenta", forControlEvents: .ValueChanged)
if arrayMainPage.count > 0 {
activityView.alpha = 0.0
arrayNewSLenta = arrayMainPage
self.tableView.reloadData()
}else{
// loading first time news
activityView.alpha = 0.5
activityIndicator.startAnimating()
isFirstReq = true
arrayNewSLenta.removeAll(keepCapacity: false)
CommonFunctions.JSONRequest2(urlString, tableName: tableView, action: desirializeJSONToArray, refresh: self.refreshController, viewContr: self, hidLoadingViewAndStopAnimating)
}
I put the code above on viewdidload. This code is where I am reloading :
func hidLoadingViewAndStopAnimating() {
activityView.alpha = 0
activityIndicator.stopAnimating()
}
func configureTableView() {
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 110.0
}
func loadDataNewsLenta() {
arrayNewSLenta.removeAll(keepCapacity: false)
isFirstReq = true
refreshController.beginRefreshing()
CommonFunctions.JSONRequest2(urlString, tableName: tableView, action: desirializeJSONToArray, refresh: self.refreshController, viewContr: self, hidLoadingViewAndStopAnimating)
}
This is all about tableview code and desirialization of json to array:
// 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 {
println("it is news lenta count \(arrayNewSLenta.count)")
return arrayNewSLenta.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("lentaCell", forIndexPath: indexPath) as LentaTableViewCell
let inex = indexPath.row
cell.labelDateOfPublication.text = arrayNewSLenta[indexPath.row].pubDate
cell.labeltitle.text = arrayNewSLenta[indexPath.row].pageTitle
return cell
}
func desirializeJSONToArray(jsonArray: NSArray){
for singleJSON in jsonArray{
var singleArticle = ArticleInfo()
if let arrayText = singleJSON as? NSDictionary{
if let articleID = arrayText["id"] as? NSString{
singleArticle.articleID = articleID
}
if let pageTitle = arrayText["pagetitle"] as? NSString {
singleArticle.pageTitle = pageTitle
}
if let longTitle = arrayText["longtitle"] as? String{
singleArticle.longTitle = longTitle
}
if let introText = arrayText["introtext"] as? String{
singleArticle.introText = introText
}
if let contentText = arrayText["content_text"] as? String{
singleArticle.contentText = contentText
}
if let category = arrayText["category"] as? String{
singleArticle.category = category
}
if let imageLink = arrayText["thumbnail"] as? String{
singleArticle.linkToImage = imageLink
}
if let videoLink = arrayText["video"] as? String{
singleArticle.videoLink = videoLink
println("hre is video link")
println(videoLink)
}
if let sity = arrayText["sity"] as? String{
singleArticle.sity = sity
}
if let visible = arrayText["visible"] as? String{
singleArticle.visible = visible
}
if let visits = arrayText["visits"] as? String{
singleArticle.visits = visits
}
if let pubDate = arrayText["pubdate"] as? String{
singleArticle.pubDate = pubDate
}
// insert result into array
arrayNewSLenta.append(singleArticle)
}
}
//cycle ended
if isFirstReq == true {
let urlStringSecondRequest = "http://www.kfdz/artifdcles/JsonMainList"
CommonFunctions.JSONRequest(urlStringSecondRequest, tableName: tableView, action: desirializeJSONToArray, refresh: self.refreshController, category: 0, viewContr: self, hidLoadingViewAndStopAnimating)
}
isFirstReq = false
}
The interesting fact I noticed , when I go to first vc and pull table immediately, for some reason it load cellAtIndex function using only the last row on screen(in my case index 4). on the other hand, when I go to first vc and choose some item (to see detailed view) and go back and pull table everything works.
On more issue to say, when I change transition style of uipageviewcontroller to PageCurl I dont have this problem!
You can instead of using 3 table view in a single view, you should use the container view in the first view and put all the view tables in different views as shown in the picture below :
You need 1 file for each table view created.
the bug can probably disappear like that
Related
I'm creating a simple chat app, it has a loading screen with a segue to either the login screen if the user is not logged in or directly to his chats if he is. The chats are displayed in a UICollectionView. When I was first testing, I populated it with dummy data which I declared in the class itself, and everything worked fine. Now I am fetching the user's chats from an online database in the Loading Screen, and storing them in an array called user_chats which is declared globally.
I use the following code to populate the UICollectionView :
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// getUserChats()
return user_chats.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("chat_cell" , forIndexPath: indexPath) as! SingleChat
cell.chatName?.text = user_chats[indexPath.row].chat_partner!.name
cell.chatTextPreview?.text = user_chats[indexPath.row].chat_messages!.last!.text
let profile_pic_URL = NSURL(string : user_chats[indexPath.row].chat_partner!.profile_pic!)
downloadImage(profile_pic_URL!, imageView: cell.chatProfilePic)
cell.chatProfilePic.layer.cornerRadius = 26.5
cell.chatProfilePic.layer.masksToBounds = true
let dividerLineView: UIView = {
let view = UIView()
view.backgroundColor = UIColor(white: 0.5, alpha: 0.5)
return view
}()
dividerLineView.translatesAutoresizingMaskIntoConstraints = false
cell.addSubview(dividerLineView)
cell.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-1-[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": dividerLineView]))
cell.addSubview(dividerLineView)
cell.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[v0(1)]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": dividerLineView]))
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("showChat", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "showChat") {
let IndexPaths = self.collectionView!.indexPathsForSelectedItems()!
let IndexPath = IndexPaths[0] as NSIndexPath
let vc = segue.destinationViewController as! SingleChatFull
vc.title = user_chats[IndexPath.row].chat_partner!.name
}
}
DATA FETCH :
func getUserChats() {
let scriptUrl = "*****"
let userID = self.defaults.stringForKey("userId")
let params = "user_id=" + userID!
let myUrl = NSURL(string: scriptUrl);
let request: NSMutableURLRequest = NSMutableURLRequest(URL: myUrl!)
request.HTTPMethod = "POST"
let data = params.dataUsingEncoding(NSUTF8StringEncoding)
request.timeoutInterval = 10
request.HTTPBody=data
request.HTTPShouldHandleCookies=false
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
let queue:NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in
do {
if (data != nil) {
do {
var dataString = String(data: data!, encoding: NSUTF8StringEncoding)
var delimiter = "]"
var token = dataString!.componentsSeparatedByString(delimiter)
dataString = token[0] + "]"
print(dataString)
let data_fixed = dataString!.dataUsingEncoding(NSUTF8StringEncoding)
do {
let jsonArray = try NSJSONSerialization.JSONObjectWithData(data_fixed!, options:[])
// LOOP THROUGH JSON ARRAY AND FETCH VALUES
for anItem in jsonArray as! [Dictionary<String, AnyObject>] {
let curr_chat = Chat()
if let chatId = anItem["chatId"] as? String {
curr_chat.id = chatId
}
let friend = Friend()
let user1id = anItem["user1_id"] as! String
let user2id = anItem["user2_id"] as! String
if (user1id == userID) {
if let user2id = anItem["user2_id"] as? String {
friend.id = user2id
}
if let user2name = anItem["user2_name"] as? String {
friend.name = user2name
}
if let user2profilepic = anItem["user2_profile_pic"] as? String {
friend.profile_pic = user2profilepic
}
}
else if (user2id == userID){
if let user1id = anItem["user1_id"] as? String {
friend.id = user1id
}
if let user1name = anItem["user1_name"] as? String {
friend.name = user1name
}
if let user1profilepic = anItem["user1_profile_pic"] as? String {
friend.profile_pic = user1profilepic
}
}
curr_chat.chat_partner = friend
var chat_messages = [Message]()
if let dataArray = anItem["message"] as? [String : AnyObject] {
for (_, messageDictionary) in dataArray {
if let onemessage = messageDictionary as? [String : AnyObject] { let curr_message = Message()
if let messageid = onemessage["message_id"] as? String {
curr_message.id = messageid
}
if let messagedate = onemessage["timestamp"] as? String {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date = dateFormatter.dateFromString(messagedate)
curr_message.date = date
}
if let messagesender = onemessage["sender"] as? String {
curr_message.sender = messagesender
}
if let messagetext = onemessage["text"] as? String {
curr_message.text = messagetext
}
chat_messages.append(curr_message)
}}
}
curr_chat.chat_messages = chat_messages
user_chats.append(curr_chat)
}
}
catch {
print("Error: \(error)")
}
}
// NSUserDefaults.standardUserDefaults().setObject(user_chats, forKey: "userChats")
}
else {
dispatch_async(dispatch_get_main_queue(), {
let uiAlert = UIAlertController(title: "No Internet Connection", message: "Please check your internet connection.", preferredStyle: UIAlertControllerStyle.Alert)
uiAlert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: { action in
self.dismissViewControllerAnimated(true, completion:nil)
}))
self.presentViewController(uiAlert, animated: true, completion: nil)
})
}
} catch _ {
NSLog("error")
}
})
}
The problem is that the collection view is always empty now. I have done some debugging and put a breakpoint inside the first function, and I saw that this method is called when the Loading Screen is still displayed to the user and the chat screen hasn't even been loaded. My suspicion is that this is called before the data is fetched from the internet in the Loading Screen, and as a result the size of the user_chats array is 0. I am used to working with Android and ListView where the ListView are never populated until the parent view is displayed on screen, hence why I am confused. The method which fetches the data from the online database works fine as I have already extensively debugged it, so the problem isn't there.
The best option is to add a completionHandler to your function to be notified when the data is return and/or when the async function is finished executing. The code below is a truncated version of your getUserCharts function with a completionHandler, which returns a true or false when the data is load (You could modify this to return anything you wish). You can read more about closures/ completion handlers https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html or google.
function
func getUserChats(completionHandler: (loaded: Bool, dataNil: Bool) -> ()) -> (){
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in
do {
if (data != nil) {
do {
var dataString = String(data: data!, encoding: NSUTF8StringEncoding)
var delimiter = "]"
var token = dataString!.componentsSeparatedByString(delimiter)
dataString = token[0] + "]"
print(dataString)
let data_fixed = dataString!.dataUsingEncoding(NSUTF8StringEncoding)
do {
let jsonArray = try NSJSONSerialization.JSONObjectWithData(data_fixed!, options:[])
// LOOP THROUGH JSON ARRAY AND FETCH VALUES
completionHandler(loaded: true, dataNil: false)
}
catch {
print("Error: \(error)")
}
}
}
else {
//Handle error or whatever you wish
completionHandler(loaded: true, dataNil: true)
}
} catch _ {
NSLog("error")
}
How to use it
override func viewDidLoad() {
getUserChats(){
status in
if status.loaded == true && status.dataNil == false{
self.collectionView?.reloadData()
}
}
}
It sounds like this is an async issue. I'm not sure how your project is setup but you need to call reloadData() on your collection view when the response is returned.
After you have received the data back from the server, and updated the data source for the collection view you need to refresh the collection view (Make sure you are on the main thread, since it is modifying the UI):
dispatch_async(dispatch_get_main_queue()) {
self.collectionView.reloadData()
}
Edit:
Also, I'm not completely sure how you have your project setup, but you could create a delegate for your data fetch, so every time you get something back from the server it calls a delegate method that there are new messages. Your collection view controller would subscribe to that delegate, and every time the that method is called it would reload your collection view.
The Delegate:
protocol ChatsDelegate {
func didUpdateChats(chatsArray: NSArray)
}
In your Data Fetch:
user_chats.append(cur_chat)
self.delegate.didUpdateChats(user_chats)
In your collectionView controller:
class viewController: ChatsDelegate, ... {
...
func didUpdateChats(chatsArray: NSArray) {
dispatch_async(dispatch_get_main_queue()) {
self.collectionView.reloadData()
}
}
I have the following two functions in my first ViewController. They load a UITableView with over 300 rows. I call the loadRemoteData function inside the ViewDidLoad. Everything works fine in the first ViewController.
// MARK: - parseJSON
func parseJSON(data: NSData) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers)
if let rootDictionary = json as? [NSObject: AnyObject], rootResults = rootDictionary["results"] as? [[NSObject: AnyObject]] {
for childResults in rootResults {
if let firstName = childResults["first_name"] as? String,
let lastName = childResults["last_name"] as? String,
let bioguideId = childResults["bioguide_id"] as? String,
let state = childResults["state"] as? String,
let stateName = childResults["state_name"] as? String,
let title = childResults["title"] as? String,
let party = childResults["party"] as? String {
let eachLegislator = Legislator(firstName: firstName, lastName: lastName, bioguideId: bioguideId, state: state, stateName: stateName, title: title, party: party)
legislators.append(eachLegislator)
}
}
}
} catch {
print(error)
}
}
// MARK: - Remote Data configuration
func loadRemoteData() {
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let url = "https://somedomain.com/legislators?order=state_name__asc,last_name__asc&fields=first_name,last_name,bioguide_id"
if let url = NSURL(string: url) {
let task = session.dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
if let error = error {
print("Data Task failed with error: \(error)")
return
}
if let http = response as? NSHTTPURLResponse, data = data {
if http.statusCode == 200 {
dispatch_async(dispatch_get_main_queue()) {
self.parseJSON(data)
self.tableView.reloadData()
}
}
}
})
task.resume()
}
}
In the second ViewController, I want to display more information about the individual listed in the cell that is tapped, for that I use a different URL such as https://somedomain.com/legislators?bioguide_id=\"\(bioguideId)\" which provides me with a lot more detail. (The data being requested from the JSON Dictionary is different)
The code I use in the second ViewController is just like shown above with the only difference being the URL. I can print the url coming from the previous ViewController and it is displayed in the console log but no json data is shown.
I would appreciate any help.
Thanks
Below is the code for my second ViewController:
import UIKit
class DetailViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var bioguideId: String?
var currentLegislator: Legislator? = nil
var currentLegislatorUrl: String?
let reuseIdentifier = "Cell"
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var tableView: UITableView!
// MARK: - parseJSON
private func parseJSON(data: NSData) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers)
if let rootDictionary = json as? [NSObject: AnyObject],
rootResults = rootDictionary["results"] as? [[NSObject: AnyObject]] {
for childResults in rootResults {
if let firstName = childResults["first_name"] as? String,
let lastName = childResults["last_name"] as? String,
let bioguideId = childResults["bioguide_id"] as? String,
let state = childResults["state"] as? String,
let stateName = childResults["state_name"] as? String,
let title = childResults["title"] as? String,
let party = childResults["party"] as? String {
currentLegislator = Legislator(firstName: firstName, lastName: lastName, bioguideId: bioguideId, state: state, stateName: stateName, title: title, party: party)
}
}
}
} catch {
print(error)
}
}
// MARK: - Remote Data configuration
func loadRemoteData(url: String) {
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let url = currentLegislatorUrl
if let url = NSURL(string: url!) {
let task = session.dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
if let error = error {
print("Data Task failed with error: \(error)")
return
}
print("Success")
if let http = response as? NSHTTPURLResponse, data = data {
if http.statusCode == 200 {
dispatch_async(dispatch_get_main_queue()) {
self.parseJSON(data)
self.tableView.reloadData()
}
}
}
})
task.resume()
}
}
func loadImage(urlString:String) {
let imgURL: NSURL = NSURL(string: urlString)!
let request: NSURLRequest = NSURLRequest(URL: imgURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request){
(data, response, error) -> Void in
if (error == nil && data != nil) {
func display_image() {
self.imageView.image = UIImage(data: data!)
}
dispatch_async(dispatch_get_main_queue(), display_image)
}
}
task.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
print(currentLegislatorUrl!)
loadRemoteData(currentLegislatorUrl!)
loadImage("https://theunitedstates.io/images/congress/225x275/\(bioguideId!).jpg")
self.title = bioguideId
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier, forIndexPath: indexPath)
cell.textLabel!.text = currentLegislator?.firstName
return cell
}
}
Thanks to Adam H. His comment made me reevaluate the URL I was using and by adding additional operators, now the data is shown in my second ViewController.
i am having in table view which will display the data from one url.But when i open first time.I am seeing two times same data or some time no data are showing in my table view.
import UIKit
import CoreLocation
class ContainerViewController: UIViewController, CLLocationManagerDelegate, UITableViewDelegate, UITableViewDataSource {
// check the current view controller
weak var currentViewController: UIViewController?
// show the location
#IBOutlet weak var LocationLabel: UILabel!
var locationManager: CLLocationManager = CLLocationManager()
#IBOutlet var TypeLabel: UILabel!
#IBOutlet var TableViewList: UITableView!
var startLocation: CLLocation!
var NewCurrentLatitude: Double!
var NewCurrentLongitude: Double!
var BTdata = BTData?()
var BTypeId : String?
// array to store the value from json
var arrDict = [Businessdata]()
var selectedIndex:NSIndexPath?
override func viewDidLoad() {
super.viewDidLoad()
isSearching = false;
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
startLocation = nil
// nib for custom cell (table view)
let nib = UINib(nibName:"customCell", bundle: nil)
TableViewList.registerNib(nib, forCellReuseIdentifier: "cell")
// LoadBusinesses()
}
override func viewDidAppear(animated: Bool)
{
self.TypeLabel.text = BTdata?.BTNames
self.BTypeId = BTdata?.BTIds
locationManager.startUpdatingLocation()
}
// current location
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location : CLLocationCoordinate2D = manager.location!.coordinate;
CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: {(placemarks, error)->Void in
if (error != nil)
{
print("Reverse geocoder failed with error" + error!.localizedDescription)
return
}
if placemarks!.count > 0
{
let pm : CLPlacemark = placemarks![0]
//stop updating location to save battery life
let locality = (pm.locality != nil) ? pm.locality : ""
let state = pm.administrativeArea
let countryCode = pm.ISOcountryCode
if(countryCode == "CAN")
{
self.LocationLabel.text = "in "+locality!+", "+state!
self.NewCurrentLongitude = location.longitude;
self.NewCurrentLatitude = location.latitude;
}
else
{
self.LocationLabel.text = "in Toronto, ON"
self.NewCurrentLatitude = 43.761539;
self.NewCurrentLongitude = -79.411079;
print("Manual location Label.")
}
self.locationManager.stopUpdatingLocation()
}
else
{
print("Problem with the data received from geocoder")
}
self.LoadBusinesses()
NSUserDefaults.standardUserDefaults().setDouble(self.NewCurrentLongitude, forKey: "UserLongitude")
NSUserDefaults.standardUserDefaults().setDouble(self.NewCurrentLatitude, forKey: "UserLatitude")
NSUserDefaults.standardUserDefaults().synchronize()
})
}
// location load failure
func locationManager(manager: CLLocationManager, didFailWithError error: NSError)
{
print("Error while updating location " + error.localizedDescription)
}
// web services method
func LoadBusinesses()
{
print("Inside Load Business")
let token = NSUserDefaults.standardUserDefaults().valueForKey("access_token") as! String
let headers = ["x-access-token": token]
var StringUrl:String = "http:some url"
StringUrl += "?lat=\(self.NewCurrentLatitude)"
StringUrl += "&long=\(self.NewCurrentLongitude)"
print(StringUrl)
let request = NSMutableURLRequest(URL: NSURL(string: StringUrl)!,
cachePolicy: .UseProtocolCachePolicy,
timeoutInterval: 10.0)
request.HTTPMethod = "GET"
request.allHTTPHeaderFields = headers
let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
if (error != nil)
{
print(error)
}
else
{
if let json = (try? NSJSONSerialization.JSONObjectWithData(data!, options: [])) as? NSDictionary
{
let success = json["success"] as? Int
if (success == 1)
{
if let reposArray = json["data"] as? [NSDictionary]
{
self.arrDict.removeAll()
for item in reposArray
{
let itemObj = item as? Dictionary<String,AnyObject>
let b_type = itemObj!["business_type"]
// taxis type
if (b_type as? String == self.BTypeId)
{
self.arrDict.append(Businessdata(json:item))
print("load data")
}
}
dispatch_async(dispatch_get_main_queue(),{
self.TableViewList.reloadData()
print("load data 1")
})
}
}
else
{
let message = json["message"] as? String
print(message)
}
}
}
})
dataTask.resume()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
print("load data 2")
return self.arrDict.count
}
// number of rows
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 1
}
// calling each cell based on tap and users ( premium / non premium )
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
print("load data 3")
let cell:customCell = self.TableViewList.dequeueReusableCellWithIdentifier("cell") as! customCell
cell.vendorName.text = arrDict[indexPath.row].BusinessName
cell.vendorAddress.text = arrDict[indexPath.row].Address
cell.VendorRating.rating = arrDict[indexPath.row].Rating!
return cell
}
// height of cell based on tap
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
if(isTapped == true && selectedIndex == indexPath)
{
return 125.0;
}
return 80.0;
}
// did select row of table view
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
selectedIndex = indexPath;
isTapped = true;
print("load data 4")
// TableViewList.reloadData();
}
}
My full code .In this same problem also i am facing.Showing same data 3 time .In my consloe, the print function is also printing two time.I measn that function executing two times:
load data 2
load data 2
load data 2
Manual location Label.
Inside Load Business
http://some url?lat=43.761539&long=-79.411079
load data
load data 2
load data 1
load data 3
0
1
2
3
4
Your code should be something like this. You should reload your tableView after you have parsed the data.
if let reposArray = json["data"] as? [NSDictionary] {
self.arrDict.removeAll()
for item in reposArray {
let itemObj = item as? Dictionary<String,AnyObject>
let b_type = itemObj!["business_type"]
// taxis type
if (b_type as? String == self.BTypeId) {
self.arrDict.append(Businessdata(json:item))
}
}
dispatch_async(dispatch_get_main_queue(),{
self.TableViewList.reloadData()
})
}
I'm populating my tableView with JSON data, most of the time the data shows but for some strange reason other times it doesn't. I tested the JSON data in Chrome and the info is there. I also made print statements to print the info after it has downloaded and it appears to download correctly. I can't figure out why 80% of the time the data populates the tableView correctly and 20% of the time it doesn't. Here is a sample of my code, there are many more cells but I shortened it to 2 for this example:
var task : NSURLSessionTask?
var newURL : String?
var bannerArray: [String] = []
var overViewArray: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
getJSON(newURL!)
}
func getJSON (urlString: String) {
let url = NSURL(string: urlString)!
let session = NSURLSession.sharedSession()
task = session.dataTaskWithURL(url) {(data, response, error) in
dispatch_async(dispatch_get_main_queue()) {
if (error == nil) {
self.updateDetailShowInfo(data)
}
else {
"Not getting JSON"
}
}
}
task!.resume()
}
func updateDetailShowInfo (data: NSData!) {
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
guard let banner = jsonResult["banner"] as? String,
let overview = jsonResult["overview"] as? String
else { return }
_ = ""
print(overview)
bannerArray.append(banner)
overViewArray.append(overview)
}
catch {
print("It ain't working")
}
self.DetailTvTableView.reloadData()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0: return bannerArray.count
case 1: return overViewArray.count
default: fatalError("Unknown Selection")
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
switch indexPath.section {
case 0:
let cell = tableView.dequeueReusableCellWithIdentifier("bannerCell", forIndexPath: indexPath) as! BannerCell
cell.bannerImage.sd_setImageWithURL(NSURL(string: bannerArray[indexPath.row]))
self.DetailTvTableView.rowHeight = 100
DetailTvTableView.allowsSelection = false
return cell
case 1:
let cell = tableView.dequeueReusableCellWithIdentifier("overviewCell", forIndexPath: indexPath) as! OverviewCell
let overViewText = overViewArray[indexPath.row]
if overViewText != "" {
cell.overView.text = overViewText
} else {
cell.overView.text = "N/A"
}
self.DetailTvTableView.rowHeight = 200
print(overViewArray[indexPath.row])
return cell
default: ""
}
return cell
}
I'm just doing this off the web. And I think there are some errors. You need to debug them yourself.
Your understanding of fetching the JSON and GCD is totally wrong. I believe these codes you got somewhere off the web. Go read up what is dispatch_async.
Basically, you need to create session to fetch JSON data, which you have done it correctly, however, within the NSJSONSerialization, you need to store them in a variable and append it to your array. This is fetched asynchronously. Your dispatch_async will reload data serially.
func getJSON (urlString: String) {
let url = NSURL(string: urlString)!
let session = NSURLSession.sharedSession()
task = session.dataTaskWithURL(url) {(data, response, error) in
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
guard let banner = jsonResult["banner"] as? String,
let overview = jsonResult["overview"] as? String
bannerArray.append(banner)
overViewArray.append(overview)
} dispatch_async(dispatch_get_main_queue()) {
if (error == nil) {
self.DetailTvTableView.reloadData()
}
else {
"Not getting JSON"
}
}
catch {
print("It ain't working")
}
}
}
task!.resume()
}
How do automatically stop activity indicator after the refresh button is pressed and loading of content is done in UITableview JSON
I already have to the code to start the spinning and the button
Here is full code
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var json_data_url = "http://www.howtotechworld.com/json_table_view_images%20(1).json"
var image_base_url = ""
var isProgressShowing = true;
var TableData:Array< datastruct > = Array < datastruct >()
enum ErrorHandler:ErrorType
{
case ErrorFetchingResults
}
struct datastruct
{
var imageurl:String?
var description:String?
var image:UIImage? = nil
init(add: NSDictionary)
{
imageurl = add["url"] as? String
description = add["description"] as? String
}
}
#IBOutlet var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableview.dataSource = self
tableview.delegate = self
get_data_from_url(json_data_url)
}
override func viewWillAppear(animated: Bool) {
let barButtonItem = UIBarButtonItem(title: "Refresh", style: .Plain, target: self, action: "refreshTapped");
self.navigationItem.rightBarButtonItem = barButtonItem;
}
func refreshTapped() {
addProgressIndicator(isProgressShowing);
get_data_from_url(json_data_url)
}
func addProgressIndicator(show : Bool) {
isProgressShowing = !show;
if(show) {
let myActivityIndicator = UIActivityIndicatorView(activityIndicatorStyle:UIActivityIndicatorViewStyle.Gray)
myActivityIndicator.startAnimating()
let barButtonItem = UIBarButtonItem(customView: myActivityIndicator)
self.navigationItem.rightBarButtonItem = barButtonItem
} else {
self.navigationItem.rightBarButtonItem = nil;
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
let data = TableData[indexPath.row]
cell.textLabel?.text = data.description
if (data.image == nil)
{
cell.imageView?.image = UIImage(named:"image.jpg")
load_image(image_base_url + data.imageurl!, imageview: cell.imageView!, index: indexPath.row)
}
else
{
cell.imageView?.image = TableData[indexPath.row].image
}
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return TableData.count
}
func get_data_from_url(url:String)
{
let url:NSURL = NSURL(string: url)!
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "GET"
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringCacheData
let task = session.dataTaskWithRequest(request) {
(
let data, let response, let error) in
guard let _:NSData = data, let _:NSURLResponse = response where error == nil else {
print("error")
return
}
dispatch_async(dispatch_get_main_queue(), {
self.extract_json(data!)
return
})
}
task.resume()
}
func extract_json(jsonData:NSData)
{
let json: AnyObject?
do {
json = try NSJSONSerialization.JSONObjectWithData(jsonData, options: [])
} catch {
json = nil
return
}
if let list = json as? NSArray
{
for (var i = 0; i < list.count ; i++ )
{
if let data_block = list[i] as? NSDictionary
{
TableData.append(datastruct(add: data_block))
}
}
do
{
try read()
}
catch
{
}
do_table_refresh()
}
}
func do_table_refresh()
{
dispatch_async(dispatch_get_main_queue(), {
self.tableview.reloadData()
return
})
}
func load_image(urlString:String, imageview:UIImageView, index:NSInteger)
{
let url:NSURL = NSURL(string: urlString)!
let session = NSURLSession.sharedSession()
let task = session.downloadTaskWithURL(url) {
(
let location, let response, let error) in
guard let _:NSURL = location, let _:NSURLResponse = response where error == nil else {
print("error")
return
}
let imageData = NSData(contentsOfURL: location!)
dispatch_async(dispatch_get_main_queue(), {
self.TableData[index].image = UIImage(data: imageData!)
self.save(index,image: self.TableData[index].image!)
imageview.image = self.TableData[index].image
return
})
}
task.resume()
}
func read() throws
{
do
{
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
let fetchRequest = NSFetchRequest(entityName: "Images")
let fetchedResults = try managedContext.executeFetchRequest(fetchRequest)
for (var i=0; i < fetchedResults.count; i++)
{
let single_result = fetchedResults[i]
let index = single_result.valueForKey("index") as! NSInteger
let img: NSData? = single_result.valueForKey("image") as? NSData
TableData[index].image = UIImage(data: img!)
}
}
catch
{
print("error")
throw ErrorHandler.ErrorFetchingResults
}
}
func save(id:Int,image:UIImage)
{
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
let entity = NSEntityDescription.entityForName("Images",
inManagedObjectContext: managedContext)
let options = NSManagedObject(entity: entity!,
insertIntoManagedObjectContext:managedContext)
let newImageData = UIImageJPEGRepresentation(image,1)
options.setValue(id, forKey: "index")
options.setValue(newImageData, forKey: "image")
do {
try managedContext.save()
} catch
{
print("error")
}
}
}
You will need to keep a reference to myActivityIndicator and stop it in do_table_refresh() as shown below.
func do_table_refresh()
{
dispatch_async(dispatch_get_main_queue(), {
self.tableview.reloadData()
myActivityIndicator.stopAnimating()
return
})
}
Edit: Just had a quick thought after looking at your code in the question.
If you would like to hide myActivitIndicator using addProgressIndicator(isProgressShowing) , you will have to make sure that isProgressShowing is set to FALSE, then in the code that I have given above, replace myActivityIndicator.stopAnimating() with addProgressIndicator(isProgressShowing).
You will also need to update the addProgressIndicator function and add myActivityIndicator.stopAnimating() to the else condition.
If I understand your question right then I think below is what you want.
-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if([indexPath row] == ((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject]).row){
//end of loading
//for example [activityIndicator stopAnimating];
}
}
and for swift it is like this
func cellForRowAtIndexPath(_ indexPath: NSIndexPath) -> UITableViewCell?{
if(indexPath.row = tableView.indexPathsForVisibleRows().lastObject().row{
//end of loading
//for example [activityIndicator stopAnimating];
}
return yourCell;
}
I don't know swift much, but i guess you will have got the idea how to do it.
Add this code after do_table_refresh(): addProgressIndicator(!isProgressShowing);