Swift,将更多数据加载到表视图中会导致滚动滞后

时间:2018-02-28 10:34:01

标签: ios swift uitableview

我有一个UITableView数据,从网址解析。

UITableView会在向右滚动时加载更多数据(或者有更多空间可以滚动,但接近结尾 - 两者都做,结果相同)

当加载更多数据时 - 我简单地将它附加到我的类的数组中,其中包含TableView的数据,然后列表滚动回列表的一半以上(例如,获得40个项目,再加载10个 - >滚动回20-25)。

追加完成后调用TableView.reloadData()

计划中是否有错误? 我可以分享代码,但这很常见。

class TabAllTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, XMLParserDelegate {

@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
@IBOutlet weak var BlogAllTableView: UITableView!

var loadMoreStatus = false
var maxPage = 1.0
var currentPageLoad = 1.0

var blogList: [DetailAndRenderBlogObject] = []
var eName: String = String()
var category = String()
var full_text = String()
var short_text = String()
var blog_title = String()
var avatar = String()
var full_name = String()
var url = String()
var created_at = String()

private let CATEGORY = ConfigData.CATEGORY_ALL

let cellIdentifier = "BlogTableViewCell"

override func viewDidLoad() {
    super.viewDidLoad()
    setupNavMenuButtons()

    self.BlogAllTableView.insertSubview(refreshControl, at: 0)

    self.BlogAllTableView.tableFooterView?.isHidden = true

    downloadBlogData(1, true, true)

    BlogAllTableView.delegate = self
    BlogAllTableView.dataSource = self
}

var refreshControl: UIRefreshControl = {
    let refreshControl = UIRefreshControl()
    refreshControl.addTarget(self, action:
        #selector(TabAllTableViewController.handleRefresh(_:)),
                             for: UIControlEvents.valueChanged)
    refreshControl.tintColor = Colors.ColorLoadingIndicator

    return refreshControl
}()

@objc func handleRefresh(_ refreshControl: UIRefreshControl) {
    downloadBlogData(1, true, false)
}

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let currentOffset = scrollView.contentOffset.y
    let maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height
    let deltaOffset = maximumOffset - currentOffset

    if deltaOffset <= 0 && currentPageLoad < maxPage {
        loadMore()
    }
}

func loadMore() {
    if ( !loadMoreStatus ) {
        self.loadMoreStatus = true
        self.activityIndicator.startAnimating()
        self.BlogAllTableView.tableFooterView?.isHidden = false
        downloadBlogData(currentPageLoad, false, false)
    }
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.sideMenuController?.isLeftViewEnabled = true
    AppDelegate.tabBarReference.isHidden = false
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    self.navigationController?.navigationBar.topItem?.title = "all".localized()
}

private func setupNavMenuButtons() {
    navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .refresh, target: self, action: #selector(handleMenuRefresh))

    let image = UIImage(named:"menu_ham.png")
    let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
    imageView.contentMode = .scaleAspectFit
    imageView.isUserInteractionEnabled = true
    imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    imageView.image = image

    let imageHeight = navigationController?.navigationBar.frame.size.height
    let wrapperView = UIView(frame: CGRect(x: 0, y: 0, width: imageHeight!, height: imageHeight!))
    wrapperView.addSubview(imageView)

    imageView.center = CGPoint(x: imageView.frame.size.width / 2, y: wrapperView.frame.size.height / 2)

    let tap = UITapGestureRecognizer(target: self, action: #selector(TabAllTableViewController.menuButtonClick))
    wrapperView.addGestureRecognizer(tap)

    let btnHamburgerMenu: UIBarButtonItem = UIBarButtonItem(customView: wrapperView)
    navigationItem.setLeftBarButton(btnHamburgerMenu, animated: false)
}

@objc private func menuButtonClick()
{
    self.sideMenuController?.showLeftViewAnimated()
}

@objc private func handleMenuRefresh() {
    downloadBlogData(1, true, true)
}

func numberOfSections(in tableView: UITableView) -> Int {
    return blogList.count
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 1
}

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    if (section != 0) {
        return 12
    } else {
        return CGFloat.leastNonzeroMagnitude
    }
}

