Swift - Problems receiving images from parse and displaying them - ios

I'm having issues fetching the users images from parse and loading them on a table view.. I think it is something to do with the userId /Strings and pointer buissness!
The images upload fine to parse and I can see them, but I cannot seem to download them. My code is definetly the problem, I just can't figure out which part.
On parse the userId is a pointer to the user class, so I'm pretty sure I've done something wrong connecting it all..! I'm new to swift & Parse so I still have a bit of difficulty understanding it all.
Here's my code below, if anyone understands the problem I would love it if you could tell me!!
import UIKit
import Parse
class TimelineTableViewController: UITableViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UIPopoverPresentationControllerDelegate {
#IBOutlet var feedTableView: UITableView!
var refresher:UIRefreshControl!
var titles = [String]()
var username = [String]()
var imageFiles = [PFFile]()
var users = [String: String]()
override func viewDidLoad() {
super.viewDidLoad()
let query = PFUser.query()
query?.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let users = objects {
self.titles.removeAll(keepCapacity: true)
self.imageFiles.removeAll(keepCapacity: true)
self.users.removeAll(keepCapacity: true)
self.username.removeAll(keepCapacity: true)
for object in users {
if let user = object as? PFUser {
self.users[user.objectId!] = user.username!
}
}
}
let getFollowedUsersQuery = PFQuery(className: "followers")
getFollowedUsersQuery.whereKey("follower", equalTo: PFUser.currentUser()!)
getFollowedUsersQuery.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if let objects = objects {
for object in objects {
let followedUser = object ["following"] as! String
let query = PFQuery(className: "Post")
query.whereKey("userId", equalTo: followedUser)
query.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let objects = objects {
for object in objects {
self.titles.append(object["title"]! as! String)
self.imageFiles.append(object["imageFile"]! as! PFFile)
self.username.append(self.users[object["userId"] as! String]!)
self.tableView.reloadData()
} }}) } } } })
// Add the refresher
refresher = UIRefreshControl()
refresher.attributedTitle = NSAttributedString(string: "")
refresher.addTarget(self, action: "refreshData", forControlEvents: UIControlEvents.ValueChanged)
// Add the refresher as a sub view on the tableview
self.feedTableView.addSubview(refresher)
}
func refreshData() -> Void {
self.refresher.endRefreshing()}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func blogPost(sender: AnyObject) {
}
#IBAction func CameraPopover(sender: AnyObject) {
self.performSegueWithIdentifier("cameraPopover", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "cameraPopover"
{
let vc = segue.destinationViewController
let controller = vc.popoverPresentationController
if controller != nil
{
controller?.delegate = self
vc.preferredContentSize = CGSizeMake(50, 90)
}
}}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return.None
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return username.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let postCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! TimelineTableViewCell
imageFiles[indexPath.row].getDataInBackgroundWithBlock { (data, error) -> Void in
if let downloadedImage = UIImage(data: data!) {
postCell.postedImage.image = downloadedImage
}
}
postCell.usernameLabel.text = username[indexPath.row]
postCell.titleLabel.text = titles[indexPath.row]
return postCell
}
}

