├── .gitignore ├── .travis.yml ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── UITableView+Cache.podspec ├── UITableView+Cache.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── UITableView+Cache.xcscheme ├── UITableView+Cache ├── Info.plist ├── UITableView+Cache.h └── module.modulemap └── src ├── UITableView+Cache.h └── UITableView+Cache.m /.gitignore: -------------------------------------------------------------------------------- 1 | ######################### 2 | # .gitignore file for Xcode4 and Xcode5 Source projects 3 | # 4 | # Apple bugs, waiting for Apple to fix/respond: 5 | # 6 | # 15564624 - what does the xccheckout file in Xcode5 do? Where's the documentation? 7 | # 8 | # Version 2.6 9 | # For latest version, see: http://stackoverflow.com/questions/49478/git-ignore-file-for-xcode-projects 10 | # 11 | # 2015 updates: 12 | # - Fixed typo in "xccheckout" line - thanks to @lyck for pointing it out! 13 | # - Fixed the .idea optional ignore. Thanks to @hashier for pointing this out 14 | # - Finally added "xccheckout" to the ignore. Apple still refuses to answer support requests about this, but in practice it seems you should ignore it. 15 | # - minor tweaks from Jona and Coeur (slightly more precise xc* filtering/names) 16 | # 2014 updates: 17 | # - appended non-standard items DISABLED by default (uncomment if you use those tools) 18 | # - removed the edit that an SO.com moderator made without bothering to ask me 19 | # - researched CocoaPods .lock more carefully, thanks to Gokhan Celiker 20 | # 2013 updates: 21 | # - fixed the broken "save personal Schemes" 22 | # - added line-by-line explanations for EVERYTHING (some were missing) 23 | # 24 | # NB: if you are storing "built" products, this WILL NOT WORK, 25 | # and you should use a different .gitignore (or none at all) 26 | # This file is for SOURCE projects, where there are many extra 27 | # files that we want to exclude 28 | # 29 | ######################### 30 | 31 | ##### 32 | # OS X temporary files that should never be committed 33 | # 34 | # c.f. http://www.westwind.com/reference/os-x/invisibles.html 35 | 36 | .DS_Store 37 | 38 | # c.f. http://www.westwind.com/reference/os-x/invisibles.html 39 | 40 | .Trashes 41 | 42 | # c.f. http://www.westwind.com/reference/os-x/invisibles.html 43 | 44 | *.swp 45 | 46 | # 47 | # *.lock - this is used and abused by many editors for many different things. 48 | # For the main ones I use (e.g. Eclipse), it should be excluded 49 | # from source-control, but YMMV. 50 | # (lock files are usually local-only file-synchronization on the local FS that should NOT go in git) 51 | # c.f. the "OPTIONAL" section at bottom though, for tool-specific variations! 52 | # 53 | # In particular, if you're using CocoaPods, you'll want to comment-out this line: 54 | #*.lock 55 | 56 | 57 | # 58 | # profile - REMOVED temporarily (on double-checking, I can't find it in OS X docs?) 59 | #profile 60 | 61 | 62 | #### 63 | # Xcode temporary files that should never be committed 64 | # 65 | # NB: NIB/XIB files still exist even on Storyboard projects, so we want this... 66 | 67 | *~.nib 68 | 69 | 70 | #### 71 | # Xcode build files - 72 | # 73 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "DerivedData" 74 | 75 | DerivedData/ 76 | 77 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "build" 78 | 79 | build/ 80 | 81 | 82 | ##### 83 | # Xcode private settings (window sizes, bookmarks, breakpoints, custom executables, smart groups) 84 | # 85 | # This is complicated: 86 | # 87 | # SOMETIMES you need to put this file in version control. 88 | # Apple designed it poorly - if you use "custom executables", they are 89 | # saved in this file. 90 | # 99% of projects do NOT use those, so they do NOT want to version control this file. 91 | # ..but if you're in the 1%, comment out the line "*.pbxuser" 92 | 93 | # .pbxuser: http://lists.apple.com/archives/xcode-users/2004/Jan/msg00193.html 94 | 95 | *.pbxuser 96 | 97 | # .mode1v3: http://lists.apple.com/archives/xcode-users/2007/Oct/msg00465.html 98 | 99 | *.mode1v3 100 | 101 | # .mode2v3: http://lists.apple.com/archives/xcode-users/2007/Oct/msg00465.html 102 | 103 | *.mode2v3 104 | 105 | # .perspectivev3: http://stackoverflow.com/questions/5223297/xcode-projects-what-is-a-perspectivev3-file 106 | 107 | *.perspectivev3 108 | 109 | # NB: also, whitelist the default ones, some projects need to use these 110 | !default.pbxuser 111 | !default.mode1v3 112 | !default.mode2v3 113 | !default.perspectivev3 114 | 115 | 116 | #### 117 | # Xcode 4 - semi-personal settings 118 | # 119 | # Apple Shared data that Apple put in the wrong folder 120 | # c.f. http://stackoverflow.com/a/19260712/153422 121 | # FROM ANSWER: Apple says "don't ignore it" 122 | # FROM COMMENTS: Apple is wrong; Apple code is too buggy to trust; there are no known negative side-effects to ignoring Apple's unofficial advice and instead doing the thing that actively fixes bugs in Xcode 123 | # Up to you, but ... current advice: ignore it. 124 | *.xccheckout 125 | 126 | # 127 | # 128 | # OPTION 1: --------------------------------- 129 | # throw away ALL personal settings (including custom schemes! 130 | # - unless they are "shared") 131 | # As per build/ and DerivedData/, this ought to have a trailing slash 132 | # 133 | # NB: this is exclusive with OPTION 2 below 134 | xcuserdata/ 135 | 136 | # OPTION 2: --------------------------------- 137 | # get rid of ALL personal settings, but KEEP SOME OF THEM 138 | # - NB: you must manually uncomment the bits you want to keep 139 | # 140 | # NB: this *requires* git v1.8.2 or above; you may need to upgrade to latest OS X, 141 | # or manually install git over the top of the OS X version 142 | # NB: this is exclusive with OPTION 1 above 143 | # 144 | #xcuserdata/**/* 145 | 146 | # (requires option 2 above): Personal Schemes 147 | # 148 | #!xcuserdata/**/xcschemes/* 149 | 150 | #### 151 | # XCode 4 workspaces - more detailed 152 | # 153 | # Workspaces are important! They are a core feature of Xcode - don't exclude them :) 154 | # 155 | # Workspace layout is quite spammy. For reference: 156 | # 157 | # /(root)/ 158 | # /(project-name).xcodeproj/ 159 | # project.pbxproj 160 | # /project.xcworkspace/ 161 | # contents.xcworkspacedata 162 | # /xcuserdata/ 163 | # /(your name)/xcuserdatad/ 164 | # UserInterfaceState.xcuserstate 165 | # /xcshareddata/ 166 | # /xcschemes/ 167 | # (shared scheme name).xcscheme 168 | # /xcuserdata/ 169 | # /(your name)/xcuserdatad/ 170 | # (private scheme).xcscheme 171 | # xcschememanagement.plist 172 | # 173 | # 174 | 175 | #### 176 | # Xcode 4 - Deprecated classes 177 | # 178 | # Allegedly, if you manually "deprecate" your classes, they get moved here. 179 | # 180 | # We're using source-control, so this is a "feature" that we do not want! 181 | 182 | *.moved-aside 183 | 184 | #### 185 | # OPTIONAL: Some well-known tools that people use side-by-side with Xcode / iOS development 186 | # 187 | # NB: I'd rather not include these here, but gitignore's design is weak and doesn't allow 188 | # modular gitignore: you have to put EVERYTHING in one file. 189 | # 190 | # COCOAPODS: 191 | # 192 | # c.f. http://guides.cocoapods.org/using/using-cocoapods.html#what-is-a-podfilelock 193 | # c.f. http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 194 | # 195 | !Podfile.lock 196 | Pods/ 197 | 198 | # Carthage - A simple, decentralized dependency manager for Cocoa 199 | Carthage/ 200 | 201 | # 202 | # RUBY: 203 | # 204 | # c.f. http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/ 205 | # 206 | #!Gemfile.lock 207 | # 208 | # IDEA: 209 | # 210 | # c.f. https://www.jetbrains.com/objc/help/managing-projects-under-version-control.html?search=workspace.xml 211 | # 212 | #.idea/workspace.xml 213 | # 214 | # TEXTMATE: 215 | # 216 | # -- UNVERIFIED: c.f. http://stackoverflow.com/a/50283/153422 217 | # 218 | #tm_build_errors 219 | 220 | #### 221 | # UNKNOWN: recommended by others, but I can't discover what these files are 222 | .idea/* 223 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode7.3 3 | rvm: 2.2.5 4 | 5 | cache: 6 | - bundler 7 | 8 | before_install: 9 | - bundle install 10 | - bundle exec pod repo update --silent 11 | 12 | install: true 13 | 14 | script: 15 | - bundle exec pod lib lint --allow-warnings 16 | - xctool -project UITableView+Cache.xcodeproj -scheme 'UITableView+Cache' -sdk iphonesimulator build 17 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | source 'http://rubygems.org' 3 | gem 'cocoapods', '1.0.1' 4 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | CFPropertyList (2.3.5) 5 | activesupport (5.0.1) 6 | concurrent-ruby (~> 1.0, >= 1.0.2) 7 | i18n (~> 0.7) 8 | minitest (~> 5.1) 9 | tzinfo (~> 1.1) 10 | claide (1.0.1) 11 | cocoapods (1.0.1) 12 | activesupport (>= 4.0.2) 13 | claide (>= 1.0.0, < 2.0) 14 | cocoapods-core (= 1.0.1) 15 | cocoapods-deintegrate (>= 1.0.0, < 2.0) 16 | cocoapods-downloader (>= 1.0.0, < 2.0) 17 | cocoapods-plugins (>= 1.0.0, < 2.0) 18 | cocoapods-search (>= 1.0.0, < 2.0) 19 | cocoapods-stats (>= 1.0.0, < 2.0) 20 | cocoapods-trunk (>= 1.0.0, < 2.0) 21 | cocoapods-try (>= 1.0.0, < 2.0) 22 | colored (~> 1.2) 23 | escape (~> 0.0.4) 24 | fourflusher (~> 0.3.0) 25 | molinillo (~> 0.4.5) 26 | nap (~> 1.0) 27 | xcodeproj (>= 1.1.0, < 2.0) 28 | cocoapods-core (1.0.1) 29 | activesupport (>= 4.0.2) 30 | fuzzy_match (~> 2.0.4) 31 | nap (~> 1.0) 32 | cocoapods-deintegrate (1.0.1) 33 | cocoapods-downloader (1.1.3) 34 | cocoapods-plugins (1.0.0) 35 | nap 36 | cocoapods-search (1.0.0) 37 | cocoapods-stats (1.0.0) 38 | cocoapods-trunk (1.1.2) 39 | nap (>= 0.8, < 2.0) 40 | netrc (= 0.7.8) 41 | cocoapods-try (1.1.0) 42 | colored (1.2) 43 | concurrent-ruby (1.0.4) 44 | escape (0.0.4) 45 | fourflusher (0.3.2) 46 | fuzzy_match (2.0.4) 47 | i18n (0.8.0) 48 | minitest (5.10.1) 49 | molinillo (0.4.5) 50 | nanaimo (0.2.3) 51 | nap (1.1.0) 52 | netrc (0.7.8) 53 | thread_safe (0.3.5) 54 | tzinfo (1.2.2) 55 | thread_safe (~> 0.1) 56 | xcodeproj (1.4.2) 57 | CFPropertyList (~> 2.3.3) 58 | activesupport (>= 3) 59 | claide (>= 1.0.1, < 2.0) 60 | colored (~> 1.2) 61 | nanaimo (~> 0.2.3) 62 | 63 | PLATFORMS 64 | ruby 65 | 66 | DEPENDENCIES 67 | cocoapods (= 1.0.1) 68 | 69 | BUNDLED WITH 70 | 1.13.6 71 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Maxim Gubin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/Kilograpp/UITableView-Cache.svg?branch=master)](https://travis-ci.org/Kilograpp/UITableView-Cache) 2 | [![Pod Version](https://img.shields.io/cocoapods/v/UITableView+Cache.svg?style=flat)](http://cocoadocs.org/docsets/UITableView+Cache/) 3 | [![codebeat badge](https://codebeat.co/badges/1c7930d9-7431-49ff-989c-f906779f00bc?t=)](https://codebeat.co/projects/github-com-kilograpp-uitableview-cache) 4 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Kilograpp/UITableView-Cache) 5 | 6 | # UITableView + Cache 7 | [https://github.com/Kilograpp/UITableView-Cache](https://github.com/Kilograpp/UITableView-Cache) 8 | 9 | UITableView cell cache that cures scroll-lags on a cell instantiating. 10 | 11 | ## Introduction 12 | 13 | `UITableView+Cache` is a light `UITableView` category that purges scroll-lag that occurs on a new cell instantiation during scroll. 14 | It makes UITableView instantiate and cache cells before cellForRow:atIndexPath: call. It also provides simple interface very similar to existing registerClass/registerNib one. 15 | 16 | ## Installation 17 | 18 | ### CocoaPods 19 | 20 | ``` batch 21 | pod 'UITableView+Cache' 22 | ``` 23 | 24 | ### Carthage 25 | 26 | ``` batch 27 | github "Kilograpp/UITableView-Cache" "master" 28 | ``` 29 | 30 | ### Manual importing 31 | 32 | Just add all files from src directory to your project. 33 | 34 | 35 | ## Usage 36 | 37 | When registering custom cells, call overriden registerClass/registerNib method instead of a default. `UITableView+Cache` swizzles dequeueReusableCellWithIdentifier methods with a private one that uses its own cache and implements registerClass/registerNib mechanism on itself. 38 | When dequeueReusableCellWithIdentifier is called and returns nil - the cache is asked for a cell. If cache is empty then cell is created using registered class or nib. 39 | 40 | #### Swift 3 41 | 42 | ``` swift 43 | import UITableView_Cache 44 | 45 | ... 46 | 47 | override func viewDidLoad() { 48 | super.viewDidLoad() 49 | 50 | self.tableView.registerClass(TableViewCodeCell.self, forCellReuseIdentifier: "MyReuseIdentifier", cacheSize: 10) 51 | self.tableView.registerNib(TableViewCodeCell.nib, forCellReuseIdentifier: "MyReuseIdentifier", cacheSize: 10) 52 | } 53 | 54 | ... 55 | 56 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 57 | let cell = self.tableView.dequeueReusableCellWithIdentifier("MyReuseIdentifier") as! TableViewCodeCell 58 | return cell 59 | } 60 | ``` 61 | 62 | #### Objective-C 63 | 64 | ``` objective-c 65 | - (void)viewDidLoad { 66 | [super viewDidLoad]; 67 | 68 | [self.tableView registerClass:[TableViewCodeCell class] forCellReuseIdentifier:@"MyReuseIdentifier" cacheSize:10]; 69 | [self.tableView registerNib:[TableViewNibCell nib] forCellReuseIdentifier:@"MyNibReuseIdentifier" cacheSize:10]; 70 | } 71 | 72 | ... 73 | 74 | - (MyCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 75 | MyCell* cell = [tableView dequeueReusableCellWithIdentifier:@"MyReuseIdentifier"]; 76 | return cell; 77 | } 78 | ``` 79 | 80 | Make sure to call dequeueReusableCellWithIdentifier:reuseIdentifier method and **NOT** dequeueReusableCellWithIdentifier:reuseIdentifier:**forIndexPath:** one. They perform different logic and a crash will occure on wrong method use. 81 | 82 | #### Storyboards 83 | 84 | Storyboards are not supported, cached cells should be registered from code. 85 | Nevertheless, if you strongly require storyboard usage then you could swizzle basic UITableView's registerNib/registerClass methods for your own and call. But I won't recommend this solution for it tampers UITableView mechanism. 86 | 87 | ## Best Practises 88 | 89 | As you know any cache consumes some memory. Best advice I could give is to keep track of your tableViews and free them as soon as possible. Having too many tableViews may cause memory pressure on your app. 90 | 91 | ## License 92 | 93 | UITableView+Cache is available under the MIT license. See the LICENSE file for more info. 94 | 95 | ## Author 96 | 97 | [Mehdzor](https://github.com/mehdzor) 98 | -------------------------------------------------------------------------------- /UITableView+Cache.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "UITableView+Cache" 3 | s.version = "0.2.2" 4 | s.summary = "The open source UITableView cache category." 5 | s.homepage = "https://github.com/Kilograpp/UITableView-Cache.git" 6 | s.author = { "Mehdzor" => "maxim@kilograpp.com" } 7 | s.source = { :git => "https://github.com/Kilograpp/UITableView-Cache.git", :tag => s.version } 8 | s.platform = :ios, '6.0' 9 | s.requires_arc = true 10 | s.source_files = 'src/*.{h,m}' 11 | s.frameworks = ['Foundation', 'UIKit'] 12 | s.license = { 13 | :type => 'MIT', 14 | :file => 'LICENSE' 15 | } 16 | end 17 | 18 | -------------------------------------------------------------------------------- /UITableView+Cache.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D86FF9DF1D16A8350024B6A5 /* UITableView+Cache.h in Headers */ = {isa = PBXBuildFile; fileRef = D86FF9DE1D16A8350024B6A5 /* UITableView+Cache.h */; }; 11 | D86FF9EF1D16A9350024B6A5 /* UITableView+Cache.h in Headers */ = {isa = PBXBuildFile; fileRef = D86FF9ED1D16A9350024B6A5 /* UITableView+Cache.h */; settings = {ATTRIBUTES = (Public, ); }; }; 12 | D86FF9F01D16A9350024B6A5 /* UITableView+Cache.m in Sources */ = {isa = PBXBuildFile; fileRef = D86FF9EE1D16A9350024B6A5 /* UITableView+Cache.m */; }; 13 | /* End PBXBuildFile section */ 14 | 15 | /* Begin PBXFileReference section */ 16 | D86FF9DB1D16A8350024B6A5 /* UITableView_Cache.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = UITableView_Cache.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 17 | D86FF9DE1D16A8350024B6A5 /* UITableView+Cache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UITableView+Cache.h"; sourceTree = ""; }; 18 | D86FF9E01D16A8350024B6A5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 19 | D86FF9ED1D16A9350024B6A5 /* UITableView+Cache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableView+Cache.h"; sourceTree = ""; }; 20 | D86FF9EE1D16A9350024B6A5 /* UITableView+Cache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableView+Cache.m"; sourceTree = ""; }; 21 | /* End PBXFileReference section */ 22 | 23 | /* Begin PBXFrameworksBuildPhase section */ 24 | D86FF9D71D16A8350024B6A5 /* Frameworks */ = { 25 | isa = PBXFrameworksBuildPhase; 26 | buildActionMask = 2147483647; 27 | files = ( 28 | ); 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXFrameworksBuildPhase section */ 32 | 33 | /* Begin PBXGroup section */ 34 | D86FF9D11D16A8350024B6A5 = { 35 | isa = PBXGroup; 36 | children = ( 37 | D86FF9DD1D16A8350024B6A5 /* UITableView+Cache */, 38 | D86FF9DC1D16A8350024B6A5 /* Products */, 39 | ); 40 | sourceTree = ""; 41 | }; 42 | D86FF9DC1D16A8350024B6A5 /* Products */ = { 43 | isa = PBXGroup; 44 | children = ( 45 | D86FF9DB1D16A8350024B6A5 /* UITableView_Cache.framework */, 46 | ); 47 | name = Products; 48 | sourceTree = ""; 49 | }; 50 | D86FF9DD1D16A8350024B6A5 /* UITableView+Cache */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | D86FF9EC1D16A9350024B6A5 /* src */, 54 | D86FF9DE1D16A8350024B6A5 /* UITableView+Cache.h */, 55 | D86FF9E01D16A8350024B6A5 /* Info.plist */, 56 | ); 57 | path = "UITableView+Cache"; 58 | sourceTree = ""; 59 | }; 60 | D86FF9EC1D16A9350024B6A5 /* src */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | D86FF9ED1D16A9350024B6A5 /* UITableView+Cache.h */, 64 | D86FF9EE1D16A9350024B6A5 /* UITableView+Cache.m */, 65 | ); 66 | path = src; 67 | sourceTree = SOURCE_ROOT; 68 | }; 69 | /* End PBXGroup section */ 70 | 71 | /* Begin PBXHeadersBuildPhase section */ 72 | D86FF9D81D16A8350024B6A5 /* Headers */ = { 73 | isa = PBXHeadersBuildPhase; 74 | buildActionMask = 2147483647; 75 | files = ( 76 | D86FF9EF1D16A9350024B6A5 /* UITableView+Cache.h in Headers */, 77 | D86FF9DF1D16A8350024B6A5 /* UITableView+Cache.h in Headers */, 78 | ); 79 | runOnlyForDeploymentPostprocessing = 0; 80 | }; 81 | /* End PBXHeadersBuildPhase section */ 82 | 83 | /* Begin PBXNativeTarget section */ 84 | D86FF9DA1D16A8350024B6A5 /* UITableView+Cache */ = { 85 | isa = PBXNativeTarget; 86 | buildConfigurationList = D86FF9E31D16A8350024B6A5 /* Build configuration list for PBXNativeTarget "UITableView+Cache" */; 87 | buildPhases = ( 88 | D86FF9D61D16A8350024B6A5 /* Sources */, 89 | D86FF9D71D16A8350024B6A5 /* Frameworks */, 90 | D86FF9D81D16A8350024B6A5 /* Headers */, 91 | D86FF9D91D16A8350024B6A5 /* Resources */, 92 | ); 93 | buildRules = ( 94 | ); 95 | dependencies = ( 96 | ); 97 | name = "UITableView+Cache"; 98 | productName = "UITableView+Cache"; 99 | productReference = D86FF9DB1D16A8350024B6A5 /* UITableView_Cache.framework */; 100 | productType = "com.apple.product-type.framework"; 101 | }; 102 | /* End PBXNativeTarget section */ 103 | 104 | /* Begin PBXProject section */ 105 | D86FF9D21D16A8350024B6A5 /* Project object */ = { 106 | isa = PBXProject; 107 | attributes = { 108 | LastUpgradeCheck = 0730; 109 | ORGANIZATIONNAME = Kilograpp; 110 | TargetAttributes = { 111 | D86FF9DA1D16A8350024B6A5 = { 112 | CreatedOnToolsVersion = 7.3.1; 113 | }; 114 | }; 115 | }; 116 | buildConfigurationList = D86FF9D51D16A8350024B6A5 /* Build configuration list for PBXProject "UITableView+Cache" */; 117 | compatibilityVersion = "Xcode 3.2"; 118 | developmentRegion = English; 119 | hasScannedForEncodings = 0; 120 | knownRegions = ( 121 | en, 122 | ); 123 | mainGroup = D86FF9D11D16A8350024B6A5; 124 | productRefGroup = D86FF9DC1D16A8350024B6A5 /* Products */; 125 | projectDirPath = ""; 126 | projectRoot = ""; 127 | targets = ( 128 | D86FF9DA1D16A8350024B6A5 /* UITableView+Cache */, 129 | ); 130 | }; 131 | /* End PBXProject section */ 132 | 133 | /* Begin PBXResourcesBuildPhase section */ 134 | D86FF9D91D16A8350024B6A5 /* Resources */ = { 135 | isa = PBXResourcesBuildPhase; 136 | buildActionMask = 2147483647; 137 | files = ( 138 | ); 139 | runOnlyForDeploymentPostprocessing = 0; 140 | }; 141 | /* End PBXResourcesBuildPhase section */ 142 | 143 | /* Begin PBXSourcesBuildPhase section */ 144 | D86FF9D61D16A8350024B6A5 /* Sources */ = { 145 | isa = PBXSourcesBuildPhase; 146 | buildActionMask = 2147483647; 147 | files = ( 148 | D86FF9F01D16A9350024B6A5 /* UITableView+Cache.m in Sources */, 149 | ); 150 | runOnlyForDeploymentPostprocessing = 0; 151 | }; 152 | /* End PBXSourcesBuildPhase section */ 153 | 154 | /* Begin XCBuildConfiguration section */ 155 | D86FF9E11D16A8350024B6A5 /* Debug */ = { 156 | isa = XCBuildConfiguration; 157 | buildSettings = { 158 | ALWAYS_SEARCH_USER_PATHS = NO; 159 | CLANG_ANALYZER_NONNULL = YES; 160 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 161 | CLANG_CXX_LIBRARY = "libc++"; 162 | CLANG_ENABLE_MODULES = YES; 163 | CLANG_ENABLE_OBJC_ARC = YES; 164 | CLANG_WARN_BOOL_CONVERSION = YES; 165 | CLANG_WARN_CONSTANT_CONVERSION = YES; 166 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 167 | CLANG_WARN_EMPTY_BODY = YES; 168 | CLANG_WARN_ENUM_CONVERSION = YES; 169 | CLANG_WARN_INT_CONVERSION = YES; 170 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 171 | CLANG_WARN_UNREACHABLE_CODE = YES; 172 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 173 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 174 | COPY_PHASE_STRIP = NO; 175 | CURRENT_PROJECT_VERSION = 1; 176 | DEBUG_INFORMATION_FORMAT = dwarf; 177 | ENABLE_STRICT_OBJC_MSGSEND = YES; 178 | ENABLE_TESTABILITY = YES; 179 | GCC_C_LANGUAGE_STANDARD = gnu99; 180 | GCC_DYNAMIC_NO_PIC = NO; 181 | GCC_NO_COMMON_BLOCKS = YES; 182 | GCC_OPTIMIZATION_LEVEL = 0; 183 | GCC_PREPROCESSOR_DEFINITIONS = ( 184 | "DEBUG=1", 185 | "$(inherited)", 186 | ); 187 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 188 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 189 | GCC_WARN_UNDECLARED_SELECTOR = YES; 190 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 191 | GCC_WARN_UNUSED_FUNCTION = YES; 192 | GCC_WARN_UNUSED_VARIABLE = YES; 193 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 194 | MTL_ENABLE_DEBUG_INFO = YES; 195 | ONLY_ACTIVE_ARCH = YES; 196 | SDKROOT = iphoneos; 197 | TARGETED_DEVICE_FAMILY = "1,2"; 198 | VERSIONING_SYSTEM = "apple-generic"; 199 | VERSION_INFO_PREFIX = ""; 200 | }; 201 | name = Debug; 202 | }; 203 | D86FF9E21D16A8350024B6A5 /* Release */ = { 204 | isa = XCBuildConfiguration; 205 | buildSettings = { 206 | ALWAYS_SEARCH_USER_PATHS = NO; 207 | CLANG_ANALYZER_NONNULL = YES; 208 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 209 | CLANG_CXX_LIBRARY = "libc++"; 210 | CLANG_ENABLE_MODULES = YES; 211 | CLANG_ENABLE_OBJC_ARC = YES; 212 | CLANG_WARN_BOOL_CONVERSION = YES; 213 | CLANG_WARN_CONSTANT_CONVERSION = YES; 214 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 215 | CLANG_WARN_EMPTY_BODY = YES; 216 | CLANG_WARN_ENUM_CONVERSION = YES; 217 | CLANG_WARN_INT_CONVERSION = YES; 218 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 219 | CLANG_WARN_UNREACHABLE_CODE = YES; 220 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 221 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 222 | COPY_PHASE_STRIP = NO; 223 | CURRENT_PROJECT_VERSION = 1; 224 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 225 | ENABLE_NS_ASSERTIONS = NO; 226 | ENABLE_STRICT_OBJC_MSGSEND = YES; 227 | GCC_C_LANGUAGE_STANDARD = gnu99; 228 | GCC_NO_COMMON_BLOCKS = YES; 229 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 230 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 231 | GCC_WARN_UNDECLARED_SELECTOR = YES; 232 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 233 | GCC_WARN_UNUSED_FUNCTION = YES; 234 | GCC_WARN_UNUSED_VARIABLE = YES; 235 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 236 | MTL_ENABLE_DEBUG_INFO = NO; 237 | SDKROOT = iphoneos; 238 | TARGETED_DEVICE_FAMILY = "1,2"; 239 | VALIDATE_PRODUCT = YES; 240 | VERSIONING_SYSTEM = "apple-generic"; 241 | VERSION_INFO_PREFIX = ""; 242 | }; 243 | name = Release; 244 | }; 245 | D86FF9E41D16A8350024B6A5 /* Debug */ = { 246 | isa = XCBuildConfiguration; 247 | buildSettings = { 248 | DEFINES_MODULE = YES; 249 | DYLIB_COMPATIBILITY_VERSION = 1; 250 | DYLIB_CURRENT_VERSION = 1; 251 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 252 | INFOPLIST_FILE = "UITableView+Cache/Info.plist"; 253 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 254 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 255 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 256 | MODULEMAP_FILE = "UITableView+Cache/module.modulemap"; 257 | PRODUCT_BUNDLE_IDENTIFIER = "com.kilograpp.UITableView-Cache"; 258 | PRODUCT_NAME = UITableView_Cache; 259 | SKIP_INSTALL = YES; 260 | }; 261 | name = Debug; 262 | }; 263 | D86FF9E51D16A8350024B6A5 /* Release */ = { 264 | isa = XCBuildConfiguration; 265 | buildSettings = { 266 | DEFINES_MODULE = YES; 267 | DYLIB_COMPATIBILITY_VERSION = 1; 268 | DYLIB_CURRENT_VERSION = 1; 269 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 270 | INFOPLIST_FILE = "UITableView+Cache/Info.plist"; 271 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 272 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 273 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 274 | MODULEMAP_FILE = "UITableView+Cache/module.modulemap"; 275 | PRODUCT_BUNDLE_IDENTIFIER = "com.kilograpp.UITableView-Cache"; 276 | PRODUCT_NAME = UITableView_Cache; 277 | SKIP_INSTALL = YES; 278 | }; 279 | name = Release; 280 | }; 281 | /* End XCBuildConfiguration section */ 282 | 283 | /* Begin XCConfigurationList section */ 284 | D86FF9D51D16A8350024B6A5 /* Build configuration list for PBXProject "UITableView+Cache" */ = { 285 | isa = XCConfigurationList; 286 | buildConfigurations = ( 287 | D86FF9E11D16A8350024B6A5 /* Debug */, 288 | D86FF9E21D16A8350024B6A5 /* Release */, 289 | ); 290 | defaultConfigurationIsVisible = 0; 291 | defaultConfigurationName = Release; 292 | }; 293 | D86FF9E31D16A8350024B6A5 /* Build configuration list for PBXNativeTarget "UITableView+Cache" */ = { 294 | isa = XCConfigurationList; 295 | buildConfigurations = ( 296 | D86FF9E41D16A8350024B6A5 /* Debug */, 297 | D86FF9E51D16A8350024B6A5 /* Release */, 298 | ); 299 | defaultConfigurationIsVisible = 0; 300 | defaultConfigurationName = Release; 301 | }; 302 | /* End XCConfigurationList section */ 303 | }; 304 | rootObject = D86FF9D21D16A8350024B6A5 /* Project object */; 305 | } 306 | -------------------------------------------------------------------------------- /UITableView+Cache.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /UITableView+Cache.xcodeproj/xcshareddata/xcschemes/UITableView+Cache.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /UITableView+Cache/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.2.2 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /UITableView+Cache/UITableView+Cache.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITableView+Cache.h 3 | // UITableView+Cache 4 | // 5 | // Created by Maxim Gubin on 19/06/16. 6 | // Copyright © 2016 Kilograpp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for UITableView+Cache. 12 | FOUNDATION_EXPORT double UITableView_CacheVersionNumber; 13 | 14 | //! Project version string for UITableView+Cache. 15 | FOUNDATION_EXPORT const unsigned char UITableView_CacheVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | #import 20 | -------------------------------------------------------------------------------- /UITableView+Cache/module.modulemap: -------------------------------------------------------------------------------- 1 | framework module UITableView_Cache { 2 | umbrella header "UITableView+Cache.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /src/UITableView+Cache.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Maxim Gubin on 18/06/16. 3 | // Copyright (c) 2016 Kilograpp. All rights reserved. 4 | // 5 | 6 | #import 7 | 8 | NS_ASSUME_NONNULL_BEGIN 9 | @interface UITableView (Cache) 10 | - (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString*)identifier cacheSize:(NSUInteger)size; 11 | - (void)registerNib:(UINib*)nib forCellReuseIdentifier:(NSString*)identifier cacheSize:(NSUInteger)size; 12 | @end 13 | NS_ASSUME_NONNULL_END -------------------------------------------------------------------------------- /src/UITableView+Cache.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Maxim Gubin on 18/06/16. 3 | // Copyright (c) 2016 Kilograpp. All rights reserved. 4 | // 5 | 6 | #import "UITableView+Cache.h" 7 | #import 8 | #import 9 | 10 | @interface UITableView () 11 | 12 | @property (nonatomic, strong) NSMutableDictionary* cachedCells; // Do not use NSCache instead. 13 | @property (nonatomic, strong) NSMutableDictionary* registeredNibs; 14 | @property (nonatomic, strong) NSMutableDictionary* registeredClasses; 15 | 16 | @end 17 | 18 | 19 | @implementation UITableView (Cache) 20 | 21 | #pragma mark - Public 22 | 23 | - (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString*)identifier cacheSize:(NSUInteger)size { 24 | [self kg_prepareCache]; 25 | self.registeredClasses[identifier] = cellClass; 26 | [self kg_cacheCellsForIdentifier:identifier cacheSize:size]; 27 | } 28 | 29 | - (void)registerNib:(UINib*)nib forCellReuseIdentifier:(NSString*)identifier cacheSize:(NSUInteger)size { 30 | [self kg_prepareCache]; 31 | self.registeredNibs[identifier] = nib; 32 | [self kg_cacheCellsForIdentifier:identifier cacheSize:size]; 33 | } 34 | 35 | #pragma mark - Load 36 | 37 | + (void)load { 38 | static dispatch_once_t onceToken; 39 | dispatch_once(&onceToken, ^{ 40 | Class class = [self class]; 41 | 42 | SEL originalSelector = @selector(dequeueReusableCellWithIdentifier:); 43 | SEL swizzledSelector = @selector(privateDequeueReusableCellWithIdentifier:); 44 | 45 | Method originalMethod = class_getInstanceMethod(class, originalSelector); 46 | Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); 47 | 48 | 49 | BOOL didAddMethod = 50 | class_addMethod(class, 51 | originalSelector, 52 | method_getImplementation(swizzledMethod), 53 | method_getTypeEncoding(swizzledMethod)); 54 | 55 | if (didAddMethod) { 56 | class_replaceMethod(class, 57 | swizzledSelector, 58 | method_getImplementation(originalMethod), 59 | method_getTypeEncoding(originalMethod)); 60 | } else { 61 | method_exchangeImplementations(originalMethod, swizzledMethod); 62 | } 63 | }); 64 | } 65 | 66 | #pragma mark - Swizzling 67 | 68 | #pragma clang diagnostic push 69 | #pragma ide diagnostic ignored "InfiniteRecursion" 70 | - (UITableViewCell*)privateDequeueReusableCellWithIdentifier:(NSString*)identifier { 71 | UITableViewCell *cell = [self privateDequeueReusableCellWithIdentifier:identifier]; 72 | if (!cell) { 73 | cell = [self.cachedCells[identifier] firstObject]; 74 | if (cell) { 75 | [self.cachedCells[identifier] removeObject:cell]; 76 | } else { 77 | NSLog(@"WARNING! %@ cache is empty for identifier %@. Instantiating cell now.", self.class, identifier); 78 | cell = [self kg_instantiateCellForReuseIdentifier:identifier]; 79 | } 80 | } 81 | return cell; 82 | 83 | } 84 | #pragma clang diagnostic pop 85 | 86 | 87 | #pragma mark - Private 88 | 89 | - (UITableViewCell*)kg_instantiateCellForReuseIdentifier:(NSString*)identifier { 90 | if (self.registeredNibs[identifier]) { 91 | return [[(UINib*) self.registeredNibs[identifier] instantiateWithOwner:self options:nil] firstObject]; 92 | } 93 | if (self.registeredClasses[identifier]) { 94 | return [[self.registeredClasses[identifier] alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; 95 | } 96 | return nil; 97 | } 98 | 99 | - (void)kg_cacheCellsForIdentifier:(NSString*)identifier cacheSize:(NSUInteger)cacheSize { 100 | 101 | NSMutableArray *cells = [NSMutableArray arrayWithCapacity:cacheSize]; 102 | 103 | for (unsigned int i = 0; i < cacheSize; i++) { 104 | [cells addObject:[self kg_instantiateCellForReuseIdentifier:identifier]]; 105 | } 106 | 107 | self.cachedCells[identifier] = cells; 108 | 109 | } 110 | 111 | - (void)kg_prepareCache { 112 | if(!self.cachedCells) { 113 | [self setCachedCells:[NSMutableDictionary dictionary]]; 114 | } 115 | if (!self.registeredClasses) { 116 | [self setRegisteredClasses:[NSMutableDictionary dictionary]]; 117 | } 118 | if (!self.registeredNibs) { 119 | [self setRegisteredNibs:[NSMutableDictionary dictionary]]; 120 | } 121 | } 122 | 123 | 124 | #pragma mark - Properties 125 | 126 | - (NSDictionary *)cachedCells { 127 | return objc_getAssociatedObject(self, @selector(cachedCells)); 128 | } 129 | 130 | - (void)setCachedCells:(NSDictionary *)dictionary { 131 | objc_setAssociatedObject(self, @selector(cachedCells), dictionary, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 132 | } 133 | 134 | - (NSDictionary *)registeredNibs { 135 | return objc_getAssociatedObject(self, @selector(registeredNibs)); 136 | } 137 | 138 | - (void)setRegisteredNibs:(NSDictionary *)dictionary { 139 | objc_setAssociatedObject(self, @selector(registeredNibs), dictionary, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 140 | } 141 | 142 | - (NSDictionary *)registeredClasses { 143 | return objc_getAssociatedObject(self, @selector(registeredClasses)); 144 | } 145 | 146 | - (void)setRegisteredClasses:(NSDictionary *)dictionary { 147 | objc_setAssociatedObject(self, @selector(registeredClasses), dictionary, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 148 | } 149 | 150 | 151 | 152 | @end --------------------------------------------------------------------------------