SlideShare a Scribd company logo
1 of 30
Download to read offline
Andrea Prearo
Master Software Engineer - iOS @ Capital One SF
https://github.com/andrea-prearo
https://medium.com/@andrea.prearo
https://twitter.com/andrea_prearo
Smooth scrolling in
UITableView and
UICollectionView
Apple SDK Components to display
Scrollable Data
UITableView
iOS 2.0+
An instance of UITableView (or simply, a table view) is a means for displaying and editing hierarchical lists of information.
UICollectionView
iOS 6.0+
The UICollectionView class manages an ordered collection of data items and presents them using customizable layouts.
Collection views provide the same general function as table views except that a collection view is able to support more
than just single-column layouts. Collection views support customizable layouts that can be used to implement multi-
column grids, tiled layouts, circular layouts, and many more. You can even change the layout of a collection view
dynamically if you want.
Scrolling and User Experience
Table views and collection views are both designed to support
displaying sets of data that can be scrolled. However, when
displaying a very large amount of data, it could be very tricky to
achieve a perfectly smooth scrolling. This is not ideal because it
negatively affects the user experience.
Strategies to achieve Smooth
Scrolling: UITableView and
UICollectionView
Example: Display a set of users
Cells Rendering is a Critical Task
UITableViewCell lifecycle
1. Request the cell: tableView(_:cellForRowAt:)
2. Display the cell: tableView(_:willDisplay:forRowAt:)
3. Remove the cell: tableView(_:didEndDisplaying:forRowAt:)
Basic cell rendering
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
// Configure the cell ...
return cell
}
User Model
enum Role: String {
case unknown = "Unknown"
case user = "User"
case owner = "Owner"
case admin = "Admin"
static func get(from: String) -> Role {
if from == user.rawValue {
return .user
} else if from == owner.rawValue {
return .owner
} else if from == admin.rawValue {
return .admin
}
return .unknown
}
}
struct User {
let avatarUrl: String
let username: String
let role: Role
init(avatarUrl: String, username: String, role: Role) {
self.avatarUrl = avatarUrl
self.username = username
self.role = role
}
}
User View Model (MVVM)
struct UserViewModel {
let avatarUrl: String
let username: String
let role: Role
let roleText: String
init(user: User) {
// Avatar
avatarUrl = user.avatarUrl
// Username
username = user.username
// Role
role = user.role
roleText = user.role.rawValue
}
}
Fetch Data Asynchronously and Cache
View Models
• Avoid blocking the main thread while fetching data
• Update the table view right after we retrieve the data
User View Model Controller: Wrap and
Cache View Model
class UserViewModelController {
fileprivate var viewModels: [UserViewModel?] = []
[...]
var viewModelsCount: Int {
return viewModels.count
}
func viewModel(at index: Int) -> UserViewModel? {
guard index >= 0 && index < viewModelsCount else { return nil }
return viewModels[index]
}
}
User View Model Controller:
Asynchronous Data Fetch
func retrieveUsers(_ completionBlock: @escaping (_ success: Bool, _ error: NSError?) -> ()) {
let urlString = ... // Users Web Service URL
let session = URLSession.shared
guard let url = URL(string: urlString) else {
completionBlock(false, nil)
return
}
let task = session.dataTask(with: url) { [weak self] (data, response, error) in
guard let strongSelf = self else { return }
guard let data = data else {
completionBlock(false, error as NSError?)
return
}
let error = ... // Define a NSError for failed parsing
if let jsonData = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [[String: AnyObject]] {
guard let jsonData = jsonData else {
completionBlock(false, error)
return
}
var users = [User?]()
for json in jsonData {
if let user = UserViewModelController.parse(json) {
users.append(user)
}
}
strongSelf.viewModels = UserViewModelController.initViewModels(users)
completionBlock(true, nil)
} else {
completionBlock(false, error)
}
}
task.resume()
}
User View Model Controller Extension:
Parse JSON
private extension UserViewModelController {
static func parse(_ json: [String: AnyObject]) -> User? {
let avatarUrl = json["avatar"] as? String ?? ""
let username = json["username"] as? String ?? ""
let role = json["role"] as? String ?? ""
return User(avatarUrl: avatarUrl, username: username, role: Role.get(from: role))
}
static func initViewModels(_ users: [User?]) -> [UserViewModel?] {
return users.map { user in
if let user = user {
return UserViewModel(user: user)
} else {
return nil
}
}
}
}
Scenarios for Fetching Data
• Only the when loading the table view the first time, by
placing it in viewDidLoad()
• Every time the table view is displayed, by placing it in
viewWillAppear(_:)
• On user demand (for instance via a pull-down-to-refresh), by
placing it in the method call that will take care of refreshing
the data
Load Images Asynchronously and Cache
Them
Extend UIImage and Leverage URLSession
extension UIImage {
static func downloadImageFromUrl(_ url: String, completionHandler: @escaping (UIImage?) -> Void) {
guard let url = URL(string: url) else {
completionHandler(nil)
return
}
let task: URLSessionDataTask = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) -> Void in
guard let httpURLResponse = response as? HTTPURLResponse , httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data , error == nil,
let image = UIImage(data: data) else {
completionHandler(nil)
return
}
completionHandler(image)
})
task.resume()
}
}
Open Source Libraries for Asynchronous
Image Downloading and Caching
• SDWebImage
• AlamofireImage
Customize the Cell
Subclass the Default Cell
class UserCell: UITableViewCell {
@IBOutlet weak var avatar: UIImageView!
@IBOutlet weak var username: UILabel!
@IBOutlet weak var role: UILabel!
func configure(_ viewModel: UserViewModel) {
UIImage.downloadImageFromUrl(viewModel.avatarUrl) { [weak self] (image) in
guard let strongSelf = self,
let image = image else {
return
}
strongSelf.avatar.image = image
}
username.text = viewModel.username
role.text = viewModel.roleText
}
}
Use Opaque Layers and Avoid Gradients
class UserCell: UITableViewCell {
@IBOutlet weak var avatar: UIImageView!
@IBOutlet weak var username: UILabel!
@IBOutlet weak var role: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
setOpaqueBackground()
[...]
}
}
private extension UserCell {
static let DefaultBackgroundColor = UIColor.groupTableViewBackgroundColor
func setOpaqueBackground() {
alpha = 1.0
backgroundColor = UserCell.DefaultBackgroundColor
avatar.alpha = 1.0
avatar.backgroundColor = UserCell.DefaultBackgroundColor
}
}
Putting Everything Together: Optimized
Cell Rendering
Optimized Cell Rendering
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "UserCell", for: indexPath) as! UserCell
if let viewModel = userViewModelController.viewModel(at: (indexPath as NSIndexPath).row) {
cell.configure(viewModel)
}
return cell
}
Cell Rendering should now be really fast
• We are using the cached View Model data
• We are fetching the images asynchronously
Strategies to achieve Smooth
Scrolling: UITableView only
Use Self-Sizing Cells for Cells of Variable
Height
Initialize estimatedRowHeight and rowHeight
override func viewDidLoad() {
[...]
tableView.estimatedRowHeight = ... // Estimated default row height
tableView.rowHeight = UITableViewAutomaticDimension
[...]
}
Variable Height Cells with no support for
Self-Sizing (iOS7)
• Pre-calculate all the row heights at once
• Return the cached value when
tableView(_:heightForRowAt:) is called
Strategies to achieve Smooth
Scrolling: UICollectionView only
Calculate your Cell Size
Implement collectionView(_:layout:sizeForItemAt:)
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize
// Calculate the appropriate cell size
return CGSize(width: ..., height: ...)
}
Handle Size Classes and Orientation
Changes
Implement viewWillTransition(to:with:)
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
collectionView?.collectionViewLayout.invalidateLayout()
}
Refresh Collection View Layout when
• Transitioning to a different Size Class
• Rotating the device
Dynamically Adjust Cell Layout
Override apply(_:)
override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
super.apply(layoutAttributes)
// Customize the cell layout
[...]
}
Example: Adjust multi-line UILabel
Maximum Width using
preferredMaxLayoutWidth
override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
super.apply(layoutAttributes)
// Customize the cell layout
let width = CGRectGetWidth(layoutAttributes.frame)
username.preferredMaxLayoutWidth = width - 16
}
Code samples
All code is available on GitHub:
SmoothScrolling

