限制tableView中显示的单元格数量,滚动到最后一个单元格时加载更多单元格

时间:2017-12-13 17:05:34

标签: ios json swift uitableview

我正在尝试设置一个只显示特定数量单元格的表格视图。一旦显示该单元格,用户就可以继续滚动以显示更多单元格。截至目前,我正在检索要在viewDidLoad中显示的所有JSON数据并将它们存储在数组中。仅仅作为示例目的,我试图仅首先显示2个单元格,一个用户滚动到屏幕底部,下一个单元格将出现。到目前为止,这是我的代码:

class DrinkViewController: UIViewController {

  @IBOutlet weak var drinkTableView: UITableView!


  private let networkManager = NetworkManager.sharedManager

  fileprivate var totalDrinksArray: [CocktailModel] = []
  fileprivate var drinkImage: UIImage?

  fileprivate let DRINK_CELL_REUSE_IDENTIFIER = "drinkCell"
  fileprivate let DRINK_SEGUE = "detailDrinkSegue"

  var drinksPerPage = 2
  var loadingData = false

  override func viewDidLoad() {
    super.viewDidLoad()

    drinkTableView.delegate = self
    drinkTableView.dataSource = self

    networkManager.getJSONData(function: urlFunction.search, catagory: urlCatagory.cocktail, listCatagory: nil, drinkType: "margarita", isList: false, completion: { data in

      self.parseJSONData(data)

    })
  }

}

extension DrinkViewController {

  //MARK: JSON parser
  fileprivate func parseJSONData(_ jsonData: Data?){
    if let data = jsonData {

      do {
        let jsonDictionary = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String : AnyObject]//Parses data into a dictionary

        // print(jsonDictionary!)
        if let drinkDictionary = jsonDictionary!["drinks"] as? [[String: Any]] {
          for drink in drinkDictionary {
            let drinkName = drink["strDrink"] as? String ?? ""
            let catagory = drink["strCategory"] as? String
            let drinkTypeIBA = drink["strIBA"] as? String
            let alcoholicType = drink["strAlcoholic"] as? String
            let glassType = drink["strGlass"] as? String
            let drinkInstructions = drink["strInstructions"] as? String
            let drinkThumbnailUrl = drink["strDrinkThumb"] as? String

            let cocktailDrink = CocktailModel(drinkName: drinkName, catagory: catagory, drinkTypeIBA: drinkTypeIBA, alcoholicType: alcoholicType, glassType: glassType, drinkInstructions: drinkInstructions, drinkThumbnailUrl: drinkThumbnailUrl)

            self.totalDrinksArray.append(cocktailDrink)
          }
        }
      } catch let error as NSError {
        print("Error: \(error.localizedDescription)")

      }
    }

    DispatchQueue.main.async {
      self.drinkTableView.reloadData()
    }
  }


  //MARK: Image Downloader
  func updateImage (imageUrl: String, onSucceed: @escaping () -> Void, onFailure: @escaping (_ error:NSError)-> Void){
    //named imageData because this is the data to be used to get image, can be named anything
    networkManager.downloadImage(imageUrl: imageUrl, onSucceed: { (imageData) in
      if let image = UIImage(data: imageData) {
       self.drinkImage = image
      }
      onSucceed()//must call completion handler
    }) { (error) in
      onFailure(error)
    }
  }
}


//MARK: Tableview Delegates
extension DrinkViewController: UITableViewDelegate, UITableViewDataSource {

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

    //return numberOfRows
    return drinksPerPage
  }

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = drinkTableView.dequeueReusableCell(withIdentifier: DRINK_CELL_REUSE_IDENTIFIER) as! DrinkCell


    //get image from separate url
    if let image = totalDrinksArray[indexPath.row].drinkThumbnailUrl{//index out of range error here
      updateImage(imageUrl: image, onSucceed: {
        if let currentImage = self.drinkImage{
          DispatchQueue.main.async {
            cell.drinkImage.image = currentImage
          }
        }
      }, onFailure: { (error) in
        print(error)
      })
    }
    cell.drinkLabel.text = totalDrinksArray[indexPath.row].drinkName
    return cell
  }

  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    if let image = totalDrinksArray[indexPath.row].drinkThumbnailUrl{
      updateImage(imageUrl: image, onSucceed: {
      }, onFailure: { (error) in
        print(error)
      })
    }
    performSegue(withIdentifier: DRINK_SEGUE, sender: indexPath.row)
  }


  func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    let lastElement = drinksPerPage
      if indexPath.row == lastElement {
         self.drinkTableView.reloadData()
      }
  }
}

我看到了这篇文章:tableview-loading-more-cell-when-scroll-to-bottom并实现了willDisplay函数,但是我发现了“索引超出范围”错误。

1 个答案:

答案 0 :(得分:0)

如果您一次性获得所有结果,可以告诉我为什么要这样做,那么您不必限制显示,因为它是由tableview自动管理的。在tableview中,所有单元格都被重用,因此不存在内存问题。 UITableViewCell将在显示时创建。

因此无需限制细胞计数。

我现在不知道你在代码中做了什么,但是:

  func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
     let lastElement = drinksPerPage // no need to write this line
     if indexPath.row == lastElement { // if block will never be executed since indexPath.row is never equal to drinksPerPage.
     // As indexPath starts from zero, So its value will never be 2.
        self.drinkTableView.reloadData()
  }
}

您的应用可能会崩溃,因为您可能只从服务器获得一项。

如果你真的想加载更多,那么你可以试试这段代码:

声明 numberOfItem ,它应该等于 drinksPerPage

   var numberOfItem = drinksPerPage

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

     //return numberOfRows
     return numberOfItem
   }

   func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
     if indexPath.row == numberOfItem - 1 { 
        if self.totalDrinksArray.count > numberOfItem {
           let result = self.totalDrinksArray.count - numberOfItem
           if result > drinksPerPage {
              numberOfItem = numberOfItem + drinksPerPage
           }
           else {
              numberOfItem = result
           }   
           self.drinkTableView.reloadData()
        }
     }
   }