Populating table with API response Swift - ios

I'm trying to get search results to display on a tableView. I believe I have correctly parsed the JSON, the only problem is that the results won't display on my tableView.
Here is the code:
var searchText : String! {
didSet {
getSearchResults(searchText)
}
}
var itemsArray = [[String:AnyObject]]()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
self.tableView.reloadData()
}
// MARK: - Get data
func getSearchResults(text: String) {
if let excapedText = text.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet()) {
Alamofire.request(.GET, "https://api.duckduckgo.com/?q=\(excapedText)&format=json")
.responseJSON { response in
guard response.result.error == nil else {
// got an error in getting the data, need to handle it
print("error \(response.result.error!)")
return
}
let items = JSON(response.result.value!)
if let relatedTopics = items["RelatedTopics"].arrayObject {
self.itemsArray = relatedTopics as! [[String:AnyObject]]
}
if self.itemsArray.count > 0 {
self.tableView.reloadData()
}
}
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 6 // itemsArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("SearchResultCell", forIndexPath: indexPath) as! SearchResultCell
if itemsArray.count > 0 {
var dict = itemsArray[indexPath.row]
cell.resultLabel?.text = dict["Text"] as? String
} else {
print("Results not loaded yet")
}
return cell
}
If I had a static API request I think this code would work because I could fetch in the viewDidLoad and avoid a lot of the .isEmpty checks.
When I run the program I get 6 Results not loaded yet (from my print in cellForRowAtIndexPath).
When the completion handler is called response in, it goes down to self.items.count > 3 (which passes) then hits self.tableView.reloadData() which does nothing (I checked by putting a breakpoint on it).
What is the problem with my code?
Edit
if self.itemsArray.count > 0 {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
}
Tried this but the tableView still did not reload even though its reloading 6 times before the alamofire hander is called...
Here is the strange thing, obviously before the hander is called my itemsArray.count is going to be 0 so that's why I get Results not loaded yet. I figured out why it repeats 6 times though; I set it in numberOfRowsInSection... So #Rob, I can't check dict["Text"] or cell.resultLabel?.text because they're never getting called. "Text" is correct though, here is the link to the JSON: http://api.duckduckgo.com/?q=DuckDuckGo&format=json&pretty=1
Also, I do have the label linked up to a custom cell class SearchResultCell
Lastly, I am getting visible results.

Two problems.
One issue is prepareForSegue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let searchResultTVC = SearchResultsTVC()
searchResultTVC.searchText = searchField.text
}
That's not using the "destination" view controller that was already instantiated, but rather creating a second SearchResultsTVC, setting its searchText and then letting it fall out of scope and be deallocated, losing the search text in the process.
Instead, you want:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let searchResultTVC = segue.destination as? SearchResultsTVC {
searchResultTVC.searchText = searchField.text
}
}
You shouldn't rely on didSet in the destination view controller to trigger the search, because that property is getting set by source view controller before the table view has even been instantiated. You do not want to initiate the search until view has loaded (viewDidLoad).
I would advise replacing the didSet logic and just perform search in viewDidLoad of that SearchResultsTVC.
My original answer, discussing the code provided in the original question is below.
--
I used the code originally provided in the question and it worked fine. Personally, I might streamline it further:
eliminate the rid of the hard coded "6" in numberOfRowsInSection, because that's going to give you false positive errors in the console;
the percent escaping not quite right (certain characters are going to slip past, unescaped); rather than dwelling on the correct way to do this yourself, it's better to just let Alamofire do that for you, using parameters;
I'd personally eliminate SwiftyJSON as it's not offering any value ... Alamofire already did the JSON parsing for us.
Anyway, my simplified rendition looks like:
class ViewController: UITableViewController {
var searchText : String!
override func viewDidLoad() {
super.viewDidLoad()
getSearchResults("DuckDuckGo")
}
var itemsArray: [[String:AnyObject]]?
func getSearchResults(text: String) {
let parameters = ["q": text, "format" : "json"]
Alamofire.request("https://api.duckduckgo.com/", parameters: parameters)
.responseJSON { response in
guard response.result.error == nil else {
print("error \(response.result.error!)")
return
}
self.itemsArray = response.result.value?["RelatedTopics"] as? [[String:AnyObject]]
self.tableView.reloadData()
}
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return itemsArray?.count ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SearchResultCell", for: indexPath) as! SearchResultCell
let dict = itemsArray?[indexPath.row]
cell.resultLabel?.text = dict?["Text"] as? String
return cell
}
}
When I did that, I got the following:
The problem must rest elsewhere. Perhaps it's in the storyboard. Perhaps it's in the code in which searchText is updated that you didn't share with us (which triggers the query via didSet). It's hard to say. But it doesn't appear to be a problem in the code snippet you provided.
But when doing your debugging, make sure you don't conflate the first time the table view delegate methods are called and the second time they are, as triggered by the responseJSON block. By eliminating the hardcoded "6" in numberOfRowsInSection, that will reduce some of those false positives.

