I am using Parse to load some data for title , http req for ImageView and ratingView. I also have a segment control view with 2 options.
I put an indicator where the image is presented , but when I scroll down
or when I press another segment(different movies) I get for 1-2 seconds the previous image , ratingView , titles and they change after this instantly. And obviously not all together. Can I somehow sync the changes or give an indicator 'waiting to load' until everything loads ?
func segmentView(segmentView: SMSegmentView, didSelectSegmentAtIndex index: Int) {
self.currentSegmentSelected = index
loadData().findObjectsInBackgroundWithBlock { [weak self] objects, error in
if error == nil {
dispatch_async(dispatch_get_main_queue(), {
self?.loadObjects()
})}
}
}
queryForTable(Parse)
override func queryForTable() -> PFQuery {
let query:PFQuery = PFQuery(className:"Movies")
if(objects?.count == 0)
{
query.cachePolicy = PFCachePolicy.CacheThenNetwork
}
if self.currentSegmentSelected == 0 {
query.whereKey("genres", containsString: "Adventure")
}
else if currentSegmentSelected == 1 {
query.whereKey("genres", containsString: "Comedy")
}
return query
}
cellForRowAtIndexPath
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell? {
let cellIdentifier:String = "cell"
var cell:TableCell? = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? TableCell
if(cell == nil) {
cell = TableCell(style: UITableViewCellStyle.Default, reuseIdentifier: cellIdentifier)
}
//??? indicator.startAnimating()
//??? indicator.backgroundColor = UIColor.whiteColor()
if let pfObject = object {
PFCloud.callFunctionInBackground("averageStars", withParameters: ["movieId":pfObject["movieId"]]) {
(response: AnyObject?, error: NSError?) -> Void in
if let rating = response as! Float? {
cell?.rating.rating = rating
}}
if let movieId = pfObject["movieId"] as? Int {
if RecommendedSet.ratings[movieId] == nil {
cell?.userRating.rating = 0
} else {
cell?.userRating.rating = RecommendedSet.ratings[movieId]!
}
}
cell?.title?.text = pfObject["title"] as? String
if let Id = pfObject["tmdbId"] as? Int {
Alamofire.request(.GET, timdb , parameters: ["api_key": APIkey])
.responseJSON { response in
if let JSON...
let imgData: NSData = NSData(contentsOfURL: imgURL)!
dispatch_async(dispatch_get_main_queue(), {
cell?.posterView?.image = UIImage(data: imgData)
//??? self.indicator.stopAnimating()
//??? self.indicator.hidesWhenStopped = true
})
}}}}}}}
return cell
}
Related
Am make a tableview listing, data loading dynamically and a image
contain in cell and some text. When it scroll gets jerking and
irritating the users
Any idea for improving the table view performance?
class MainViewController: UIViewController,UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var listTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.listTable.delegate = self
self.listTable.dataSource = self
self.listTable.separatorStyle = .None
self.listTable.registerNib(UINib(nibName: "cellOne", bundle: nil), forCellReuseIdentifier: "cellOne")
self.listTable.registerNib(UINib(nibName: "cellTwo", bundle: nil), forCellReuseIdentifier: "cellTwo")
self.listTable.registerNib(UINib(nibName: "cellThree", bundle: nil), forCellReuseIdentifier: "cellThree")
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int{
return 1
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if pageIndex == 7 {
return 120
} else if indexPath.row == 0 {
return 280
} else if (indexPath.row == 5 || indexPath.row == 4) {
return 200
} else if (indexPath.row == 8 || indexPath.row == 11) {
return 160
}
return 80
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return 50
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
let listArray = self.listDataArray[indexPath.row]
if indexPath.row == 0 && pageIndex != 7 {
var cell = tableView.dequeueReusabl eCellWithIdentifier("cellOne") as! cellOne!
if cell == nil {
tableView.registerClass(cellOne.classForCoder(), forCellReuseIdentifier: "cellOne")
cell = cellOne(style: UITableViewCellStyle.Default, reuseIdentifier: "cellOne")
}
cell.headLbl.text = "\(listArray["title"]!)".html2String
cell.addImageView.userInteractionEnabled = true
let guster = UITapGestureRecognizer(target: self, action: "addTarget:")
cell.addImageView.addGestureRecognizer(guster)
cell.addImageView.tag = 1
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
if listArray["image"] != nil && self.imagesLocalDictionary[indexPath.row] == nil {
let u = listArray["image"] as! String
let url = NSURL(string: u)
if url != nil {
let data = NSData(contentsOfURL: url!)
if data != nil {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
cell.imageViews.image = UIImage(data: data!)
})
self.imagesLocalDictionary.setObject(data!, forKey: indexPath.row)
}
}
} else {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
cell.imageViews.image = UIImage(data: self.imagesLocalDictionary[indexPath.row] as! NSData)
})
}
})
if Constants.sharedInstance.addData["1"] != nil {
cell.addImageView.image = UIImage(data: Constants.sharedInstance.addData["1"] as! NSData)
}
return cell
} else if indexPath.row == 4 && pageIndex != 7 {
var cell = tableView.dequeueReusableCellWithIdentifier("cellTwo") as! cellTwo!
if cell == nil {
tableView.registerClass(cellTwo.classForCoder(), forCellReuseIdentifier: "cellTwo")
cell = cellTwo(style: UITableViewCellStyle.Default, reuseIdentifier: "cellTwo")
}
cell.headLbl.text = "\(listArray["title"]!)".html2String
cell.addImageView.userInteractionEnabled = true
let guster = UITapGestureRecognizer(target: self, action: "addTarget:")
cell.addImageView.addGestureRecognizer(guster)
cell.addImageView.tag = 2
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
if listArray["image"] != nil && self.imagesLocalDictionary[indexPath.row] == nil {
let u = listArray["image"] as! String
let url = NSURL(string: u)
if url != nil {
let data = NSData(contentsOfURL: url!)
if data != nil {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
cell.imageViews.image = UIImage(data: data!)
})
self.imagesLocalDictionary.setObject(data!, forKey: indexPath.row)
}
}
} else {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
cell.imageViews.image = UIImage(data: self.imagesLocalDictionary[indexPath.row] as! NSData)
})
}
})
if Constants.sharedInstance.addData["2"] != nil {
cell.addImageView.image = UIImage(data: Constants.sharedInstance.addData["2"] as! NSData)
}
return cell
} else if indexPath.row == 5 && pageIndex != 7 {
var cell = tableView.dequeueReusableCellWithIdentifier("cellThree") as! cellThree!
if cell == nil {
tableView.registerClass(cellThree.classForCoder(), forCellReuseIdentifier: "cellThree")
cell = cellThree(style: UITableViewCellStyle.Default, reuseIdentifier: "cellThree")
}
cell.headLbl.text = "\(listArray["title"]!)".html2String
cell.addImageView.userInteractionEnabled = true
let guster = UITapGestureRecognizer(target: self, action: "addTarget:")
cell.addImageView.addGestureRecognizer(guster)
cell.addImageView.tag = 3
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
if listArray["image"] != nil && self.imagesLocalDictionary[indexPath.row] == nil {
let u = listArray["image"] as! String
let url = NSURL(string: u)
if url != nil {
let data = NSData(contentsOfURL: url!)
if data != nil {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
cell.imageViews.image = UIImage(data: data!)
})
self.imagesLocalDictionary.setObject(data!, forKey: indexPath.row)
}
}
} else {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
cell.imageViews.image = UIImage(data: self.imagesLocalDictionary[indexPath.row] as! NSData)
})
}
})
if Constants.sharedInstance.addData["3"] != nil {
cell.addImageView.image = UIImage(data: Constants.sharedInstance.addData["3"] as! NSData)
}
return cell
} else {
cell.backgroundColor = UIColor.groupTableViewBackgroundColor()
let container = UIView()
container.frame = CGRectMake(2, 1, tableView.frame.width-4, 78)
container.backgroundColor = UIColor.whiteColor()
cell.addSubview(container)
let headerLbl = UILabel()
headerLbl.backgroundColor = UIColor.clearColor()
headerLbl.frame = CGRectMake(120, 1, self.view.frame.width-130, 76)
headerLbl.numberOfLines = 0
headerLbl.font = UIFont(name: "TelegramHead", size: 18)
headerLbl.text = "\(listArray["title"]!)".html2String
headerLbl.lineBreakMode = NSLineBreakMode.ByCharWrapping
let imageView = UIImageView()
imageView.frame = CGRectMake(5, 5, 100, 68)
imageView.image = UIImage(named: "place_holder.jpg")
imageView.contentMode = UIViewContentMode.ScaleAspectFill
imageView.clipsToBounds = true
let blackLayerView = UIView();
blackLayerView.frame = CGRectMake(0, 0, 0, 0);
blackLayerView.backgroundColor = UIColor.blackColor();
blackLayerView.alpha = 0.4;
container.addSubview(imageView)
container.addSubview(blackLayerView);
container.addSubview(headerLbl)
if pageIndex == 7 {
container.frame = CGRectMake(2, 1, tableView.frame.width-4, 118)
imageView.frame = CGRectMake(5, 5, container.frame.width-10, 108)
blackLayerView.frame = imageView.frame;
headerLbl.frame = imageView.frame//CGRectMake(10, 5, self.view.frame.width-20, 100)
headerLbl.textColor = UIColor.whiteColor()
headerLbl.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.2)
} else if (indexPath.row == 8 || indexPath.row == 11) && pageIndex != 7 {
let add = UIImageView()
add.frame = CGRectMake(2, 82, tableView.frame.width-4, 76)
//add.contentMode = UIViewContentMode.ScaleAspectFit;
cell.addSubview(add)
add.userInteractionEnabled = true
let guster = UITapGestureRecognizer(target: self, action: "addTarget:")
add.addGestureRecognizer(guster)
if Constants.sharedInstance.addData["4"] != nil && indexPath.row == 8{
add.image = UIImage(data: Constants.sharedInstance.addData["4"] as! NSData)
add.tag = 4
}
if Constants.sharedInstance.addData["5"] != nil && indexPath.row == 11{
add.image = UIImage(data: Constants.sharedInstance.addData["5"] as! NSData)
add.tag = 5
}
}
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
if listArray["image"] != nil && self.imagesLocalDictionary[indexPath.row] == nil {
let u = listArray["image"] as! String
let url = NSURL(string: u)
if url != nil {
let data = NSData(contentsOfURL: url!)
if data != nil {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
imageView.image = UIImage(data: data!)
self.imagesLocalDictionary.setObject(data!, forKey: indexPath.row)
})
}
}
} else {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
imageView.image = UIImage(data: self.imagesLocalDictionary[indexPath.row] as! NSData)
})
}
})
}
return cell
}
}
I do get the required functionality. But there is a very unnatural jerk on the TableView which results in a bad User experience.
There are a number of improvements you could make to this code to improve performance.
Don't register cells in cellForRow
Define specific custom classes for each cell and register them for reuse in viewDidLoad
Reduce the logic used in cellForRowAt
At the start of cellForRowAt you create a new instance of a cell and later add subviews to it if none of the other cases match. there is no reuse for this cell. Always reuse. you'll use less resources
I have built a complex table view before and created functions that configure and return reusble cells for the particular indexPath.
class LargeImageCell: UITableViewCell {
var imageView: UIImageView()
// in init, init the cell, add imageview as subview, setup constraints
func setContent(imageURL: URL) {
// load image from cache or fetch from network in background
}
}
// in tableview/view controller
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch indexPath.row {
case 0:
return self.getLargeImageCell(indexPath: indexPath)
// all other cases
}
}
This is just roughly how I implemented mine, just try to reduce the amount of logic in the cell and I've alwayss found it best to create custom cells rather than adding them in cellForRowAt, let the cell configure itself
func getLargeImageCell(indexPath: NSIndexPath) -> UITableViewCell {
let data = self.data[indexPath.row] // model from db
var cell: UITableViewCell
if let c = self.tableView.dequeueReusableCellWithIdentifier("largeImageCell") as? LargeImageCell {
cell = c
} else {
cell = LargeImageCell(style: UITableViewCellStyle.Default, reuseIdentifier: "largeImageCell")
}
cell.setContent(imageURL: data.imageURL)
return cell
}
I'm populating my tableView with JSON data, most of the time the data shows but for some strange reason other times it doesn't. I tested the JSON data in Chrome and the info is there. I also made print statements to print the info after it has downloaded and it appears to download correctly. I can't figure out why 80% of the time the data populates the tableView correctly and 20% of the time it doesn't. Here is a sample of my code, there are many more cells but I shortened it to 2 for this example:
var task : NSURLSessionTask?
var newURL : String?
var bannerArray: [String] = []
var overViewArray: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
getJSON(newURL!)
}
func getJSON (urlString: String) {
let url = NSURL(string: urlString)!
let session = NSURLSession.sharedSession()
task = session.dataTaskWithURL(url) {(data, response, error) in
dispatch_async(dispatch_get_main_queue()) {
if (error == nil) {
self.updateDetailShowInfo(data)
}
else {
"Not getting JSON"
}
}
}
task!.resume()
}
func updateDetailShowInfo (data: NSData!) {
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
guard let banner = jsonResult["banner"] as? String,
let overview = jsonResult["overview"] as? String
else { return }
_ = ""
print(overview)
bannerArray.append(banner)
overViewArray.append(overview)
}
catch {
print("It ain't working")
}
self.DetailTvTableView.reloadData()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0: return bannerArray.count
case 1: return overViewArray.count
default: fatalError("Unknown Selection")
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
switch indexPath.section {
case 0:
let cell = tableView.dequeueReusableCellWithIdentifier("bannerCell", forIndexPath: indexPath) as! BannerCell
cell.bannerImage.sd_setImageWithURL(NSURL(string: bannerArray[indexPath.row]))
self.DetailTvTableView.rowHeight = 100
DetailTvTableView.allowsSelection = false
return cell
case 1:
let cell = tableView.dequeueReusableCellWithIdentifier("overviewCell", forIndexPath: indexPath) as! OverviewCell
let overViewText = overViewArray[indexPath.row]
if overViewText != "" {
cell.overView.text = overViewText
} else {
cell.overView.text = "N/A"
}
self.DetailTvTableView.rowHeight = 200
print(overViewArray[indexPath.row])
return cell
default: ""
}
return cell
}
I'm just doing this off the web. And I think there are some errors. You need to debug them yourself.
Your understanding of fetching the JSON and GCD is totally wrong. I believe these codes you got somewhere off the web. Go read up what is dispatch_async.
Basically, you need to create session to fetch JSON data, which you have done it correctly, however, within the NSJSONSerialization, you need to store them in a variable and append it to your array. This is fetched asynchronously. Your dispatch_async will reload data serially.
func getJSON (urlString: String) {
let url = NSURL(string: urlString)!
let session = NSURLSession.sharedSession()
task = session.dataTaskWithURL(url) {(data, response, error) in
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
guard let banner = jsonResult["banner"] as? String,
let overview = jsonResult["overview"] as? String
bannerArray.append(banner)
overViewArray.append(overview)
} dispatch_async(dispatch_get_main_queue()) {
if (error == nil) {
self.DetailTvTableView.reloadData()
}
else {
"Not getting JSON"
}
}
catch {
print("It ain't working")
}
}
}
task!.resume()
}
I have 4 functions containing Parse query.findObjectsInBackgroundWithBlock. These are being called to grab data and then populate the table view. Using dispatch groups.
Here is two examples of my parse querys
func getEventImages() {
print("getEventImages enter")
dispatch_group_enter(self.group)
let query = PFQuery(className: "events")
query.orderByAscending("eventDate")
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!, error: NSError!) -> Void in
// Initialize your array to contain all nil objects as
// placeholders for your images
if error == nil {
self.eventMainImageArray = [UIImage?](count: objects.count, repeatedValue: nil)
for i in 0...objects.count - 1 {
let object: AnyObject = objects[i]
let mainImage = object["mainImage"] as! PFFile
//dispatch_group_enter(self.group)
mainImage.getDataInBackgroundWithBlock({
(imageData: NSData!, error: NSError!) -> Void in
if (error == nil) {
let mainImage = UIImage(data:imageData)
self.eventMainImageArray[i] = mainImage
print("getEventImages appended")
}
else {
print("error!!")
}
})
}
}
print("getEventImages leave")
dispatch_group_leave(self.group)
}
}
func getEventInfo() {
print("eventInfo enter")
dispatch_group_enter(group)
let query = PFQuery(className: "events")
query.orderByAscending("eventDate")
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!,error: NSError!) -> Void in
self.eventNameArray = [String?](count: objects.count, repeatedValue: nil)
self.eventInfoArray = [String?](count: objects.count, repeatedValue: nil)
self.eventDateArray = [NSDate?](count: objects.count, repeatedValue: nil)
self.eventTicketsArray = [String?](count: objects.count, repeatedValue: nil)
if error == nil {
for i in 0...objects.count - 1 {
let object: AnyObject = objects[i]
let eventName = object["eventName"] as! String
let eventInfo = object["eventInfo"] as! String
let eventDate = object["eventDate"] as! NSDate
let eventTicket = object["Tickets"] as! String
self.eventNameArray[i] = eventName
self.eventInfoArray[i] = eventInfo
self.eventDateArray[i] = eventDate
self.eventTicketsArray[i] = eventTicket
print("event info appended")
}
}
print("event info leave")
dispatch_group_leave(self.group)
}
}
And my dispatch_group_nofity
dispatch_group_notify(group, dispatch_get_main_queue()) { () -> Void in
print("Finished reloadDataFromServer()")
self.tableView.reloadData()
self.refreshControl?.finishingLoading()
}
}
The problem is that its hit and miss if the data gets retrieved quick enough before dispatch_group_leave(self.group) is called leading to reloading the tableview data too soon. I need to get this so the dispatch_group_leave gets called when the appending is completed.
There is no need for two methods to retrieve the data, no need to unpack the data into multiple arrays and no need to use dispatch groups.
All you need is a simple method to retrieve your event data
var events:[PFObject]=[PFObject]()
func getEventInfo() {
let query = PFQuery(className: "events")
query.orderByAscending("eventDate")
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!,error: NSError!) -> Void in
if error==nil {
self.events=objects as! [PFObject]
self.tableView.reloadData()
} else {
print("Something went wrong! - \(error)"
}
self.refreshControl?.finishingLoading()
}
}
Then, you haven't shown your cellForRowAtIndexPath but you would have something like
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! MyTableViewCell
let event=self.events[indexPath.row]
cell.eventName.text=event["eventName"] as? String
cell.eventInfo.text=event["eventInfo"] as? String
if let mainImageFile=event["mainImage"] as? PFFile {
mainImageFile.getDataInBackgroundWithBlock({
(imageData: NSData!, error: NSError!) -> Void in
if (error == nil) {
let mainImage = UIImage(data:imageData)
cell.mainImage= mainImage
}
else {
print("error!!")
}
}
return cell;
}
You can use a PFImageView or a framework like SDWebImage to handle image caching and putting a placeholder image in place while the image is loaded.
If you want to update an event is as easy as
var event=self.events[someindex];
event["eventName"]=newValue
event.saveInBackground()
Below is the my code!!!
import UIKit
class myhomefeedViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var tableData = []
var imageCache = [String:UIImage]()
var imageCache1 = [String:UIImage]()
var imageCache2 = [String:UIImage]()
#IBOutlet var appsTableView: UITableView!
var refreshControl:UIRefreshControl!
let PageSize = 5
#IBOutlet var tableViewFooter:Reload!
var loading = false
override func viewDidLoad() {
super.viewDidLoad()
self.tableViewFooter.hidden = true
loadmyhomefeeddata(0, size: PageSize)
self.refreshControl = UIRefreshControl()
self.refreshControl.attributedTitle = NSAttributedString(string: "Pull to refresh")
self.refreshControl.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)
self.appsTableView.addSubview(refreshControl)
}
func refresh(refreshControl: UIRefreshControl) {
loadmyhomefeeddata(0, size: PageSize)
refreshControl.endRefreshing()
}
func loadSegment(offset:Int, size:Int) {
if (!self.loading) {
self.setLoadingState(true)
if currentpage < toPage {
}
else if currentpage > toPage
{
self.setLoadingState(false)
}
}
else
{
println("Not Loading")
}
}
func setLoadingState(loading:Bool) {
self.loading = loading
self.tableViewFooter.hidden = !loading
}
func scrollViewDidScroll(scrollView: UIScrollView) {
let currentOffset = scrollView.contentOffset.y
let maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height
if (maximumOffset - currentOffset) <= 40 {
loadSegment(currentpage, size: tableData.count)
}
}
// pull to refresh list
#IBAction func writeyouridea(sender: AnyObject) {
let viewController=self.storyboard?.instantiateViewControllerWithIdentifier("writeyouridea") as? UIViewController
self.presentViewController(viewController!, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return tableData.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("myhomefeedcell", forIndexPath: indexPath) as! myhomefeedTableViewCell
// Configure the cell...
if let rowData: NSDictionary = self.tableData[indexPath.row] as? NSDictionary,
urlString = rowData["imgfile"] as? String,
imgURL = NSURL(string: urlString),
// imgData = NSData(contentsOfURL: imgURL),
countryString = rowData["cflag"] as? String,
countryimgURL = NSURL(string: countryString),
// countryimgData = NSData(contentsOfURL: countryimgURL),
ideaimageString = rowData["ideapicture"] as? String,
ideaimageURL = NSURL(string: ideaimageString),
// ideaimagedata = NSData(contentsOfURL: ideaimageURL),
userfullname = rowData["name"] as? String,
category = rowData["categoryname"] as? String,
ideadesc = rowData["idea"] as? String,
time = rowData["createddate"] as? String,
ideatitle = rowData["title"] as? String {
cell.ideadesc.text = ideadesc
cell.username.text = userfullname
cell.categoryname.text = category
cell.feedimage.image = UIImage(named: "cross")
cell.userimage.image = UIImage(named: "cross")
cell.country.image = UIImage(named: "cross")
cell.title.text = ideatitle
cell.time.text = time
//country
if let countryimg = imageCache1[countryString] {
cell.country.image = countryimg
}
else {
let request: NSURLRequest = NSURLRequest(URL: countryimgURL)
let mainQueue = NSOperationQueue.mainQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: mainQueue, completionHandler: { (response, data, error) -> Void in
if error == nil {
let imagecountry = UIImage(data: data)
self.imageCache1[countryString] = imagecountry
// Update the cell
dispatch_async(dispatch_get_main_queue(), {
cell.country.image = imagecountry
})
}
else {
println("Error: \(error.localizedDescription)")
}
})
}
//userimage
if let userimg = imageCache2[urlString] {
cell.userimage.image = userimg
}
else {
let request: NSURLRequest = NSURLRequest(URL: imgURL)
let mainQueue = NSOperationQueue.mainQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: mainQueue, completionHandler: { (response, data, error) -> Void in
if error == nil {
let imageuser = UIImage(data: data)
self.imageCache2[urlString] = imageuser
// Update the cell
dispatch_async(dispatch_get_main_queue(), {
cell.userimage.image = imageuser
})
}
else {
println("Error: \(error.localizedDescription)")
}
})
}
if cell.feedimage.image != nil
{
if let img = imageCache[ideaimageString] {
cell.feedimage.image = img
}
else {
let request: NSURLRequest = NSURLRequest(URL: ideaimageURL)
let mainQueue = NSOperationQueue.mainQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: mainQueue, completionHandler: { (response, data, error) -> Void in
if error == nil {
let image = UIImage(data: data)
self.imageCache[ideaimageString] = image
// Update the cell
dispatch_async(dispatch_get_main_queue(), {
cell.feedimage.image = image
})
}
else {
println("Error: \(error.localizedDescription)")
}
})
}
}
}
return cell
}
func loadmyhomefeeddata(offset:Int, size:Int) {
let rowslimit = size
let urlPath = "url address?noofrowslimit=\(rowslimit)"
let url = NSURL(string: urlPath)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
if(error != nil) {
// If there is an error in the web request, print it to the console
println(error.localizedDescription)
}
var err: NSError?
if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as? NSDictionary {
if(err != nil) {
// If there is an error parsing JSON, print it to the console
println("JSON Error \(err!.localizedDescription)")
}
// println(jsonResult)
if let results: NSArray = jsonResult["results"] as? NSArray {
dispatch_async(dispatch_get_main_queue(), {
self.tableData = results
self.appsTableView!.reloadData()
})
}
}
})
task.resume()
}
}
Are you trying to have a infinite scroll? If so, you could try the following. First of all, make the task an instance variable of your controller then add the following UIScrollViewDelegate.
override func scrollViewDidScroll(scrollView: UIScrollView) {
// If you are already loading elements, return. This method is called multiple times
if task?.state == .Running { // You could use the isLoading variable instead
return
}
let offSetY = scrollView.contentOffset.y
let triggerY = scrollView.contentSize.height - tableView.frame.size.height
if (offSetY >= triggerY) {
// The offset of elements should be the amount you currently have, and you want to fetch 5 more elements
self.loadmyhomefeeddata(tableData.count, size: 5)
}
}
I got an error in self.tableView.reloadData(). Can it be because I use the SSASideMenu lib, where there are no segues between the menu and other views? To me, it seems like my tableView was not initialized.
class GroupListViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var TableData:Array< String > = Array < String >()
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
get_data_from_url("http://www.kaleidosblog.com/tutorial/tutorial.json")
title = "title"
var menuImage:UIImage = UIImage(named: "sidebtn")!
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "1", style: .Plain, target: self, action: "presentLeftMenuViewController")
menuImage = menuImage.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
self.navigationItem.leftBarButtonItem?.image = menuImage
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return TableData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel?.text = TableData[indexPath.row]
return cell
}
func get_data_from_url(url:String)
{
let httpMethod = "GET"
let timeout = 15
let url = NSURL(string: url)
let urlRequest = NSMutableURLRequest(URL: url!,
cachePolicy: .ReloadIgnoringLocalAndRemoteCacheData,
timeoutInterval: 15.0)
let queue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(
urlRequest,
queue: queue,
completionHandler: {(response: NSURLResponse!,
data: NSData!,
error: NSError!) in
if data.length > 0 && error == nil{
let json = NSString(data: data, encoding: NSASCIIStringEncoding)
self.extract_json(json!)
}else if data.length == 0 && error == nil{
println("Nothing was downloaded")
} else if error != nil{
println("Error happened = \(error)")
}
}
)
}
func extract_json(data:NSString)
{
var parseError: NSError?
let jsonData:NSData? = data.dataUsingEncoding(NSASCIIStringEncoding)!
let json: AnyObject? = NSJSONSerialization.JSONObjectWithData(jsonData!, options: nil, error: &parseError)
if (parseError == nil)
{
if let countries_list = json as? NSArray
{
for (var i = 0; i < countries_list.count ; i++ )
{
if let country_obj = countries_list[i] as? NSDictionary
{
if let country_name = country_obj["country"] as? String
{
if let country_code = country_obj["code"] as? String
{
TableData.append(country_name + " [" + country_code + "]")
}
}
}
}
}
}
do_table_refresh();
}
func do_table_refresh()
{
self.tableView.reloadData()
}
Ok. The exception you are getting is because your tableView is nil after viewdidLoad. It can be a connection problem, so first try this answer:
IBOutlet UITableView is null after View did load
Second, if all your nibs and are proper and you are still seeing this error. Then try the below code. [put tableview frame as you want]. This does everything you want to do programmatically and will work.
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var TableData:Array< String > = Array < String >()
var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
get_data_from_url("http://www.kaleidosblog.com/tutorial/tutorial.json")
tableView = UITableView(frame: self.view.frame)
title = "title"
var menuImage:UIImage = UIImage(named: "sidebtn")!
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "1", style: .Plain, target: self, action: "presentLeftMenuViewController")
menuImage = menuImage.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
self.navigationItem.leftBarButtonItem?.image = menuImage
self.view.addSubView(tableView)
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return TableData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel?.text = TableData[indexPath.row]
return cell
}
func get_data_from_url(url:String)
{
let httpMethod = "GET"
let timeout = 15
let url = NSURL(string: url)
let urlRequest = NSMutableURLRequest(URL: url!,
cachePolicy: .ReloadIgnoringLocalAndRemoteCacheData,
timeoutInterval: 15.0)
let queue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(
urlRequest,
queue: queue,
completionHandler: {(response: NSURLResponse!,
data: NSData!,
error: NSError!) in
if data.length > 0 && error == nil{
let json = NSString(data: data, encoding: NSASCIIStringEncoding)
self.extract_json(json!)
}else if data.length == 0 && error == nil{
println("Nothing was downloaded")
} else if error != nil{
println("Error happened = \(error)")
}
}
)
}
func extract_json(data:NSString)
{
var parseError: NSError?
let jsonData:NSData? = data.dataUsingEncoding(NSASCIIStringEncoding)!
let json: AnyObject? = NSJSONSerialization.JSONObjectWithData(jsonData!, options: nil, error: &parseError)
if (parseError == nil)
{
if let countries_list = json as? NSArray
{
for (var i = 0; i < countries_list.count ; i++ )
{
if let country_obj = countries_list[i] as? NSDictionary
{
if let country_name = country_obj["country"] as? String
{
if let country_code = country_obj["code"] as? String
{
TableData.append(country_name + " [" + country_code + "]")
}
}
}
}
}
}
do_table_refresh();
}
func do_table_refresh()
{
self.tableView.reloadData()
}
}
In storyboard, find your tableviewcontroller, and right click on the tableview to check the referencing outlets, there are big chances under the referencing outlets, your defined your tableview variable not the same as the one you defined in your codes.
Then you just need to delete the referencing outlets of the tableview, also delete the codes "var tableView: UITableView!", then re-contorl drag the tableview to your code to make a new reference. It happened to me once for this reason.