The issue you mentioned with the userId pointer is here:
let followedUser = object ["following"] as! String
let query = PFQuery(className: "Post")
query.whereKey("userId", equalTo: followedUser)
You mentioned that the userId column of the Post class is a User pointer, however you reference it as a string with followedUser.
With regards to the images not displaying properly, they will not be displayed until you refresh the UI. You are using the correct code to load the data which is
imageFiles[indexPath.row].getDataInBackgroundWithBlock { (data, error) -> Void in
if let downloadedImage = UIImage(data: data!) {
postCell.postedImage.image = downloadedImage
}
However, the issue is that since the data is loaded asynchronously, the postCell will be returned to the table before the data has been fetched. The key piece that's missing is to refresh the table using self.tableView.reloadData() in the completion handler.
imageFiles[indexPath.row].getDataInBackgroundWithBlock { (data, error) -> Void in
if let downloadedImage = UIImage(data: data!) {
postCell.postedImage.image = downloadedImage
self.tableView.reloadData()
}
Alternatively, I would recommend looking into using a PFImageView from the ParseUI framework. You assign each of the PFFiles to a PFImageView in the completion of the query and then call loadInBackground on each PFImageView. The PFImageView class automatically handles updating the images once the data has been loaded.

Related

Pictures shifting randomly in UITableView after reloading data

Pictures in my tableView are shifting around and are not being displayed on the correct posts after reloading the tableView. I cannot figure how to fix this.
https://i.stack.imgur.com/4hUbYm.jpg
The image above would be the normal image, however sometimes I get:
https://i.stack.imgur.com/PVEVrm.png
I've also did the prepareForReuse() function in the custom cell class but still doesn't work.
Here is the source code:
class UserPostsController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var userPostsTable: UITableView!
var images : [UIImage] = []
var descriptions : [String] = []
var likes : [String] = []
var dislikes : [String] = []
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.font: UIFont(name: "Pacifico", size: 30)!]
userPostsTable.dataSource = self
userPostsTable.delegate = self
let postQuery = PFQuery(className: "Post")
postQuery.whereKey("userid", equalTo: PFUser.current()?.objectId as Any)
postQuery.findObjectsInBackground { (objects, error) in
if let posts = objects {
for post in posts {
if let descripiton = post["description"] {
self.descriptions.append(descripiton as! String)
}
if let l = post["likes"] {
self.likes.append(l as! String)
}
if let d = post["dislikes"] {
self.dislikes.append(d as! String)
}
if let imageFile = post["imageFile"] as? PFFile {
imageFile.getDataInBackground(block: { (data, error) in
if let imageData = data {
if let image = UIImage(data: imageData) {
self.images.append(image)
}
}
self.userPostsTable.reloadData()
})
}
}
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return images.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = userPostsTable.dequeueReusableCell(withIdentifier: "userPostCell", for: indexPath) as? UserPostsTableViewCell {
cell.descriptionLabel.text = descriptions[indexPath.row]
cell.numerLikes.text = "\(likes[indexPath.row]) likes"
cell.numberDislikes.text = "\(dislikes[indexPath.row]) dislikes"
cell.postImage.image = images[indexPath.row]
cell.selectionStyle = .none
return cell
}
return UITableViewCell()
}
I think you problem is here:
if let imageFile = post["imageFile"] as? PFFile {
imageFile.getDataInBackground(block: { (data, error) in
if let imageData = data {
if let image = UIImage(data: imageData) {
self.images.append(image)
}
}
self.userPostsTable.reloadData()
})
You are starting background tasks to get images data.
Any of these tasks could finish first, please debug your array and you might find that the images are not in the desired order.
Here is an apple sample project that might help you properly load those images in background.
https://developer.apple.com/library/archive/samplecode/LazyTableImages/Introduction/Intro.html

How do I fix laggy UITableView scrolling performance when downloading JSON?

In my application, I download a JSON file off of the internet and fill up a UITableView with items from the file. It does work well, and there are no problems or errors, but the scrolling performance is very laggy, and the UI glitches out a tiny bit.
I assume this is because of the images that I'm downloading from the JSON file, so I've looked into multi-threading, but I don't think I am doing it right because it does load much faster, but scrolling performance is still the same as before.
Can somebody please tell me how to fix this? This UITableView is the most important thing in the app, and I have been spending much time on trying to fix it. Thank you!
Here is my code-
import UIKit
class ViewController: UIViewController, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var nameArray = [String]()
var idArray = [String]()
var ageArray = [String]()
var genderArray = [String]()
var descriptionArray = [String]()
var imgURLArray = [String]()
let myActivityIndicator = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.gray)
final let urlString = "https://pbsocfilestorage.000webhostapp.com/jsonDogs.json"
override func viewDidLoad() {
super.viewDidLoad()
self.downloadJsonWithURL()
// Activity Indicator
myActivityIndicator.center = view.center
myActivityIndicator.hidesWhenStopped = true
myActivityIndicator.startAnimating()
view.addSubview(myActivityIndicator)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func downloadJsonWithURL() {
let url = NSURL(string:urlString)
URLSession.shared.dataTask(with: (url as? URL)!, completionHandler: {(data, response, error) ->
Void in
print("Good so far...")
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
print(jsonObj!.value(forKey: "dogs"))
if let dogArray = jsonObj!.value(forKey: "dogs") as? NSArray {
print("Why u no work!")
for dog in dogArray {
if let dogDict = dog as? NSDictionary {
if let name = dogDict.value(forKey: "name") {
self.nameArray.append(name as! String)
}
if let name = dogDict.value(forKey: "id") {
self.idArray.append(name as! String)
}
if let name = dogDict.value(forKey: "age") {
self.ageArray.append(name as! String)
}
if let name = dogDict.value(forKey: "gender") {
self.genderArray.append(name as! String)
}
if let name = dogDict.value(forKey: "image") {
self.imgURLArray.append(name as! String)
}
if let name = dogDict.value(forKey: "description") {
self.descriptionArray.append(name as! String)
}
OperationQueue.main.addOperation ({
self.myActivityIndicator.stopAnimating()
self.tableView.reloadData()
})
}
}
}
}
}).resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return nameArray.count
}
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let imgURL = NSURL(string: imgURLArray[indexPath.row])
let cell = tableView.dequeueReusableCell(withIdentifier: "reusableCell") as! TableViewCell
URLSession.shared.dataTask(with: (imgURL as! URL), completionHandler: {(data, resp, error) -> Void in
if (error == nil && data != nil) {
OperationQueue.main.addOperation({
cell.dogNameLabel.text = self.nameArray[indexPath.row]
cell.idLabel.text = self.idArray[indexPath.row]
cell.ageLabel.text = self.ageArray[indexPath.row]
cell.genderLabel.text = self.genderArray[indexPath.row]
print("Cell info was filled in!")
if imgURL != nil {
let data = NSData(contentsOf: (imgURL as? URL)!)
cell.dogImage.image = UIImage(data: data as! Data)
}
})
}
}).resume()
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDog" {
if let indexPath = self.tableView.indexPathForSelectedRow{
let detailViewController = segue.destination as! DetailViewController
detailViewController.imageString = imgURLArray[indexPath.row]
detailViewController.nameString = nameArray[indexPath.row]
detailViewController.idString = idArray[indexPath.row]
detailViewController.ageString = ageArray[indexPath.row]
detailViewController.descriptionString = descriptionArray[indexPath.row]
detailViewController.genderString = genderArray[indexPath.row]
}
}
}
}
There is a big mistake. You are loading data with dataTask but you aren't using that returned data at all. Rather than you are loading the data a second time with synchronous contentsOf. Don't do that.
And don't update the labels in the asynchronous completion block. The strings are not related to the image data.
This is more efficient:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let imgURL = URL(string: imgURLArray[indexPath.row])
let cell = tableView.dequeueReusableCell(withIdentifier: "reusableCell", for: indexPath) as! TableViewCell
cell.dogNameLabel.text = self.nameArray[indexPath.row]
cell.idLabel.text = self.idArray[indexPath.row]
cell.ageLabel.text = self.ageArray[indexPath.row]
cell.genderLabel.text = self.genderArray[indexPath.row]
print("Cell info was filled in!")
URLSession.shared.dataTask(with: imgURL!) { (data, resp, error) in
if let data = data {
OperationQueue.main.addOperation({
cell.dogImage.image = UIImage(data: data)
})
}
}.resume()
return cell
}
Note: You are strongly discouraged from using multiple arrays as data source. It's very error-prone. Use a custom struct or class. And create imgURLArray with URL instances rather than strings. This is also much more efficient.
Nevertheless, you should use a download manager which caches the images and cancels downloads if a cell goes off-screen. At the moment each image is downloaded again when the user scrolls and cellForRow is called again for this particular cell.

