├── CollectionViewTableCell.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── viettran.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── viettran.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── CollectionViewTableCell ├── AppDelegate.swift ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── CollectionViewController.swift ├── CollectionViewController.xib ├── CollectionViewTableViewCell.swift ├── CollectionViewTableViewCell.xib ├── Extensions.swift ├── Info.plist ├── SceneDelegate.swift └── ViewController.swift └── README.md /CollectionViewTableCell.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F64BCA24253735C5004BE10D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F64BCA23253735C5004BE10D /* AppDelegate.swift */; }; 11 | F64BCA26253735C5004BE10D /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F64BCA25253735C5004BE10D /* SceneDelegate.swift */; }; 12 | F64BCA28253735C5004BE10D /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F64BCA27253735C5004BE10D /* ViewController.swift */; }; 13 | F64BCA2B253735C5004BE10D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F64BCA29253735C5004BE10D /* Main.storyboard */; }; 14 | F64BCA2D253735C8004BE10D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F64BCA2C253735C8004BE10D /* Assets.xcassets */; }; 15 | F64BCA30253735C8004BE10D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F64BCA2E253735C8004BE10D /* LaunchScreen.storyboard */; }; 16 | F64BCA3B25373DA5004BE10D /* CollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F64BCA3925373DA5004BE10D /* CollectionViewController.swift */; }; 17 | F64BCA3C25373DA5004BE10D /* CollectionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F64BCA3A25373DA5004BE10D /* CollectionViewController.xib */; }; 18 | F64BCA4025373DF8004BE10D /* CollectionViewTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F64BCA3E25373DF8004BE10D /* CollectionViewTableViewCell.swift */; }; 19 | F64BCA4125373DF8004BE10D /* CollectionViewTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F64BCA3F25373DF8004BE10D /* CollectionViewTableViewCell.xib */; }; 20 | F64BCA4425373E42004BE10D /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = F64BCA4325373E42004BE10D /* README.md */; }; 21 | F64BCA4C25374677004BE10D /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F64BCA4B25374677004BE10D /* Extensions.swift */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXFileReference section */ 25 | F64BCA20253735C5004BE10D /* CollectionViewTableCell.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CollectionViewTableCell.app; sourceTree = BUILT_PRODUCTS_DIR; }; 26 | F64BCA23253735C5004BE10D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 27 | F64BCA25253735C5004BE10D /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 28 | F64BCA27253735C5004BE10D /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 29 | F64BCA2A253735C5004BE10D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 30 | F64BCA2C253735C8004BE10D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 31 | F64BCA2F253735C8004BE10D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 32 | F64BCA31253735C8004BE10D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | F64BCA3925373DA5004BE10D /* CollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewController.swift; sourceTree = ""; }; 34 | F64BCA3A25373DA5004BE10D /* CollectionViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CollectionViewController.xib; sourceTree = ""; }; 35 | F64BCA3E25373DF8004BE10D /* CollectionViewTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewTableViewCell.swift; sourceTree = ""; }; 36 | F64BCA3F25373DF8004BE10D /* CollectionViewTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CollectionViewTableViewCell.xib; sourceTree = ""; }; 37 | F64BCA4325373E42004BE10D /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 38 | F64BCA4B25374677004BE10D /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; 39 | /* End PBXFileReference section */ 40 | 41 | /* Begin PBXFrameworksBuildPhase section */ 42 | F64BCA1D253735C5004BE10D /* Frameworks */ = { 43 | isa = PBXFrameworksBuildPhase; 44 | buildActionMask = 2147483647; 45 | files = ( 46 | ); 47 | runOnlyForDeploymentPostprocessing = 0; 48 | }; 49 | /* End PBXFrameworksBuildPhase section */ 50 | 51 | /* Begin PBXGroup section */ 52 | F64BCA17253735C5004BE10D = { 53 | isa = PBXGroup; 54 | children = ( 55 | F64BCA4325373E42004BE10D /* README.md */, 56 | F64BCA22253735C5004BE10D /* CollectionViewTableCell */, 57 | F64BCA21253735C5004BE10D /* Products */, 58 | ); 59 | sourceTree = ""; 60 | }; 61 | F64BCA21253735C5004BE10D /* Products */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | F64BCA20253735C5004BE10D /* CollectionViewTableCell.app */, 65 | ); 66 | name = Products; 67 | sourceTree = ""; 68 | }; 69 | F64BCA22253735C5004BE10D /* CollectionViewTableCell */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | F64BCA23253735C5004BE10D /* AppDelegate.swift */, 73 | F64BCA25253735C5004BE10D /* SceneDelegate.swift */, 74 | F64BCA27253735C5004BE10D /* ViewController.swift */, 75 | F64BCA29253735C5004BE10D /* Main.storyboard */, 76 | F64BCA2C253735C8004BE10D /* Assets.xcassets */, 77 | F64BCA2E253735C8004BE10D /* LaunchScreen.storyboard */, 78 | F64BCA31253735C8004BE10D /* Info.plist */, 79 | F64BCA3E25373DF8004BE10D /* CollectionViewTableViewCell.swift */, 80 | F64BCA3F25373DF8004BE10D /* CollectionViewTableViewCell.xib */, 81 | F64BCA3925373DA5004BE10D /* CollectionViewController.swift */, 82 | F64BCA3A25373DA5004BE10D /* CollectionViewController.xib */, 83 | F64BCA4B25374677004BE10D /* Extensions.swift */, 84 | ); 85 | path = CollectionViewTableCell; 86 | sourceTree = ""; 87 | }; 88 | /* End PBXGroup section */ 89 | 90 | /* Begin PBXNativeTarget section */ 91 | F64BCA1F253735C5004BE10D /* CollectionViewTableCell */ = { 92 | isa = PBXNativeTarget; 93 | buildConfigurationList = F64BCA34253735C8004BE10D /* Build configuration list for PBXNativeTarget "CollectionViewTableCell" */; 94 | buildPhases = ( 95 | F64BCA1C253735C5004BE10D /* Sources */, 96 | F64BCA1D253735C5004BE10D /* Frameworks */, 97 | F64BCA1E253735C5004BE10D /* Resources */, 98 | ); 99 | buildRules = ( 100 | ); 101 | dependencies = ( 102 | ); 103 | name = CollectionViewTableCell; 104 | productName = CollectionViewTableCell; 105 | productReference = F64BCA20253735C5004BE10D /* CollectionViewTableCell.app */; 106 | productType = "com.apple.product-type.application"; 107 | }; 108 | /* End PBXNativeTarget section */ 109 | 110 | /* Begin PBXProject section */ 111 | F64BCA18253735C5004BE10D /* Project object */ = { 112 | isa = PBXProject; 113 | attributes = { 114 | LastSwiftUpdateCheck = 1200; 115 | LastUpgradeCheck = 1200; 116 | TargetAttributes = { 117 | F64BCA1F253735C5004BE10D = { 118 | CreatedOnToolsVersion = 12.0; 119 | }; 120 | }; 121 | }; 122 | buildConfigurationList = F64BCA1B253735C5004BE10D /* Build configuration list for PBXProject "CollectionViewTableCell" */; 123 | compatibilityVersion = "Xcode 9.3"; 124 | developmentRegion = en; 125 | hasScannedForEncodings = 0; 126 | knownRegions = ( 127 | en, 128 | Base, 129 | ); 130 | mainGroup = F64BCA17253735C5004BE10D; 131 | productRefGroup = F64BCA21253735C5004BE10D /* Products */; 132 | projectDirPath = ""; 133 | projectRoot = ""; 134 | targets = ( 135 | F64BCA1F253735C5004BE10D /* CollectionViewTableCell */, 136 | ); 137 | }; 138 | /* End PBXProject section */ 139 | 140 | /* Begin PBXResourcesBuildPhase section */ 141 | F64BCA1E253735C5004BE10D /* Resources */ = { 142 | isa = PBXResourcesBuildPhase; 143 | buildActionMask = 2147483647; 144 | files = ( 145 | F64BCA4425373E42004BE10D /* README.md in Resources */, 146 | F64BCA3C25373DA5004BE10D /* CollectionViewController.xib in Resources */, 147 | F64BCA4125373DF8004BE10D /* CollectionViewTableViewCell.xib in Resources */, 148 | F64BCA30253735C8004BE10D /* LaunchScreen.storyboard in Resources */, 149 | F64BCA2D253735C8004BE10D /* Assets.xcassets in Resources */, 150 | F64BCA2B253735C5004BE10D /* Main.storyboard in Resources */, 151 | ); 152 | runOnlyForDeploymentPostprocessing = 0; 153 | }; 154 | /* End PBXResourcesBuildPhase section */ 155 | 156 | /* Begin PBXSourcesBuildPhase section */ 157 | F64BCA1C253735C5004BE10D /* Sources */ = { 158 | isa = PBXSourcesBuildPhase; 159 | buildActionMask = 2147483647; 160 | files = ( 161 | F64BCA4025373DF8004BE10D /* CollectionViewTableViewCell.swift in Sources */, 162 | F64BCA3B25373DA5004BE10D /* CollectionViewController.swift in Sources */, 163 | F64BCA28253735C5004BE10D /* ViewController.swift in Sources */, 164 | F64BCA24253735C5004BE10D /* AppDelegate.swift in Sources */, 165 | F64BCA26253735C5004BE10D /* SceneDelegate.swift in Sources */, 166 | F64BCA4C25374677004BE10D /* Extensions.swift in Sources */, 167 | ); 168 | runOnlyForDeploymentPostprocessing = 0; 169 | }; 170 | /* End PBXSourcesBuildPhase section */ 171 | 172 | /* Begin PBXVariantGroup section */ 173 | F64BCA29253735C5004BE10D /* Main.storyboard */ = { 174 | isa = PBXVariantGroup; 175 | children = ( 176 | F64BCA2A253735C5004BE10D /* Base */, 177 | ); 178 | name = Main.storyboard; 179 | sourceTree = ""; 180 | }; 181 | F64BCA2E253735C8004BE10D /* LaunchScreen.storyboard */ = { 182 | isa = PBXVariantGroup; 183 | children = ( 184 | F64BCA2F253735C8004BE10D /* Base */, 185 | ); 186 | name = LaunchScreen.storyboard; 187 | sourceTree = ""; 188 | }; 189 | /* End PBXVariantGroup section */ 190 | 191 | /* Begin XCBuildConfiguration section */ 192 | F64BCA32253735C8004BE10D /* Debug */ = { 193 | isa = XCBuildConfiguration; 194 | buildSettings = { 195 | ALWAYS_SEARCH_USER_PATHS = NO; 196 | CLANG_ANALYZER_NONNULL = YES; 197 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 198 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 199 | CLANG_CXX_LIBRARY = "libc++"; 200 | CLANG_ENABLE_MODULES = YES; 201 | CLANG_ENABLE_OBJC_ARC = YES; 202 | CLANG_ENABLE_OBJC_WEAK = YES; 203 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 204 | CLANG_WARN_BOOL_CONVERSION = YES; 205 | CLANG_WARN_COMMA = YES; 206 | CLANG_WARN_CONSTANT_CONVERSION = YES; 207 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 208 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 209 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 210 | CLANG_WARN_EMPTY_BODY = YES; 211 | CLANG_WARN_ENUM_CONVERSION = YES; 212 | CLANG_WARN_INFINITE_RECURSION = YES; 213 | CLANG_WARN_INT_CONVERSION = YES; 214 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 215 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 216 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 217 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 218 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 219 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 220 | CLANG_WARN_STRICT_PROTOTYPES = YES; 221 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 222 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 223 | CLANG_WARN_UNREACHABLE_CODE = YES; 224 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 225 | COPY_PHASE_STRIP = NO; 226 | DEBUG_INFORMATION_FORMAT = dwarf; 227 | ENABLE_STRICT_OBJC_MSGSEND = YES; 228 | ENABLE_TESTABILITY = YES; 229 | GCC_C_LANGUAGE_STANDARD = gnu11; 230 | GCC_DYNAMIC_NO_PIC = NO; 231 | GCC_NO_COMMON_BLOCKS = YES; 232 | GCC_OPTIMIZATION_LEVEL = 0; 233 | GCC_PREPROCESSOR_DEFINITIONS = ( 234 | "DEBUG=1", 235 | "$(inherited)", 236 | ); 237 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 238 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 239 | GCC_WARN_UNDECLARED_SELECTOR = YES; 240 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 241 | GCC_WARN_UNUSED_FUNCTION = YES; 242 | GCC_WARN_UNUSED_VARIABLE = YES; 243 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 244 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 245 | MTL_FAST_MATH = YES; 246 | ONLY_ACTIVE_ARCH = YES; 247 | SDKROOT = iphoneos; 248 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 249 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 250 | }; 251 | name = Debug; 252 | }; 253 | F64BCA33253735C8004BE10D /* Release */ = { 254 | isa = XCBuildConfiguration; 255 | buildSettings = { 256 | ALWAYS_SEARCH_USER_PATHS = NO; 257 | CLANG_ANALYZER_NONNULL = YES; 258 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 259 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 260 | CLANG_CXX_LIBRARY = "libc++"; 261 | CLANG_ENABLE_MODULES = YES; 262 | CLANG_ENABLE_OBJC_ARC = YES; 263 | CLANG_ENABLE_OBJC_WEAK = YES; 264 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 265 | CLANG_WARN_BOOL_CONVERSION = YES; 266 | CLANG_WARN_COMMA = YES; 267 | CLANG_WARN_CONSTANT_CONVERSION = YES; 268 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 269 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 270 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 271 | CLANG_WARN_EMPTY_BODY = YES; 272 | CLANG_WARN_ENUM_CONVERSION = YES; 273 | CLANG_WARN_INFINITE_RECURSION = YES; 274 | CLANG_WARN_INT_CONVERSION = YES; 275 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 276 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 277 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 278 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 279 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 280 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 281 | CLANG_WARN_STRICT_PROTOTYPES = YES; 282 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 283 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 284 | CLANG_WARN_UNREACHABLE_CODE = YES; 285 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 286 | COPY_PHASE_STRIP = NO; 287 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 288 | ENABLE_NS_ASSERTIONS = NO; 289 | ENABLE_STRICT_OBJC_MSGSEND = YES; 290 | GCC_C_LANGUAGE_STANDARD = gnu11; 291 | GCC_NO_COMMON_BLOCKS = YES; 292 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 293 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 294 | GCC_WARN_UNDECLARED_SELECTOR = YES; 295 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 296 | GCC_WARN_UNUSED_FUNCTION = YES; 297 | GCC_WARN_UNUSED_VARIABLE = YES; 298 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 299 | MTL_ENABLE_DEBUG_INFO = NO; 300 | MTL_FAST_MATH = YES; 301 | SDKROOT = iphoneos; 302 | SWIFT_COMPILATION_MODE = wholemodule; 303 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 304 | VALIDATE_PRODUCT = YES; 305 | }; 306 | name = Release; 307 | }; 308 | F64BCA35253735C8004BE10D /* Debug */ = { 309 | isa = XCBuildConfiguration; 310 | buildSettings = { 311 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 312 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 313 | CODE_SIGN_STYLE = Automatic; 314 | DEVELOPMENT_TEAM = 7BY73UAH3Q; 315 | INFOPLIST_FILE = CollectionViewTableCell/Info.plist; 316 | LD_RUNPATH_SEARCH_PATHS = ( 317 | "$(inherited)", 318 | "@executable_path/Frameworks", 319 | ); 320 | PRODUCT_BUNDLE_IDENTIFIER = com.viettran.CollectionViewTableCell; 321 | PRODUCT_NAME = "$(TARGET_NAME)"; 322 | SWIFT_VERSION = 5.0; 323 | TARGETED_DEVICE_FAMILY = "1,2"; 324 | }; 325 | name = Debug; 326 | }; 327 | F64BCA36253735C8004BE10D /* Release */ = { 328 | isa = XCBuildConfiguration; 329 | buildSettings = { 330 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 331 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 332 | CODE_SIGN_STYLE = Automatic; 333 | DEVELOPMENT_TEAM = 7BY73UAH3Q; 334 | INFOPLIST_FILE = CollectionViewTableCell/Info.plist; 335 | LD_RUNPATH_SEARCH_PATHS = ( 336 | "$(inherited)", 337 | "@executable_path/Frameworks", 338 | ); 339 | PRODUCT_BUNDLE_IDENTIFIER = com.viettran.CollectionViewTableCell; 340 | PRODUCT_NAME = "$(TARGET_NAME)"; 341 | SWIFT_VERSION = 5.0; 342 | TARGETED_DEVICE_FAMILY = "1,2"; 343 | }; 344 | name = Release; 345 | }; 346 | /* End XCBuildConfiguration section */ 347 | 348 | /* Begin XCConfigurationList section */ 349 | F64BCA1B253735C5004BE10D /* Build configuration list for PBXProject "CollectionViewTableCell" */ = { 350 | isa = XCConfigurationList; 351 | buildConfigurations = ( 352 | F64BCA32253735C8004BE10D /* Debug */, 353 | F64BCA33253735C8004BE10D /* Release */, 354 | ); 355 | defaultConfigurationIsVisible = 0; 356 | defaultConfigurationName = Release; 357 | }; 358 | F64BCA34253735C8004BE10D /* Build configuration list for PBXNativeTarget "CollectionViewTableCell" */ = { 359 | isa = XCConfigurationList; 360 | buildConfigurations = ( 361 | F64BCA35253735C8004BE10D /* Debug */, 362 | F64BCA36253735C8004BE10D /* Release */, 363 | ); 364 | defaultConfigurationIsVisible = 0; 365 | defaultConfigurationName = Release; 366 | }; 367 | /* End XCConfigurationList section */ 368 | }; 369 | rootObject = F64BCA18253735C5004BE10D /* Project object */; 370 | } 371 | -------------------------------------------------------------------------------- /CollectionViewTableCell.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CollectionViewTableCell.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CollectionViewTableCell.xcodeproj/project.xcworkspace/xcuserdata/viettran.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viettran/CollectionViewTableCell/074f5314d6a434208f747c5c84b119bfafe8649d/CollectionViewTableCell.xcodeproj/project.xcworkspace/xcuserdata/viettran.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /CollectionViewTableCell.xcodeproj/xcuserdata/viettran.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 21 | 22 | 23 | 25 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /CollectionViewTableCell.xcodeproj/xcuserdata/viettran.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | CollectionViewTableCell.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /CollectionViewTableCell/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // CollectionViewTableCell 4 | // 5 | // Created by Viet Tran on 10/14/20. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | // MARK: UISceneSession Lifecycle 21 | 22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 23 | // Called when a new scene session is being created. 24 | // Use this method to select a configuration to create the new scene with. 25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 26 | } 27 | 28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 29 | // Called when the user discards a scene session. 30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /CollectionViewTableCell/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /CollectionViewTableCell/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /CollectionViewTableCell/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /CollectionViewTableCell/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /CollectionViewTableCell/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /CollectionViewTableCell/CollectionViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CollectionViewController.swift 3 | // CollectionViewTableCell 4 | // 5 | // Created by Viet Tran on 10/14/20. 6 | // 7 | 8 | import UIKit 9 | 10 | class CollectionViewController: UICollectionViewController { 11 | 12 | // Set this action during initialization to get a callback when the collection view finishes its layout. 13 | // To prevent infinite loop, this action should be called only once. Once it is called, it resets itself 14 | // to nil. 15 | var didLayoutAction: (() -> Void)? 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | self.collectionView.register(UICollectionViewCell.self, 21 | forCellWithReuseIdentifier: String(describing: UICollectionViewCell.self)) 22 | } 23 | 24 | override func viewDidLayoutSubviews() { 25 | super.viewDidLayoutSubviews() 26 | 27 | didLayoutAction?() 28 | didLayoutAction = nil // Call only once 29 | } 30 | 31 | // MARK: UICollectionViewDataSource 32 | 33 | override func numberOfSections(in collectionView: UICollectionView) -> Int { 34 | return 1 35 | } 36 | 37 | 38 | override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 39 | return 10 40 | } 41 | 42 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 43 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: UICollectionViewCell.self), for: indexPath) 44 | cell.backgroundColor = .lightGray 45 | 46 | return cell 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /CollectionViewTableCell/CollectionViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /CollectionViewTableCell/CollectionViewTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CollectionViewTableViewCell.swift 3 | // CollectionViewTableCell 4 | // 5 | // Created by Viet Tran on 10/14/20. 6 | // 7 | 8 | import UIKit 9 | 10 | class CollectionViewTableViewCell: UITableViewCell { 11 | 12 | var collectionViewController: CollectionViewController! 13 | 14 | override func awakeFromNib() { 15 | super.awakeFromNib() 16 | initCollectionView() 17 | } 18 | 19 | private func initCollectionView() { 20 | 21 | collectionViewController = CollectionViewController(nibName: String(describing: CollectionViewController.self), bundle: nil) 22 | 23 | // Need to update row height when collection view finishes its layout. 24 | collectionViewController.didLayoutAction = updateRowHeight 25 | 26 | contentView.addSubview(collectionViewController.view) 27 | collectionViewController.view.translatesAutoresizingMaskIntoConstraints = false 28 | NSLayoutConstraint.activate([ 29 | collectionViewController.view.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16), 30 | collectionViewController.view.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16), 31 | collectionViewController.view.topAnchor.constraint(equalTo: contentView.topAnchor), 32 | collectionViewController.view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) 33 | ]) 34 | 35 | } 36 | 37 | private func updateRowHeight() { 38 | DispatchQueue.main.async { [weak self] in 39 | self?.tableView?.updateRowHeightsWithoutReloadingRows() 40 | } 41 | } 42 | 43 | override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize { 44 | return collectionViewController.collectionView.contentSize 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /CollectionViewTableCell/CollectionViewTableViewCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /CollectionViewTableCell/Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // CollectionViewTableCell 4 | // 5 | // Created by Viet Tran on 10/14/20. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIView { 11 | var parentViewController: UIViewController? { 12 | if let nextResponder = self.next as? UIViewController { 13 | return nextResponder 14 | } else if let nextResponder = self.next as? UIView { 15 | return nextResponder.parentViewController 16 | } else { 17 | return nil 18 | } 19 | } 20 | } 21 | 22 | extension UITableViewCell { 23 | var tableView: UITableView? { 24 | return (next as? UITableView) ?? (parentViewController as? UITableViewController)?.tableView 25 | } 26 | } 27 | 28 | extension UITableView { 29 | func updateRowHeightsWithoutReloadingRows(animated: Bool = false) { 30 | let block = { 31 | self.beginUpdates() 32 | self.endUpdates() 33 | } 34 | 35 | if animated { 36 | block() 37 | } 38 | else { 39 | UIView.performWithoutAnimation { 40 | block() 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /CollectionViewTableCell/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UIApplicationSupportsIndirectInputEvents 43 | 44 | UILaunchStoryboardName 45 | LaunchScreen 46 | UIMainStoryboardFile 47 | Main 48 | UIRequiredDeviceCapabilities 49 | 50 | armv7 51 | 52 | UISupportedInterfaceOrientations 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationLandscapeLeft 56 | UIInterfaceOrientationLandscapeRight 57 | 58 | UISupportedInterfaceOrientations~ipad 59 | 60 | UIInterfaceOrientationPortrait 61 | UIInterfaceOrientationPortraitUpsideDown 62 | UIInterfaceOrientationLandscapeLeft 63 | UIInterfaceOrientationLandscapeRight 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /CollectionViewTableCell/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // CollectionViewTableCell 4 | // 5 | // Created by Viet Tran on 10/14/20. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let _ = (scene as? UIWindowScene) else { return } 20 | } 21 | 22 | func sceneDidDisconnect(_ scene: UIScene) { 23 | // Called as the scene is being released by the system. 24 | // This occurs shortly after the scene enters the background, or when its session is discarded. 25 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 26 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 27 | } 28 | 29 | func sceneDidBecomeActive(_ scene: UIScene) { 30 | // Called when the scene has moved from an inactive state to an active state. 31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 32 | } 33 | 34 | func sceneWillResignActive(_ scene: UIScene) { 35 | // Called when the scene will move from an active state to an inactive state. 36 | // This may occur due to temporary interruptions (ex. an incoming phone call). 37 | } 38 | 39 | func sceneWillEnterForeground(_ scene: UIScene) { 40 | // Called as the scene transitions from the background to the foreground. 41 | // Use this method to undo the changes made on entering the background. 42 | } 43 | 44 | func sceneDidEnterBackground(_ scene: UIScene) { 45 | // Called as the scene transitions from the foreground to the background. 46 | // Use this method to save data, release shared resources, and store enough scene-specific state information 47 | // to restore the scene back to its current state. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /CollectionViewTableCell/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // CollectionViewTableCell 4 | // 5 | // Created by Viet Tran on 10/14/20. 6 | // 7 | 8 | import UIKit 9 | 10 | class ViewController: UIViewController { 11 | 12 | @IBOutlet weak var tableView: UITableView! 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: String(describing: UITableViewCell.self)) 17 | tableView.register(UINib(nibName: String(describing: CollectionViewTableViewCell.self), bundle: nil), forCellReuseIdentifier: String(describing: CollectionViewTableViewCell.self)) 18 | } 19 | } 20 | 21 | extension ViewController : UITableViewDataSource { 22 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 23 | return 10 24 | } 25 | 26 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 27 | var cell: UITableViewCell? 28 | if indexPath.row == 5 { 29 | cell = tableView.dequeueReusableCell(withIdentifier: String(describing: CollectionViewTableViewCell.self)) as? CollectionViewTableViewCell 30 | } 31 | else { 32 | cell = tableView.dequeueReusableCell(withIdentifier: String(describing: UITableViewCell.self)) 33 | cell?.textLabel?.text = "Row \(indexPath.row)" 34 | } 35 | 36 | return cell ?? UITableViewCell() 37 | } 38 | } 39 | 40 | extension ViewController : UITableViewDelegate { 41 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 42 | return UITableView.automaticDimension 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CollectionViewTableCell 2 | UICollectionView Inside A UITableViewCell With Self Sizing 3 | 4 | If you are looking for a way to put a collection view inside a table cell without manually calculating table row heights, you are in the right place. 5 | 6 | If your app target is iOS 14, you might consider using UICollectionLayoutListConfiguration (https://developer.apple.com/documentation/uikit/uicollectionlayoutlistconfiguration) to construct sections of the collection view as lists like in table views. Here is a WWDC-20 video, https://developer.apple.com/videos/play/wwdc2020/10026/, showing how to do that. 7 | 8 | This was written when iOS 14 just came out. Many apps still target iOS 13 or earlier. This method is for those apps. 9 | --------------------------------------------------------------------------------