├── .DS_Store ├── .gitignore ├── LICENSE ├── README.md └── ZYColumnViewDemo ├── .DS_Store ├── ZYColumnViewDemo.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata └── ZYColumnViewDemo ├── .DS_Store ├── AppDelegate.swift ├── Assets.xcassets └── AppIcon.appiconset │ └── Contents.json ├── Base.lproj └── LaunchScreen.storyboard ├── Info.plist ├── Introduce └── column.gif ├── OC-ZYColumnViewController.zip ├── ViewController.swift └── ZYColumnView ├── .DS_Store ├── ZYColumnConfig.swift ├── ZYColumnDownPromptView.swift ├── ZYColumnHScrollView.swift ├── ZYColumnItem.swift ├── ZYColumnResource ├── column_edit_button@2x.png ├── column_edit_delete@2x.png ├── column_item_bg@2x.png └── column_spread@2x.png ├── ZYColumnSpreadView.swift ├── ZYColumnUpPromptView.swift └── ZYColumnViewController.swift /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r9ronaldozhang/ZYColumnViewController/cb8a7c396312af0aa4fdb430c2f45d24769e442a/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 r9ronaldozhang 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 | # ZYColumnViewController 2 | 仿网易新闻 频道选择器 支持swift3.0 简单易用 编译测试 XCode 8.0 XCode8.1 3 | 4 | ## 关于 ZYColumnViewController 5 | * ZYColumnViewController是一款简单易用的轻量级频道选择控件,其UI界面参考网易新闻。但是我为用户留下大量的可定制DIY常量供使用者修改以满足各自项目的实际需求 6 | 7 | ## 支持版本 8 | * 目前此控件尽支持swift3.0 已经在XCode8.0以及XCode8.1上进行了编译测试。需要OC版本或者支持swift2.3的可以issues我 9 | 10 | ## OC 版本已经加入 11 | * 看来还是有不少OC的死忠,已经上传了OC版本,花了1个小时调试... 12 | * 解压包内的压缩文件,就是OC的DEMO,源文件使用跟swift的一致,详见DEMO 13 | 14 | ## 如何使用 15 | * 下载 16 | * 本控件暂时不支持cocoapods 需要下载到本地 17 | * 集成和使用 18 | * 控件的内容存在于演示Demo中,将整个ZYColumnView 文件夹内的文件拖拽到你的项目中即可,无其他的任何依赖 19 | * ZYColumnViewController 力求最精简的API 让用户不需要关注太多 用户只需要按照Demo中的方法初始化,并且设置代理以后即可通过三个代理方法拿到你需要的一切回调 20 | * 如果你不需要下面的indicator指示条,可以进行简单处理,在配置文件中,将高度调整为0,或者将颜色设置为透明即可 21 | * 你需要关注的 22 | * 面向你的是 ZYColumnViewController 里面的三个属性 以及三个代理方法 23 | * DIY 保证你的项目能达到你想要的效果,去ZYColumnConfig 里面去捣腾吧! 24 | 25 | ## 效果图 26 | 27 |   ** gif 易挂 复制链接到浏览器看效果 http://www.code4app.com/data/attachment/forum/201610/30/141214iw12tueoo9ex9x2e.gif 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /ZYColumnViewDemo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r9ronaldozhang/ZYColumnViewController/cb8a7c396312af0aa4fdb430c2f45d24769e442a/ZYColumnViewDemo/.DS_Store -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 79C4B9731DC3A6600047EF23 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C4B9721DC3A6600047EF23 /* AppDelegate.swift */; }; 11 | 79C4B9751DC3A6600047EF23 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C4B9741DC3A6600047EF23 /* ViewController.swift */; }; 12 | 79C4B97A1DC3A6600047EF23 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 79C4B9791DC3A6600047EF23 /* Assets.xcassets */; }; 13 | 79C4B97D1DC3A6600047EF23 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 79C4B97B1DC3A6600047EF23 /* LaunchScreen.storyboard */; }; 14 | 79C4B9911DC3A69B0047EF23 /* ZYColumnConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C4B9851DC3A69B0047EF23 /* ZYColumnConfig.swift */; }; 15 | 79C4B9921DC3A69B0047EF23 /* ZYColumnDownPromptView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C4B9861DC3A69B0047EF23 /* ZYColumnDownPromptView.swift */; }; 16 | 79C4B9931DC3A69B0047EF23 /* ZYColumnHScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C4B9871DC3A69B0047EF23 /* ZYColumnHScrollView.swift */; }; 17 | 79C4B9941DC3A69B0047EF23 /* ZYColumnItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C4B9881DC3A69B0047EF23 /* ZYColumnItem.swift */; }; 18 | 79C4B9951DC3A69B0047EF23 /* column_edit_button@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 79C4B98A1DC3A69B0047EF23 /* column_edit_button@2x.png */; }; 19 | 79C4B9961DC3A69B0047EF23 /* column_edit_delete@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 79C4B98B1DC3A69B0047EF23 /* column_edit_delete@2x.png */; }; 20 | 79C4B9971DC3A69B0047EF23 /* column_item_bg@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 79C4B98C1DC3A69B0047EF23 /* column_item_bg@2x.png */; }; 21 | 79C4B9981DC3A69B0047EF23 /* column_spread@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 79C4B98D1DC3A69B0047EF23 /* column_spread@2x.png */; }; 22 | 79C4B9991DC3A69B0047EF23 /* ZYColumnSpreadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C4B98E1DC3A69B0047EF23 /* ZYColumnSpreadView.swift */; }; 23 | 79C4B99A1DC3A69B0047EF23 /* ZYColumnUpPromptView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C4B98F1DC3A69B0047EF23 /* ZYColumnUpPromptView.swift */; }; 24 | 79C4B99B1DC3A69B0047EF23 /* ZYColumnViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C4B9901DC3A69B0047EF23 /* ZYColumnViewController.swift */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 79C4B96F1DC3A6600047EF23 /* ZYColumnViewDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ZYColumnViewDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 79C4B9721DC3A6600047EF23 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 30 | 79C4B9741DC3A6600047EF23 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 31 | 79C4B9791DC3A6600047EF23 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 32 | 79C4B97C1DC3A6600047EF23 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 33 | 79C4B97E1DC3A6600047EF23 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 34 | 79C4B9851DC3A69B0047EF23 /* ZYColumnConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZYColumnConfig.swift; sourceTree = ""; }; 35 | 79C4B9861DC3A69B0047EF23 /* ZYColumnDownPromptView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZYColumnDownPromptView.swift; sourceTree = ""; }; 36 | 79C4B9871DC3A69B0047EF23 /* ZYColumnHScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZYColumnHScrollView.swift; sourceTree = ""; }; 37 | 79C4B9881DC3A69B0047EF23 /* ZYColumnItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZYColumnItem.swift; sourceTree = ""; }; 38 | 79C4B98A1DC3A69B0047EF23 /* column_edit_button@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "column_edit_button@2x.png"; sourceTree = ""; }; 39 | 79C4B98B1DC3A69B0047EF23 /* column_edit_delete@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "column_edit_delete@2x.png"; sourceTree = ""; }; 40 | 79C4B98C1DC3A69B0047EF23 /* column_item_bg@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "column_item_bg@2x.png"; sourceTree = ""; }; 41 | 79C4B98D1DC3A69B0047EF23 /* column_spread@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "column_spread@2x.png"; sourceTree = ""; }; 42 | 79C4B98E1DC3A69B0047EF23 /* ZYColumnSpreadView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZYColumnSpreadView.swift; sourceTree = ""; }; 43 | 79C4B98F1DC3A69B0047EF23 /* ZYColumnUpPromptView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZYColumnUpPromptView.swift; sourceTree = ""; }; 44 | 79C4B9901DC3A69B0047EF23 /* ZYColumnViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZYColumnViewController.swift; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 79C4B96C1DC3A6600047EF23 /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXFrameworksBuildPhase section */ 56 | 57 | /* Begin PBXGroup section */ 58 | 79C4B9661DC3A6600047EF23 = { 59 | isa = PBXGroup; 60 | children = ( 61 | 79C4B9711DC3A6600047EF23 /* ZYColumnViewDemo */, 62 | 79C4B9701DC3A6600047EF23 /* Products */, 63 | ); 64 | sourceTree = ""; 65 | }; 66 | 79C4B9701DC3A6600047EF23 /* Products */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | 79C4B96F1DC3A6600047EF23 /* ZYColumnViewDemo.app */, 70 | ); 71 | name = Products; 72 | sourceTree = ""; 73 | }; 74 | 79C4B9711DC3A6600047EF23 /* ZYColumnViewDemo */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 79C4B9841DC3A69B0047EF23 /* ZYColumnView */, 78 | 79C4B9721DC3A6600047EF23 /* AppDelegate.swift */, 79 | 79C4B9741DC3A6600047EF23 /* ViewController.swift */, 80 | 79C4B9791DC3A6600047EF23 /* Assets.xcassets */, 81 | 79C4B97B1DC3A6600047EF23 /* LaunchScreen.storyboard */, 82 | 79C4B97E1DC3A6600047EF23 /* Info.plist */, 83 | ); 84 | path = ZYColumnViewDemo; 85 | sourceTree = ""; 86 | }; 87 | 79C4B9841DC3A69B0047EF23 /* ZYColumnView */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | 79C4B9851DC3A69B0047EF23 /* ZYColumnConfig.swift */, 91 | 79C4B9861DC3A69B0047EF23 /* ZYColumnDownPromptView.swift */, 92 | 79C4B9871DC3A69B0047EF23 /* ZYColumnHScrollView.swift */, 93 | 79C4B9881DC3A69B0047EF23 /* ZYColumnItem.swift */, 94 | 79C4B9891DC3A69B0047EF23 /* ZYColumnResource */, 95 | 79C4B98E1DC3A69B0047EF23 /* ZYColumnSpreadView.swift */, 96 | 79C4B98F1DC3A69B0047EF23 /* ZYColumnUpPromptView.swift */, 97 | 79C4B9901DC3A69B0047EF23 /* ZYColumnViewController.swift */, 98 | ); 99 | path = ZYColumnView; 100 | sourceTree = ""; 101 | }; 102 | 79C4B9891DC3A69B0047EF23 /* ZYColumnResource */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 79C4B98A1DC3A69B0047EF23 /* column_edit_button@2x.png */, 106 | 79C4B98B1DC3A69B0047EF23 /* column_edit_delete@2x.png */, 107 | 79C4B98C1DC3A69B0047EF23 /* column_item_bg@2x.png */, 108 | 79C4B98D1DC3A69B0047EF23 /* column_spread@2x.png */, 109 | ); 110 | path = ZYColumnResource; 111 | sourceTree = ""; 112 | }; 113 | /* End PBXGroup section */ 114 | 115 | /* Begin PBXNativeTarget section */ 116 | 79C4B96E1DC3A6600047EF23 /* ZYColumnViewDemo */ = { 117 | isa = PBXNativeTarget; 118 | buildConfigurationList = 79C4B9811DC3A6600047EF23 /* Build configuration list for PBXNativeTarget "ZYColumnViewDemo" */; 119 | buildPhases = ( 120 | 79C4B96B1DC3A6600047EF23 /* Sources */, 121 | 79C4B96C1DC3A6600047EF23 /* Frameworks */, 122 | 79C4B96D1DC3A6600047EF23 /* Resources */, 123 | ); 124 | buildRules = ( 125 | ); 126 | dependencies = ( 127 | ); 128 | name = ZYColumnViewDemo; 129 | productName = ZYColumnViewDemo; 130 | productReference = 79C4B96F1DC3A6600047EF23 /* ZYColumnViewDemo.app */; 131 | productType = "com.apple.product-type.application"; 132 | }; 133 | /* End PBXNativeTarget section */ 134 | 135 | /* Begin PBXProject section */ 136 | 79C4B9671DC3A6600047EF23 /* Project object */ = { 137 | isa = PBXProject; 138 | attributes = { 139 | LastSwiftUpdateCheck = 0800; 140 | LastUpgradeCheck = 0910; 141 | ORGANIZATIONNAME = "成都拓尔思"; 142 | TargetAttributes = { 143 | 79C4B96E1DC3A6600047EF23 = { 144 | CreatedOnToolsVersion = 8.0; 145 | LastSwiftMigration = 0910; 146 | ProvisioningStyle = Automatic; 147 | }; 148 | }; 149 | }; 150 | buildConfigurationList = 79C4B96A1DC3A6600047EF23 /* Build configuration list for PBXProject "ZYColumnViewDemo" */; 151 | compatibilityVersion = "Xcode 3.2"; 152 | developmentRegion = English; 153 | hasScannedForEncodings = 0; 154 | knownRegions = ( 155 | en, 156 | Base, 157 | ); 158 | mainGroup = 79C4B9661DC3A6600047EF23; 159 | productRefGroup = 79C4B9701DC3A6600047EF23 /* Products */; 160 | projectDirPath = ""; 161 | projectRoot = ""; 162 | targets = ( 163 | 79C4B96E1DC3A6600047EF23 /* ZYColumnViewDemo */, 164 | ); 165 | }; 166 | /* End PBXProject section */ 167 | 168 | /* Begin PBXResourcesBuildPhase section */ 169 | 79C4B96D1DC3A6600047EF23 /* Resources */ = { 170 | isa = PBXResourcesBuildPhase; 171 | buildActionMask = 2147483647; 172 | files = ( 173 | 79C4B9971DC3A69B0047EF23 /* column_item_bg@2x.png in Resources */, 174 | 79C4B9961DC3A69B0047EF23 /* column_edit_delete@2x.png in Resources */, 175 | 79C4B9951DC3A69B0047EF23 /* column_edit_button@2x.png in Resources */, 176 | 79C4B97D1DC3A6600047EF23 /* LaunchScreen.storyboard in Resources */, 177 | 79C4B97A1DC3A6600047EF23 /* Assets.xcassets in Resources */, 178 | 79C4B9981DC3A69B0047EF23 /* column_spread@2x.png in Resources */, 179 | ); 180 | runOnlyForDeploymentPostprocessing = 0; 181 | }; 182 | /* End PBXResourcesBuildPhase section */ 183 | 184 | /* Begin PBXSourcesBuildPhase section */ 185 | 79C4B96B1DC3A6600047EF23 /* Sources */ = { 186 | isa = PBXSourcesBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | 79C4B99B1DC3A69B0047EF23 /* ZYColumnViewController.swift in Sources */, 190 | 79C4B99A1DC3A69B0047EF23 /* ZYColumnUpPromptView.swift in Sources */, 191 | 79C4B9751DC3A6600047EF23 /* ViewController.swift in Sources */, 192 | 79C4B9941DC3A69B0047EF23 /* ZYColumnItem.swift in Sources */, 193 | 79C4B9921DC3A69B0047EF23 /* ZYColumnDownPromptView.swift in Sources */, 194 | 79C4B9931DC3A69B0047EF23 /* ZYColumnHScrollView.swift in Sources */, 195 | 79C4B9991DC3A69B0047EF23 /* ZYColumnSpreadView.swift in Sources */, 196 | 79C4B9911DC3A69B0047EF23 /* ZYColumnConfig.swift in Sources */, 197 | 79C4B9731DC3A6600047EF23 /* AppDelegate.swift in Sources */, 198 | ); 199 | runOnlyForDeploymentPostprocessing = 0; 200 | }; 201 | /* End PBXSourcesBuildPhase section */ 202 | 203 | /* Begin PBXVariantGroup section */ 204 | 79C4B97B1DC3A6600047EF23 /* LaunchScreen.storyboard */ = { 205 | isa = PBXVariantGroup; 206 | children = ( 207 | 79C4B97C1DC3A6600047EF23 /* Base */, 208 | ); 209 | name = LaunchScreen.storyboard; 210 | sourceTree = ""; 211 | }; 212 | /* End PBXVariantGroup section */ 213 | 214 | /* Begin XCBuildConfiguration section */ 215 | 79C4B97F1DC3A6600047EF23 /* Debug */ = { 216 | isa = XCBuildConfiguration; 217 | buildSettings = { 218 | ALWAYS_SEARCH_USER_PATHS = NO; 219 | CLANG_ANALYZER_NONNULL = YES; 220 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 221 | CLANG_CXX_LIBRARY = "libc++"; 222 | CLANG_ENABLE_MODULES = YES; 223 | CLANG_ENABLE_OBJC_ARC = YES; 224 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 225 | CLANG_WARN_BOOL_CONVERSION = YES; 226 | CLANG_WARN_COMMA = YES; 227 | CLANG_WARN_CONSTANT_CONVERSION = YES; 228 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 229 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 230 | CLANG_WARN_EMPTY_BODY = YES; 231 | CLANG_WARN_ENUM_CONVERSION = YES; 232 | CLANG_WARN_INFINITE_RECURSION = YES; 233 | CLANG_WARN_INT_CONVERSION = YES; 234 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 235 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 236 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 237 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 238 | CLANG_WARN_STRICT_PROTOTYPES = YES; 239 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 240 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 241 | CLANG_WARN_UNREACHABLE_CODE = YES; 242 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 243 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 244 | COPY_PHASE_STRIP = NO; 245 | DEBUG_INFORMATION_FORMAT = dwarf; 246 | ENABLE_STRICT_OBJC_MSGSEND = YES; 247 | ENABLE_TESTABILITY = YES; 248 | GCC_C_LANGUAGE_STANDARD = gnu99; 249 | GCC_DYNAMIC_NO_PIC = NO; 250 | GCC_NO_COMMON_BLOCKS = YES; 251 | GCC_OPTIMIZATION_LEVEL = 0; 252 | GCC_PREPROCESSOR_DEFINITIONS = ( 253 | "DEBUG=1", 254 | "$(inherited)", 255 | ); 256 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 257 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 258 | GCC_WARN_UNDECLARED_SELECTOR = YES; 259 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 260 | GCC_WARN_UNUSED_FUNCTION = YES; 261 | GCC_WARN_UNUSED_VARIABLE = YES; 262 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 263 | MTL_ENABLE_DEBUG_INFO = YES; 264 | ONLY_ACTIVE_ARCH = YES; 265 | SDKROOT = iphoneos; 266 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 267 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 268 | }; 269 | name = Debug; 270 | }; 271 | 79C4B9801DC3A6600047EF23 /* Release */ = { 272 | isa = XCBuildConfiguration; 273 | buildSettings = { 274 | ALWAYS_SEARCH_USER_PATHS = NO; 275 | CLANG_ANALYZER_NONNULL = YES; 276 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 277 | CLANG_CXX_LIBRARY = "libc++"; 278 | CLANG_ENABLE_MODULES = YES; 279 | CLANG_ENABLE_OBJC_ARC = YES; 280 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 281 | CLANG_WARN_BOOL_CONVERSION = YES; 282 | CLANG_WARN_COMMA = YES; 283 | CLANG_WARN_CONSTANT_CONVERSION = YES; 284 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 285 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 286 | CLANG_WARN_EMPTY_BODY = YES; 287 | CLANG_WARN_ENUM_CONVERSION = YES; 288 | CLANG_WARN_INFINITE_RECURSION = YES; 289 | CLANG_WARN_INT_CONVERSION = YES; 290 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 291 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 292 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 293 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 294 | CLANG_WARN_STRICT_PROTOTYPES = YES; 295 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 296 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 297 | CLANG_WARN_UNREACHABLE_CODE = YES; 298 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 299 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 300 | COPY_PHASE_STRIP = NO; 301 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 302 | ENABLE_NS_ASSERTIONS = NO; 303 | ENABLE_STRICT_OBJC_MSGSEND = YES; 304 | GCC_C_LANGUAGE_STANDARD = gnu99; 305 | GCC_NO_COMMON_BLOCKS = YES; 306 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 307 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 308 | GCC_WARN_UNDECLARED_SELECTOR = YES; 309 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 310 | GCC_WARN_UNUSED_FUNCTION = YES; 311 | GCC_WARN_UNUSED_VARIABLE = YES; 312 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 313 | MTL_ENABLE_DEBUG_INFO = NO; 314 | SDKROOT = iphoneos; 315 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 316 | VALIDATE_PRODUCT = YES; 317 | }; 318 | name = Release; 319 | }; 320 | 79C4B9821DC3A6600047EF23 /* Debug */ = { 321 | isa = XCBuildConfiguration; 322 | buildSettings = { 323 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 324 | INFOPLIST_FILE = ZYColumnViewDemo/Info.plist; 325 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 326 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 327 | PRODUCT_BUNDLE_IDENTIFIER = com.trs.test.ZYColumnViewDemo; 328 | PRODUCT_NAME = "$(TARGET_NAME)"; 329 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 330 | SWIFT_VERSION = 4.0; 331 | }; 332 | name = Debug; 333 | }; 334 | 79C4B9831DC3A6600047EF23 /* Release */ = { 335 | isa = XCBuildConfiguration; 336 | buildSettings = { 337 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 338 | INFOPLIST_FILE = ZYColumnViewDemo/Info.plist; 339 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 340 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 341 | PRODUCT_BUNDLE_IDENTIFIER = com.trs.test.ZYColumnViewDemo; 342 | PRODUCT_NAME = "$(TARGET_NAME)"; 343 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 344 | SWIFT_VERSION = 4.0; 345 | }; 346 | name = Release; 347 | }; 348 | /* End XCBuildConfiguration section */ 349 | 350 | /* Begin XCConfigurationList section */ 351 | 79C4B96A1DC3A6600047EF23 /* Build configuration list for PBXProject "ZYColumnViewDemo" */ = { 352 | isa = XCConfigurationList; 353 | buildConfigurations = ( 354 | 79C4B97F1DC3A6600047EF23 /* Debug */, 355 | 79C4B9801DC3A6600047EF23 /* Release */, 356 | ); 357 | defaultConfigurationIsVisible = 0; 358 | defaultConfigurationName = Release; 359 | }; 360 | 79C4B9811DC3A6600047EF23 /* Build configuration list for PBXNativeTarget "ZYColumnViewDemo" */ = { 361 | isa = XCConfigurationList; 362 | buildConfigurations = ( 363 | 79C4B9821DC3A6600047EF23 /* Debug */, 364 | 79C4B9831DC3A6600047EF23 /* Release */, 365 | ); 366 | defaultConfigurationIsVisible = 0; 367 | defaultConfigurationName = Release; 368 | }; 369 | /* End XCConfigurationList section */ 370 | }; 371 | rootObject = 79C4B9671DC3A6600047EF23 /* Project object */; 372 | } 373 | -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r9ronaldozhang/ZYColumnViewController/cb8a7c396312af0aa4fdb430c2f45d24769e442a/ZYColumnViewDemo/ZYColumnViewDemo/.DS_Store -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ZYColumnViewDemo 4 | // 5 | // Created by 张宇 on 2016/10/28. 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 | 19 | window = UIWindow(frame: UIScreen.main.bounds) 20 | let navVC = UINavigationController(rootViewController: ViewController()) 21 | let tbVC = UITabBarController() 22 | tbVC.addChildViewController(navVC) 23 | tbVC.title = "首页" 24 | window?.rootViewController = tbVC 25 | window?.makeKeyAndVisible() 26 | return true 27 | } 28 | 29 | func applicationWillResignActive(_ application: UIApplication) { 30 | // 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. 31 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 32 | } 33 | 34 | func applicationDidEnterBackground(_ application: UIApplication) { 35 | // 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. 36 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 37 | } 38 | 39 | func applicationWillEnterForeground(_ application: UIApplication) { 40 | // 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. 41 | } 42 | 43 | func applicationDidBecomeActive(_ application: UIApplication) { 44 | // 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. 45 | } 46 | 47 | func applicationWillTerminate(_ application: UIApplication) { 48 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 49 | } 50 | 51 | 52 | } 53 | 54 | -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo/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 | 27 | 28 | -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo/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 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo/Introduce/column.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r9ronaldozhang/ZYColumnViewController/cb8a7c396312af0aa4fdb430c2f45d24769e442a/ZYColumnViewDemo/ZYColumnViewDemo/Introduce/column.gif -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo/OC-ZYColumnViewController.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r9ronaldozhang/ZYColumnViewController/cb8a7c396312af0aa4fdb430c2f45d24769e442a/ZYColumnViewDemo/ZYColumnViewDemo/OC-ZYColumnViewController.zip -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ZYColumnViewDemo 4 | // 5 | // Created by 张宇 on 2016/10/28. 6 | // Copyright © 2016年 成都拓尔思. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController , ZYColumnViewControllerDelegate { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | view.backgroundColor = UIColor.gray 17 | 18 | // 注意,如果frame 位置,显示出现问题,尝试设置这个属性 19 | self.automaticallyAdjustsScrollViewInsets = false 20 | 21 | // 注意,arrayTitles 不能为空 22 | let arrayTitles = ["头条","财经","体育","娱乐圈","段子","健康","图片","军事","精选","国际足球","历史","跟帖","居家"] 23 | let arraySpareTitles = ["房产","直播","轻松一刻","独家","社会","手机","数码","酒香","美女","艺术","读书","情感","论坛","博客","NBA","旅游","跑步","影视","政务","本地","汽车","公开课","游戏","独家","时尚","轻松一刻","社会","漫画"] 24 | self.initColumnVC(arrayTitles, arraySpareTitles, 1) 25 | 26 | } 27 | 28 | /// 使用: 将ZYColumnView 文件夹 拖拽到你的工程中,不需要添加任何的依赖 29 | /// 在ZYColumnConfig里面DIY 按照你需求调整UI 30 | /// 展开view 里面的 item 代码比较简单,修改Item样式 可以直接修改 ZYColumnItem ,不会影响代码逻辑功能 31 | /// 注意,应用默认在导航条位置有一个透明控件,加载到window上,以响应点击导航条,收回展开的操作 size :(屏幕宽度 :64) 32 | /// 创建 columnVC 就这几行代码,啥功能都有了 33 | private func initColumnVC(_ titles : [String], _ spareTitles : [String] , _ fixCount : Int){ 34 | // 初始化columnVC 35 | let columnVC = ZYColumnViewController() 36 | columnVC.view.frame = CGRect(x: 0, y: 64, width: ZYScreenWidth, height: 40) 37 | columnVC.arrayTitles = titles 38 | if spareTitles.count > 0 { 39 | columnVC.arraySpareTitles = spareTitles 40 | } 41 | columnVC.fixedCount = fixCount 42 | columnVC.delegate = self 43 | view.addSubview(columnVC.view) 44 | addChildViewController(columnVC) 45 | } 46 | 47 | /// 三个代理方法,能够满足你需求的数据 48 | func columnViewControllerSetTitle(setTitle: String, index: Int) { 49 | 50 | } 51 | 52 | func columnViewControllerSelectedTitle(selectedTitle: String, index: Int) { 53 | 54 | } 55 | 56 | func columnViewControllerTitlesDidChanged(arrayTitles: [String]!, spareTitles: [String]?) { 57 | 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo/ZYColumnView/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r9ronaldozhang/ZYColumnViewController/cb8a7c396312af0aa4fdb430c2f45d24769e442a/ZYColumnViewDemo/ZYColumnViewDemo/ZYColumnView/.DS_Store -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo/ZYColumnView/ZYColumnConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ZYColumnConfig.swift 3 | // 掌上遂宁 4 | // 5 | // Created by 张宇 on 2016/10/10. 6 | // Copyright © 2016年 张宇. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /******** 在此文件中定义了此控件常用的常用尺寸变量,用户可根据自己需求进行修改 ********/ 12 | 13 | let kColumnScreenW = UIScreen.main.bounds.size.width 14 | let kColumnScreenH = UIScreen.main.bounds.size.height 15 | 16 | let kColumnItemEqualWidth = true // 默认横向item是否等宽 还是根据文字匹配 17 | let kColumnViewH:CGFloat = 40.0 // 默认高度 18 | let kColumnViewW = kColumnScreenW // 默认宽度 19 | let kSpreadMaxH:CGFloat = kColumnScreenH - 64 // 默认下拉展开的最大高度 20 | let kSpreadDuration = 0.4 // 动画展开收拢的时长 21 | let kColumnLayoutCount = 4 // 排序item 每行个数 22 | let kColumnItemH:CGFloat = (ZYinch55()) ? 40: 30 // item的高度 23 | let kColumnItemW:CGFloat = (ZYinch55()) ? 85: 70 // item的宽度 24 | let kColumnMarginW:CGFloat = (ZYScreenWidth-CGFloat(kColumnLayoutCount)*kColumnItemW)/CGFloat(kColumnLayoutCount+1) // item的横向间距 25 | let kColumnMarginH:CGFloat = (ZYinch55()) ? 20:((ZYinch47()) ? 20: 15) // item的纵向间距 26 | let kColumnEditBtnW:CGFloat = 60 // 排序删除按钮的宽度 27 | let kColumnEditBtnH:CGFloat = 36 // 排序删除按钮的高度 28 | let kColumnEditBtnFont:CGFloat = 10 // 排序删除按钮的字体 29 | let kColumnEditBtnNorTitle = "排序删除" // 排序删除普通状态文字 30 | let kColumnEditBtnSelTitle = "完成" // 排序删除选中状态文字 31 | let kColumnItemBorderColor = UIColor.red.cgColor // item的色环 32 | let kColumnItemColor = UIColor.red // item的文字颜色 33 | 34 | let kColumnTitleMargin:CGFloat = 8 // title按钮之间默认没有间距,这个值保证两个按钮的间距 35 | let kColumnTitleNorColor = UIColor.darkText // title普通状态颜色 36 | let kColumnTitleSelColor = UIColor.red // title选中状态颜色 37 | let kColumnTitleNorFont:CGFloat = (ZYinch47() || ZYinch55()) ? 14 : 13 // title普通状态字体 38 | let kColumnTitleSelFont:CGFloat = (ZYinch47() || ZYinch55()) ? 16 : 15 // title选中状态字体 39 | let kColumnHasIndicator = true // 默认有指示线条 40 | let kColumnIndictorH:CGFloat = 2.0 // 指示线条高度 41 | let kColumnIndictorMinW : CGFloat = 60 // 指示条默认最小宽度 42 | let kColumnIndicatorColor = UIColor.red // 默认使用系统风格颜色 43 | 44 | /************** 常量定义 ****************/ 45 | /// 是否是3.5英寸屏 46 | func ZYinch35() -> Bool { 47 | return UIScreen.main.bounds.size.height == 480.0 48 | } 49 | 50 | /// 是否是4.0英寸屏 51 | func ZYinch40() -> Bool { 52 | return UIScreen.main.bounds.size.height == 568.0 53 | } 54 | /// 是否是4.7英寸屏 55 | func ZYinch47() -> Bool { 56 | return UIScreen.main.bounds.size.height == 667.0 57 | } 58 | /// 是否是5.5英寸屏 59 | func ZYinch55() -> Bool { 60 | return UIScreen.main.bounds.size.height == 736.0 61 | } 62 | 63 | let ZYScreenWidth = UIScreen.main.bounds.width 64 | let ZYScreenHeight = UIScreen.main.bounds.height 65 | -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo/ZYColumnView/ZYColumnDownPromptView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ZYColumnDownPromptView.swift 3 | // 掌上遂宁 4 | // 5 | // Created by 张宇 on 2016/10/10. 6 | // Copyright © 2016年 张宇. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ZYColumnDownPromptView: UIView { 12 | override init(frame: CGRect) { 13 | super.init(frame: frame) 14 | // 初始化提示label 15 | let label = UILabel(frame:CGRect(x: kColumnMarginW, y: 0, width: 150, height: frame.height)) 16 | label.text = "点击添加更多栏目" 17 | label.textColor = UIColor.darkGray 18 | addSubview(label) 19 | } 20 | 21 | required init?(coder aDecoder: NSCoder) { 22 | fatalError("init(coder:) has not been implemented") 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo/ZYColumnView/ZYColumnHScrollView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ZYColumnHScrollView.swift 3 | // 掌上遂宁 4 | // 5 | // Created by 张宇 on 2016/10/10. 6 | // Copyright © 2016年 张宇. All rights reserved. 7 | // 水平滚动的scrollView 8 | 9 | import UIKit 10 | 11 | typealias titleClickClosure = (_ tag: Int, _ title: String) -> Void 12 | 13 | class ZYColumnHScrollView: UIScrollView { 14 | // 闭包 15 | var backClosure : titleClickClosure? // 分类 标题数组 16 | var arrayTitles = [String]() { 17 | didSet { 18 | // 创建标题按钮item 19 | creatScrollItems() 20 | // 初始化自身属性 为了匹配indicator的宽度,这个方法后调用 21 | doInit() 22 | } 23 | } 24 | // 标识是否已经初始化过了 25 | fileprivate var hasInit = false 26 | 27 | // 记录选中的title button 28 | fileprivate var selButton = UIButton() 29 | // 指示器 30 | fileprivate var indicator = UIView() 31 | // 存放按钮的数组 32 | fileprivate var arrayItems = [UIButton]() 33 | } 34 | 35 | // MARK: - 初始化方法 36 | extension ZYColumnHScrollView { 37 | // 初始化一些属性 38 | fileprivate func doInit(){ 39 | if hasInit { 40 | return 41 | } 42 | self.showsHorizontalScrollIndicator = false 43 | self.showsVerticalScrollIndicator = false 44 | guard kColumnHasIndicator else {return} 45 | let indicator = UIView(frame: CGRect(x:0, y: kColumnViewH - kColumnIndictorH, width: arrayItems[0].frame.width, height: kColumnIndictorH)) // 默认取第一个按钮的宽度 46 | self.indicator = indicator 47 | indicator.backgroundColor = kColumnIndicatorColor 48 | insertSubview(indicator, at: 0) 49 | addSubview(indicator) 50 | hasInit = true 51 | } 52 | } 53 | 54 | // MARK: - 点击事件处理 55 | extension ZYColumnHScrollView { 56 | @objc fileprivate func itemDidClick(button : UIButton) { 57 | // 切换选中item 58 | changeItemStatus(button: button) 59 | 60 | // 回调 61 | if self.backClosure != nil { 62 | self.backClosure!(button.tag, (button.titleLabel?.text)!) 63 | } 64 | } 65 | } 66 | 67 | // MARK: - 公共控制方法 68 | extension ZYColumnHScrollView { 69 | 70 | // 将对应的title的item设置为选中状态 71 | open func changeItemToSelected(title : String) { 72 | for item in arrayItems { 73 | if item.titleLabel?.text == title { 74 | changeItemStatus(button: item) 75 | return 76 | } 77 | } 78 | } 79 | } 80 | 81 | // MARK: - 私有控制方法 82 | extension ZYColumnHScrollView { 83 | // 添加横向滚动的item 84 | fileprivate func creatScrollItems(){ 85 | // 先移除已经创建了的 86 | for item in arrayItems { 87 | item.removeFromSuperview() 88 | } 89 | arrayItems.removeAll() 90 | 91 | var sumItemW:CGFloat = 0.0 92 | for i in 0 ..< self.arrayTitles.count{ 93 | let title = arrayTitles[i] 94 | let item = UIButton() 95 | item.setTitle(title, for: UIControlState.normal) 96 | item.sizeToFit() 97 | item.setTitleColor(kColumnTitleNorColor, for: UIControlState.normal) 98 | item.setTitleColor(kColumnTitleSelColor, for: UIControlState.selected) 99 | item.titleLabel?.font = UIFont.systemFont(ofSize: kColumnTitleNorFont) 100 | if kColumnItemEqualWidth { 101 | item.frame.size = CGSize(width: (kColumnScreenW-(CGFloat(kColumnLayoutCount)+1)*kColumnTitleMargin)/CGFloat(kColumnLayoutCount), height: kColumnHasIndicator ? kColumnViewH - kColumnIndictorH : kColumnViewH) 102 | } else { 103 | item.sizeToFit() 104 | item.frame.size = CGSize(width: (item.frame.width + kColumnTitleMargin) > kColumnIndictorMinW ? item.frame.width + kColumnTitleMargin : kColumnIndictorMinW, height: kColumnHasIndicator ? kColumnViewH - kColumnIndictorH : kColumnViewH) // 增加按钮宽度,达到间隙效果 105 | } 106 | item.frame = CGRect(x: sumItemW, y: 0, width: item.frame.width, height: item.frame.height) 107 | sumItemW += item.frame.width 108 | item.tag = i 109 | item.addTarget(self, action: #selector(self.itemDidClick(button:)), for: .touchUpInside) 110 | arrayItems.append(item) 111 | addSubview(item) 112 | contentSize = CGSize(width: sumItemW, height: 0) 113 | } 114 | } 115 | 116 | // 改变选中按钮状态 117 | fileprivate func changeItemStatus(button : UIButton) { 118 | selButton.titleLabel?.font = UIFont.systemFont(ofSize: kColumnTitleNorFont) 119 | selButton.isSelected = false 120 | button.titleLabel?.font = UIFont.systemFont(ofSize: kColumnTitleSelFont) 121 | button.isSelected = true 122 | selButton = button 123 | autoSuitItemsPosition() 124 | UIView.animate(withDuration: 0.3) { () -> Void in 125 | self.indicator.frame = CGRect(x: button.frame.origin.x, y: kColumnViewH - kColumnIndictorH, width: button.frame.width, height: kColumnIndictorH) 126 | } 127 | } 128 | 129 | // 自动匹配scrollView 的位置,让选中的置于中间位置 130 | private func autoSuitItemsPosition() { 131 | UIView.animate(withDuration: kSpreadDuration) { 132 | if self.contentSize.width > self.frame.size.width { 133 | var desiredX = self.selButton.center.x - self.bounds.width/2 134 | if desiredX < 0 { 135 | desiredX = 0 136 | } 137 | if desiredX > (self.contentSize.width - self.bounds.width) { 138 | desiredX = self.contentSize.width - self.bounds.width 139 | } 140 | if !(self.bounds.width > self.contentSize.width) { 141 | self.setContentOffset(CGPoint(x: desiredX, y: 0), animated: true) 142 | } 143 | } 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo/ZYColumnView/ZYColumnItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ZYColumnItem.swift 3 | // 掌上遂宁 4 | // 5 | // Created by 张宇 on 2016/10/10. 6 | // Copyright © 2016年 张宇. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | typealias delClickClosure = (_ item : ZYColumnItem) -> Void 12 | 13 | class ZYColumnItem: UIView { 14 | open var title : String!{ 15 | didSet { 16 | doInit() 17 | } 18 | } 19 | // 标识是否是固定的,固定的item没有圆角,没有背景色效果的 20 | open var isFixed = false { 21 | didSet { 22 | if isFixed { 23 | setupFixedItem() 24 | } 25 | } 26 | } 27 | 28 | var delClosure : delClickClosure? 29 | 30 | var delBtn = UIButton() 31 | 32 | // 拉大item大小 33 | func scaleItemToLarge() { 34 | for item in subviews { 35 | 36 | if item.isKind(of: UIButton.self) && item.frame.width == self.frame.width { 37 | item.bounds = CGRect(x: 0, y: 0, width: self.frame.width * 1.2, height: self.frame.height * 1.2) 38 | } 39 | } 40 | } 41 | // 还原item大小 42 | func scaleItemToOriginal() { 43 | for item in subviews { 44 | if item.isKind(of: UIButton.self) && item.frame.width > self.frame.width { 45 | item.bounds = CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height) 46 | } 47 | } 48 | } 49 | 50 | private func doInit(){ 51 | // 添加按钮 52 | let item = UIButton() 53 | item.frame = CGRect(x:0 , y: 0, width:self.frame.width, height: self.frame.height) 54 | item.setTitle(title, for: .normal) 55 | item.setTitleColor(UIColor.darkGray, for: .normal) 56 | item.setBackgroundImage(UIImage(named: "column_item_bg.png"), for:.normal) 57 | item.isUserInteractionEnabled = false 58 | if ((title as NSString).length == 4) { 59 | item.titleLabel?.font = UIFont.systemFont(ofSize: 13) 60 | } else if ((title as NSString).length == 3) { 61 | item.titleLabel?.font = UIFont.systemFont(ofSize: 15) 62 | } else { 63 | item.titleLabel?.font = UIFont.systemFont(ofSize: 16) 64 | } 65 | self.addSubview(item) 66 | // 添加删除图标 67 | let WH = ZYinch55() ? 20 : (ZYinch47() ? 16 : 14) 68 | delBtn = UIButton(frame: CGRect(x: -1, y: -4, width: WH, height: WH)) 69 | delBtn.setImage(UIImage(named: "column_edit_delete.png"), for: .normal) 70 | delBtn.addTarget(self, action: #selector(self.delBtnDidClick(delBtn:)), for: .touchUpInside) 71 | delBtn.isHidden = true 72 | addSubview(delBtn) 73 | 74 | } 75 | 76 | private func setupFixedItem() { 77 | for button in subviews { 78 | // 判断不是删除的按钮 79 | if button.frame.width == self.frame.width && button.isKind(of: UIButton.self) { 80 | let btn : UIButton = button as! UIButton 81 | btn.setBackgroundImage(nil, for: .normal) 82 | } 83 | } 84 | } 85 | 86 | @objc private func delBtnDidClick(delBtn : UIButton) { 87 | // 闭包传值 88 | if self.delClosure != nil { 89 | self.delClosure!(self) 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo/ZYColumnView/ZYColumnResource/column_edit_button@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r9ronaldozhang/ZYColumnViewController/cb8a7c396312af0aa4fdb430c2f45d24769e442a/ZYColumnViewDemo/ZYColumnViewDemo/ZYColumnView/ZYColumnResource/column_edit_button@2x.png -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo/ZYColumnView/ZYColumnResource/column_edit_delete@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r9ronaldozhang/ZYColumnViewController/cb8a7c396312af0aa4fdb430c2f45d24769e442a/ZYColumnViewDemo/ZYColumnViewDemo/ZYColumnView/ZYColumnResource/column_edit_delete@2x.png -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo/ZYColumnView/ZYColumnResource/column_item_bg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r9ronaldozhang/ZYColumnViewController/cb8a7c396312af0aa4fdb430c2f45d24769e442a/ZYColumnViewDemo/ZYColumnViewDemo/ZYColumnView/ZYColumnResource/column_item_bg@2x.png -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo/ZYColumnView/ZYColumnResource/column_spread@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r9ronaldozhang/ZYColumnViewController/cb8a7c396312af0aa4fdb430c2f45d24769e442a/ZYColumnViewDemo/ZYColumnViewDemo/ZYColumnView/ZYColumnResource/column_spread@2x.png -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo/ZYColumnView/ZYColumnSpreadView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ZYColumnSpreadView.swift 3 | // 掌上遂宁 4 | // 5 | // Created by 张宇 on 2016/10/10. 6 | // Copyright © 2016年 张宇. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | // 上面item点击的闭包 11 | typealias upItemTapClosure = (_ item : ZYColumnItem) -> Void 12 | 13 | // 上面item长按的闭包 14 | typealias upItemLongPressClosure = () -> Void 15 | 16 | // 收起spreadView闭包 17 | typealias foldSpreadViewClosure = (_ upTitles : [String] , _ downTitles : [String]) -> Void 18 | 19 | class ZYColumnSpreadView: UIScrollView { 20 | 21 | // 上面的排序 标题 数组 22 | open var arrayUpTitles = [String]() 23 | // 下面的备选 标题 数组 24 | open var arrayDownTitles = [String]() 25 | // 固定不变的item个数 26 | open var fixedCount = 1 27 | // 标识当前是否排序状态 28 | open var isSortStatus = false 29 | // 上面item tap 的闭包 30 | open var tapClosure : upItemTapClosure? 31 | // 上面item长按闭包 32 | open var longPressClosure : upItemLongPressClosure? 33 | // 收起后的闭包 34 | open var flodClosure : foldSpreadViewClosure? 35 | 36 | /************* 上面是对外属性 -----华丽分割线----- 下面是私有属性 ***************/ 37 | 38 | // 存放up items的数组 39 | fileprivate var arrayUpItems = [ZYColumnItem!]() 40 | // 存放down items 的数组 41 | fileprivate var arrayDownItems = [ZYColumnItem!]() 42 | // 拖动view的下一个view的中心点 逐个赋值 43 | fileprivate var valuePoint = CGPoint(x: 0, y: 0) 44 | // 用于赋值 45 | fileprivate var nextPoint = CGPoint(x: 0, y: 0) 46 | // 下提示view 47 | fileprivate lazy var downPromptView : ZYColumnDownPromptView = { 48 | let upLastItem = self.arrayUpItems.last 49 | let downView = ZYColumnDownPromptView(frame: CGRect(x: 0, y: upLastItem!.frame.maxY + kColumnMarginH, width: ZYScreenWidth, height: kColumnViewH)) 50 | downView.backgroundColor = UIColor.init(red: 240.0/255, green: 241.0/255, blue: 246.0/255, alpha: 1.0) 51 | return downView 52 | }() 53 | } 54 | 55 | // MARK: - 公共方法 56 | extension ZYColumnSpreadView { 57 | /// 展开或者合拢 58 | open func doSpreadOrFold(toSpread : Bool , cover : UIView , upPromptView : UIView) { 59 | UIApplication.shared.keyWindow?.isUserInteractionEnabled = false 60 | UIView.animate(withDuration: kSpreadDuration, animations: { [weak self]() -> Void in 61 | self?.frame = CGRect(x: 0, y: 0, width: kColumnViewW, height: toSpread ? kSpreadMaxH : 0.0) 62 | }) {(_) -> Void in 63 | upPromptView.isHidden = toSpread ? false : true 64 | cover.isHidden = toSpread ? false : true 65 | UIApplication.shared.keyWindow?.isUserInteractionEnabled = true 66 | } 67 | // 添加items 68 | if toSpread && arrayUpItems.isEmpty { 69 | addItems() 70 | } 71 | 72 | // 闭包回传值 73 | if self.flodClosure != nil && !toSpread { 74 | // 同步数组顺序 75 | arrayUpTitles.removeAll() 76 | arrayDownTitles.removeAll() 77 | for i in 0 ..< arrayUpItems.count { 78 | let item = arrayUpItems[i]! 79 | arrayUpTitles.append(item.title) 80 | } 81 | for i in 0 ..< arrayDownItems.count { 82 | let item = arrayDownItems[i]! 83 | arrayDownTitles.append(item.title) 84 | } 85 | self.flodClosure!(arrayUpTitles, arrayDownTitles) 86 | } 87 | } 88 | 89 | /// 隐藏或者显示下面的item和downPromptView 参数传false 则变为 显示 90 | open func hideDownItemsAndPromptView(toHide : Bool) { 91 | if arrayDownItems.isEmpty { // 下面备选只要没有,一定不显示 92 | downPromptView.isHidden = true 93 | } else { 94 | downPromptView.isHidden = toHide 95 | } 96 | for i in 0 ..< arrayDownItems.count { 97 | let item : ZYColumnItem = arrayDownItems[i] 98 | item.isHidden = toHide 99 | } 100 | } 101 | 102 | /// 开启或者关闭排序按钮删除状态 103 | open func openDelStatus(showDel : Bool) { 104 | for i in 0 ..< arrayUpItems.count { 105 | let item : ZYColumnItem = arrayUpItems[i] 106 | if !item.isFixed { 107 | item.delBtn.isHidden = !showDel 108 | } 109 | } 110 | } 111 | 112 | /// 自动回滚到顶部 113 | open func scrollToTop() { 114 | var offect = self.contentOffset; 115 | offect.y = -self.contentInset.top; 116 | self.setContentOffset(offect, animated: false) 117 | } 118 | } 119 | 120 | // MARK: - 私有控制方法 121 | extension ZYColumnSpreadView { 122 | // 通过index 计算出上面item的frame 123 | fileprivate func getUpFrameWithIndex(_ index : Int) -> CGRect { 124 | let x = CGFloat(index%kColumnLayoutCount) * (kColumnMarginW + kColumnItemW) + kColumnMarginW 125 | let y = CGFloat(index/kColumnLayoutCount) * (kColumnMarginH + kColumnItemH) + kColumnMarginH + kColumnViewH 126 | let frame = CGRect(x: x, y: y, width: kColumnItemW, height: kColumnItemH) 127 | return frame 128 | } 129 | // 从下面移动到上面 130 | fileprivate func addItemToUp(item : ZYColumnItem) { 131 | let lastItem = arrayUpItems.last 132 | // 判断下面的item是否需要下移 133 | if (lastItem!.tag + 1) % kColumnLayoutCount == 0 { 134 | moveItemsRowDownOrUp(isUp: false) 135 | } 136 | let newFrame = getUpFrameWithIndex(lastItem!.tag + 1) 137 | UIView.animate(withDuration: kSpreadDuration, animations: { 138 | item.frame = newFrame 139 | }) { (_) in 140 | self.reSetContentSize() 141 | } 142 | } 143 | // 将arrayDownItems 的items 整体下移或者上移一行 包含downPromptView 144 | private func moveItemsRowDownOrUp(isUp : Bool) { 145 | let rowH = isUp ? -(kColumnItemH + kColumnMarginH) : (kColumnItemH + kColumnMarginH) 146 | 147 | downPromptView.frame = CGRect(x: downPromptView.frame.origin.x, y: downPromptView.frame.origin.y+rowH, width: downPromptView.frame.width, height: downPromptView.frame.height) 148 | for i in 0 ..< arrayDownItems.count { 149 | let item = arrayDownItems[i]! 150 | item.frame = CGRect(x: item.frame.origin.x, y: item.frame.origin.y+rowH, width: item.frame.width, height: item.frame.height) 151 | } 152 | } 153 | // 从上面删除 item插入到下面 154 | fileprivate func removeItemFromUp(item : ZYColumnItem) { 155 | item.delBtn.isHidden = true 156 | item.isHidden = true 157 | reLayoutUpItemsWithAnimation() 158 | reLayoutDownItemsWithAnimation(withAnimate: false) 159 | let lastItem = arrayUpItems.last 160 | if (lastItem!.tag + 1)%kColumnLayoutCount == 0 { 161 | moveItemsRowDownOrUp(isUp: true) 162 | } 163 | reSetContentSize() 164 | } 165 | 166 | fileprivate func synchronizeItemsTag() { 167 | var x = 0 168 | for i in 0 ..< arrayUpItems.count { 169 | let item = arrayUpItems[i]! 170 | item.tag = i 171 | x += 1 172 | } 173 | 174 | var j = x 175 | for y in 0 ..< arrayDownItems.count { 176 | let item = arrayDownItems[y]! 177 | item.tag = j 178 | j += 1 179 | } 180 | } 181 | // 重新计算最新的contentSize 182 | private func reSetContentSize() { 183 | let lastItem = arrayDownItems.isEmpty ? arrayUpItems.last : arrayDownItems.last 184 | self.contentSize = CGSize(width: 0, height: lastItem!.frame.maxY + kColumnMarginH) 185 | } 186 | 187 | // 点击后 动态排序上面的item 188 | private func reLayoutUpItemsWithAnimation(){ 189 | for i in 0 ..< arrayUpItems.count { 190 | let item = arrayUpItems[i]! 191 | let newFrame = getUpFrameWithIndex(item.tag) 192 | UIView.animate(withDuration: kSpreadDuration, animations: { 193 | item.frame = newFrame 194 | }) 195 | } 196 | } 197 | // 点击后 动态排序下面的item 198 | fileprivate func reLayoutDownItemsWithAnimation(withAnimate : Bool) { 199 | for i in 0 ..< arrayDownItems.count { 200 | let item = arrayDownItems[i]! 201 | let x = CGFloat((item.tag - arrayUpItems.count) % kColumnLayoutCount) * (kColumnMarginW + kColumnItemW) + kColumnMarginW 202 | let y = CGFloat((item.tag - arrayUpItems.count) / kColumnLayoutCount) * (kColumnMarginH + kColumnItemH) + kColumnMarginH + downPromptView.frame.maxY 203 | if withAnimate { 204 | UIView.animate(withDuration: kSpreadDuration, animations: { 205 | item.frame = CGRect(x: x, y: y, width: item.frame.width, height: item.frame.height) 206 | }) 207 | } else { 208 | item.frame = CGRect(x: x, y: y, width: item.frame.width, height: item.frame.height) 209 | } 210 | } 211 | } 212 | 213 | // 删除图标点击闭包回调处理 214 | fileprivate func handleClosure (item : ZYColumnItem) { 215 | guard !item.isFixed else { 216 | return 217 | } 218 | // 从上面数组移除item 添加到下面数组 219 | arrayUpItems.remove(at: item.tag) 220 | arrayDownItems.insert(item, at: 0) 221 | synchronizeItemsTag() 222 | removeItemFromUp(item: item) 223 | } 224 | } 225 | 226 | // MARK: - 初始化 上 下 item 和 downPromptView 227 | extension ZYColumnSpreadView { 228 | fileprivate func addItems(){ 229 | // 添加上面 items 230 | 231 | for i in 0 ..< arrayUpTitles.count { 232 | let item = ZYColumnItem(frame: getUpFrameWithIndex(i)) // 通过公共方法计算frame 233 | item.title = arrayUpTitles[i] 234 | item.tag = i 235 | if i < fixedCount { 236 | item.isFixed = true 237 | } 238 | // 闭包回调 239 | item.delClosure = { 240 | [weak self](item : ZYColumnItem) -> Void in 241 | self!.handleClosure(item: item) 242 | } 243 | // 添加长按手势 244 | addLongPressGesture(item: item) 245 | 246 | // 添加tap手势 247 | addTapGesture(item: item) 248 | addSubview(item) 249 | arrayUpItems.append(item) 250 | } 251 | 252 | let upLastItem = arrayUpItems.last 253 | // 添加下提示view 254 | insertSubview(downPromptView, at: 0) 255 | guard !arrayDownTitles.isEmpty else { 256 | // 下面没有备选 直接算出spreadView contentSize 257 | self.contentSize = CGSize(width: 0, height: upLastItem!.frame.maxY + kColumnMarginH + downPromptView.frame.height) 258 | return 259 | } 260 | 261 | // 添加下排序items 262 | for i in 0 ..< arrayDownTitles.count { 263 | let x = CGFloat(i%kColumnLayoutCount) * (kColumnMarginW + kColumnItemW) + kColumnMarginW 264 | let y = CGFloat(i/kColumnLayoutCount) * (kColumnMarginH + kColumnItemH) + kColumnMarginH + downPromptView.frame.maxY 265 | let item = ZYColumnItem(frame: CGRect(x: x, y: y, width: kColumnItemW, height: kColumnItemH)) 266 | item.title = arrayDownTitles[i] 267 | item.tag = i + arrayUpItems.count 268 | // 添加长按手势 269 | addLongPressGesture(item: item) 270 | // 添加tap手势 271 | addTapGesture(item: item) 272 | // 闭包回调 273 | item.delClosure = { 274 | [weak self](item : ZYColumnItem) -> Void in 275 | self!.handleClosure(item: item) 276 | } 277 | addSubview(item) 278 | arrayDownItems.append(item) 279 | if i == arrayDownTitles.count - 1 { 280 | let downLastItem = arrayDownItems.last 281 | self.contentSize = CGSize(width: 0, height: downLastItem!.frame.maxY + kColumnMarginH) 282 | } 283 | } 284 | } 285 | } 286 | 287 | // MARK: - 手势绑定 与手势处理 288 | extension ZYColumnSpreadView { 289 | /// 为一个item绑定长按手势 290 | fileprivate func addLongPressGesture(item : ZYColumnItem) { 291 | let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(self.upItmesDidLongPress(longPress:))) 292 | item.addGestureRecognizer(longPressGesture) 293 | } 294 | /// item 长按手势处理 295 | @objc fileprivate func upItmesDidLongPress(longPress: UILongPressGestureRecognizer) { 296 | let item = longPress.view as! ZYColumnItem 297 | if item.isFixed { // 固定的item 不做处理 298 | return 299 | } 300 | // 只响应上面的item的长按事件 301 | for i in 0 ..< arrayUpItems.count { 302 | let element = arrayUpItems[i] 303 | if element == item { 304 | handleLongPressGesture(item: item, longPress: longPress) 305 | return 306 | } 307 | } 308 | } 309 | 310 | /// 为一个item绑定tap手势 311 | fileprivate func addTapGesture(item : ZYColumnItem) { 312 | let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.itemDidTap(tapGesture:))) 313 | item.addGestureRecognizer(tapGesture) 314 | } 315 | // item tap事件处理 316 | @objc fileprivate func itemDidTap(tapGesture : UITapGestureRecognizer) { 317 | let item = tapGesture.view as! ZYColumnItem 318 | 319 | // 点击的是上面的item 回传值 320 | for i in 0 ..< arrayUpItems.count { 321 | let element = arrayUpItems[i] 322 | if element == item { 323 | if isSortStatus { 324 | return 325 | } 326 | // 闭包传值 327 | if self.tapClosure != nil { 328 | self.tapClosure!(item) 329 | } 330 | return 331 | } 332 | } 333 | // 点击的是下面的item 上移 334 | for i in 0 ..< arrayDownItems.count { 335 | let element = arrayDownItems[i] 336 | if element == item { 337 | addItemToUp(item: item) 338 | arrayDownItems.remove(at: item.tag - arrayUpItems.count) 339 | arrayUpItems.append(item) 340 | synchronizeItemsTag() 341 | reLayoutDownItemsWithAnimation(withAnimate: true) 342 | if arrayDownItems.isEmpty { // 当最后一个item都上移了,下面的指示view 隐藏掉 343 | downPromptView.isHidden = true 344 | } 345 | return 346 | } 347 | } 348 | } 349 | 350 | private func handleLongPressGesture(item : ZYColumnItem, longPress : UILongPressGestureRecognizer) { 351 | 352 | // 拿到当前的最新位置 353 | let recognizerPoint = longPress.location(in: self) 354 | 355 | switch longPress.state { 356 | case UIGestureRecognizerState.began: 357 | // 如果没有显示“删除”按钮,回传闭包,开启删除排序模式 358 | if !isSortStatus { 359 | if longPressClosure != nil { 360 | longPressClosure!() 361 | } 362 | } 363 | 364 | // 禁用其他item的交互 365 | for i in 0 ..< arrayUpItems.count { 366 | let element = arrayUpItems[i]! 367 | if element != item { 368 | element.isUserInteractionEnabled = false 369 | } 370 | } 371 | 372 | self.bringSubview(toFront: item) 373 | valuePoint = item.center 374 | item.scaleItemToLarge() 375 | case UIGestureRecognizerState.changed: 376 | // 更新长按item的位置 377 | item.center = recognizerPoint 378 | // 判断item的center是否移动到了其他item的位置上了 379 | for i in 0 ..< arrayUpItems.count { 380 | let element = arrayUpItems[i]! 381 | if element.frame.contains(item.center) && element != item && element.tag >= fixedCount { 382 | // 开始位置索引 383 | let fromIndex = item.tag 384 | // 需要移动到的索引 385 | let toIndex = element.tag 386 | if toIndex > fromIndex { // 往后移 387 | // 把移动view的下一个view移动到记录的view的位置(valuePoint),并把下一view的位置记为新的nextPoint,并把view的tag值-1,依次类推 388 | UIView.animate(withDuration: kSpreadDuration, animations: { 389 | for i in fromIndex + 1 ... toIndex { 390 | let nextItem = self.viewWithTag(i) as! ZYColumnItem 391 | self.nextPoint = nextItem.center 392 | nextItem.center = self.valuePoint 393 | self.valuePoint = self.nextPoint 394 | nextItem.tag -= 1 395 | } 396 | item.tag = toIndex 397 | }) 398 | } else { // 往前移 399 | // 把移动view的上一个view移动到记录的view的位置(valuePoint),并把上一view的位置记为新的nextPoint,并把view的tag值+1,依次类推 400 | UIView.animate(withDuration: kSpreadDuration, animations: { 401 | for i in (toIndex ... fromIndex - 1).reversed(){ 402 | let nextItem = self.viewWithTag(i) as! ZYColumnItem 403 | self.nextPoint = nextItem.center 404 | nextItem.center = self.valuePoint 405 | self.valuePoint = self.nextPoint 406 | nextItem.tag += 1 407 | } 408 | item.tag = toIndex 409 | }) 410 | } 411 | } 412 | } 413 | case UIGestureRecognizerState.ended: 414 | // 恢复其他item的交互 415 | for i in 0 ..< arrayUpItems.count { 416 | let element = arrayUpItems[i]! 417 | if element != item { 418 | element.isUserInteractionEnabled = true 419 | } 420 | } 421 | // 还原item的大小 422 | item.scaleItemToOriginal() 423 | // 返回对应的中心点 424 | UIView.animate(withDuration: kSpreadDuration, animations: { 425 | }) 426 | UIView.animate(withDuration: kSpreadDuration, animations: { 427 | item.center = self.valuePoint 428 | 429 | }, completion: { [weak self](_) in 430 | // 同步数组 431 | var temp = [ZYColumnItem!]() 432 | for i in 0 ..< self!.arrayUpItems.count { 433 | for j in 0 ..< self!.arrayUpItems.count { 434 | let item = self!.arrayUpItems[j]! 435 | if item.tag == i { 436 | temp.append(item) 437 | break 438 | } 439 | } 440 | } 441 | self!.arrayUpItems = temp 442 | }) 443 | default: 444 | break 445 | } 446 | } 447 | } 448 | -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo/ZYColumnView/ZYColumnUpPromptView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ZYColumnUpPromptView.swift 3 | // 掌上遂宁 4 | // 5 | // Created by 张宇 on 2016/10/10. 6 | // Copyright © 2016年 张宇. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | typealias editBtnClickClosure = (_ isSelected : Bool) -> Void 12 | 13 | class ZYColumnUpPromptView: UIView { 14 | 15 | var editBtn : UIButton? 16 | // 编辑按钮回调闭包 17 | var clickClosure : editBtnClickClosure? 18 | 19 | override init(frame: CGRect) { 20 | super.init(frame:frame) 21 | // 初始化子控件 22 | let label = UILabel(frame: CGRect(x: kColumnMarginW, y: 0, width: 100, height: kColumnViewH)) 23 | label.text = "栏目切换" 24 | label.textColor = UIColor.darkGray 25 | addSubview(label) 26 | 27 | // 切换状态按钮 28 | let editBtn = UIButton(frame: CGRect(x: kColumnViewW - kColumnViewH - kColumnEditBtnW - 10, y: (kColumnViewH - kColumnEditBtnH)*0.5, width: kColumnEditBtnW, height: kColumnEditBtnH)) 29 | self.editBtn = editBtn 30 | editBtn.setTitle(kColumnEditBtnNorTitle, for: .normal) 31 | editBtn.setTitle(kColumnEditBtnSelTitle, for: .selected) 32 | editBtn.setBackgroundImage(UIImage(named:"column_edit_button"), for: .normal) 33 | editBtn.setBackgroundImage(UIImage(named:"column_edit_button"), for: .highlighted) 34 | editBtn.titleLabel?.font = UIFont.systemFont(ofSize: kColumnEditBtnFont) 35 | editBtn.setTitleColor(kColumnItemColor, for: .normal) 36 | // editBtn.layer.borderWidth = 1.0 37 | // editBtn.layer.borderColor = kColumnItemBorderColor 38 | // editBtn.layer.cornerRadius = kColumnEditBtnH * 0.5 39 | editBtn.addTarget(self, action: #selector(self.editButtonClick(editBtn:)), for: .touchUpInside) 40 | addSubview(editBtn) 41 | } 42 | 43 | required init?(coder aDecoder: NSCoder) { 44 | fatalError("init(coder:) has not been implemented") 45 | } 46 | 47 | // MARK: - 点击事件监听 48 | @objc open func editButtonClick(editBtn: UIButton) { 49 | editBtn.isSelected = !editBtn.isSelected 50 | if self.clickClosure != nil { 51 | self.clickClosure!(editBtn.isSelected) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ZYColumnViewDemo/ZYColumnViewDemo/ZYColumnView/ZYColumnViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ZYColumnViewController.swift 3 | // 掌上遂宁 4 | // 5 | // Created by 张宇 on 2016/10/10. 6 | // Copyright © 2016年 张宇. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @objc protocol ZYColumnViewControllerDelegate : class { 12 | // 上面选择的数组和被选择的数组 回调传值 13 | @objc optional func columnViewControllerTitlesDidChanged(arrayTitles : [String]!, spareTitles : [String]?) 14 | // 当前用户选择的按钮标题和所处的索引 15 | @objc optional func columnViewControllerSelectedTitle(selectedTitle : String, index : Int) 16 | // 用户手动调用控制器,选择的title 和 所在索引 17 | @objc optional func columnViewControllerSetTitle(setTitle: String , index : Int) 18 | } 19 | 20 | class ZYColumnViewController: UIViewController { 21 | 22 | open var arrayTitles = [String](){ 23 | didSet { 24 | // 数据校验 25 | guard arrayTitles.count > 0 else { 26 | fatalError("if you want to use it, arrayTitles can not be nil ") 27 | } 28 | for _ in view.subviews { // 已经初始化过了,直接return 避免赋值后重复执行下面的代码 29 | return 30 | } 31 | // 默认选中第0个栏目名称 32 | selTitle = arrayTitles[0] 33 | // 初始化横向滚动scrollView 34 | view.addSubview(hScrollView) 35 | // 初始化"展开"按钮 36 | setupSpreadButton() 37 | } 38 | } 39 | // 备选 string 数组 40 | open var arraySpareTitles = [String]() 41 | // 固定不变的item 个数 42 | open var fixedCount = 1 43 | // 代理 44 | weak var delegate : ZYColumnViewControllerDelegate? 45 | 46 | /************* 上面是对外属性 -----华丽分割线----- 下面是私有属性 ***************/ 47 | 48 | // 右边的展开按钮 49 | fileprivate var spreadBtn: UIButton = UIButton() 50 | // 是否 展开 标识 51 | fileprivate var toSpread:Bool = false 52 | // 用户当前选中的栏目名称 53 | fileprivate var selTitle = String() 54 | 55 | // MARK: - 懒加载 56 | /// 垂直展开的scrollView 57 | fileprivate lazy var spreadView:ZYColumnSpreadView = { 58 | let spreadView = ZYColumnSpreadView(frame: CGRect(x: 0, y: 0, width: kColumnScreenW, height: 0.1)) 59 | spreadView.arrayUpTitles = self.arrayTitles 60 | spreadView.arrayDownTitles = self.arraySpareTitles 61 | spreadView.backgroundColor = UIColor.init(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.9) 62 | spreadView.fixedCount = self.fixedCount 63 | self.view.insertSubview(spreadView, belowSubview: self.hScrollView) 64 | 65 | spreadView.tapClosure = { 66 | [weak self](item : ZYColumnItem) -> Void in 67 | // 回调处理 68 | // 收起spreadView 69 | self!.spreadOrFlod() 70 | if self!.selTitle != item.title { // 点击的不是当前选中的才设置,并且回传值 71 | // 这里可以拿到用户点击的具体title 如果用户已经删除之前选中的title 则认为用户选择了第0个 72 | self!.selTitle = (self!.arrayTitles.contains(item.title)) ? item.title : self!.arrayTitles[0] 73 | self!.hScrollView.changeItemToSelected(title: self!.selTitle) 74 | // 代理传值 75 | self!.delegate?.columnViewControllerSelectedTitle!(selectedTitle : self!.selTitle, index: 76 | self!.arrayTitles.index(of: self!.selTitle)!) 77 | } 78 | } 79 | spreadView.longPressClosure = { 80 | [weak self]() -> Void in 81 | // 模拟用户点击了一下edit按钮 82 | self!.upPromptView.editButtonClick(editBtn: self!.upPromptView.editBtn!) 83 | } 84 | spreadView.flodClosure = { 85 | [weak self](upArrayTitles : [String], downArrayTitles : [String]) -> Void in 86 | guard upArrayTitles != self!.arrayTitles else { // 两个数组相等,说明没有变化,下面代码不用执行 87 | return 88 | } 89 | self!.arrayTitles = upArrayTitles 90 | self!.arraySpareTitles = downArrayTitles 91 | self!.hScrollView.arrayTitles = upArrayTitles 92 | // 如果用户已经删除之前选中的title 则认为用户选择了第0个 93 | self!.selTitle = (upArrayTitles.contains(self!.selTitle)) ? self!.selTitle : self!.arrayTitles[0] 94 | self!.hScrollView.changeItemToSelected(title: self!.selTitle) 95 | // 代理传值 96 | self!.delegate?.columnViewControllerSelectedTitle!(selectedTitle: self!.selTitle, index: 97 | self!.arrayTitles.index(of: self!.selTitle)!) 98 | self!.delegate?.columnViewControllerTitlesDidChanged!(arrayTitles: self!.arrayTitles, spareTitles: self!.arraySpareTitles) 99 | 100 | } 101 | return spreadView 102 | }() 103 | 104 | /// 横向滚动的scrollView 105 | fileprivate lazy var hScrollView : ZYColumnHScrollView = { 106 | let hScrollView = ZYColumnHScrollView() 107 | // 回调 108 | hScrollView.backClosure = { 109 | [weak self](tag : Int, title: String) -> Void in 110 | if title != self!.selTitle { // 点击的不是当前选中的才设置,并且回传值 111 | self!.selTitle = title 112 | // 代理传值 113 | self!.delegate?.columnViewControllerSelectedTitle!(selectedTitle: self!.selTitle, index: 114 | self!.arrayTitles.index(of: self!.selTitle)!) 115 | } 116 | } 117 | hScrollView.arrayTitles = self.arrayTitles 118 | hScrollView.frame = CGRect(x: 0, y: 0, width: ZYScreenWidth-kColumnViewH, height: kColumnViewH) 119 | hScrollView.backgroundColor = UIColor.white 120 | hScrollView.changeItemToSelected(title: self.arrayTitles[0]) // 初始化的时候默认选中第0个 121 | self.view.addSubview(hScrollView) 122 | return hScrollView 123 | }() 124 | 125 | /// 透明的cover,当展开状态,导航条覆盖一个cover,点击cover会收起 126 | lazy fileprivate var cover : UIButton = { 127 | let btn = UIButton(frame: CGRect(x: 0, y: 0, width: kColumnScreenW , height: 64)) 128 | btn.backgroundColor = UIColor(white: 1.0, alpha: 0.001) 129 | UIApplication.shared.keyWindow?.addSubview(btn) 130 | btn.addTarget(self, action: #selector(self.spreadBtnClick(spreadBtn:)), for: .touchUpInside) //等价于点了展开按钮 131 | return btn 132 | }() 133 | /// 上提示view 134 | lazy var upPromptView : ZYColumnUpPromptView = { 135 | let upView = ZYColumnUpPromptView(frame: CGRect(x: 0, y: 0, width: kColumnViewW - kColumnViewH, height: kColumnViewH)) 136 | upView.backgroundColor = UIColor.white 137 | // 闭包回调处理 138 | upView.clickClosure = { 139 | [weak self](isSelected : Bool) -> Void in 140 | // 编辑按钮 点击回调事件 141 | self!.spreadView.hideDownItemsAndPromptView(toHide: isSelected) 142 | self!.spreadView.openDelStatus(showDel: isSelected) 143 | self!.spreadView.isSortStatus = isSelected 144 | self!.spreadView.scrollToTop() 145 | } 146 | self.view.insertSubview(upView, aboveSubview: self.hScrollView) 147 | return upView 148 | }() 149 | 150 | override func viewDidLoad() { 151 | super.viewDidLoad() 152 | self.automaticallyAdjustsScrollViewInsets = false 153 | view.backgroundColor = UIColor.clear 154 | } 155 | } 156 | 157 | // MARK: - 对外公共方法 158 | extension ZYColumnViewController { 159 | open func setSelectItem(title: String) { 160 | hScrollView.changeItemToSelected(title: title) 161 | delegate?.columnViewControllerSetTitle!(setTitle: title, index: arrayTitles.index(of: title)!) 162 | } 163 | } 164 | 165 | // MARK: - 初始化展开收拢按钮 166 | extension ZYColumnViewController { 167 | fileprivate func setupSpreadButton() { 168 | spreadBtn = UIButton(type: UIButtonType.custom) 169 | // 包一层UIView 作为背景 170 | let bgView = UIView(frame: CGRect(x: kColumnScreenW - kColumnViewH, y: 0.0, width: kColumnViewH, height: kColumnViewH)) 171 | // 右边展开按钮(按钮为正方形,边长为控制器view的高度) 172 | spreadBtn.frame = CGRect(x: 0 , y: 0 , width: bgView.frame.width, height: bgView.frame.height) 173 | bgView.backgroundColor = UIColor.white 174 | bgView.addSubview(spreadBtn) 175 | spreadBtn.backgroundColor = UIColor.clear 176 | spreadBtn.adjustsImageWhenHighlighted = false 177 | spreadBtn.setImage(UIImage(named: "column_spread.png"), for: UIControlState.normal) 178 | spreadBtn.addTarget(self, action: #selector(self.spreadBtnClick(spreadBtn:)), for: UIControlEvents.touchUpInside) // 按钮点击事件监听 179 | view.addSubview(bgView) 180 | } 181 | } 182 | 183 | // MARK: - 控制方法 184 | extension ZYColumnViewController { 185 | // MARK: - 展开收拢控制 186 | fileprivate func spreadOrFlod() { 187 | toSpread = !toSpread 188 | // cover 和 upPromptView通过参数交给内部scrollView控制显示隐藏 189 | spreadView.doSpreadOrFold(toSpread: toSpread, cover: cover, upPromptView: upPromptView) 190 | // 与子控件scrollView同步高度,保证子控件能够响应事件 191 | self.view.frame = CGRect(x: view.frame.origin.x, y: view.frame.origin.y, width: view.frame.width, height: toSpread ? kSpreadMaxH : kColumnViewH) 192 | // 旋转按钮layer图层 193 | transitionSpreadBtn(toSpread: toSpread) 194 | // 当编辑按钮在编辑状态时,收起时要复原 195 | if !toSpread && (upPromptView.editBtn?.isSelected)! { 196 | // 模拟用户点击了一下edit按钮 197 | upPromptView.editButtonClick(editBtn: upPromptView.editBtn!) 198 | } 199 | spreadView.isSortStatus = !toSpread 200 | self.tabBarController?.tabBar.isHidden = toSpread //控制tabBar的隐藏或显示 201 | } 202 | } 203 | 204 | // MARK: - 点击事件处理 205 | extension ZYColumnViewController { 206 | /// 展开收拢按钮点击事件 207 | @objc fileprivate func spreadBtnClick(spreadBtn : UIButton) { 208 | spreadOrFlod() 209 | } 210 | /// 旋转展开按钮 211 | fileprivate func transitionSpreadBtn(toSpread : Bool) { 212 | UIView.animate(withDuration: 0.2) { () -> Void in 213 | let angle = toSpread ? .pi * 0.25 : -.pi * 0.25 214 | self.spreadBtn.transform = self.spreadBtn.transform.rotated(by: CGFloat(angle)) 215 | } 216 | } 217 | } 218 | --------------------------------------------------------------------------------