├── README.md ├── WCLWaterFallLayout.gif ├── WCLWaterFallLayout.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── wangchonglei.xcuserdatad │ └── xcschemes │ ├── WCLWaterFallLayout.xcscheme │ └── xcschememanagement.plist └── WCLWaterFallLayout ├── AppDelegate.swift ├── Assets.xcassets └── AppIcon.appiconset │ └── Contents.json ├── Base.lproj └── Main.storyboard ├── Info.plist ├── Launch Screen.storyboard ├── ViewController.swift └── WCLWaterFallLayout.swift /README.md: -------------------------------------------------------------------------------- 1 | ## WCLWaterFallLayout 2 | 3 | ![aaaa](https://github.com/631106979/WCLWaterFallLayout/blob/master/WCLWaterFallLayout.gif?raw=true) 4 | 5 | ## 简介 6 | 7 | 用swift写的简单的瀑布流布局,用于UICollectionView,支持拖拽布局~ 8 | 9 | ## 使用 10 | 11 | ```swift 12 | let layout = WCLWaterFallLayout.init(lineSpacing: 11, columnSpacing: 11, sectionInsets: UIEdgeInsetsMake(0, 16, 10, 16)) 13 | layout.delegate = self 14 | contentCV = UICollectionView.init(frame: CGRect.zero, collectionViewLayout: layout) 15 | ``` 16 | 17 | 或者使用拖拽布局 18 | 19 | ![bbbb](http://imwcl.oss-cn-shanghai.aliyuncs.com/github/WCLWaterFallLayout/2860B3D7-CC8B-4D15-90E4-1AA14D1B4703.png) 20 | 21 | ```swift 22 | @objc protocol WCLWaterFallLayoutDelegate { 23 | //waterFall的列数 24 | func columnOfWaterFall(_ collectionView: UICollectionView) -> Int 25 | //每个item的高度 26 | func waterFall(_ collectionView: UICollectionView, layout waterFallLayout: WCLWaterFallLayout, heightForItemAt indexPath: IndexPath) -> CGFloat 27 | } 28 | ``` 29 | 30 | ## 属性列表 31 | 32 | ```swift 33 | //代理 34 | weak var delegate: WCLWaterFallLayoutDelegate? 35 | //行间距 36 | @IBInspectable var lineSpacing: CGFloat = 0 37 | //列间距 38 | @IBInspectable var columnSpacing: CGFloat = 0 39 | //section的top 40 | @IBInspectable var sectionTop: CGFloat = 0 { 41 | willSet { 42 | sectionInsets.top = newValue 43 | } 44 | } 45 | //section的Bottom 46 | @IBInspectable var sectionBottom: CGFloat = 0 { 47 | willSet { 48 | sectionInsets.bottom = newValue 49 | } 50 | } 51 | //section的left 52 | @IBInspectable var sectionLeft: CGFloat = 0 { 53 | willSet { 54 | sectionInsets.left = newValue 55 | } 56 | } 57 | //section的right 58 | @IBInspectable var sectionRight: CGFloat = 0 { 59 | willSet { 60 | sectionInsets.right = newValue 61 | } 62 | } 63 | //section的Insets 64 | @IBInspectable var sectionInsets: UIEdgeInsets = UIEdgeInsets.zero 65 | ``` 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /WCLWaterFallLayout.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imwcl/WCLWaterFallLayout/af16a919f533f839e134c22f1188df10ec9e422c/WCLWaterFallLayout.gif -------------------------------------------------------------------------------- /WCLWaterFallLayout.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 973557FE1E0A8BD2000F6604 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973557FD1E0A8BD2000F6604 /* AppDelegate.swift */; }; 11 | 973558001E0A8BD2000F6604 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973557FF1E0A8BD2000F6604 /* ViewController.swift */; }; 12 | 973558031E0A8BD2000F6604 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 973558011E0A8BD2000F6604 /* Main.storyboard */; }; 13 | 973558051E0A8BD2000F6604 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 973558041E0A8BD2000F6604 /* Assets.xcassets */; }; 14 | 973558101E0A8C29000F6604 /* WCLWaterFallLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9735580F1E0A8C29000F6604 /* WCLWaterFallLayout.swift */; }; 15 | 973558121E0A8F66000F6604 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 973558111E0A8F66000F6604 /* Launch Screen.storyboard */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | 973557FA1E0A8BD2000F6604 /* WCLWaterFallLayout.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WCLWaterFallLayout.app; sourceTree = BUILT_PRODUCTS_DIR; }; 20 | 973557FD1E0A8BD2000F6604 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 21 | 973557FF1E0A8BD2000F6604 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 22 | 973558021E0A8BD2000F6604 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 23 | 973558041E0A8BD2000F6604 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 24 | 973558091E0A8BD2000F6604 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 25 | 9735580F1E0A8C29000F6604 /* WCLWaterFallLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WCLWaterFallLayout.swift; sourceTree = ""; }; 26 | 973558111E0A8F66000F6604 /* Launch Screen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = ""; }; 27 | /* End PBXFileReference section */ 28 | 29 | /* Begin PBXFrameworksBuildPhase section */ 30 | 973557F71E0A8BD2000F6604 /* Frameworks */ = { 31 | isa = PBXFrameworksBuildPhase; 32 | buildActionMask = 2147483647; 33 | files = ( 34 | ); 35 | runOnlyForDeploymentPostprocessing = 0; 36 | }; 37 | /* End PBXFrameworksBuildPhase section */ 38 | 39 | /* Begin PBXGroup section */ 40 | 973557F11E0A8BD2000F6604 = { 41 | isa = PBXGroup; 42 | children = ( 43 | 973557FC1E0A8BD2000F6604 /* WCLWaterFallLayout */, 44 | 973557FB1E0A8BD2000F6604 /* Products */, 45 | ); 46 | sourceTree = ""; 47 | }; 48 | 973557FB1E0A8BD2000F6604 /* Products */ = { 49 | isa = PBXGroup; 50 | children = ( 51 | 973557FA1E0A8BD2000F6604 /* WCLWaterFallLayout.app */, 52 | ); 53 | name = Products; 54 | sourceTree = ""; 55 | }; 56 | 973557FC1E0A8BD2000F6604 /* WCLWaterFallLayout */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | 9735580F1E0A8C29000F6604 /* WCLWaterFallLayout.swift */, 60 | 973557FD1E0A8BD2000F6604 /* AppDelegate.swift */, 61 | 973557FF1E0A8BD2000F6604 /* ViewController.swift */, 62 | 973558011E0A8BD2000F6604 /* Main.storyboard */, 63 | 973558041E0A8BD2000F6604 /* Assets.xcassets */, 64 | 973558111E0A8F66000F6604 /* Launch Screen.storyboard */, 65 | 973558091E0A8BD2000F6604 /* Info.plist */, 66 | ); 67 | path = WCLWaterFallLayout; 68 | sourceTree = ""; 69 | }; 70 | /* End PBXGroup section */ 71 | 72 | /* Begin PBXNativeTarget section */ 73 | 973557F91E0A8BD2000F6604 /* WCLWaterFallLayout */ = { 74 | isa = PBXNativeTarget; 75 | buildConfigurationList = 9735580C1E0A8BD2000F6604 /* Build configuration list for PBXNativeTarget "WCLWaterFallLayout" */; 76 | buildPhases = ( 77 | 973557F61E0A8BD2000F6604 /* Sources */, 78 | 973557F71E0A8BD2000F6604 /* Frameworks */, 79 | 973557F81E0A8BD2000F6604 /* Resources */, 80 | ); 81 | buildRules = ( 82 | ); 83 | dependencies = ( 84 | ); 85 | name = WCLWaterFallLayout; 86 | productName = WCLWaterFallLayout; 87 | productReference = 973557FA1E0A8BD2000F6604 /* WCLWaterFallLayout.app */; 88 | productType = "com.apple.product-type.application"; 89 | }; 90 | /* End PBXNativeTarget section */ 91 | 92 | /* Begin PBXProject section */ 93 | 973557F21E0A8BD2000F6604 /* Project object */ = { 94 | isa = PBXProject; 95 | attributes = { 96 | LastSwiftUpdateCheck = 0810; 97 | LastUpgradeCheck = 0810; 98 | ORGANIZATIONNAME = "王崇磊"; 99 | TargetAttributes = { 100 | 973557F91E0A8BD2000F6604 = { 101 | CreatedOnToolsVersion = 8.1; 102 | ProvisioningStyle = Automatic; 103 | }; 104 | }; 105 | }; 106 | buildConfigurationList = 973557F51E0A8BD2000F6604 /* Build configuration list for PBXProject "WCLWaterFallLayout" */; 107 | compatibilityVersion = "Xcode 3.2"; 108 | developmentRegion = English; 109 | hasScannedForEncodings = 0; 110 | knownRegions = ( 111 | en, 112 | Base, 113 | ); 114 | mainGroup = 973557F11E0A8BD2000F6604; 115 | productRefGroup = 973557FB1E0A8BD2000F6604 /* Products */; 116 | projectDirPath = ""; 117 | projectRoot = ""; 118 | targets = ( 119 | 973557F91E0A8BD2000F6604 /* WCLWaterFallLayout */, 120 | ); 121 | }; 122 | /* End PBXProject section */ 123 | 124 | /* Begin PBXResourcesBuildPhase section */ 125 | 973557F81E0A8BD2000F6604 /* Resources */ = { 126 | isa = PBXResourcesBuildPhase; 127 | buildActionMask = 2147483647; 128 | files = ( 129 | 973558121E0A8F66000F6604 /* Launch Screen.storyboard in Resources */, 130 | 973558051E0A8BD2000F6604 /* Assets.xcassets in Resources */, 131 | 973558031E0A8BD2000F6604 /* Main.storyboard in Resources */, 132 | ); 133 | runOnlyForDeploymentPostprocessing = 0; 134 | }; 135 | /* End PBXResourcesBuildPhase section */ 136 | 137 | /* Begin PBXSourcesBuildPhase section */ 138 | 973557F61E0A8BD2000F6604 /* Sources */ = { 139 | isa = PBXSourcesBuildPhase; 140 | buildActionMask = 2147483647; 141 | files = ( 142 | 973558001E0A8BD2000F6604 /* ViewController.swift in Sources */, 143 | 973557FE1E0A8BD2000F6604 /* AppDelegate.swift in Sources */, 144 | 973558101E0A8C29000F6604 /* WCLWaterFallLayout.swift in Sources */, 145 | ); 146 | runOnlyForDeploymentPostprocessing = 0; 147 | }; 148 | /* End PBXSourcesBuildPhase section */ 149 | 150 | /* Begin PBXVariantGroup section */ 151 | 973558011E0A8BD2000F6604 /* Main.storyboard */ = { 152 | isa = PBXVariantGroup; 153 | children = ( 154 | 973558021E0A8BD2000F6604 /* Base */, 155 | ); 156 | name = Main.storyboard; 157 | sourceTree = ""; 158 | }; 159 | /* End PBXVariantGroup section */ 160 | 161 | /* Begin XCBuildConfiguration section */ 162 | 9735580A1E0A8BD2000F6604 /* Debug */ = { 163 | isa = XCBuildConfiguration; 164 | buildSettings = { 165 | ALWAYS_SEARCH_USER_PATHS = NO; 166 | CLANG_ANALYZER_NONNULL = YES; 167 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 168 | CLANG_CXX_LIBRARY = "libc++"; 169 | CLANG_ENABLE_MODULES = YES; 170 | CLANG_ENABLE_OBJC_ARC = YES; 171 | CLANG_WARN_BOOL_CONVERSION = YES; 172 | CLANG_WARN_CONSTANT_CONVERSION = YES; 173 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 174 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 175 | CLANG_WARN_EMPTY_BODY = YES; 176 | CLANG_WARN_ENUM_CONVERSION = YES; 177 | CLANG_WARN_INFINITE_RECURSION = YES; 178 | CLANG_WARN_INT_CONVERSION = YES; 179 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 180 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 181 | CLANG_WARN_UNREACHABLE_CODE = YES; 182 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 183 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 184 | COPY_PHASE_STRIP = NO; 185 | DEBUG_INFORMATION_FORMAT = dwarf; 186 | ENABLE_STRICT_OBJC_MSGSEND = YES; 187 | ENABLE_TESTABILITY = YES; 188 | GCC_C_LANGUAGE_STANDARD = gnu99; 189 | GCC_DYNAMIC_NO_PIC = NO; 190 | GCC_NO_COMMON_BLOCKS = YES; 191 | GCC_OPTIMIZATION_LEVEL = 0; 192 | GCC_PREPROCESSOR_DEFINITIONS = ( 193 | "DEBUG=1", 194 | "$(inherited)", 195 | ); 196 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 197 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 198 | GCC_WARN_UNDECLARED_SELECTOR = YES; 199 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 200 | GCC_WARN_UNUSED_FUNCTION = YES; 201 | GCC_WARN_UNUSED_VARIABLE = YES; 202 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 203 | MTL_ENABLE_DEBUG_INFO = YES; 204 | ONLY_ACTIVE_ARCH = YES; 205 | SDKROOT = iphoneos; 206 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 207 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 208 | }; 209 | name = Debug; 210 | }; 211 | 9735580B1E0A8BD2000F6604 /* Release */ = { 212 | isa = XCBuildConfiguration; 213 | buildSettings = { 214 | ALWAYS_SEARCH_USER_PATHS = NO; 215 | CLANG_ANALYZER_NONNULL = YES; 216 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 217 | CLANG_CXX_LIBRARY = "libc++"; 218 | CLANG_ENABLE_MODULES = YES; 219 | CLANG_ENABLE_OBJC_ARC = YES; 220 | CLANG_WARN_BOOL_CONVERSION = YES; 221 | CLANG_WARN_CONSTANT_CONVERSION = YES; 222 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 223 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 224 | CLANG_WARN_EMPTY_BODY = YES; 225 | CLANG_WARN_ENUM_CONVERSION = YES; 226 | CLANG_WARN_INFINITE_RECURSION = YES; 227 | CLANG_WARN_INT_CONVERSION = YES; 228 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 229 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 230 | CLANG_WARN_UNREACHABLE_CODE = YES; 231 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 232 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 233 | COPY_PHASE_STRIP = NO; 234 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 235 | ENABLE_NS_ASSERTIONS = NO; 236 | ENABLE_STRICT_OBJC_MSGSEND = YES; 237 | GCC_C_LANGUAGE_STANDARD = gnu99; 238 | GCC_NO_COMMON_BLOCKS = YES; 239 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 240 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 241 | GCC_WARN_UNDECLARED_SELECTOR = YES; 242 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 243 | GCC_WARN_UNUSED_FUNCTION = YES; 244 | GCC_WARN_UNUSED_VARIABLE = YES; 245 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 246 | MTL_ENABLE_DEBUG_INFO = NO; 247 | SDKROOT = iphoneos; 248 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 249 | VALIDATE_PRODUCT = YES; 250 | }; 251 | name = Release; 252 | }; 253 | 9735580D1E0A8BD2000F6604 /* Debug */ = { 254 | isa = XCBuildConfiguration; 255 | buildSettings = { 256 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 257 | INFOPLIST_FILE = WCLWaterFallLayout/Info.plist; 258 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 259 | PRODUCT_BUNDLE_IDENTIFIER = WCL.WCLWaterFallLayout; 260 | PRODUCT_NAME = "$(TARGET_NAME)"; 261 | SWIFT_VERSION = 3.0; 262 | }; 263 | name = Debug; 264 | }; 265 | 9735580E1E0A8BD2000F6604 /* Release */ = { 266 | isa = XCBuildConfiguration; 267 | buildSettings = { 268 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 269 | INFOPLIST_FILE = WCLWaterFallLayout/Info.plist; 270 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 271 | PRODUCT_BUNDLE_IDENTIFIER = WCL.WCLWaterFallLayout; 272 | PRODUCT_NAME = "$(TARGET_NAME)"; 273 | SWIFT_VERSION = 3.0; 274 | }; 275 | name = Release; 276 | }; 277 | /* End XCBuildConfiguration section */ 278 | 279 | /* Begin XCConfigurationList section */ 280 | 973557F51E0A8BD2000F6604 /* Build configuration list for PBXProject "WCLWaterFallLayout" */ = { 281 | isa = XCConfigurationList; 282 | buildConfigurations = ( 283 | 9735580A1E0A8BD2000F6604 /* Debug */, 284 | 9735580B1E0A8BD2000F6604 /* Release */, 285 | ); 286 | defaultConfigurationIsVisible = 0; 287 | defaultConfigurationName = Release; 288 | }; 289 | 9735580C1E0A8BD2000F6604 /* Build configuration list for PBXNativeTarget "WCLWaterFallLayout" */ = { 290 | isa = XCConfigurationList; 291 | buildConfigurations = ( 292 | 9735580D1E0A8BD2000F6604 /* Debug */, 293 | 9735580E1E0A8BD2000F6604 /* Release */, 294 | ); 295 | defaultConfigurationIsVisible = 0; 296 | }; 297 | /* End XCConfigurationList section */ 298 | }; 299 | rootObject = 973557F21E0A8BD2000F6604 /* Project object */; 300 | } 301 | -------------------------------------------------------------------------------- /WCLWaterFallLayout.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WCLWaterFallLayout.xcodeproj/xcuserdata/wangchonglei.xcuserdatad/xcschemes/WCLWaterFallLayout.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /WCLWaterFallLayout.xcodeproj/xcuserdata/wangchonglei.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | WCLWaterFallLayout.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 973557F91E0A8BD2000F6604 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /WCLWaterFallLayout/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // WCLWaterFallLayout 4 | // 5 | // Created by 王崇磊 on 2016/12/21. 6 | // Copyright © 2016年 王崇磊. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /WCLWaterFallLayout/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /WCLWaterFallLayout/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 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /WCLWaterFallLayout/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | Launch Screen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /WCLWaterFallLayout/Launch Screen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 26 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /WCLWaterFallLayout/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // WCLWaterFallLayout 4 | // 5 | // Created by 王崇磊 on 2016/12/21. 6 | // Copyright © 2016年 王崇磊. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController, 12 | UICollectionViewDataSource, 13 | WCLWaterFallLayoutDelegate, 14 | UICollectionViewDelegate { 15 | 16 | var dataCount = 20 17 | var columnCount = 2 18 | @IBOutlet weak var collectionView: UICollectionView! 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | // Do any additional setup after loading the view, typically from a nib. 23 | (collectionView.collectionViewLayout as? WCLWaterFallLayout)?.delegate = self 24 | } 25 | 26 | @IBAction func segmentAction(_ sender: UISegmentedControl) { 27 | defer { 28 | collectionView.reloadData() 29 | } 30 | if sender.selectedSegmentIndex == 1 { 31 | dataCount = 30 32 | columnCount = 3 33 | }else { 34 | dataCount = 20 35 | columnCount = 2 36 | } 37 | } 38 | 39 | 40 | //MARK: UICollectionViewDelegate 41 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 42 | collectionView.deselectItem(at: indexPath, animated: true) 43 | } 44 | 45 | //MARK: UICollectionViewDataSource 46 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 47 | return dataCount 48 | } 49 | 50 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 51 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Identifier", for: indexPath) 52 | cell.contentView.backgroundColor = [UIColor.blue, UIColor.red, UIColor.yellow][indexPath.row % 3] 53 | cell.contentView.clipsToBounds = true 54 | cell.contentView.layer.cornerRadius = 5 55 | return cell 56 | } 57 | 58 | //MARK: WCLWaterFallLayoutDelegate 59 | func waterFall(_ collectionView: UICollectionView, layout waterFallLayout: WCLWaterFallLayout, heightForItemAt indexPath: IndexPath) -> CGFloat { 60 | let height = 200 + arc4random() % 100 61 | return CGFloat(height) 62 | } 63 | 64 | func columnOfWaterFall(_ collectionView: UICollectionView) -> Int { 65 | return columnCount 66 | } 67 | 68 | override func didReceiveMemoryWarning() { 69 | super.didReceiveMemoryWarning() 70 | // Dispose of any resources that can be recreated. 71 | } 72 | 73 | 74 | } 75 | 76 | -------------------------------------------------------------------------------- /WCLWaterFallLayout/WCLWaterFallLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WCLWaterFallLayout.swift 3 | // HotLine 4 | // 5 | // ************************************************** 6 | // * _____ * 7 | // * __ _ __ ___ \ / * 8 | // * \ \/ \/ / / __\ / / * 9 | // * \ _ / | (__ / / * 10 | // * \/ \/ \___/ / /__ * 11 | // * /_____/ * 12 | // * * 13 | // ************************************************** 14 | // Github :https://github.com/631106979 15 | // HomePage:https://imwcl.com 16 | // CSDN :http://blog.csdn.net/wang631106979 17 | // 18 | // Created by 王崇磊 on 16/9/14. 19 | // Copyright © 2016年 王崇磊. All rights reserved. 20 | // 21 | // @class WCLWaterFallLayout 22 | // @abstract 瀑布流的layout 23 | // @discussion 瀑布流的layout 24 | // 25 | 26 | import UIKit 27 | 28 | @objc protocol WCLWaterFallLayoutDelegate { 29 | //waterFall的列数 30 | func columnOfWaterFall(_ collectionView: UICollectionView) -> Int 31 | //每个item的高度 32 | func waterFall(_ collectionView: UICollectionView, layout waterFallLayout: WCLWaterFallLayout, heightForItemAt indexPath: IndexPath) -> CGFloat 33 | } 34 | 35 | 36 | class WCLWaterFallLayout: UICollectionViewLayout { 37 | 38 | //代理 39 | weak var delegate: WCLWaterFallLayoutDelegate? 40 | //行间距 41 | @IBInspectable var lineSpacing: CGFloat = 0 42 | //列间距 43 | @IBInspectable var columnSpacing: CGFloat = 0 44 | //section的top 45 | @IBInspectable var sectionTop: CGFloat = 0 { 46 | willSet { 47 | sectionInsets.top = newValue 48 | } 49 | } 50 | //section的Bottom 51 | @IBInspectable var sectionBottom: CGFloat = 0 { 52 | willSet { 53 | sectionInsets.bottom = newValue 54 | } 55 | } 56 | //section的left 57 | @IBInspectable var sectionLeft: CGFloat = 0 { 58 | willSet { 59 | sectionInsets.left = newValue 60 | } 61 | } 62 | //section的right 63 | @IBInspectable var sectionRight: CGFloat = 0 { 64 | willSet { 65 | sectionInsets.right = newValue 66 | } 67 | } 68 | //section的Insets 69 | @IBInspectable var sectionInsets: UIEdgeInsets = UIEdgeInsets.zero 70 | //每行对应的高度 71 | private var columnHeights: [Int: CGFloat] = [Int: CGFloat]() 72 | private var attributes: [UICollectionViewLayoutAttributes] = [UICollectionViewLayoutAttributes]() 73 | 74 | //MARK: Initial Methods 75 | init(lineSpacing: CGFloat, columnSpacing: CGFloat, sectionInsets: UIEdgeInsets) { 76 | super.init() 77 | self.lineSpacing = lineSpacing 78 | self.columnSpacing = columnSpacing 79 | self.sectionInsets = sectionInsets 80 | } 81 | 82 | required init?(coder aDecoder: NSCoder) { 83 | super.init(coder: aDecoder) 84 | } 85 | 86 | //MARK: Public Methods 87 | 88 | 89 | //MARK: Override 90 | override var collectionViewContentSize: CGSize { 91 | var maxHeight: CGFloat = 0 92 | for height in columnHeights.values { 93 | if height > maxHeight { 94 | maxHeight = height 95 | } 96 | } 97 | return CGSize.init(width: collectionView?.frame.width ?? 0, height: maxHeight + sectionInsets.bottom) 98 | } 99 | 100 | override func prepare() { 101 | super.prepare() 102 | guard collectionView != nil else { 103 | return 104 | } 105 | if let columnCount = delegate?.columnOfWaterFall(collectionView!) { 106 | for i in 0.. UICollectionViewLayoutAttributes? { 120 | if let collectionView = collectionView { 121 | //根据indexPath获取item的attributes 122 | let att = UICollectionViewLayoutAttributes.init(forCellWith: indexPath) 123 | //获取collectionView的宽度 124 | let width = collectionView.frame.width 125 | if let columnCount = delegate?.columnOfWaterFall(collectionView) { 126 | guard columnCount > 0 else { 127 | return nil 128 | } 129 | //item的宽度 = (collectionView的宽度 - 内边距与列间距) / 列数 130 | let totalWidth = (width - sectionInsets.left - sectionInsets.right - (CGFloat(columnCount) - 1) * columnSpacing) 131 | let itemWidth = totalWidth / CGFloat(columnCount) 132 | //获取item的高度,由外界计算得到 133 | let itemHeight = delegate?.waterFall(collectionView, layout: self, heightForItemAt: indexPath) ?? 0 134 | //找出最短的那一列 135 | var minIndex = 0 136 | for column in columnHeights { 137 | if column.value < columnHeights[minIndex] ?? 0 { 138 | minIndex = column.key 139 | } 140 | } 141 | //根据最短列的列数计算item的x值 142 | let itemX = sectionInsets.left + (columnSpacing + itemWidth) * CGFloat(minIndex) 143 | //item的y值 = 最短列的最大y值 + 行间距 144 | let itemY = (columnHeights[minIndex] ?? 0) + lineSpacing 145 | //设置attributes的frame 146 | att.frame = CGRect.init(x: itemX, y: itemY, width: itemWidth, height: itemHeight) 147 | //更新字典中的最大y值 148 | columnHeights[minIndex] = att.frame.maxY 149 | } 150 | return att 151 | } 152 | return nil 153 | } 154 | 155 | override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 156 | return attributes 157 | } 158 | } 159 | --------------------------------------------------------------------------------