Pictures shifting randomly in UITableView after being retrieved from Firebase

As my title states, the pictures in my tableView shift around and are not being displayed on the correct posts when scrolling through the table view. After I stop scrolling they seem to be back into place.
I've been trying to make a sense out of the following articles:
new Firebase retrieve data and put on the tableview swift
retrieve image from Firebase storage to show on tableview swift
swift Firebase sort posts in tableview by date
But I cannot figure out how to make the pictures to display better.
Here's what I have:
import UIKit
import FirebaseAuth
import FirebaseDatabase
import FirebaseStorage
class MainFeedTableViewController: UITableViewController {
var posts = [Post]()
let alert = AlertsViewController()
var databaseRef: FIRDatabaseReference! {
return FIRDatabase.database().reference()
}
var storageRef: FIRStorage! {
return FIRStorage.storage()
}
override func viewDidLoad() {
super.viewDidLoad()
fetchPosts()
}
// populates the tableView with posts content in real time
private func fetchPosts(){
let postRefs = databaseRef.child("posts")
postRefs.observe(.value) { (snapshot: FIRDataSnapshot) in
var newPost = [Post]()
for post in snapshot.children{
let postObject = Post(snapshot: post as! FIRDataSnapshot)
newPost.insert(postObject, at: 0)
}
self.posts = newPost
self.tableView.reloadData()
}
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let postsAtIndexPath = posts[indexPath.row]
if postsAtIndexPath.postWithImage == true {
let cell = tableView.dequeueReusableCell(withIdentifier: "postWithImage", for: indexPath) as! PostWithImageTableViewCell
let postUser = postsAtIndexPath.uid
let userRef = databaseRef.child("users/\(postUser!)")
userRef.observe(.value, with: { (snapshot) in
let user = User(snapshot: snapshot)
DispatchQueue.main.async(execute: {
cell.userRealNameLabel.text = user.name
cell.usernameLabel.text = "#" + user.username
cell.postTextView.text = postsAtIndexPath.postText
cell.timeStampLabel.text = postsAtIndexPath.date
})
self.storageRef.reference(forURL: user.photoURL).data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) in
if error == nil{
DispatchQueue.main.async(execute: {
if let data = data{
cell.userProfilePicture.image = UIImage(data: data)
}
})
}
else{
print(error!.localizedDescription)
}
})
self.storageRef.reference(forURL: postsAtIndexPath.postPictureURL).data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) in
if error == nil{
DispatchQueue.main.async(execute: {
if let data = data{
cell.postImage.image = UIImage(data: data)
}
})
}
else{
self.alert.displayAlert(alertTitle: "Error", alertMessage: error!.localizedDescription, fromController: self)
}
})
}) { (error) in
self.alert.displayAlert(alertTitle: "Error", alertMessage: error.localizedDescription, fromController: self)
}
return cell
}
else{
let cell = tableView.dequeueReusableCell(withIdentifier: "postWithText", for: indexPath) as! PostTableViewCell
let postUser = postsAtIndexPath.uid
let userRef = databaseRef.child("users/\(postUser!)")
userRef.observe(.value, with: { (snapshot) in
let user = User(snapshot: snapshot)
DispatchQueue.main.async(execute: {
cell.userRealNameLabel.text = user.name
cell.usernameLabel.text = "#" + user.username
cell.postTextView.text = postsAtIndexPath.postText
cell.timestampLabel.text = postsAtIndexPath.date
})
self.storageRef.reference(forURL: user.photoURL).data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) in
if error == nil{
DispatchQueue.main.async(execute: {
if let data = data{
cell.userProfilePicture.image = UIImage(data: data)
}
})
}
else{
print(error!.localizedDescription)
}
})
}) { (error) in
self.alert.displayAlert(alertTitle: "Error", alertMessage: error.localizedDescription, fromController: self)
}
return cell
}
}
}
The reason I'm having a hard time, it's because I want the user's username, name, and profile picture to change everywhere and in real time when they edit their info. That's why I'm retrieving the user info based on the post's user uid.
What's a better way to implement this?
You have to override the prepareForReuse function in PostWithImageTableViewCell.
EXAMPLE
override func prepareForReuse() {
super.prepareForReuse()
self.userProfilePicture.image = nil
//reset the rest of the values on your `UITableViewCell` subclass
}
EDIT
Since the reuse issue has been resolved, i would like to recommend the following caching framework, Kingfisher, for more convenient image display experience.
Kingfisher has a great extension on UIImageView, what will take care of the caching for your.
Here how you would use it:
let url = URL(string: "https://domain.com/image.jpg")!
imageView.kf.setImage(with: url)
You only set to the URL, what will uniquely identify the image resource, and downloads only once. Here is a cheat sheet, how to use it.

