Diego Salinas Flores Diego Salinas Flores - 5 months ago 8
Swift Question

Activity Indicator doesn't disappear when load array in background with JSON, with election in a tableview

I'm new in swift and the problem is this, is a database of careers and want to load the classes according to the selected career in the table, this with the link that appears in the code, but in case of a bad connection, the array "Courses" is loading after finishing the function, for which I wanted to add a sentence to keep the function running while the array isn`t load, but I realized that the Activity Indicator should appear while the array is loading, actually appears at the end of the function although I call it at the start of function, then my problem is how to make the activity indicator is present since career pressed on the table, until the array of "Courses" load all courses of career. Help Me please :(! This is the code...

[import UIKit
class CarreraTableViewController: UIViewController {

//Var to InicioApp ViewController
var CarreraSeleccionada = String()

//OK BarButton
@IBOutlet weak var Listo: UIBarButtonItem!

//Courses array
var Cursos = \[String\]()

//The Activity Indicator is in a UIView in the storyboard to more easily customize
@IBOutlet weak var ActivityIndicatorView: UIActivityIndicatorView!
@IBOutlet weak var ActivityContainer: UIView!

//Careers Array
var Carreras = \["Enlace Escolar - Antofagasta", "Enlace Escolar - Coquimbo", "Geología", "Ingeniería Civil Ambiental", "ICCI – Antofagasta", "ICCI – Coquimbo", "Ingeniería Civil", "Ingeniería Civil Industrial – Antofagasta", "Ingeniería Civil Industrial – Coquimbo", "Ingeniería Civil Metalúrgica", "Ingeniería Civil de Minas", "Ingeniería Civil Química", "Ingeniería en Construcción", "Ingeniería en Computación e Informática", "Ingeniería en Metalurgia", "Ingeniería en Procesos Químicos", "IPRYMA - Antofagasta", "IPRYMA - Coquimbo"\]

override func viewDidLoad() {
super.viewDidLoad()

}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Carreras.count
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "MyCell"
let showData = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! CustomCellCarrera
let Carrera = Carreras\[indexPath.row\]
showData.CarreraLabel.text = Carrera

return showData

}
// Identifies which table row the user selected
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {




// ================= At this point I have my question, because ActivityContainer and ActivityIndicatorView does not appear until the end function

Listo.enabled = false
ActivityContainer.hidden = false
ActivityIndicatorView.startAnimating()

let currentCell = tableView.cellForRowAtIndexPath(indexPath)! as! CustomCellCarrera
print(currentCell.CarreraLabel.text)

var arregloArea = \[String\]()
var arregloID = \[Int\]()

Cursos.removeAll()

var i = -1

if let CR = currentCell.CarreraLabel.text {
switch CR {
case "ICCI – Coquimbo":
i = 1
case "Ingeniería Civil Industrial – Coquimbo":
i = 2
case "ICCI – Antofagasta":
i = 10
case "Enlace Escolar - Antofagasta":
i = 15
case "Enlace Escolar - Coquimbo":
i = 14
case "Geología":
i = 16
case "Ingeniería Civil Ambiental":
i = 12
case "Ingeniería Civil":
i = 5
case "Ingeniería Civil Industrial – Antofagasta":
i = 4
case "Ingeniería Civil Metalúrgica":
i = 7
case "Ingeniería Civil de Minas":
i = 11
case "Ingeniería Civil Química":
i = 6
case "Ingeniería en Construcción":
i = 3
case "Ingeniería en Computación e Informática":
i = 13
case "Ingeniería en Metalurgia":
i = 17
case "Ingeniería en Procesos Químicos":
i = 18
case "IPRYMA - Antofagasta":
i = 8
case "IPRYMA - Coquimbo":
i = 9
default:
i = -1
}

}

//Varia i según carrera
let url = NSURL(string: "http://146.83.128.64/tongoy/g.php?&sala=-1&curso=-1&profesor=-1&semestre=-1&semestrec=-1&carrera=\(i)&area=-1")!
print(url)
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in

if let urlContent = data {

do {

let jsonResult = try NSJSONSerialization.JSONObjectWithData(urlContent, options: NSJSONReadingOptions.MutableContainers)

for json in jsonResult as! Array<AnyObject> {

if let area = json\["area"\] as? String {
arregloArea.append(area)
} else {

}
if let id = json\["id"\] as? Int {
arregloID.append(id)
}else{

}
if let curso = json\["curso"\] as? String {
self.Cursos.append(curso)
}else{

}

}

} catch {
print("JSON serialization failed")
}

dump(self.Cursos)

}
}
task.resume()

CarreraSeleccionada = currentCell.CarreraLabel.text!

while Cursos.isEmpty{

}
if !Cursos.isEmpty{
Listo.enabled = true
ActivityIndicatorView.stopAnimating()
ActivityContainer.hidden = true

}
}
}

//To InicioApp VC

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
let DestViewController : InicioApp = segue.destinationViewController as! InicioApp

//Llamado a funcion de eliminar ramos duplicados
var RepCurso = removeDuplicates(Cursos)
RepCurso.sortInPlace(before)

print(RepCurso)

DestViewController.CursosCarrera = RepCurso
DestViewController.TableText = CarreraSeleccionada

}

func before(value1: String, value2: String) -> Bool {
// One string is alphabetically first.
// ... True means value1 precedes value2.
return value1 < value2;
}

func removeDuplicates(array: \[String\]) -> \[String\] {
var encountered = Set<String>()
var result: \[String\] = \[\]
for value in array {
if encountered.contains(value) {
// Do not add a duplicate element.
}
else {
// Add value to the set.
encountered.insert(value)
// ... Append the value.
result.append(value)
}
}
return result
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}][1]

Answer

Your loop while Cursos.isEmpty{ will block the main thread, preventing any updates to the UI.

You need to perform the tasks you need in the completion closure, not at the end of the function. Also, since you are updating the UI, you need to dispatch these on the main queue, as the completion closure will be executing on another thread.

let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in

    if let urlContent = data {

        do {

            let jsonResult = try NSJSONSerialization.JSONObjectWithData(urlContent, options: NSJSONReadingOptions.MutableContainers)

            for json in jsonResult as! Array<AnyObject> {

                if let area = json\["area"\] as? String {
                    arregloArea.append(area)
                } else {

                }
                if let id = json\["id"\] as? Int {
                    arregloID.append(id)
                }else{

                }
                if let curso = json\["curso"\] as? String {
                    self.Cursos.append(curso)
                }else{

                }

            }

        } catch {
            print("JSON serialization failed")
        }
        dispatch_async(dispatch_get_main_queue(), ^{
             Listo.enabled = true
             ActivityIndicatorView.stopAnimating()
             ActivityContainer.hidden = true
        });

        dump(self.Cursos)

    }
}