1

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") 
        } 

    } 


}
Katz
  • 826
  • 3
  • 19
  • 40
system21
  • 361
  • 4
  • 16
  • 1
    how's your `get_data_from_url(json_data_url)` method looks like? – Bista Jan 14 '16 at 06:20
  • ill add all the code – system21 Jan 14 '16 at 06:22
  • 1
    stop your activity indicator after your call for the webservice, in viewdid load method.write the code to stop activity indicator after your 'get_data_from_url(json_data_url)' method call in viewdidload method and try this. – caldera.sac Jan 14 '16 at 06:33
  • i need full code i am practicing swift from open source projects – system21 Jan 14 '16 at 06:35
  • 1
    Why not using `UIRefreshControl` to implement pull-to-refresh honestly? – Alexander Perechnev Jan 14 '16 at 06:37
  • @AlexanderPerechnev - I think he wants to refresh table by clicking on button instead of pulling table view to refresh, but 'puul to refresh' is suitable. – Bista Jan 14 '16 at 06:39
  • 1
    @system21 it works like in Twitter – when you pull your feed down, it starts to refresh. Here is an example of how to implement: http://stackoverflow.com/questions/10291537/pull-to-refresh-uitableview-without-uitableviewcontroller – Alexander Perechnev Jan 14 '16 at 06:41

3 Answers3

3

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.

Shumais Ul Haq
  • 1,427
  • 1
  • 15
  • 26
  • I would change self.table... and myActivity..... lines' order, but this is the right method that you should stop indicator in. – turushan Jan 14 '16 at 07:13
  • @turushan If the reload was taking a long time, bad programming perhaps or maybe just a lot of stuff to load, then essentially the indicator would hide before the reload was complete. To get around that, it is better that we stop the indicator after reloading the table. For smaller/Faster tableview reloads the delay would be negligible though. – Shumais Ul Haq Jan 14 '16 at 07:19
  • actually in your code stopAnimating() line doesn't wait till reloadData() ends, because it's not in completion part, so just after run time reads reloadData() in 0.00001 seconds jumps to next line, and again stops the animation without depending on reloadData is done or not. – turushan Jan 14 '16 at 07:40
  • I agree. calling `reloadData` only calls `tableView:numberOfRowsInSection:` and `tableView:heightForRowAtIndexPath:` and then the call returns... `tableView:cellForRowAtIndexPath:` is only called when the table is actually redrawn. So I think that my way and your way, will essentially be indistinguishable by the user :) both are correct. – Shumais Ul Haq Jan 14 '16 at 07:53
  • @turushan what should i replace myActivityIndicator with self dont work – system21 Jan 14 '16 at 08:53
  • @system21 you should connect outlet from your storyboard to code, so whatever you name it, you should replace with it. – turushan Jan 14 '16 at 08:56
  • You should create a reference to the activity indicator on top, like where you have `var json_data_url`... initialize the activity indicator in `func addProgressIndicator(show : Bool)`. Now you have a reference to the activity indicator that is accessible everywhere in the file! – Shumais Ul Haq Jan 14 '16 at 12:05
1

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.

Syed Ali Salman
  • 2,894
  • 4
  • 33
  • 48
1

Add this code after do_table_refresh(): addProgressIndicator(!isProgressShowing);

Bista
  • 7,869
  • 3
  • 27
  • 55