I think you should edit :
func getSearchResults(text: String) {
if let excapedText = text.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet()) {
Alamofire.request(.GET, "https://api.duckduckgo.com/?q=\(excapedText)&format=json")
.responseJSON { response in
guard response.result.error == nil else {
// got an error in getting the data, need to handle it
print("error \(response.result.error!)")
return
}
let items = JSON(response.result.value!)
if let relatedTopics = items["RelatedTopics"].arrayObject {
self.itemsArray = relatedTopics as! [[String:AnyObject]]
// if have result data -> reload , & no if no
if self.itemsArray.count > 0 {
self.tableView.reloadData()
}
}else{
print("Results not loaded yet")
}
}
}
}
And
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("SearchResultCell", forIndexPath: indexPath) as! SearchResultCell
// i 'm sure: itemsArray.count > 0 in here if in numberOfRowsInSection return itemsArray.count
var dict = itemsArray[indexPath.row]
cell.resultLabel?.text = dict["Text"] as? String
return cell
}
And you should share json result(format) ,print dict in cellForRowAtIndexPath, so it s easy for help

Related

Why JSON data from decoder to become UITableView datasource does not assigns?

Recently got stuck on a problem of assigning freshly downloaded JSON data to table view datasource variable. I suppose the problem is something obvious but my skill is not enough to gather the big picture. Let me share a bunch of code.
(1) A function retrieves the data from Open Weather Map API (defined in the separate class 'GetWeather').
func getMowForecast(completion: #escaping ((WeatherForecast?, Bool)) -> Void) {
let url = URL(string: "http://api.openweathermap.org/data/2.5/forecast?id=524901&APPID=b3d57a41f87619daf456bfefa990fce4&units=metric")!
let request = URLRequest(url: url)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
do {
let json = try JSONDecoder().decode(WeatherForecast.self, from: data)
completion((json, true))
} catch {
print(error)
completion((nil, false))
}
} else {
print(error)
}
}
task.resume()
}
Everything works fine here. JSON loads correctly and fits the data model.
Here's a link to JSON data to be displayed in tableView: https://pastebin.com/KkXwxYgS
(2) A controller handles the display of retrieved JSON data in tableView format
import UIKit
class ForecastViewController: UITableViewController {
#IBOutlet weak var tableV: UITableView! // tableView outlet in the IB
let weatherGetter = GetWeather() // object to handle the JSON retrieval
var tableData: WeatherForecast? // tableView data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.tableData?.list.count ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableVCCell
cell.dateLabel.text = "\(self.tableData?.list[indexPath.row].dt)"
cell.tempLabel.text = "\(self.tableData?.list[indexPath.row].main.temp)"
cell.feelsLikeLabel.text = "\(self.tableData?.list[indexPath.row].main.feels_like)"
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
tableV.delegate = self
tableV.dataSource = self
weatherGetter.getMowForecast { (data, status) in
if let data = data, status {
} else if status {
print("-------- Ошибка разбора данных прогноза погоды --------")
} else {
print("-------- Ошибка получения данных прогноза погоды --------")
}
self.tableData = data
print(self.tableData)
}
print(self.tableData?.list.count) // returns nil
self.tableData = weatherGetter.getMowForecast(completion: ((tableData, true))) // error - Cannot convert value of type '(WeatherForecast?, Bool)' to expected argument type '((WeatherForecast?, Bool)) -> Void'
}
}
The problem is - the table view gets nil datasource so it is unable to load the data and shows the blank screen.
I suppose the mistake is in scope - I try to retrieve the JSON data inside a function and it does not go anywhere else. What I am wondering about is - how comes that assigning the data to self.tableData does not makes any effect?
Could you please help.
Thank you!
Regards
First of all delete
print(self.tableData?.list.count) // returns nil
self.tableData = weatherGetter.getMowForecast(completion: ((tableData, true))) // error - Cannot convert value of type '(WeatherForecast?, Bool)' to expected argument type '((WeatherForecast?, Bool)) -> Void'
The error occurs because the method does not return anything and the completion handler syntax is wrong. Both lines are pointless anyway due to the asynchronous behavior of getMowForecast
Secondly I recommend to declare the data source array as a non-optional array of the type which represents List. Then you get rid of all those unnecessary optionals.
var tableData = [List]()
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.tableData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableVCCell
let weatherData = self.tableData[indexPath.row]
cell.dateLabel.text = "\(weatherData.dt)"
cell.tempLabel.text = "\(weatherData.main.temp)"
cell.feelsLikeLabel.text = "\(weatherData.main.feels_like)"
return cell
}
To be able to display the data – as already mentioned by others – you have to reload the table view in the completion handler. And assign the data only if status is true.
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
tableV.delegate = self
tableV.dataSource = self
weatherGetter.getMowForecast { [weak self] (data, status) in
if let data = data, status {
self?.tableData = data.list
DispatchQueue.main.async {
self?.tableV.reloadData()
}
} else if status {
print("-------- Ошибка разбора данных прогноза погоды --------")
} else {
print("-------- Ошибка получения данных прогноза погоды --------")
}
}
}
And consider that the message Ошибка разбора данных прогноза погоды will be never displayed.
You need to reload the table inside the callback as it's asynchronous
self.tableData = data
print(self.tableData)
DispatchQueue.main.async { self.tableV.reloadData() }

