2016-07-15 2 views
2

Я использую файлы XIB для создания ячеек в своем UITableView. Я также использую dequeue mecanism, например: let cell = tableView.dequeueReusableCellWithIdentifier("articleCell", forIndexPath: indexPath) as! ArticleTableViewCell. Я предварительно вычисляю всю высоту строки в viewDidLoad моего ViewController, поэтому метод func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat мгновенно возвращает правильное значение. Все это работает.UITableView с автоопределением не является гладким при прокрутке

В моем UITableViewCell я использую много динамических меток высоты (lines = 0). Компоновка такова:

enter image description here

Я не использую прозрачный фон, все мои подвиды являются непрозрачными с определенным цветом фона. Я проверил с Color Blended Layers (все зеленые) и Color Misaligned Images (ничего не желтого).

Вот мой метод cellForRowAtIndexPath:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
    let tableViewSection = tableViewSectionAtIndex(indexPath.section) 
    let tableViewRow = tableViewSection.rows.objectAtIndex(indexPath.row) as! TableViewRow 

    switch tableViewRow.type! { 

    case TableViewRowType.Article : 
     let article = tableViewRow.article! 

     if article.type == TypeArticle.Article { 

      let cell = tableView.dequeueReusableCellWithIdentifier("articleCell", forIndexPath: indexPath) as! ArticleTableViewCell 
      return cell 

     } else { 

      let cell = tableView.dequeueReusableCellWithIdentifier("chroniqueCell", forIndexPath: indexPath) as! ChroniqueTableViewCell 
      return cell 

     } 

    default: 
     return UITableViewCell() 

    } 
} 

И затем, в методе willDisplayCell:

func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { 
    let tableViewSection = tableViewSectionAtIndex(indexPath.section) 
    let tableViewRow = tableViewSection.rows.objectAtIndex(indexPath.row) as! TableViewRow 
    let article = tableViewRow.article! 

    if cell.isKindOfClass(ArticleTableViewCell) { 
     let cell = cell as! ArticleTableViewCell 
     cell.delegate = self 
     cell.article = article 

     if let imageView = articleImageCache[article.id] { 
      cell.articleImage.image = imageView 
      cell.shareControl.image = imageView 
     } else { 
      loadArticleImage(article, articleCell: cell) 
     } 
    } else { 
     let cell = cell as! ChroniqueTableViewCell 
     cell.delegate = self 
     cell.article = article 

     if let chroniqueur = article.getChroniqueur() { 
      if let imageView = chroniqueurImageCache[chroniqueur.id] { 
       cell.chroniqueurImage.image = imageView 
      } else { 
       loadChroniqueurImage(article, articleCell: cell) 
      } 
     } 
    } 
} 

все мои изображения загружаются в фоновом потоке, так что нет загрузки изображения при прокрутке ,

Компоновка модифицируется в моем ArticleTableViewCell, когда я поставил "статью" свойство: cell.article = article:

var article: Article? { 
    didSet { 
     updateUI() 
    } 
} 

И моя updateUI функция:

func updateUI() -> Void { 

    if let article = article { 
     if let surtitre = article.surtitre { 
      self.surtitre.text = surtitre.uppercaseString 
      self.surtitre.setLineHeight(3) 
     } else { 
      self.surtitre.hidden = true 
     } 

     self.titre.text = article.titre 
     self.titre.setLineHeight(3) 

     if let amorce = article.amorce { 
      self.amorce.text = amorce 
      self.amorce.setLineHeight(3) 
     } else { 
      self.amorce.hidden = true 
     } 

     if let section = article.sectionSource { 
      if section.couleurFoncee != "" { 
       self.bordureSection.backgroundColor = UIColor(hexString: section.couleurFoncee) 
       self.surtitre.textColor = UIColor(hexString: section.couleurFoncee) 
      } 
     } 
    } 
} 