More Related Content

What's hot

MariaDB 10.11 key features overview for DBAs
MariaDB 10.11 key features overview for DBAsMariaDB 10.11 key features overview for DBAs
MariaDB 10.11 key features overview for DBAsFederico Razzoli
 
Implementation of EAV pattern for ActiveRecord models
Implementation of EAV pattern for ActiveRecord modelsImplementation of EAV pattern for ActiveRecord models
Implementation of EAV pattern for ActiveRecord modelsKostyantyn Stepanyuk
 
MySQL Slow Query log Monitoring using Beats & ELK
MySQL Slow Query log Monitoring using Beats & ELKMySQL Slow Query log Monitoring using Beats & ELK
MySQL Slow Query log Monitoring using Beats & ELKYoungHeon (Roy) Kim
 
스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해beom kyun choi
 
How to Take Advantage of Optimizer Improvements in MySQL 8.0
How to Take Advantage of Optimizer Improvements in MySQL 8.0How to Take Advantage of Optimizer Improvements in MySQL 8.0
How to Take Advantage of Optimizer Improvements in MySQL 8.0Norvald Ryeng
 
ElasticSearch at berlinbuzzwords 2010
ElasticSearch at berlinbuzzwords 2010ElasticSearch at berlinbuzzwords 2010
ElasticSearch at berlinbuzzwords 2010Elasticsearch
 
