I've been working on a UITableView that makes 2 API calls for every cell for a while now. It's been working fine. However today I encountered a massive issue. For the first time, there were more cells on the screen than there was loaded cells.
So when I scrolled down my tableView the screen froze for several seconds because it hadn't loaded the last cell.
A problem I've been having trying to load this data properly. Is that my second API call for each cell depends on the first one.
Here's how it's currently set up:
my tableView:
// #tableView
tableView.setTranslatesAutoresizingMaskIntoConstraints(false)
tableView.backgroundColor = UIColor.formulaWhiteColor()
tableView.frame = CGRectMake(0, 0, 0, 0)
tableView.delegate = self
tableView.dataSource = self
self.tableView.tableFooterView = UIView(frame: CGRectZero)
self.tableView.separatorColor = UIColor.clearColor()
tableView.registerClass(mySuggestionsCell.self, forCellReuseIdentifier: "com.Formula.mySuggestionsCell")
tableView.addSubview(refreshControl)
self.view.addSubview(tableView)
When my view Appears I run this function:
func loadSuggestions() {
DNService.suggestionsForSection() { (JSON) -> () in
self.suggestions = JSON["actionable"]
self.tableView.reloadData()
self.refreshControl.endRefreshing()
}
}
(My DNSService is a struct using Alomafire)
struct DNService {
static func suggestionsForSection(response: (JSON) -> ()) {
let urlString = suggestionsURL
Alamofire.request(.GET, urlString).responseJSON { (_, _, data, _) -> Void in
let suggestions = JSON(data ?? [])
response(suggestions)
}
}
}
From there I start setting up the cells:
Number of rows:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return suggestions.count
}
And the actual configuration:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("com.Formula.mySuggestionsCell", forIndexPath: indexPath) as! mySuggestionsCell
let suggestion = suggestions[indexPath.row]
cell.configureWithSuggestion(suggestion)
cell.selectionStyle = UITableViewCellSelectionStyle.None
return cell
}
The rest is currently taking place in my UITableViewCell which I'm pretty sure is where I'm going completely wrong in how I'm doing this.
func configureWithSuggestion(suggestion: JSON) {
tickerString = suggestion["ticker"].string!
tickerLabel.text = tickerString
// I'm setting up other things like above, but cut them out as they aren't relevant.
var quoteAPI = NSURL(string: "http://dev.markitondemand.com/Api/v2/Quote/json?symbol=\(tickerString)")
// This is the API that depends on data from the first API.
// This is being called for every cell that's being configured...
var request = NSURLRequest(URL: quoteAPI!)
var data = NSURLConnection.sendSynchronousRequest(request, returningResponse: nil, error: nil)
if data != nil {
var quote = JSON(data: data!)
lastPrice = quote["LastPrice"]
// Again calling more data than just this.
}
lastPriceLabel.text = lastPrice
// I'm also setting up and animating some charts here.
}
How do I best go about retrieving and setting up this API data, when one of the calls depends on the first one?
I ended up making a coreData entity for the data.
Then when the view loads, I call the first api, loop through it, save it to coreData and in the loop I can use the variable to call the second api as well.
Then when I'm loading my cells from coreData instead of making api calls when scrolling down, there's no problem with loading time.
Related
When I run the application I can see a blank table like the below screenshot loaded for certain milliseconds and then loading the table with actual data.As the items array is having 0 elements at the beginning, numberOfRowsInSection returns 0 and the blank table view is loading. Is it like that?Please help me on this
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count
}
I changed above code to the one below, but same issue exists and in debug mode I found out that the print("Item array is empty") is executing twice, then the blank table view is displaying for a fraction of seconds, after that the actual API call is happening and data is correctly displayed in the tableview
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if items.isEmpty{
print("Item array is empty")
return 0
} else {
return self.items.count
}
}
import UIKit
class MainVC: UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet weak var bookslideShow: UIView!
#IBOutlet weak var bookTableView: UITableView!
var items : [Items] = []
override func viewDidLoad() {
super.viewDidLoad()
bookTableView.dataSource = self
bookTableView.delegate = self
self.view.backgroundColor = UIColor.lightGray
bookTableView.rowHeight = 150
// self.view.backgroundColor = UIColor(patternImage: UIImage(named: "background.jpeg")!)
self.fetchBooks { data in
self.items.self = data
DispatchQueue.main.async {
self.bookTableView.reloadData()
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if items.isEmpty{
print("Item array is empty")
return 0
} else {
return self.items.count
//bookTableView.reloadData()
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "BookCell",for:indexPath) as! BookCell
//cell.backgroundColor = UIColor(red: 180, green: 254, blue: 232, alpha: 1.00)
let info = items[indexPath.row].volumeInfo
cell.bookTitle.text = info.title
cell.bookCategory.text = info.categories?.joined(separator: ",")
cell.bookAuthor.text = info.authors?.joined(separator: ", ")
let imageString = (info.imageLinks?.thumbnail)!
if let data = try? Data(contentsOf: imageString) {
if let image = UIImage(data: data) {
DispatchQueue.main.async {
cell.bookImage.image = image
}
}
}
return cell
}
func fetchBooks(comp : #escaping ([Items])->()){
let urlString = "https://www.googleapis.com/books/v1/volumes?q=quilting"
let url = URL(string: urlString)
guard url != nil else {
return
}
let session = URLSession.shared
let dataTask = session.dataTask(with: url!) { [self] (data, response, error) in
//check for errors
if error == nil && data != nil{
//parse json
do {
let result = try JSONDecoder().decode(Book.self, from: data!)
comp(result.items)
}
catch {
print("Error in json parcing\(error)")
}
}
}
//make api call
dataTask.resume()
}
}
The delegates methods may be called multiple times. If you want to remove those empty cells initially. You can add this in viewDidLoad:
bookTableView.tableFooterView = UIView()
You are crossing the network for the data. That can take a long time especially if the connection is slow. An empty tableview isn't necessarily bad if you are waiting on the network as long as the user understands what's going on. Couple of solutions,
Fetch the data early in application launch and store locally. The problem with this approach is that the user may not ever need the downloaded resources. For instance if instantgram did that it would be a huge download that wasn't needed for the user. If you know the resource is going to be used entirely get it early or at least a small part of it that you know will be used.
2)Start fetching it early even before the segue. In your code you need it for the table view but you're waiting all the way until view did load. This is pretty late in the lifecycle.
3)If you have to have the user wait on a resource let them know you're loading. Table View has a refresh control that you can call while you are waiting on the network or use a progress indicator or spinner. You can even hide your whole view and present a view so the user knows what's going on.
Also tableview is calling the datasource when it loads automatically and you're calling it when you say reloadData() in your code, that's why you get two calls.
So to answer your question this can be accomplished any number of ways, you could create a protocol or a local copy of the objects instance ie: MainVC in your presentingViewController then move your fetch code to there and set items on the local copy when the fetch comes back. And just add a didset to items variable to reload the tableview when the variable gets set. Or you could in theory at least perform the fetch block in the segue passing the MainVC items in the block.
For instance
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as MainVC
self.fetchBooks { data in
vc.items.self = data // not sure what the extra self is?
DispatchQueue.main.async {
vc.bookTableView.reloadData()
}
}
}
Since the closure captures a strong pointer you can do it this way.
Normally I will do data task as below code show, please see the comments in code.
// show a spinner to users when data is loading
self.showSpinner()
DispatchQueue.global().async { [weak self] in
// Put your heavy lifting task here,
// get data from some completion handler or whatever
loadData()
// After data is fetched OK, push back to main queue for UI update
DispatchQueue.main.async {
self?.tableView.reloadData()
// remove spinner when data loading is complete
self?.removeSpinner()
}
}
After making sure that you have the reloadData() called, make sure your constraints for labels/images are correct. This makes sure that you're labels/images can be seen within the cell.
I have a search bar and a table view under it. When I search for something a network call is made and 10 items are added to an array to populate the table. When I scroll to the bottom of the table, another network call is made for another 10 items, so now there is 20 items in the array... this could go on because it's an infinite scroll similar to Facebook's news feed.
Every time I make a network call, I also call self.tableView.reloadData() on the main thread. Since each cell has an image, you can see flickering - the cell images flash white.
I tried implementing this solution but I don't know where to put it in my code or how to. My code is Swift and that is Objective-C.
Any thoughts?
Update To Question 1
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(R.reuseIdentifier.searchCell.identifier, forIndexPath: indexPath) as! CustomTableViewCell
let book = booksArrayFromNetworkCall[indexPath.row]
// Set dynamic text
cell.titleLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
cell.authorsLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleFootnote)
// Update title
cell.titleLabel.text = book.title
// Update authors
cell.authorsLabel.text = book.authors
/*
- Getting the CoverImage is done asynchronously to stop choppiness of tableview.
- I also added the Title and Author inside of this call, even though it is not
necessary because there was a problem if it was outside: the first time a user
presses Search, the call for the CoverImage was too slow and only the Title
and Author were displaying.
*/
Book.convertURLToImagesAsynchronouslyAndUpdateCells(book, cell: cell, task: task)
return cell
}
cellForRowAtIndexPath uses this method inside it:
class func convertURLToImagesAsynchronouslyAndUpdateCells(bookObject: Book, cell: CustomTableViewCell, var task: NSURLSessionDataTask?) {
guard let coverImageURLString = bookObject.coverImageURLString, url = NSURL(string: coverImageURLString) else {
return
}
// Asynchronous work being done here.
task = NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
dispatch_async(dispatch_get_main_queue(), {
// Update cover image with data
guard let data = data else {
return
}
// Create an image object from our data
let coverImage = UIImage(data: data)
cell.coverImageView.image = coverImage
})
})
task?.resume()
}
When I scroll to the bottom of the table, I detect if I reach the bottom with willDisplayCell. If it is the bottom, then I make the same network call again.
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row+1 == booksArrayFromNetworkCall.count {
// Make network calls when we scroll to the bottom of the table.
refreshItems(currentIndexCount)
}
}
This is the network call code. It is called for the first time when I press Enter on the search bar, then it is called everytime I reach the bottom of the cell as you can see in willDisplayCell.
func refreshItems(index: Int) {
// Make to network call to Google Books
GoogleBooksClient.getBooksFromGoogleBooks(self.searchBar.text!, startIndex: index) { (books, error) -> Void in
guard let books = books else {
return
}
self.footerView.hidden = false
self.currentIndexCount += 10
self.booksArrayFromNetworkCall += books
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
}
}
If only the image flash white, and the text next to it doesn't, maybe when you call reloadData() the image is downloaded again from the source, which causes the flash. In this case you may need to save the images in cache.
I would recommend to use SDWebImage to cache images and download asynchronously. It is very simple and I use it in most of my projects. To confirm that this is the case, just add a static image from your assets to the cell instead of calling convertURLToImagesAsynchronouslyAndUpdateCells, and you will see that it will not flash again.
I dont' program in Swift but I see it is as simple as cell.imageView.sd_setImageWithURL(myImageURL). And it's done!
Here's an example of infinite scroll using insertRowsAtIndexPaths(_:withRowAnimation:)
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var dataSource = [String]()
var currentStartIndex = 0
// We use this to only fire one fetch request (not multiple) when we scroll to the bottom.
var isLoading = false
override func viewDidLoad() {
super.viewDidLoad()
// Load the first batch of items.
loadNextItems()
}
// Loads the next 20 items using the current start index to know from where to start the next fetch.
func loadNextItems() {
MyFakeDataSource().fetchItems(currentStartIndex, callback: { fetchedItems in
self.dataSource += fetchedItems // Append the fetched items to the existing items.
self.tableView.beginUpdates()
var indexPathsToInsert = [NSIndexPath]()
for i in self.currentStartIndex..<self.currentStartIndex + 20 {
indexPathsToInsert.append(NSIndexPath(forRow: i, inSection: 0))
}
self.tableView.insertRowsAtIndexPaths(indexPathsToInsert, withRowAnimation: .Bottom)
self.tableView.endUpdates()
self.isLoading = false
// The currentStartIndex must point to next index.
self.currentStartIndex = self.dataSource.count
})
}
// #MARK: - Table View Data Source Methods
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel!.text = dataSource[indexPath.row]
return cell
}
// #MARK: - Table View Delegate Methods
func scrollViewDidScroll(scrollView: UIScrollView) {
if isLoading == false && scrollView.contentOffset.y + scrollView.bounds.size.height > scrollView.contentSize.height {
isLoading = true
loadNextItems()
}
}
}
MyFakeDataSource is irrelevant, it's could be your GoogleBooksClient.getBooksFromGoogleBooks, or whatever data source you're using.
Try to change table alpha value before and after calling [tableView reloadData] method..Like
dispatch_async(dispatch_get_main_queue()) {
self.aTable.alpha = 0.4f;
self.tableView.reloadData()
[self.aTable.alpha = 1.0f;
}
I have used same approach in UIWebView reloading..its worked for me.
I have a TableView where I am fetching data from api. Everything working fine with fetching/displaying data and pagination/infinite-scroll. However, when I try to use my RefreshControl, it crashes with an error:
Array index out of range
var theRefreshControl: UIRefreshControl!
var results: [JSON]! = []
// more stuff..
func refresh(sender: AnyObject) {
if isFirstLoad == false {
self.results = []
currentPage = 1
getPosts()
}
self.theRefreshControl.endRefreshing()
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : PostCell = tableView.dequeueReusableCellWithIdentifier("PostCell") as! PostCell
cell.delegate = self
print(indexPath.row) // check below.
let post = self.results![indexPath.row] // 'array index out of range' here.
}
To get more detail about the error, I tried adding print(indexPath.row). What I receive is, I receive from 0 to 10 in first load, and then as soon as I refresh, it gets weird:
Also, when I click on the (i):
{length = 2, path = 0 - 9}
Interestingly, exactly same approach works in my other TableViewControllers, only this one is bugging. What may be the problem?
func getPosts() {
isLoading = true
Alamofire.request(.GET, link & params: ["page":"\(currentPage ?? 1)"])
.responseJSON { response in
// error checks
if let data = json["data"].arrayValue as [JSON]? {
self.lastPage = json["last_page"].int!
self.currentPage = json["current_page"].int!
if self.currentPage == 1 {
self.results = data
self.tableView.reloadData()
} else {
var currentCount = self.results!.count;
var indxesPath : [NSIndexPath] = [NSIndexPath]()
for result in data {
indxesPath.append(NSIndexPath(forRow:currentCount,inSection:0));
self.results?.append(result)
currentCount++
}
self.tableView.insertRowsAtIndexPaths(indxesPath, withRowAnimation: UITableViewRowAnimation.Bottom)
}
Edit: Please note that I tried results.removeAll() as well as results.removeAll(keepCapacity: false) in refresh button action function, but no luck. I am still getting the same error and same log for `print(indexPath.row) - 0 to 10 in first load, and in refresh, weirdly 6, 7, 8, [], 9
Swift 3.0 - Same issue here, solved in a similar way to #mcclux but with one less step.
Use the inbuilt UIRefreshControl.
var refreshControl: UIRefreshControl!, then within viewDidLoad configure it:
refreshControl = UIRefreshControl.init()
refreshControl.attributedTitle = NSAttributedString(string: "Pull to refresh")
refreshControl.addTarget(self, action: #selector(yourViewControllerName.yourFunctionToCallOnRefresh), for: UIControlEvents.valueChanged)
tableView.addSubview(refreshControl)
Then you can wrap your code within cellForRowAt indexPath with
if !self.refreshControl.isRefreshing {
...
}
Worked perfectly for me after struggling through it crashing for hours. Hope this helps!
I had a similar issue; it was caused in my case by tableView being called during the refresh, during the period when the content was being reset. Because the index was empty for a period, the app was occasionally throwing index out of range. It didn't happen every time. I solved it by creating a boolean var (isRefreshing) that gets set to true when the refresh starts and then set to false once the refresh is over. Inside of tableView() I have a wrapper for the functionality. In your case it would look like:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if !self.isRefreshing {
let cell : PostCell = tableView.dequeueReusableCellWithIdentifier("PostCell") as! PostCell
cell.delegate = self
print(indexPath.row) // check below.
let post = self.results![indexPath.row] // 'array index out of range' here.
}
}
Edit: here's how I'm using it. The cell creation and return are outside of the if:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("SessionTableViewCell", forIndexPath: indexPath) as! SessionTableViewCell
if !self.isReloading {
...
}
return cell
}
Solved my problem by deletingself.results = [] from refresh() function. Interestingly, if I empty the array before I refresh, it was creating a problem, but if I directly overwrite the data in the array, it works!
I was facing similar issue when the backing array was emptied like what you did w/ self.results = []. In my finding, there are 2 additional ways to solve this issue.
//1. is to reload table view as soon as you empty the array
func refresh(sender: AnyObject) {
if isFirstLoad == false {
self.results = []
self.tableView.reloadData()
currentPage = 1
getPosts()
}
self.theRefreshControl.endRefreshing()
}
//2. is to delay the execution of the code inside the if scope to give enough time for the table view to get up to date on the count of the results array
func refresh(sender: AnyObject) {
if isFirstLoad == false {
self.delayExecutionByMilliseconds(500) {
self.results = []
currentPage = 1
getPosts()
}
}
self.theRefreshControl.endRefreshing()
}
func delayExecutionByMilliseconds(_ delay: Int, for anonFunc: #escaping () -> Void) {
let when = DispatchTime.now() + .milliseconds(delay)
DispatchQueue.main.asyncAfter(deadline: when, execute: anonFunc)
}
In your refresh function, add self.tableView.reloadData() after cleaning your arrays, and before the function getPosts()
First of all, I would like to tell you that I've already tried to search and I've found so many questions with the same problem, but the solutions proposed there didn't work in my case.
I want insert a tableView inside the view of my UIViewController when a specific button is clicked, The data of the UITableView will come from the server.
I have UITableView not UITableViewController
my problem is that the data not being updated unless I scroll
I already found this question UITableViewCell textLabel, does not update until a scroll, or touch happens, while using GCD the solution there is to call setNeedLayout and another guy suggested to use setNeedsDisplay. both didn't solve my problem
This question also raises the same problem and the answer states to call the reloadData, which I'm doing from the first place
This is the delegate and data adapter for my UITableView
class CusinePreferencesTableView: NSObject, UITableViewDelegate, UITableViewDataSource {
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(CellIdentefiers.oneCusinePreferencesCell.rawValue, forIndexPath: indexPath) as! OneCusinePreferencesTableViewCell
let row = indexPath.row
print("row = \(row)")
let oneCusineDataLeft = Preferences2ViewController.cusines![2*row]
cell.leftButton.titleLabel?.text = oneCusineDataLeft
if (2*row+1) < Preferences2ViewController.cusines!.count{
let oneCusineDataRight = Preferences2ViewController.cusines![2*row+1]
cell.rightButton.titleLabel?.text = oneCusineDataRight
}else {
//I should hide the right button
cell.rightButton.titleLabel?.text = "wow"
}
cell.setNeedsDisplay()
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let cusines = Preferences2ViewController.cusines {
if cusines.count % 2 == 0 {
return cusines.count/2
}else {
return (cusines.count+1)/2
}
}else {
return 0
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
}
when someone clicks a button in my UIViewController, I do this:
setConstraintsForTableView(self.cusineTableView)
loadCusiens()
and finally this is the loadCusines function
func loadCusiens(){
let url = NSURL(string: ConstantData.getWebserviceFullAddress()+"preferences/cusines")
let request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "POST"
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error ) in
if let data = data {
do{
// here I handle the response
Preferences2ViewController.cusines = results
dispatch_async(dispatch_get_main_queue(), {
self.cusineTableView.reloadData()
})
} catch{
}
}
})
task.resume()
}
Eventhough in the morning i asked a similar question, but this is a different one because in the morning i had a not good code in the tableViewCell
What is this :
setConstraintsForTableView
Also, try removing the dispatch_async in the network call
Hello I’ve been having this problem for awhile. I want to stop the tableview from reusing the cell. It keeps displaying the wrong information when i scroll then it shows the right thing like a few milliseconds. How can i stop the tableview from reusing the cell or how can i reuse the cell and make it not do that.
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cats.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "CategoryTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! CategoryTableViewCell
cell.nameLabel.text = cats[indexPath.row].categoryName
cell.subNameLabel.text = cats[indexPath.row].appShortDesc
let catImageUrl = cats[indexPath.row].imageUrl
let url = NSURL(string: "https:\(catImageUrl)")
let urlRequest = NSURLRequest(URL: url!)
NSURLConnection.sendAsynchronousRequest(urlRequest, queue: NSOperationQueue.mainQueue()) { (response, data, error) -> Void in
if error != nil {
print(error)
} else {
if let ass = UIImage(data: data!) {
cell.photoImageView.image = ass
}
self.loading.stopAnimating()
}
}
return cell
}
The problem is that you are seeing an image from a previous cell. Simply initialize the image to nil when you dequeue the reused cell:
cell.photoImageView.image = nil
or set it to a default image of your choosing.
Note, the way you are updating the image after it loads has issues.
The row may no longer be on screen when the image finally loads, so you will be updating a cell that has been reused itself.
The update should be done on the main thread.
A better way to do this would be to have an array that caches the images for the cells. Load the image into the array, and then tell the tableView to reload that row.
Something like this:
dispatch_async(dispatch_get_main_queue()) {
self.imageCache[row] = ass
self.tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: row, inSection: 0)],
withRowAnimation: .None)
}
override prepareForReuse() method in your cell class and reset your values
override func prepareForReuse() {
super.prepareForReuse()
nameLabel.text = ""
}
This method is called every time the UITableView before reuses this cell