swift: Completion handler - ios

So, I have method loadData() which download datas from parse.com
And I should present all images show in table view.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ReusableCell", forIndexPath: indexPath) as! LeaguesTableViewCell
loadData { (success) in
if success {
cell.leagueImage.image = UIImage(data: self.leaguesImage[indexPath.row])
cell.leagueNameLabel.text = self.leagues[indexPath.row]["name"] as? String
} else {
cell.leagueNameLabel.text = "Wait"
}
}
return cell
}
Its didn't work. I call my function in viewDidLoad() but its not correct too, table view is empty.
Cuz my array is empty
My

The basic procedure for loading data into a UITableView is:
Load the data
Reload the table view
Return the number of sections in numberOfSectionsInTableView: method: In your case there is only 1 section.
Return the number of rows in tableView:numberOfRowsInSection:: In your case return the number of leagues if the data is loaded. If the data is not loaded then return 1 so that the table view has at least one row to display the "Wait" message.
Create and populate the cells from the data: Use leagues and leaguesImage.
Example:
private var loaded = false
override func viewDidLoad() {
super.viewDidLoad()
loaded = false
loadData() { success in
NSOperationQueue.mainQueue().addOperationWithBlock() {
self.loaded = success
self.tableView.reloadData()
}
}
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection: Int) -> Int {
if loaded {
return leagues.count
}
else {
return 1
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ReusableCell", forIndexPath: indexPath) as! LeaguesTableViewCell
if loaded {
cell.leagueImage.image = UIImage(data: self.leaguesImage[indexPath.row])
cell.leagueNameLabel.text = self.leagues[indexPath.row]["name"] as? String
}
else {
cell.leagueNameLabel.text = "Wait"
}
return cell
}

Try to set delegate and datasource first. If you have separate datasource other than view controller, retain it otherwise you will not get any callback.

Related

How to wait for an array to be filled in Swift

I am writing an app in Swift and having problems filling a tableview the right way.
I am getting my data from Firestore and have a class to help me get that data. The basic process is that I have a getProducts function which sets a local array variable with products. The next step is to create an array of objects in my tableview class but my there seems to be a fault where my tableview gets build before my function has the time to load in the array.
So my loadProducts fills the array products but my count seems to be 0.
Hope you guys can help, thank you in advance
My code:
class ProductTableViewController: UITableViewController {
var products = [Product]()
override func viewDidLoad() {
super.viewDidLoad()
loadProducts()
// 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
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return products.count
}
private func loadProducts(){
let dbhelper = DBHelper()
dbhelper.getProducts(){ success in
self.products = dbhelper.products
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "ProductTableViewCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? ProductTableViewCell else {
fatalError("The dequeued cell is not an instance of ProductTableViewCell.")
}
let product = products[indexPath.row]
cell.titleLabel.text = product.Titel
cell.priceLabel.text = product.Prijs
return cell
}
}
Inside you load products func add a table view reload statment in order to reload the table with your new data :
private func loadProducts(){
let dbhelper = DBHelper()
dbhelper.getProducts(){ success in
self.products = dbhelper.products
Self.tableView.reloadData()
}
}

table view not displaying contents in the cells

I am trying to fetch some data from stack exchange api using alamofire and swifty json. I am able to print the required data in the log, but when i run the app the simulator shows only empty cells. I checked the identifier and i have set the prototype cell value to 1.`
class MainTableViewController: UITableViewController,UISearchResultsUpdating {
var searchKeyword: String = "questions"
// empty array to store the search results
var searchResults: [SearchResult] = []
func alamofireFunction() {
Alamofire.request(.GET, "https://api.stackexchange.com/2.2/questions?=%20\(searchKeyword)%20viewpage=1&fromdate=1183075200&todate=2554416000&order=asc&sort=activity&tagged=ios&site=stackoverflow").responseJSON { (response) -> Void in
switch response.result {
case .Success:
if let value = response.result.value {
let json = JSON(value)
for (var idx=0; idx<=json["items"].count; idx++) {
let result = SearchResult()
//print(json["items"][idx]["title"].stringValue)
result.name = json["items"][idx]["owner"]["display_name"].stringValue
result.question = json["items"][idx]["title"].stringValue
result.image = json["items"][idx]["owner"]["profile_image"].stringValue
self.searchResults.append(result)
}
}
case .Failure:
print("error")
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
alamofireFunction()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! MainTableViewCell
cell.questionLabel.text = searchResults[indexPath.row].question
cell.nameLabel.text = searchResults[indexPath.row].name
return cell
}
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 searchResults.count
}`
You must call self.reloadData() when your data source changes. This will cause tableView to call dataSource methods.
This is because you are using a request from Url, this process executes on a thread, so when you fetch your data in this way, you must call tableView.reloadData() to let know to the data source that the process have finished

Slow/Stuttered scroll in tableview

I'm loading about 150 elements from an array of arrays of dictionaries (tasks) and I can get all of the data into my tableview but when I scroll its stupid slow. When I print out the information of one of my functions to the console, it looks like I am getting all of the data back every time I scroll. Is this something I am not loading well (i.e. asynchronously) or do I need to change my functions?
func querySections() -> [String] {
var sectionsArray = [String]()
for task in tasks {
let dueTimes = task.dueTime
sectionsArray.append(dueTimes)
}
let uniqueSectionsArray = Array(Set(sectionsArray.sort()))
// print(uniqueSectionsArray)
return uniqueSectionsArray
}
func queryDueTimes(section:Int) -> [Task] {
var sectionItems = [Task]()
for task in tasks {
let dueTimes = task.dueTime
if dueTimes == querySections()[section] {
sectionItems.append(task)
}
}
print(sectionItems)
return sectionItems
}
// MARK: - Table view data source
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return querySections()[section]
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return querySections().count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return queryDueTimes(section).count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("TaskCell", forIndexPath: indexPath) as! TaskCell
// Configure the cell...
cell.selectionStyle = .None
let times = queryDueTimes(indexPath.section)
let task = times[indexPath.row]
cell.label.text = task.title
if task.done == true {
cell.checkBox.image = UIImage(named: "checkedbox")
cell.detailLabel.text = "Completed By: \(task.completedBy)"
}
else {
cell.checkBox.image = UIImage(named: "uncheckedbox")
cell.detailLabel.text = ""
}
cell.delegate = self
return cell
}
Basically, in querySections, I'm iterating through all of the dueTimes for each task and then changing them into an array of a set so I can filter out all of the duplicates. This is giving me all of my sections. For queryDueTimes, I'm iterating through the tasks and matching them to a section.
I had a thought about calling the functions in viewDidLoad but that isn't working (it keeps giving me an empty array when I try to pass it to another empty array thats more accessible outside of the function) and I can't access section (for queryDueTimes) in viewDidLoad (as far as what I know how to do).
Update 1:
I think the mistake is on my end. I said that I tasks is an array of arrays when its just an array of Tasks (a struct with all of the properties of each task). When I load the app, I append all of the tasks from my backend to a local array ("tasks"). Should I have an array of arrays for this to work or can I amend my code somehow and get it to work?
Update 2:
I'm getting sectionTimes and tasksInSectionArray as empty arrays when I print them.
var sectionTimes = [String]()
var tasksInSectionArray = [[Task]]()
var tasks = [Task]() {
didSet {
tableView?.reloadData()
}
}
func updateTableView() {
sectionTimes = Set(tasks.map{$0.dueTime}).sort()
tasksInSectionArray = sectionTimes.map{section in tasks.filter{$0.dueTime == section}}
print(sectionTimes)
print(tasksInSectionArray)
tableView.reloadData()
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sectionTimes[section]
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sectionTimes.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tasksInSectionArray[section].count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("TaskCell", forIndexPath: indexPath) as! TaskCell
// Configure the cell...
cell.selectionStyle = .None
let task = tasksInSectionArray[indexPath.section][indexPath.row]
Like you guessed, the data is being loaded and sorted over and over again, instead of only once. Save the results of querySelections and queryDueTimes and use that inside the table view data source methods.
You can do this in viewDidLoad - call both functions once and assign the results to a variable at the class level, and then call tableView.reloadData() (assuming you have a reference to the table view).
var sections: [String] = []
var data: [[Tasks]] = []
func updateTableView() {
sections = Set(tasks.map { $0.dueTime }).sort()
data = sections.map { section in tasks.filter { $0.dueTime == section } }
tableView.reloadData()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sections.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data[section].count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let task = data[indexPath.section][indexPath.row]
// Cell configuration
}
This is basically what DMan said, but I've made an example for you.

TableView Cell data is being disappeared on click on the cell. Why this is happening?

I have created a tableview. Then calling an url to get the data and accordingly loading the tableview. But whenever i am clicking a cell the data is disappearing.
I have tried with making userinteractionenabled as false or the selection style as .None. Still it is not working. Any reason for this?
class CheckInTableViewController: UITableViewController {
var flightInfo : [Flight]!
var multipleChoiceQuestion : [MultipleChoiceQuestion]!
var question : [String] = [String]()
var answer : [[String]] = [[String]]()
var correctAnswer: [Int] = [Int]()
override func viewDidLoad() {
super.viewDidLoad()
loadData()
}
func loadData(){
Request.CheckInFlight().execute().validate().responseJSON { (request, _, data, error)
-> Void in
if error != nil {
print("error")
}
else{
self.flightInfo = Response.flightsFromJSON(data)
self.multipleChoiceQuestion = self.flightInfo[0].checkInUpdate.checkInTest.multipleChoiceQuestion
for questionNew in self.multipleChoiceQuestion {
self.question.append(questionNew.question)
self.answer.append(questionNew.answerArray)
self.correctAnswer.append(questionNew.correctAnswerId)
}
self.tableView.reloadData()
}
}
}
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 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
if flightInfo != nil {
return flightInfo.count + 1
}
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
switch indexPath.row {
case 0: _ = tableView.dequeueReusableCellWithIdentifier("headercell", forIndexPath: indexPath) as! CheckInHeaderTableViewCell
default:
let cell = tableView.dequeueReusableCellWithIdentifier("datacell", forIndexPath: indexPath) as! CheckInTableViewCell
cell.flightNumber.text = flightInfo[indexPath.row - 1].fullFlightId
cell.departureCity.text = flightInfo[indexPath.row - 1].departureInfo!.airport.iataCode
cell.departureDate.text = dateFormatter(flightInfo[indexPath.row - 1].departureInfo!.scheduledDateTime)
cell.departureTime.text = flightInfo[indexPath.row - 1].departureInfo!.scheduledDateTime.flightTimeString()
cell.arrivalCity.text = flightInfo[indexPath.row - 1].arrivalInfo!.airport.iataCode
cell.arrivalDate.text = dateFormatter(flightInfo[indexPath.row - 1].arrivalInfo!.scheduledDateTime)
cell.arrivalTime.text = flightInfo[indexPath.row - 1].arrivalInfo!.scheduledDateTime.flightTimeString()
}
return cell
}
Just to round this up, I'm turning my comment into an answer.
You are returning the cell from the first line. That is a fresh cell.
The dequeue'd cell you are modifying and throwing away w/o return in the switch.
The dequeue in case 0 you are throwing away instantly.
This should give you a warning regarding scope ambiguity on the second let cell =, btw.
If the problem persists, please update your question and let me know:)