func tableView(_ tableView: UITableView,  viewForHeaderInSection section: Int) -> UIView? {
    let view = UIView()
    view.backgroundColor = UIColor.black.withAlphaComponent(0.0)
    return view
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return CGFloat(ConfigData.CELL_HEIGHT)
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    // Configure the cell...

    guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? BlogTableViewCell  else {
        fatalError("The dequeued cell is not an instance of BlogTableViewCell.")
    }

    cell.layer.cornerRadius = 10

    let blogItem = blogList[indexPath.section]

    cell.avatarBackground.contentMode = .scaleAspectFill
    cell.avatarBackground.layer.cornerRadius = cell.avatarBackground.frame.size.width / 2;
    cell.avatarBackground.clipsToBounds = true;


    if let authorImgUrl = URL(string: blogItem.authorImg) {
        cell.authorRoundImage.contentMode = .scaleAspectFill
        cell.authorRoundImage.layer.cornerRadius = cell.authorRoundImage.frame.size.width / 2;
        cell.authorRoundImage.clipsToBounds = true;
        //Helper.downloadImage(url: authorImgUrl, imageview: cell.authorRoundImage)
        cell.authorRoundImage.sd_setImage(with: authorImgUrl)
    }

    if let headerImageUrl = URL(string: blogItem.image) {
        cell.bigImage.contentMode = .scaleToFill
        //Helper.downloadImage(url: headerImageUrl, imageview: cell.bigImage)
        cell.bigImage.sd_setImage(with: headerImageUrl)
    }

    cell.authorLabel.text = blogItem.author
    cell.dateLabel.text = blogItem.date
    cell.title.text = blogItem.title
    cell.titleDescription.text = blogItem.shortDescription

    return cell
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let secondViewController = self.storyboard!.instantiateViewController(withIdentifier: "ShowArticleViewController") as! ShowArticleViewController

    secondViewController.blogItem = blogList[indexPath.section]

    self.navigationController!.pushViewController(secondViewController, animated: true)
}

private func downloadBlogData(_ page : Double, _ refresh : Bool, _ showOverlay : Bool) {
   // print(InAppProperties.sharedInstance.getDefaulLang())

    if showOverlay {
        LoadingOverlay.shared.showOverlay(view: self.view)
    }

    let req = NSMutableURLRequest(url: NSURL(string: Helper.getBlogUrl(CATEGORY, Int(page)))! as URL)
    req.httpMethod = "GET"
    req.httpBody = "key=\"value\"".data(using: String.Encoding.utf8) //This isn't for GET requests, but for POST requests so you would

    URLSession.shared.dataTask(with: req as URLRequest) { data, response, error in
        if error != nil {
            //Your HTTP request failed.
            print(error?.localizedDescription)
        } else {
            //Your HTTP request succeeded

            if refresh {
                self.currentPageLoad = 1
            } else {
                self.currentPageLoad += 1
            }

            let stringData = String(data: data!, encoding: String.Encoding.utf8)!

            //print(stringData)

            let xml = SWXMLHash.parse(stringData)

            if page == 1 {
                self.blogList = [DetailAndRenderBlogObject]()
            }

            for elem in xml["response"]["provider"].all {
                let itemList = elem["item"]

                for article in itemList.all {
                    let blogItem = DetailAndRenderBlogObject()

                    blogItem.articleUrl = article["url"].element!.text
                    blogItem.author = article["full_name"].element!.text
                    blogItem.authorImg = article["avatar"].element!.text
                    blogItem.text = article["full_text"].element!.text
                    blogItem.shortDescription = article["short_text"].element!.text
                    blogItem.title = article["title"].element!.text
                    blogItem.categoryUrl = article["category"].element!.text
                    blogItem.image = self.repairLink(article["thumbnail"].element!.text)
                    blogItem.date = self.formatDate(article["created_at"].element!.text)

                    if (blogItem.categoryUrl.lowercased().range(of:"video") == nil &&
                        blogItem.title.lowercased().range(of: "видео") == nil) {

                        self.blogList.append(blogItem)
                    }
                }

                if let totalItemsCount = xml["response"]["pagination"]["totalCount"].element?.text {
                    self.maxPage = (Double(totalItemsCount)! / 10).rounded(.up)
                }
            }

            DispatchQueue.main.async {
                self.BlogAllTableView.reloadData()
                self.refreshControl.endRefreshing()

                self.loadMoreStatus = false
                self.activityIndicator.stopAnimating()
                self.BlogAllTableView.tableFooterView?.isHidden = true

                if showOverlay {
                    LoadingOverlay.shared.hideOverlayView()
                }

                if (page == 1) {
                    let indexPath = NSIndexPath(row: 0, section: 0)
                    self.BlogAllTableView.scrollToRow(at: indexPath as IndexPath, at: .top, animated: true)
                }

            }
        }
        }.resume()
}