MySQL8.0_performance_schema.pptx
MySQL8.0_performance_schema.pptxMySQL8.0_performance_schema.pptx
MySQL8.0_performance_schema.pptxNeoClova
 
Dependency injection in CakePHP
Dependency injection in CakePHPDependency injection in CakePHP
Dependency injection in CakePHPmarkstory
 
MySQL InnoDB Cluster 소개
MySQL InnoDB Cluster 소개MySQL InnoDB Cluster 소개
MySQL InnoDB Cluster 소개rockplace
 
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patternsJava 8-streams-collectors-patterns
Java 8-streams-collectors-patternsJosé Paumard
 
MySQL 8.0 EXPLAIN ANALYZE
MySQL 8.0 EXPLAIN ANALYZEMySQL 8.0 EXPLAIN ANALYZE
MySQL 8.0 EXPLAIN ANALYZENorvald Ryeng
 
Document Management: Opendocman and LAMP installation on Cent OS
Document Management: Opendocman and LAMP installation on Cent OSDocument Management: Opendocman and LAMP installation on Cent OS
Document Management: Opendocman and LAMP installation on Cent OSSiddharth Ram Dinesh
 
[LetSwift 2023] 객체지향-함수형 아키텍처 직접 만들기
[LetSwift 2023] 객체지향-함수형 아키텍처 직접 만들기[LetSwift 2023] 객체지향-함수형 아키텍처 직접 만들기
[LetSwift 2023] 객체지향-함수형 아키텍처 직접 만들기Moonbeom KWON
 
HBase replication
HBase replicationHBase replication
HBase replicationwchevreuil
 
Intro to git and git hub
Intro to git and git hubIntro to git and git hub
Intro to git and git hubJasleenSondhi
 
Disaster Recovery with MySQL InnoDB ClusterSet - What is it and how do I use it?
Disaster Recovery with MySQL InnoDB ClusterSet - What is it and how do I use it?Disaster Recovery with MySQL InnoDB ClusterSet - What is it and how do I use it?
Disaster Recovery with MySQL InnoDB ClusterSet - What is it and how do I use it?Miguel Araújo
 
Proxysql sharding
Proxysql shardingProxysql sharding
Proxysql shardingMarco Tusa
 

What's hot (20)

MariaDB 10.11 key features overview for DBAs
MariaDB 10.11 key features overview for DBAsMariaDB 10.11 key features overview for DBAs
MariaDB 10.11 key features overview for DBAs
 
HBase RITs
HBase RITsHBase RITs
HBase RITs
 
Mvpvm pattern
Mvpvm patternMvpvm pattern
Mvpvm pattern
 
Implementation of EAV pattern for ActiveRecord models
Implementation of EAV pattern for ActiveRecord modelsImplementation of EAV pattern for ActiveRecord models
Implementation of EAV pattern for ActiveRecord models
 
MySQL Slow Query log Monitoring using Beats & ELK
MySQL Slow Query log Monitoring using Beats & ELKMySQL Slow Query log Monitoring using Beats & ELK
MySQL Slow Query log Monitoring using Beats & ELK
 
스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해
 
How to Take Advantage of Optimizer Improvements in MySQL 8.0
How to Take Advantage of Optimizer Improvements in MySQL 8.0How to Take Advantage of Optimizer Improvements in MySQL 8.0
How to Take Advantage of Optimizer Improvements in MySQL 8.0
 