Проблема при установке текста метки, это вызывает отставание. Метод setLineHeight преобразует текст метки в NSAttributedString, чтобы указать высоту строки, но даже при удалении этого кода и просто установке текстовой метки происходит небольшое отставание при отображении новой ячейки.

Если я удалю весь код установки ярлыка, ячейки отображают текст метки по умолчанию, а прокрутка таблицы просматривается совершенно гладко, а высоты тоже верны. Всякий раз, когда я устанавливаю текст ярлыка, происходит задержка.

Я запускаю приложение на своем iPhone 6s. На симуляторе 6s абсолютно нет лаги вообще, совершенно гладкая.

Любые идеи? Может быть, это потому, что я использую UIStackView для встраивания своих ярлыков? Я сделал это, потому что легче скрыть ярлыки, когда они пустые, поэтому другие элементы перемещаются вверх, чтобы не было места, где находится пустая метка.

Я пробовал много вещей, но не могу заставить табличное представление прокручивать плавно, любая помощь будет оценена.

+0

Спасибо за подсказку. Я спрашиваю, потому что, возможно, у кого-то была такая же проблема, это очень помогло бы мне. Я все равно попытаюсь выяснить, что у Инструменты. – Tiois

+0

Кстати, отставание не в секундах ... Но вы можете видеть, что прокрутка не является жидкостью перед тем, как появится следующая табличная ячейка, медленно прокручиваясь. Инструменты Hope помогут мне разобраться, инструмент, который я еще не использовал. Какой инструмент в Инструменте я должен использовать? – Tiois

+0

Я сделал некоторую оптимизацию. Я изменил размеры изображений, чтобы UIImage имел тот же размер, что и UIImageView. Я загружал временный файл-заполнитель UIImage в свой UIImageView в методе prepareForReuse, поэтому, если у ячейки нет изображения для отображения, он отобразит это местозаполнитель. Это изображение загрузилось с «диска» и вызвало небольшую задержку. Теперь я использую тот же экземпляр UIImage, чтобы этого избежать. Теперь единственная проблема заключается в том, что, когда новая ячейка создается из NIB (никакая ячейка, подлежащая удалению), существует небольшая задержка, которой нет, когда есть достаточная ячейка для удаления. Любой способ исправить это? – Tiois

ответ

2

Все мои оптимизации сделали его почти 100% текучей средой на устройстве 6s, но на 5s, это действительно негласно. Я даже не хочу тестировать устройство 4s! Я достиг предела производительности автовыключения при использовании нескольких многострочных меток.

После глубокого анализа Time Profiler результатом является то, что динамическая высота меток (3 в моем случае) с ограничением между ними и меткой присвоили текст (используется для установки высоты строки, но это не узкое место), похоже, что отставание вызвано UIView :: layoutSubviews, которое отображает метки, обновляет ограничения и т. д. Вот почему, когда я не изменяю текст ярлыка, все гладко.Единственное решение здесь заключается не в том, чтобы программно использовать метод autolayout и layout de subview в методе layoutSubviews пользовательского подкласса UITableViewCell.

Для тех, кто задавался вопросом, как это сделать, я добился создания гладкой прокрутки на 100% без автовыключения и нескольких таблиц с динамической высотой (многострочный). Вот мой UITableView подкласс (я использую базовый класс, потому что у меня есть 2 одинаковых типов клеток):

// 
// ArticleTableViewCell.swift 
// 

import UIKit 

class ArticleTableViewCell: BaseArticleTableViewCell { 

    var articleImage = UIImageView() 
    var surtitre = UILabel() 
    var titre = UILabel() 
    var amorce = UILabel() 
    var bordureTop = UIView() 
    var bordureLeft = UIView() 