The images from the webservice are refreshed in the UITableview

I was trying to load images from a web service to my tableview. But the images are refreshed and same image rotate in every cell. I think the problem is due to not implementing caching in proper way. I tried lot of stuff. But no luck..
import UIKit
class NewsTableViewController: UITableViewController {
var SLAFImages = [String]()
var SLAFHeading = [String]()
var SLAFText = [String]()
var SLAFCount = [Int]()
#IBOutlet weak var btnAction: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
let reposURL = NSURL(string: "https://www.helitours.lk/test.html")
// 2
do{
if let JSONData = NSData(contentsOfURL: reposURL!) {
// 3
if let json = try NSJSONSerialization.JSONObjectWithData(JSONData, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary {
// 4
if let reposArray = json["items"] as? [NSDictionary] {
// 5
for item in reposArray {
if let name = item.valueForKey("title") {
SLAFHeading.append(name as! String)
}
if let Imgname = item.valueForKey("main_image") {
let urlimg=("http://airforce.lk/"+(Imgname as! String)) as String;
SLAFImages.append(urlimg)
}
}
}
}
}
}catch{}
let infoImage = UIImage(named: "crest png.png")
let imgWidth = infoImage?.size.width
let imgHeight = infoImage?.size.height
let button:UIButton = UIButton(frame: CGRect(x: 0,y: 0,width: imgWidth!, height: imgHeight!))
button.setBackgroundImage(infoImage, forState: .Normal)
//button.addTarget(self, action: Selector("openInfo"), forControlEvents: UIControlEvents.TouchUpInside)
SLAFCount=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
SLAFText = ["While they like to munch on underwater plants, you might ",
"While they like to munch on underwater plants, you might ",
"While they like to munch on underwater plants, you might "]
//SLAFImages = ["Img1.jpg","Img2.jpg","Img3.jpg"]
// 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 {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return SLAFHeading.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell =
self.tableView.dequeueReusableCellWithIdentifier(
"NewsTableCell", forIndexPath: indexPath)
as! NewsTableViewCell
let row = indexPath.row
cell.NewsHeading.font =
UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
cell.NewsHeading.text = SLAFHeading[row]
if let url = NSURL(string: SLAFImages[row]) {
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, NSHTTPURLResponse, error) -> Void in
if error != nil {
print("thers an error in the log")
} else {
dispatch_async(dispatch_get_main_queue()) {
cell.NewsImage.image = UIImage(data: data!)
}
}
}
task.resume()
}
cell.NewsCount.text=String(SLAFCount[row])
cell.layoutIfNeeded()
return cell
}
Download image in Table view cell
Better way
this code will not increase more memory use of app.
//Initialize your catch variable
var cache = NSCache()
// MARK: - Private Image Download Block here
private func downloadPhoto(url: NSURL, completion: (url: NSURL, image: UIImage) -> Void) {
dispatch_async(downloadQueue, { () -> Void in
if let data = NSData(contentsOfURL: url) {
if let image = UIImage(data: data) {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
cache.setObject(image, forKey: url)
completion(url: url, image: image)
})
}
}
})
}
//Call this Function from Table view
//Step 1
// Checking here Image inside Catch
let imageurl = NSURL(string:”Image URL here”)
let myimage = cache.objectForKey(imageurl!) as? UIImage
//Step 2
// Downloading here Promotion image
if myimage == nil {
downloadPhoto(imageurl!, completion: { (url, image) -> Void in
let indexPath_ = self.tblView.indexPathForCell(cell)
if indexPath.isEqual(indexPath_) {
cell.myImageView.image = image
}
})
}
I hope this will help you.