ElasticSearch at berlinbuzzwords 2010
ElasticSearch at berlinbuzzwords 2010ElasticSearch at berlinbuzzwords 2010
ElasticSearch at berlinbuzzwords 2010
 
MySQL8.0_performance_schema.pptx
MySQL8.0_performance_schema.pptxMySQL8.0_performance_schema.pptx
MySQL8.0_performance_schema.pptx
 
Dependency injection in CakePHP
Dependency injection in CakePHPDependency injection in CakePHP
Dependency injection in CakePHP
 
MySQL InnoDB Cluster 소개
MySQL InnoDB Cluster 소개MySQL InnoDB Cluster 소개
MySQL InnoDB Cluster 소개
 
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patternsJava 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
 
MySQL 8.0 EXPLAIN ANALYZE
MySQL 8.0 EXPLAIN ANALYZEMySQL 8.0 EXPLAIN ANALYZE
MySQL 8.0 EXPLAIN ANALYZE
 
Document Management: Opendocman and LAMP installation on Cent OS
Document Management: Opendocman and LAMP installation on Cent OSDocument Management: Opendocman and LAMP installation on Cent OS
Document Management: Opendocman and LAMP installation on Cent OS
 
[LetSwift 2023] 객체지향-함수형 아키텍처 직접 만들기
[LetSwift 2023] 객체지향-함수형 아키텍처 직접 만들기[LetSwift 2023] 객체지향-함수형 아키텍처 직접 만들기
[LetSwift 2023] 객체지향-함수형 아키텍처 직접 만들기
 
MVVM & RxSwift
MVVM & RxSwiftMVVM & RxSwift
MVVM & RxSwift
 
HBase replication
HBase replicationHBase replication
HBase replication
 
Intro to git and git hub
Intro to git and git hubIntro to git and git hub
Intro to git and git hub
 
Disaster Recovery with MySQL InnoDB ClusterSet - What is it and how do I use it?
Disaster Recovery with MySQL InnoDB ClusterSet - What is it and how do I use it?Disaster Recovery with MySQL InnoDB ClusterSet - What is it and how do I use it?
Disaster Recovery with MySQL InnoDB ClusterSet - What is it and how do I use it?
 
Proxysql sharding
Proxysql shardingProxysql sharding
Proxysql sharding
 

Similar to Smooth Scrolling in UITableView and UICollectionView

Swift Tableview iOS App Development
Swift Tableview iOS App DevelopmentSwift Tableview iOS App Development
Swift Tableview iOS App DevelopmentKetan Raval
 
Using a model view-view model architecture for iOS apps
Using a model view-view model architecture for iOS appsUsing a model view-view model architecture for iOS apps
Using a model view-view model architecture for iOS appsallanh0526
 
Swift Delhi: Practical POP
Swift Delhi: Practical POPSwift Delhi: Practical POP
Swift Delhi: Practical POPNatasha Murashev
 
Say bye to Fragments with Conductor & Kotlin
Say bye to Fragments with Conductor & KotlinSay bye to Fragments with Conductor & Kotlin
Say bye to Fragments with Conductor & KotlinMiquel Beltran Febrer
 
How to become an Android dev starting from iOS (and vice versa)
How to become an Android dev starting from iOS (and vice versa)How to become an Android dev starting from iOS (and vice versa)
How to become an Android dev starting from iOS (and vice versa)Giuseppe Filograno
 
Connect.Tech- Swift Memory Management
Connect.Tech- Swift Memory ManagementConnect.Tech- Swift Memory Management
Connect.Tech- Swift Memory Managementstable|kernel
 
ThingMaker in Swift
ThingMaker in SwiftThingMaker in Swift
ThingMaker in Swiftallanh0526
 
Practialpop 160510130818
Practialpop 160510130818Practialpop 160510130818
Practialpop 160510130818Shahzain Saeed
 
MCE^3 - Natasha Murashev - Practical Protocol-Oriented Programming in Swift
MCE^3 - Natasha Murashev - Practical Protocol-Oriented Programming in SwiftMCE^3 - Natasha Murashev - Practical Protocol-Oriented Programming in Swift
MCE^3 - Natasha Murashev - Practical Protocol-Oriented Programming in SwiftPROIDEA
 
Building Modern Apps using Android Architecture Components
Building Modern Apps using Android Architecture ComponentsBuilding Modern Apps using Android Architecture Components
Building Modern Apps using Android Architecture ComponentsHassan Abid
 