    var articleImageWidth = CGFloat(0) 
    var articleImageHeight = CGFloat(0) 

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) { 
     super.init(style: style, reuseIdentifier: reuseIdentifier) 

     self.articleImage.clipsToBounds = true 
     self.articleImage.contentMode = UIViewContentMode.ScaleAspectFill 

     self.bordureTop.backgroundColor = UIColor(colorLiteralRed: 219/255, green: 219/255, blue: 219/255, alpha: 1.0) 

     self.bordureLeft.backgroundColor = UIColor.blackColor() 

     self.surtitre.numberOfLines = 0 
     self.surtitre.font = UIFont(name: "Graphik-Bold", size: 11) 
     self.surtitre.textColor = UIColor.blackColor() 
     self.surtitre.backgroundColor = self.contentView.backgroundColor 

     self.titre.numberOfLines = 0 
     self.titre.font = UIFont(name: "PublicoHeadline-Extrabold", size: 22) 
     self.titre.textColor = UIColor(colorLiteralRed: 26/255, green: 26/255, blue: 26/255, alpha: 1.0) 
     self.titre.backgroundColor = self.contentView.backgroundColor 

     self.amorce.numberOfLines = 0 
     self.amorce.font = UIFont(name: "Graphik-Regular", size: 12) 
     self.amorce.textColor = UIColor.blackColor() 
     self.amorce.backgroundColor = self.contentView.backgroundColor 

     self.contentView.addSubview(articleImage) 
     self.contentView.addSubview(surtitre) 
     self.contentView.addSubview(titre) 
     self.contentView.addSubview(amorce) 
     self.contentView.addSubview(bordureTop) 
     self.contentView.addSubview(bordureLeft) 
    } 

    required init?(coder aDecoder: NSCoder) { 
     fatalError("init(coder:) has not been implemented") 
    } 

    override func layoutSubviews() { 

     super.layoutSubviews() 

     if let article = article { 

      var currentY = CGFloat(0) 
      let labelX = CGFloat(18) 
      let labelWidth = fullWidth - 48 

      // Taille de l'image avec un ratio de 372/243 
      articleImageWidth = ceil(fullWidth - 3) 
      articleImageHeight = ceil((articleImageWidth * 243)/372) 

      self.bordureTop.frame = CGRect(x: 3, y: 0, width: fullWidth - 3, height: 1) 

      // Image 
      if article.imagePrincipale == nil { 
       self.articleImage.frame = CGRect(x: 0, y: 0, width: 0, height: 0) 

       self.bordureTop.hidden = false 
      } else { 
       self.articleImage.frame = CGRect(x: 3, y: 0, width: self.articleImageWidth, height: self.articleImageHeight) 
       self.bordureTop.hidden = true 
       currentY += self.articleImageHeight 
      } 

      // Padding top 
      currentY += 15 

      // Surtitre 
      if let surtitre = article.surtitre { 
       self.surtitre.frame = CGRect(x: labelX, y: currentY, width: labelWidth, height: 0) 
       self.surtitre.preferredMaxLayoutWidth = self.surtitre.frame.width 
       self.surtitre.setTextWithLineHeight(surtitre.uppercaseString, lineHeight: 3) 
       self.surtitre.sizeToFit() 

       currentY += self.surtitre.frame.height 
       currentY += 15 
      } else { 
       self.surtitre.frame = CGRect(x: 0, y: 0, width: 0, height: 0) 
      } 

      // Titre 
      self.titre.frame = CGRect(x: labelX, y: currentY, width: labelWidth, height: 0) 
      self.titre.preferredMaxLayoutWidth = self.titre.frame.width 
      self.titre.setTextWithLineHeight(article.titre, lineHeight: 3) 
      self.titre.sizeToFit() 

      currentY += self.titre.frame.height 

      // Amorce 
      if let amorce = article.amorce { 
       currentY += 15 

       self.amorce.frame = CGRect(x: labelX, y: currentY, width: labelWidth, height: 0) 
       self.amorce.preferredMaxLayoutWidth = self.amorce.frame.width 
       self.amorce.setTextWithLineHeight(amorce, lineHeight: 3) 
       self.amorce.sizeToFit() 

       currentY += self.amorce.frame.height 
      } else { 
       self.amorce.frame = CGRect(x: 0, y: 0, width: 0, height: 0) 
      } 

      // Boutons 
      currentY += 9 

      self.updateButtonsPosition(currentY) 
      self.layoutUpdatedAt(currentY) 
      currentY += self.favorisButton.frame.height 

      // Padding bottom 
      currentY += 15 

      // Couleurs 
      self.bordureLeft.frame = CGRect(x: 0, y: 0, width: 3, height: currentY - 2) 
      if let section = article.sectionSource { 
       if let couleurFoncee = section.couleurFoncee { 
        self.bordureLeft.backgroundColor = couleurFoncee 
        self.surtitre.textColor = couleurFoncee 
       } 
      } 

      // Mettre à jour le frame du contentView avec la bonne hauteur totale 
      var frame = self.contentView.frame 
      frame.size.height = currentY 
      self.contentView.frame = frame 
     } 

    } 

} 

