Vertical Scrolling in iOS Not Smooth

How to enable smooth scrolling on mobile iOs?

Unfortunately Smooth Scrolling is not possible in Safari on OSX and iOS natively. But there are some JavaScript polyfills for that. For example: https://github.com/iamdustan/smoothscroll
You have to initialize it with JavaScript, not with CSS.

UITableView with autolayout not smooth when scrolling

All my optimizations made it almost 100% fluid on a 6s device but on a 5s, it's really not smooth. I even don't wanna test on a 4s device! I reached the autolayout performance limit when using multiple multiline labels.

After a deep analysis of the Time Profiler, the result is that dynamic labels height (3 in my case) with constraint between them, and those label have attributed text (used to set the line height, but this is not the bottleneck), it seems like the lag is caused by the UIView::layoutSubviews which renders the labels, update the constraints, etc... This is why what when I don't change the label text, everything is smooth. The only solution here is not to use autolayout and layout de subviews programmatically in layoutSubviews method of a custom UITableViewCell subclass.

For those wondering how to do this, I achieved to make a 100% smooth scroll without autolayout and multiple lables with dynamic height (multiline). Here is my UITableView subclass (I use a base class because I 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 the 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 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()
}

}

On the viewDidLoad of my ViewController, i pre-calculate all the 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!

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

Set the row height for the 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 the cell in cellForRowAtIndexPath (I have multiple cell types in my tableView, so there's a couple of checks to 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()

}
}

iOS overflow-x (or position absolute) makes scrolling choppy?

You could try setting width:100% on .page-wrapper and set that to overflow:hidden and position:relative. That might prevent the horizontal scroll.

Updated 10/12/2012

Thanks for the code example. It really helped me see your intent and the issue with scrolling more clearly. It looks like you need -webkit-overflow-scrolling. Add -webkit-overflow-scrolling: touch; to page-wrapper. Here's an [updated test page] with that rule applied. You can compare with test page in my comment below to see the difference.

Mobile vertical scrolling issues (sloppy/laggy) when I touch a horizontal scrolling div

You probably just need to add scroll-behavior: smooth; to the parent with the side scroll.

Not scrolling smooth on UICollectionViewController for large data loading

Do not reload collectionView in cellForItem, delete this part:

    DispatchQueue.main.async {

self.collectionView?.reloadData()

}

And delete most of those reloadData calls that you have there. It's a heavyweight operation - call it only when you really need to reload it - meaning when the data model changes for the whole collection view.

Sidenote:

Don't use indexPath.row, but rather indexPath.item, row is used for UITableViews, not for UICollectionViews.

scrolling not smooth in ios phonegap

add " -webkit-overflow-scrolling: touch;"
but there is some bugs with it



Related Topics



Leave a reply



Submit