Day 8: Dealing with Lists and ListViews
Day 8: Dealing with Lists and ListViewsDay 8: Dealing with Lists and ListViews
Day 8: Dealing with Lists and ListViewsAhsanul Karim
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejsNick Lee
 
Migrating Objective-C to Swift
Migrating Objective-C to SwiftMigrating Objective-C to Swift
Migrating Objective-C to SwiftElmar Kretzer
 
iOS Development For Android Developers
iOS Development For Android DevelopersiOS Development For Android Developers
iOS Development For Android DevelopersDarryl Bayliss
 
Android Jetpack: ViewModel and Testing
Android Jetpack: ViewModel and TestingAndroid Jetpack: ViewModel and Testing
Android Jetpack: ViewModel and TestingYongjun Kim
 
13 advanced-swing
13 advanced-swing13 advanced-swing
13 advanced-swingNataraj Dg
 

Similar to Smooth Scrolling in UITableView and UICollectionView (20)

Swift Tableview iOS App Development
Swift Tableview iOS App DevelopmentSwift Tableview iOS App Development
Swift Tableview iOS App Development
 
Using a model view-view model architecture for iOS apps
Using a model view-view model architecture for iOS appsUsing a model view-view model architecture for iOS apps
Using a model view-view model architecture for iOS apps
 
201104 iphone navigation-based apps
201104 iphone navigation-based apps201104 iphone navigation-based apps
201104 iphone navigation-based apps
 
Swift Delhi: Practical POP
Swift Delhi: Practical POPSwift Delhi: Practical POP
Swift Delhi: Practical POP
 
Say bye to Fragments with Conductor & Kotlin
Say bye to Fragments with Conductor & KotlinSay bye to Fragments with Conductor & Kotlin
Say bye to Fragments with Conductor & Kotlin
 
Backbone.js
Backbone.jsBackbone.js
Backbone.js
 
How to become an Android dev starting from iOS (and vice versa)
How to become an Android dev starting from iOS (and vice versa)How to become an Android dev starting from iOS (and vice versa)
How to become an Android dev starting from iOS (and vice versa)
 
Connect.Tech- Swift Memory Management
Connect.Tech- Swift Memory ManagementConnect.Tech- Swift Memory Management
Connect.Tech- Swift Memory Management
 
Data binding w Androidzie
Data binding w AndroidzieData binding w Androidzie
Data binding w Androidzie
 
ThingMaker in Swift
ThingMaker in SwiftThingMaker in Swift
ThingMaker in Swift
 
Practialpop 160510130818
Practialpop 160510130818Practialpop 160510130818
Practialpop 160510130818
 
MCE^3 - Natasha Murashev - Practical Protocol-Oriented Programming in Swift
MCE^3 - Natasha Murashev - Practical Protocol-Oriented Programming in SwiftMCE^3 - Natasha Murashev - Practical Protocol-Oriented Programming in Swift
MCE^3 - Natasha Murashev - Practical Protocol-Oriented Programming in Swift
 
IOS APPs Revision
IOS APPs RevisionIOS APPs Revision
IOS APPs Revision
 
Building Modern Apps using Android Architecture Components
Building Modern Apps using Android Architecture ComponentsBuilding Modern Apps using Android Architecture Components
Building Modern Apps using Android Architecture Components
 
Day 8: Dealing with Lists and ListViews
Day 8: Dealing with Lists and ListViewsDay 8: Dealing with Lists and ListViews
Day 8: Dealing with Lists and ListViews
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejs
 
Migrating Objective-C to Swift
Migrating Objective-C to SwiftMigrating Objective-C to Swift
Migrating Objective-C to Swift
 
iOS Development For Android Developers
iOS Development For Android DevelopersiOS Development For Android Developers
iOS Development For Android Developers
 
Android Jetpack: ViewModel and Testing
Android Jetpack: ViewModel and TestingAndroid Jetpack: ViewModel and Testing
Android Jetpack: ViewModel and Testing
 
13 advanced-swing
13 advanced-swing13 advanced-swing
13 advanced-swing
 

Recently uploaded

The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...ICS
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdfWave PLM
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsAlberto González Trastoy
 
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️anilsa9823
 