И базовый класс:

// 
// BaseArticleTableViewCell.swift 
// 

import UIKit 

class BaseArticleTableViewCell: UITableViewCell { 

    var backgroundThread: NSURLSessionDataTask? 
    var delegate: SectionViewController? 

    var favorisButton: FavorisButton! 
    var shareButton: ShareButton! 

    var updatedAt: UILabel! 

    var fullWidth = CGFloat(0) 

    var article: Article? { 
     didSet { 
      // Update du UI quand on set l'article 
      updateArticle() 
     } 
    } 

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) { 
     super.init(style: style, reuseIdentifier: reuseIdentifier) 

     self.selectionStyle = UITableViewCellSelectionStyle.None 
     self.contentView.backgroundColor = UIColor(colorLiteralRed: 248/255, green: 248/255, blue: 248/255, alpha: 1.0) 

     // Largeur de la cellule, qui est toujours plein écran dans notre cas 
     // self.contentView.frame.width ne donne pas la bonne valeur tant que le tableView n'a pas été layouté 
     fullWidth = UIScreen.mainScreen().bounds.width 

     self.favorisButton = FavorisButton(frame: CGRect(x: fullWidth - 40, y: 0, width: 28, height: 30)) 
     self.shareButton = ShareButton(frame: CGRect(x: fullWidth - 73, y: 0, width: 28, height: 30)) 

     self.updatedAt = UILabel(frame: CGRect(x: 18, y: 0, width: 0, height: 0)) 
     self.updatedAt.font = UIFont(name: "Graphik-Regular", size: 10) 
     self.updatedAt.textColor = UIColor(colorLiteralRed: 138/255, green: 138/255, blue: 138/255, alpha: 1.0) 
     self.updatedAt.backgroundColor = self.contentView.backgroundColor 

     self.addSubview(self.favorisButton) 
     self.addSubview(self.shareButton) 
     self.addSubview(self.updatedAt) 
    } 

    required init?(coder aDecoder: NSCoder) { 
     fatalError("init(coder:) has not been implemented") 
    } 

    // Avant qu'une cell soit réutilisée, faire un cleanup 
    override func prepareForReuse() { 
     super.prepareForReuse() 

     // Canceller un background thread si y'en a un actif 
     if let backgroundThread = self.backgroundThread { 
      backgroundThread.cancel() 
      self.backgroundThread = nil 
     } 

     resetUI() 
    } 

    // Updater le UI 
    func updateArticle() { 
     self.favorisButton.article = article 
     self.shareButton.article = article 

     if let delegate = self.delegate { 
      self.shareButton.delegate = delegate 
     } 
    } 

    // Faire un reset du UI avant de réutiliser une instance de Cell 
    func resetUI() { 

    } 

    // Mettre à jour la position des boutons 
    func updateButtonsPosition(currentY: CGFloat) { 

     // Déjà positionnés en X, width, height, reste le Y 
     var shareFrame = self.shareButton.frame 
     shareFrame.origin.y = currentY 
     self.shareButton.frame = shareFrame 

     var favorisFrame = self.favorisButton.frame 
     favorisFrame.origin.y = currentY + 1 
     self.favorisButton.frame = favorisFrame 
    } 

    // Mettre à jour la position du updatedAt et son texte 
    func layoutUpdatedAt(currentY: CGFloat) { 
     var frame = self.updatedAt.frame 
     frame.origin.y = currentY + 15 
     self.updatedAt.frame = frame 

     if let updatedAt = article?.updatedAtListe { 
      self.updatedAt.text = updatedAt 
     } else { 
      self.updatedAt.text = "" 
     } 

     self.updatedAt.sizeToFit() 
    } 

} 