How to use a custom activity indicator view while using API call in my project

I am new on Stack Overflow , learning Swift and now I am making a project of API call.
I want to use a custom activity indicator view until the data fetched from API . So please help me to short out the issue in my code .
I have imported JTMaterialSpinner library in my project.
This is the spinner (the custom activity indicator view) I want to show:
This is my code:
var spinerrview : JTMaterialSpinner = JTMaterialSpinner(frame: CGRectZero)
override func viewDidLoad()
{
super.viewDidLoad()
spinerrview.circleLayer.strokeColor = UIColor.greenColor().CGColor spinerrview.circleLayer.lineWidth=2.0
spinerrview.frame.size.height=25.0
spinerrview.frame.size.width=25.0
spinerrview.circleLayer
spinerrview.isAnimating=true
spinerrview.backgroundColor=UIColor.greenColor()
spinerrview.beginRefreshing()
getdata()
spinerrview.endRefreshing()
}
hare i give whole code
import UIKit
import Alamofire
import SwiftyJSON
import JTMaterialSpinner
class ViewController: UIViewController ,UITextViewDelegate,UITableViewDataSource{
var arrRes = [[String:AnyObject]]()
var spinerrview : JTMaterialSpinner = JTMaterialSpinner(frame: CGRectZero)
#IBOutlet weak var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
spinerrview.circleLayer.strokeColor = UIColor.redColor().CGColor
spinerrview.circleLayer.lineWidth=3.0
spinerrview.circleLayer
spinerrview.isAnimating=true
spinerrview.frame.size.height=25.0
spinerrview.frame.size.width = 25.0
spinerrview.center=table.center
spinerrview.beginRefreshing()
getdata(NSURL(string: "http:api.androidhive.info/contacts/")!, completionHandler: { (success) -> Void in
// When your API call was terminated, control flow goes here.
self.spinerrview.endRefreshing()
if success {
print("success")
} else {
print("fail")
}
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
typealias CompletionHandler = (success:Bool) -> Void
func getdata(url: NSURL,completionHandler: CompletionHandler) {
Alamofire.request(.GET, url).validate().responseJSON { response in
var flag = false // true if call is succeed,false otherwise
switch response.result {
case .Success(let data):
flag=true
let swiftyJsonVar = JSON(data)
if let resData = swiftyJsonVar["contacts"].arrayObject {
self.arrRes = resData as! [[String:AnyObject]]
}
self.table.reloadData()
case .Failure(let error):
flag=false
print("Request failed with error: \(error)")
}
completionHandler(success: flag )
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("jsonCell")!as! TableViewCell
var dict = arrRes[indexPath.row]
cell.name.text = dict["name"] as? String
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrRes.count
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("gotoview", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "gotoview") {
let indexPat = self.table.indexPathForSelectedRow!
let vc = segue.destinationViewController as! secondViewController
vc.adde = "add "+(arrRes[indexPat.row]["address"] as? String)!
vc.mail = "mail "+(arrRes[indexPat.row]["email"] as? String)!
vc.iid = "id "+(arrRes[indexPat.row]["id"] as? String)!
vc.nam = (arrRes[indexPat.row]["name"] as? String)!
vc.ph = "phone "+(arrRes[indexPat.row]["phone"]!["mobile"]as? String)!
}
}
}
Your issue is in these three lines:
spinerrview.beginRefreshing()
getdata()
spinerrview.endRefreshing()
The spinner start in main thread, getdata() was launched and it probably make some work in a background thread so you don't know when it's finish, and spinner ended right after getdata was called. It's like in real life when you press the light button ON/OFF immediatly .
To avoid this kind of issue, you could create a completionHandler for your getdata() function (API call):
typealias CompletionHandler = (success:Bool) -> Void
func getdata(url: NSURL,completionHandler: CompletionHandler) {
// API call code.
let flag = true // true if call is succeed,false otherwise
completionHandler(success: flag)
}
Usage:
spinerrview.beginRefreshing()
getdata(NSURL(string: "http://...")!, { (success) -> Void in
// When your API call was terminated, control flow goes here.
spinerrview.endRefreshing()
if success {
// API call success
} else {
// API call fail
}
})
Update:
(after your new edit to add getdata())
typealias CompletionHandler = (success:Bool) -> Void
func getdata(url: NSURL,completionHandler: CompletionHandler) {
Alamofire.request(.GET, url).validate().responseJSON { response in
var flag = false // true if call is succeed,false otherwise
switch response.result {
case .Success(let data):
flag=true
let swiftyJsonVar = JSON(data)
if let resData = swiftyJsonVar["contacts"].arrayObject {
self.arrRes = resData as! [[String:AnyObject]]
}
self.table.reloadData()
case .Failure(let error):
flag=false
print("Request failed with error: \(error)")
}
completionHandler(success: flag )
}
}
In your getData() function, add a closure parameter, and inside of it finish the spinner action.

Resources