Active Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdfActive Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdfCionsystems
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...MyIntelliSource, Inc.
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...OnePlan Solutions
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Modelsaagamshah0812
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Steffen Staab
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...gurkirankumar98700
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AIABDERRAOUF MEHENNI
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comFatema Valibhai
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerThousandEyes
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsArshad QA
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providermohitmore19
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVshikhaohhpro
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsAndolasoft Inc
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantAxelRicardoTrocheRiq
 

Recently uploaded (20)

The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
 
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
 
Active Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdfActive Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdf
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
 
Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.js
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service Consultant
 

Smooth Scrolling in UITableView and UICollectionView

  • 1. Andrea Prearo Master Software Engineer - iOS @ Capital One SF https://github.com/andrea-prearo https://medium.com/@andrea.prearo https://twitter.com/andrea_prearo
  • 2. Smooth scrolling in UITableView and UICollectionView
  • 3. Apple SDK Components to display Scrollable Data UITableView iOS 2.0+ An instance of UITableView (or simply, a table view) is a means for displaying and editing hierarchical lists of information. UICollectionView iOS 6.0+ The UICollectionView class manages an ordered collection of data items and presents them using customizable layouts. Collection views provide the same general function as table views except that a collection view is able to support more than just single-column layouts. Collection views support customizable layouts that can be used to implement multi- column grids, tiled layouts, circular layouts, and many more. You can even change the layout of a collection view dynamically if you want.
  • 4. Scrolling and User Experience Table views and collection views are both designed to support displaying sets of data that can be scrolled. However, when displaying a very large amount of data, it could be very tricky to achieve a perfectly smooth scrolling. This is not ideal because it negatively affects the user experience.
  • 5. Strategies to achieve Smooth Scrolling: UITableView and UICollectionView Example: Display a set of users
  • 6. Cells Rendering is a Critical Task UITableViewCell lifecycle 1. Request the cell: tableView(_:cellForRowAt:) 2. Display the cell: tableView(_:willDisplay:forRowAt:) 3. Remove the cell: tableView(_:didEndDisplaying:forRowAt:)
  • 7. Basic cell rendering override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // Table view cells are reused and should be dequeued using a cell identifier. let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) // Configure the cell ... return cell }
  • 8. User Model enum Role: String { case unknown = "Unknown" case user = "User" case owner = "Owner" case admin = "Admin" static func get(from: String) -> Role { if from == user.rawValue { return .user } else if from == owner.rawValue { return .owner } else if from == admin.rawValue { return .admin } return .unknown } } struct User { let avatarUrl: String let username: String let role: Role init(avatarUrl: String, username: String, role: Role) { self.avatarUrl = avatarUrl self.username = username self.role = role } }
  • 9. User View Model (MVVM) struct UserViewModel { let avatarUrl: String let username: String let role: Role let roleText: String init(user: User) { // Avatar avatarUrl = user.avatarUrl // Username username = user.username // Role role = user.role roleText = user.role.rawValue } }
  • 10. Fetch Data Asynchronously and Cache View Models • Avoid blocking the main thread while fetching data • Update the table view right after we retrieve the data
  • 11. User View Model Controller: Wrap and Cache View Model class UserViewModelController { fileprivate var viewModels: [UserViewModel?] = [] [...] var viewModelsCount: Int { return viewModels.count } func viewModel(at index: Int) -> UserViewModel? { guard index >= 0 && index < viewModelsCount else { return nil } return viewModels[index] } }
  • 12. User View Model Controller: Asynchronous Data Fetch func retrieveUsers(_ completionBlock: @escaping (_ success: Bool, _ error: NSError?) -> ()) { let urlString = ... // Users Web Service URL let session = URLSession.shared guard let url = URL(string: urlString) else { completionBlock(false, nil) return } let task = session.dataTask(with: url) { [weak self] (data, response, error) in guard let strongSelf = self else { return } guard let data = data else { completionBlock(false, error as NSError?) return } let error = ... // Define a NSError for failed parsing if let jsonData = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [[String: AnyObject]] { guard let jsonData = jsonData else { completionBlock(false, error) return } var users = [User?]() for json in jsonData { if let user = UserViewModelController.parse(json) { users.append(user) } } strongSelf.viewModels = UserViewModelController.initViewModels(users) completionBlock(true, nil) } else { completionBlock(false, error) } } task.resume() }
  • 13. User View Model Controller Extension: Parse JSON private extension UserViewModelController { static func parse(_ json: [String: AnyObject]) -> User? { let avatarUrl = json["avatar"] as? String ?? "" let username = json["username"] as? String ?? "" let role = json["role"] as? String ?? "" return User(avatarUrl: avatarUrl, username: username, role: Role.get(from: role)) } static func initViewModels(_ users: [User?]) -> [UserViewModel?] { return users.map { user in if let user = user { return UserViewModel(user: user) } else { return nil } } } }
  • 14. Scenarios for Fetching Data • Only the when loading the table view the first time, by placing it in viewDidLoad() • Every time the table view is displayed, by placing it in viewWillAppear(_:) • On user demand (for instance via a pull-down-to-refresh), by placing it in the method call that will take care of refreshing the data
  • 15. Load Images Asynchronously and Cache Them Extend UIImage and Leverage URLSession extension UIImage { static func downloadImageFromUrl(_ url: String, completionHandler: @escaping (UIImage?) -> Void) { guard let url = URL(string: url) else { completionHandler(nil) return } let task: URLSessionDataTask = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) -> Void in guard let httpURLResponse = response as? HTTPURLResponse , httpURLResponse.statusCode == 200, let mimeType = response?.mimeType, mimeType.hasPrefix("image"), let data = data , error == nil, let image = UIImage(data: data) else { completionHandler(nil) return } completionHandler(image) }) task.resume() } }
  • 16. Open Source Libraries for Asynchronous Image Downloading and Caching • SDWebImage • AlamofireImage
  • 17. Customize the Cell Subclass the Default Cell class UserCell: UITableViewCell { @IBOutlet weak var avatar: UIImageView! @IBOutlet weak var username: UILabel! @IBOutlet weak var role: UILabel! func configure(_ viewModel: UserViewModel) { UIImage.downloadImageFromUrl(viewModel.avatarUrl) { [weak self] (image) in guard let strongSelf = self, let image = image else { return } strongSelf.avatar.image = image } username.text = viewModel.username role.text = viewModel.roleText } }
  • 18. Use Opaque Layers and Avoid Gradients class UserCell: UITableViewCell { @IBOutlet weak var avatar: UIImageView! @IBOutlet weak var username: UILabel! @IBOutlet weak var role: UILabel! override func awakeFromNib() { super.awakeFromNib() setOpaqueBackground() [...] } } private extension UserCell { static let DefaultBackgroundColor = UIColor.groupTableViewBackgroundColor func setOpaqueBackground() { alpha = 1.0 backgroundColor = UserCell.DefaultBackgroundColor avatar.alpha = 1.0 avatar.backgroundColor = UserCell.DefaultBackgroundColor } }
  • 19. Putting Everything Together: Optimized Cell Rendering Optimized Cell Rendering override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "UserCell", for: indexPath) as! UserCell if let viewModel = userViewModelController.viewModel(at: (indexPath as NSIndexPath).row) { cell.configure(viewModel) } return cell }
  • 20. Cell Rendering should now be really fast • We are using the cached View Model data • We are fetching the images asynchronously
  • 21. Strategies to achieve Smooth Scrolling: UITableView only
  • 22. Use Self-Sizing Cells for Cells of Variable Height Initialize estimatedRowHeight and rowHeight override func viewDidLoad() { [...] tableView.estimatedRowHeight = ... // Estimated default row height tableView.rowHeight = UITableViewAutomaticDimension [...] }
  • 23. Variable Height Cells with no support for Self-Sizing (iOS7) • Pre-calculate all the row heights at once • Return the cached value when tableView(_:heightForRowAt:) is called
  • 24. Strategies to achieve Smooth Scrolling: UICollectionView only
  • 25. Calculate your Cell Size Implement collectionView(_:layout:sizeForItemAt:) func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize // Calculate the appropriate cell size return CGSize(width: ..., height: ...) }
  • 26. Handle Size Classes and Orientation Changes Implement viewWillTransition(to:with:) override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) collectionView?.collectionViewLayout.invalidateLayout() }
  • 27. Refresh Collection View Layout when • Transitioning to a different Size Class • Rotating the device
  • 28. Dynamically Adjust Cell Layout Override apply(_:) override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) { super.apply(layoutAttributes) // Customize the cell layout [...] }
  • 29. Example: Adjust multi-line UILabel Maximum Width using preferredMaxLayoutWidth override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) { super.apply(layoutAttributes) // Customize the cell layout let width = CGRectGetWidth(layoutAttributes.frame) username.preferredMaxLayoutWidth = width - 16 }
  • 30. Code samples All code is available on GitHub: SmoothScrolling