Reputation: 205
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.
Upvotes: 2
Views: 1854
Reputation: 19750
There are a number of improvements you could make to this code to improve performance.
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
}
Upvotes: 2