На viewDidLoad моего ViewController, я заранее рассчитать все высота строки:

// Créer une cache des row height des articles 
func calculRowHeight() { 
    self.articleRowHeights = [Int: CGFloat]() 

    // Utiliser une seule instance de chaque type de cell 
    let articleCell = tableView.dequeueReusableCellWithIdentifier("articleCell") as! BaseArticleTableViewCell 
    let chroniqueCell = tableView.dequeueReusableCellWithIdentifier("chroniqueCell") as! BaseArticleTableViewCell 

    var cell: BaseArticleTableViewCell! 

    for articleObj in section.articles { 
     let article = articleObj as! Article 

     // Utiliser le bon type de cell 
     if article.type == TypeArticle.Article { 
      cell = articleCell 
     } else { 
      cell = chroniqueCell 
     } 

     // Setter l'article et refaire le layout 
     cell.article = article 
     cell.layoutSubviews() 

     // Prendre la hauteur générée 
     self.articleRowHeights[article.id] = cell.contentView.frame.height 
    } 
} 

Установите высоту строки для запрашиваемой ячейки:

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { 
    let tableViewSection = tableViewSectionAtIndex(indexPath.section) 
    let tableViewRow = tableViewSection.rows.objectAtIndex(indexPath.row) as! TableViewRow 

    switch tableViewRow.type! { 

    case TableViewRowType.Article : 
     let article = tableViewRow.article! 
     return self.articleRowHeights[article.id]! 

    default: 
     return UITableViewAutomaticDimension 
    } 
} 

Верните клетки в cellForRowAtIndexPath (у меня есть несколько типов клеток в моем Tableview, так что есть несколько проверок, чтобы сделать):

// Cellule pour un section/row spécifique 
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
    let tableViewSection = tableViewSectionAtIndex(indexPath.section) 
    let tableViewRow = tableViewSection.rows.objectAtIndex(indexPath.row) as! TableViewRow 

    switch tableViewRow.type! { 

    case TableViewRowType.Article : 
     let article = tableViewRow.article! 

     if article.type == TypeArticle.Article { 
      let cell = tableView.dequeueReusableCellWithIdentifier("articleCell", forIndexPath: indexPath) as! ArticleTableViewCell 
      cell.delegate = self 
      cell.article = article 

      if let imageView = articleImageCache[article.id] { 
       cell.articleImage.image = imageView 
       cell.shareButton.image = imageView 
      } else { 
       cell.articleImage.image = placeholder 
       loadArticleImage(article, articleCell: cell) 
      } 

      return cell 

     } 

     return UITableViewCell() 
    default: 
     return UITableViewCell() 

    } 
} 
+0

Привет, У меня есть аналогичная проблема с вашей, в том, что прокрутка не является гладкой даже при автозапуске, загрузке и сжатии изображений. Но если вы предварительно рассчитаете все ячейки, и если есть много ячеек (например, тысячи), не будет ли начальная загрузка таблицы очень медленной? – dickyj

+0

В этом случае я бы предварительно вычислил что-то вроде 10 следующих ячеек текущих отображаемых ячеек. – Tiois

+0

Но разве это не повлияет на положение вертикальной полосы прокрутки после вычисления следующих 10 ячеек? Я имею в виду, что это может привести к рывкам снова? – dickyj

Смежные вопросы