Вопрос:

In swift - UITableview gets jerky while scrolling

ios swift uitableview scroll

645 просмотра

1 ответ

72 Репутация автора

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.

Автор: oops Источник Размещён: 12.04.2017 09:39

Ответы (1)


1 плюс

14560 Репутация автора

There are a number of improvements you could make to this code to improve performance.

  1. Don't register cells in cellForRow
  2. Define specific custom classes for each cell and register them for reuse in viewDidLoad
  3. Reduce the logic used in cellForRowAt
  4. 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
} 
Автор: Scriptable Размещён: 12.04.2017 10:20
Вопросы из категории :
32x32