Creating TableViews in Swift with an Array

I'm attempting to use the result of one Rest call as an input for my TableView.
I've got an array named GamesList[String] that is synthesized in the viewDidLoad() function. This is the viewDidLoad() fuction:
override func viewDidLoad() {
super.viewDidLoad()
getState() { (json, error) -> Void in
if let er = error {
println("\(er)")
} else {
var json = JSON(json!);
print(json);
let count: Int = json["games"].array!.count
println("found \(count) challenges")
for index in 0...count-1{
println(index);
self.GamesList.append(json["games"][index]["game_guid"].string!);
}
}
}
}
The problem is that the functions for filling the TableView get executed before my GamesList array is filled up. These are the functions that fill the TableView:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return GamesList.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Game", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel?.text = GamesList[indexPath.row]
cell.detailTextLabel?.text = GamesList[indexPath.row]
return cell
}
How do I force the tables to get filled up (refreshed) after my array has been filled?
use self.tableView.reloadData() after you append your values
getState() { (json, error) -> Void in
if let er = error {
println("\(er)")
} else {
var json = JSON(json!);
print(json);
let count: Int = json["games"].array!.count
println("found \(count) challenges")
for index in 0...count-1{
println(index);
self.GamesList.append(json["games"][index]["game_guid"].string!);
}
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
}
}

Resources