func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
    eName = elementName

    if elementName == "item" {
        category = String()
        full_text = String()
        short_text = String()
        blog_title = String()
        avatar = String()
        full_name = String()
        url = String()
        created_at = String()
    }
}

func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {

    if elementName == "item" {
        let blogListItem = DetailAndRenderBlogObject()

        blogListItem.categoryUrl = category
        blogListItem.text = full_text
        blogListItem.shortDescription = short_text
        blogListItem.title = blog_title
        blogListItem.authorImg = avatar
        blogListItem.author = full_name
        blogListItem.articleUrl = url
        blogListItem.date = created_at

        print("WRITING DATA")

        blogList.append(blogListItem)
    }
}

func parser(_ parser: XMLParser, foundCharacters string: String) {

    //print("ENAME: " + eName)
    switch eName {
    case "category":
        print("writing category: " + string)
        category = string;
    case "full_text":
        full_text = string;
    case "short_text":
        short_text = string;
    case "title":
        title = string;
    case "thumbnail":
        avatar = string;
    case "avatar":
        avatar = string;
    case "full_name":
        full_name = string;
    case "url":
        url = string;
    case "created_at":
        created_at = string;
    default:
        ()
    }
}

func parserDidEndDocument(_ parser: XMLParser) {
}

func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
    print(parseError)
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

func repairLink(_ link : String) -> String { //sometimes links looks like https://asdasd.comhttps://asdasd.com/...

    let newLink = link.suffix(from: link.index(link.startIndex, offsetBy: 1))

    if let range = newLink.range(of: "http") {
        let substring = newLink[range.lowerBound...]
        return String(substring)
    }
    return link
}

func formatDate(_ date : String) -> String {  //date    String    "2018-01-22 08:59:43"
    if let dividerIndex = date.index(of: " ") {
        return String(date[..<dividerIndex])
    }

    return date
}


}

2 个答案:

答案 0 :(得分:3)

重新加载整个表是一项非常密集的操作。更好的方法可能是使用tableView.insertRows(at:with:)方法。一个例子就是 -

func didFinishLoadingData(newBlogs: [DetailAndRenderBlogObject]) {
    tableView.beginUpdates()

    var indexPaths: [IndexPath] = []

    for row in (blogList.count..<(blogList.count + newBlogs.count)) {
        indexPaths.append(IndexPath(row: row, section: 0))
    }

    blogList.append(contentsOf: newBlogs)

    tableView.insertRows(at: indexPaths, with: .fade)
    tableView.endUpdates()
}

答案 1 :(得分:0)

经过长时间搜索,我遇到了同样的问题,并找到了另一个解决方案。

似乎,每当您调用附加到endRefreshing()滚动视图的UIRefreshControl的{​​{1}}函数时,它都会停止滚动视图的减速过程。

因此,如果获取下一页的代码正在调用UITableView函数(完全没有原因),并且用户开始滚动并从设备屏幕上抬起手指(滚动视图正在减速) ),从而停止滚动。

解决方案很简单,在代码中替换以下行:

endRefreshing()

包含以下几行:

self.refreshControl.endRefreshing()

我什至创建了一个方便的扩展程序来为我做检查,以便在需要时调用它:

if self.refreshControl.isRefreshing {
    self.refreshControl.endRefreshing()
}