TableView data from search not updated properly

I made a TableView with Swift where I show Search results of the Query that's filled in the UiTextField.
let cellReuseIdentifier = "cell"
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:SearchCell = self.tableView.dequeueReusableCell(withIdentifier:cellReuseIdentifier) as! SearchCell
cell.objectName.text = self.search_results_list[indexPath.section]
return cell
}
On the Value Changed-event I trigger the Search action:
#IBAction func searchInputChanged(_ sender: Any) {
self.search_results_list = []
let search = self.searchField.text
let parameters: Parameters = ["api_token": token, "search": search!+" "]
let URL = "https://myweb.app/api/search"
Alamofire.request(URL, parameters: parameters).responseArray { (response: DataResponse<[SearchResponse]>) in
let searchResultsArray = response.result.value
if let searchResultsArray = searchResultsArray {
for searchResult in searchResultsArray {
self.search_results_list.append(searchResult.description!)
}
}
// Then I reloeadData of the TableView
self.tableView.reloadData()
}
}
And the NumberOfSections method:
func numberOfSections(in tableView: UITableView) -> Int {
return self.search_results_list.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
It does work, but when I fill in the form quickly, the Array has the correct order, but the TableView has a different order. When I inspect the Array I see that the order IS correct, but the TableView shows a different one. When I wait for a second and add a space at the end, the TableView does show the correct order.
The same when I add letter by letter and wait after each letter that the tableview is reloaded, the output is correct
What am I missing?
Like they said in the comments. It's probably because the asynchronous calls are returning at different times. And most probably the one with fewer results (newer calls) will come back faster than the ones that have more results (older calls). So what you have to do is invalidate every API response which doesn't match your current search text.
#IBAction func searchInputChanged(_ sender: Any) {
self.search_results_list = []
let search = self.searchField.text
let parameters: Parameters = ["api_token": token, "search": search!+" "]
let URL = "https://myweb.app/api/search"
Alamofire.request(URL, parameters: parameters).responseArray { (response: DataResponse<[SearchResponse]>) in
if search == self.searchTextField.text { // Compare search text with current text in searchTextField.
let searchResultsArray = response.result.value
if let searchResultsArray = searchResultsArray {
for searchResult in searchResultsArray {
self.search_results_list.append(searchResult.description!)
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
IMPORTANT OPTIMIZATION TIP
You can wrap your API call in a timer so that it is hit only when the user stops entering characters into the text field. This helps avoid unnecessary API calls. You can achieve this by using a Timer to make the network calls. Start the timer in textDidChange and invalidate it if the delegate is called again within a short duration (0.5s or whatever you feel is optimal)

UItableView not populating while offline? swift 3

The problem is really, that while offline the UItableview is not populating. Basically while online it will read from a php coded website in json and parse its data to NSUserdefaults and It will display data using the defaults set. This works very well when online.
I tested it like this. first I run the code while online( wifi connected ) to first populate the defaults, then exit the tableview, turn wifi off, and then go back in. Nothing shows. I put a breakpoint/print text where the code should had run, but it breakpoint never got excuted, the print text never got printed.
is there a reason why the code isnt running when offline? am i missing a setting i should add?
var messagesArray:[String] = [String]()
var dateArray:[String] = [String]()
class Singleton {
static let sharedInstance: UserDefaults = {
let instance = UserDefaults.standard
// setup code
return instance
}()
}
//let defaults = UserDefaults.standard
let defaults = Singleton.sharedInstance
override func viewDidLoad() {
super.viewDidLoad()
//removeDefaults()
if (isInternetAvailable() == true)
{
self.retrieveMessages("")
//storeLocal()
}
else {
// TODO data is available but not displayed ??
for (key, value) in defaults.dictionaryRepresentation() {
print("\(key) = \(value) \n")
}
}
//display current notification
//nRead()
self.notificationTable.dataSource = self
self.notificationTable.delegate = self
// Do any additional setup after loading the view.
}
func tableView(_ tableView:UITableView, numberOfRowsInSection section: Int) -> Int{
return messagesArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// this code does not run when offline
//test
let nCell = tableView.dequeueReusableCell(withIdentifier: "nCell") as UITableViewCell!
//let myLabelTitle = nCell?.viewWithTag(1) as! UILabel
let myLabelDate = nCell?.viewWithTag(2) as! UILabel
let myLabelDescription = nCell?.viewWithTag(3) as! UILabel
//messagesArray ["nContent":["Test1", "Test2"]]
myLabelDescription.text = defaults.string(forKey: "nDescription\(indexPath.row + 1)")
myLabelDate.text = defaults.string(forKey: "nDate\(indexPath.row + 1)")
//print(defaults.string(forKey:"nDate1"))
print("this code runs even while offline")
let readValue = defaults.string(forKey: "nRead\(indexPath.row + 1)")
if (readValue == "1" )
{
myLabelDate.textColor = UIColor.black
}
else
{
myLabelDate.textColor = UIColor.red
}
return nCell!
}
func tableView(_ tableView:UITableView, numberOfRowsInSection section: Int) -> Int{
return messagesArray.count
}
messagesArray.count prints 0, thus the code isn't running. fixed my own issue

Proper way to load data to TableView

I have an application that loads list of questions from JSON data and shows them on TableView.
Everything is working fine most of the time but it seems to be that I am doing something wrong and that is why - app crashes.
It happens rarely so it is hard to detect but I am sure that there must a problem with the logic.
So I have model class for question and array for question items :
class questionItem {
var id = 0
var title : String = ""
var question : String = ""
}
var questions = [questionItem]()
Inside my ViewController I have IBOutlet for TableView and I placed data loading inside viewDidLoad
class QuestionsListVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var questionsTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
get_questions()
}
func get_questions()
{
let request = NSMutableURLRequest(URL:myURL!)
request.HTTPMethod = "POST"
let postString = ""
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
{
data, response, error in
if error != nil {
print("error=\(error)")
return
}
//clearing array for new items
questions.removeAll(keepCapacity: false)
dispatch_async(dispatch_get_main_queue(),{
var json = JSON(data: data!)
if let items = json["questions"].array {
for item in items {
let question = questionItem()
question.id = item["id"].int!
question.title = item["title"].string!;
question.question = item["question"].string!
questions.append(question)
}
}
self.questionsTableView.reloadData()
});
}
task.resume()
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return questions.count
}
Error is shown inside cellForRowAtIndexPath
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : QuestionsListCell = self.questionsTableView.dequeueReusableCellWithIdentifier("QuestionsListCell") as! QuestionsListCell
//error happens here - Index out of range
print(questions[indexPath.row].title)
It happens one time in six cases and there is no errors in other 5 of 6 tests - but I don't understand why.
This points to a problem with the
numberOfSectionsInTableView
and/or
numberOfRowsInSection
Can you post your current implementation of these?
If you only displaying one continuous list, the numberOfSectionsInTableView should always return 1, and you need to check numberOfRowsInSection is accurately returning the number of items in the datasource.
Edit:
Can you try clearing the existing datasource on the main thread immediately before updating with the new items as in the code below:
dispatch_async(dispatch_get_main_queue(),{
questions.removeAll(keepCapacity: false)
var json = JSON(data: data!)
if let items = json["questions"].array {
for item in items {
let question = questionItem()
question.id = item["id"].int!
question.title = item["title"].string!;
question.question = item["question"].string!
questions.append(question)
}
}
self.questionsTableView.reloadData()
});
The call to questions.removeAll in your code makes the following sequence of events possible:
numberOfRowsInSection is called before questions.removeAll, returning the old non-zero capacity
questions.removeAll clears questions
cellForRowAtIndexPath is called before questions are re-populated, causing index out of range exception
One way to fix is is relatively straightforward: make a newQuestions array, populate it in get_questions, and swap it in when numberOfRowsInSection is called:
// Add this to your class
var newQuestions : [questionItem]
// Change get_questions:
dispatch_async(dispatch_get_main_queue(), {
var json = JSON(data: data!)
if let items = json["questions"].array {
var tmpQuestions = [questionItem]()
for item in items {
let question = questionItem()
question.id = item["id"].int!
question.title = item["title"].string!;
question.question = item["question"].string!
tmpQuestions.append(question)
}
newQuestions = tmpQuestions
self.questionsTableView.reloadData()
}
});
// Change numberOfRowsInSection
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
if newQuestions != nil {
questions = newQuestions
newQuestions = nil
}
return questions.count
}
Note how get_questions does not populate newQuestions directly. Instead, it builds tmpQuestions, and sets it to newQuestions only when it is fully built.
Try with below code,
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell : QuestionsListCell = self.questionsTableView.dequeueReusableCellWithIdentifier("QuestionsListCell") as! QuestionsListCell
if let objModel : questionItem = questions[indexPath.row] as? questionItem
{
print(objModel.title)
}
}
Change your cellForRow with this method. Hope this helps you.
Just to avoid crash, I would have added following three safe checks.
1. Check Array count before clearing out.
if questions.count > 0
{
questions.removeAll()
}
2. Check array count before table Reload
if questions.count > 0
{
self.questionsTableView.reloadData()
}
3. In cellForRowAtIndex method Check for value in array of object, before putting on Cell
if let objModel : questionItem = questions[indexPath.row] as? questionItem
{
print(" title is \(objModel.title)")
}

How to know UITableView has finished loading data in swift

I am loading my UITableView using an Arrayin swift. What I want to do is after table has loaded my array should be ampty (want to remove all object in the array then it loads another data set to load another table view)
What I want to do is adding several UItables dinamically to a UIScrollView and load all the data to every UITableView initially. Then user can scroll the scrollview horizontally and view other tables.So in my ViewDidLoadI am doing something like this.
for i in 0..<dm.TableData.count {
self.catID=self.dm.TableData[i]["term_id"] as? String
self.jsonParser()
}
then this is my jsonParser
func jsonParser() {
let urlPath = "http://www.liveat8.lk/mobileapp/news.php?"
let category_id=catID
let catParam="category_id"
let strCatID="\(catParam)=\(category_id)"
let strStartRec:String=String(startRec)
let startRecPAram="start_record_index"
let strStartRecFull="\(startRecPAram)=\(strStartRec)"
let strNumOfRecFull="no_of_records=10"
let fullURL = "\(urlPath)\(strCatID)&\(strStartRecFull)&\(strNumOfRecFull)"
print(fullURL)
guard let endpoint = NSURL(string: fullURL) else {
print("Error creating endpoint")
return
}
let request = NSMutableURLRequest(URL:endpoint)
NSURLSession.sharedSession().dataTaskWithRequest(request) { (data, response, error) in
do {
guard let data = data else {
throw JSONError.NoData
}
guard let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary else {
throw JSONError.ConversionFailed
}
print(json)
if let countries_list = json["data"] as? NSArray
{
for (var i = 0; i < countries_list.count ; i++ )
{
if let country_obj = countries_list[i] as? NSDictionary
{
//self.TableData.append(country_obj)
self.commonData.append(country_obj)
}
}
//self.updateUI()
if self.commonData.isEmpty
{
}
else
{
self.updateUI()
}
}
} catch let error as JSONError {
print(error.rawValue)
} catch let error as NSError {
print(error.debugDescription)
}
}.resume()
}
Then UpdateUI()
func updateUI()
{
print("COMMON DATA ARRAY\(self.commonData)")
// print("NEWS DATA ARRAY\(self.newsNews)")
//print("SPORTS DATA ARRAY\(self.sportsNews)")
let tblY:CGFloat=segmentedControl.frame.origin.y+segmentedControl.frame.size.height
tblNews=UITableView.init(frame: CGRectMake(x,0 , self.screenWidth, self.screenHeight-tblY))
tblNews.tag=index
tblNews.delegate=self
tblNews.dataSource=self
tblNews.backgroundColor=UIColor.blueColor()
self.mainScroll.addSubview(tblNews)
x=x+self.screenWidth
index=index+1
tblNews.reloadData()
}
`UITableView` use this `commonData` array as the data source. Now when I scroll table view data load with previous data too.So what is the best way to do this? or else please tell me how can use `self.commonData.removeAll()` after 1 `UITableView` has loaded.Currently I did in `CellforrowAtIndex`
if indexPath.row == self.commonData.count-1
{
self.commonData.removeAll()
}
return cell
but this doesn't solve my problem
You should have separate sets of data, possibly arrays, for each UITableView. iOS will call back to your datasource delegate methods to request data.
It is important that you not delete data from the arrays because iOS is going to call your data source delegate methods expecting data. Even if you display the data in the table views initially, the user may scroll the scroll view causing one of the UITableView's to call your delegate methods to get the data again.
The data source delegate methods, such as func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell have a UITableView parameter that you can use to determine which data source is appropriate.
For example, you might have:
self.commonData1 = ["a", "b", "c"]
self.commonData2 = ["d", "e", "f"]
And you need to keep track of any tables you add to your scroll view:
self.tableView1 = ...the table view you create & add to scroll view
self.tableView2 = ...the table view you create & add to scroll view
And when you're responding to data source calls:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if tableView == self.tableView1 {
return 1
} else if tableView == self.tableView2 {
return 1
}
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == self.tableView1 {
return self.commonData1.count
} else if tableView == self.tableView2 {
return self.commonData2.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! IdeaTableViewCell
if tableView == self.tableView1 {
cell.textLabel?.text = self.commonData1[indexPath.row]
} else if tableView == self.tableView2 {
cell.textLabel?.text = self.commonData2[indexPath.row]
}
return cell
}

Resources