ios - UITableView with autolayout not smooth when scrolling -


i'm using xib files design cells in uitableview. use dequeue mecanism, instance : let cell = tableview.dequeuereusablecellwithidentifier("articlecell", forindexpath: indexpath) as! articletableviewcell. precalculate row height in viewdidload of viewcontroller method func tableview(tableview: uitableview, heightforrowatindexpath indexpath: nsindexpath) -> cgfloat returns instantly right value. of works.

in uitableviewcell, i'm using many dynamic height labels (lines = 0). layout :

enter image description here

i don't use transparent background, subviews opaque specified background color. checked color blended layers (everything green) , color misaligned images (nothing yellow).

here cellforrowatindexpath method :

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()      } } 

and then, in willdisplaycell method :

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)             }         }     } } 

all images downloaded in background thread, there no image loading when scrolling.

the layout modified in articletableviewcell when set "article" property : cell.article = article :

var article: article? {     didset {         updateui()     } } 

and updateui function :

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)             }         }     } } 

the problem when setting label text, causes lag. setlineheight method transforms text of label nsattributedstring specify line height, when removing code , setting text label, small lag occurs when displaying new cell.

if remove label setup code, cells displays default label text , tableview scroll smooth, , heights correct too. whenever set label text, lag occurs.

i'm running app on iphone 6s. on 6s simulator, there's absolutely no lag @ all, smooth.

any idead? maybe it's because i'm using uistackview embed labels? did because it's easier hide labels when empty, other elements moved up, avoid having spacing empty label is.

i tried many thing can't tableview scroll smoothly, appreciated.

all optimizations made 100% fluid on 6s device on 5s, it's not smooth. don't wanna test on 4s device! reached autolayout performance limit when using multiple multiline labels.

after deep analysis of time profiler, result dynamic labels height (3 in case) constraint between them, , label have attributed text (used set line height, not bottleneck), seems lag caused uiview::layoutsubviews renders labels, update constraints, etc... why when don't change label text, smooth. solution here not use autolayout , layout de subviews programmatically in layoutsubviews method of custom uitableviewcell subclass.

for wondering how this, achieved make 100% smooth scroll without autolayout , multiple lables dynamic height (multiline). here uitableview subclass (i use base class because have 2 similar cell types) :

// //  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         }      }  } 

and base class :

// //  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 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()     }  } 

on viewdidload of viewcontroller, pre-calculate row height :

// 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!      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     } } 

set row height requested cell :

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     } } 

return cell in cellforrowatindexpath (i have multiple cell types in tableview, there's couple of checks do) :

// 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()      } } 

Comments

Popular posts from this blog

javascript - Slick Slider width recalculation -

jsf - PrimeFaces Datatable - What is f:facet actually doing? -

angular2 services - Angular 2 RC 4 Http post not firing -