├── CustomChannel.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── May.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── May.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── CustomChannel ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── btn_close_nor.imageset │ │ ├── Contents.json │ │ ├── btn_close_nor@2x.png │ │ └── btn_close_nor@3x.png │ ├── btn_delete.imageset │ │ ├── Contents.json │ │ └── btn_delete@2x.png │ └── p2184389417.imageset │ │ ├── Contents.json │ │ └── p2184389417.jpg ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── CustomchannelView.swift ├── Info.plist ├── PureTagList.swift ├── SelectedChannelCell.swift ├── UIColorExt.swift ├── UIImageExt.swift ├── UIScreenExt.swift └── ViewController.swift ├── CustomChannelTests ├── CustomChannelTests.swift └── Info.plist ├── CustomChannelUITests ├── CustomChannelUITests.swift └── Info.plist ├── Frameworks └── SnapKit.framework │ ├── Headers │ ├── SnapKit-Swift.h │ └── SnapKit.h │ ├── Info.plist │ ├── Modules │ ├── SnapKit.swiftmodule │ │ ├── arm.swiftdoc │ │ ├── arm.swiftmodule │ │ ├── arm64.swiftdoc │ │ ├── arm64.swiftmodule │ │ ├── i386.swiftdoc │ │ ├── i386.swiftmodule │ │ ├── x86_64.swiftdoc │ │ └── x86_64.swiftmodule │ └── module.modulemap │ └── SnapKit ├── README.md └── selectTag.gif /CustomChannel.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 6C9F8A4A2070A8B5006D84A0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C9F8A492070A8B5006D84A0 /* AppDelegate.swift */; }; 11 | 6C9F8A4C2070A8B5006D84A0 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C9F8A4B2070A8B5006D84A0 /* ViewController.swift */; }; 12 | 6C9F8A4F2070A8B5006D84A0 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6C9F8A4D2070A8B5006D84A0 /* Main.storyboard */; }; 13 | 6C9F8A512070A8B6006D84A0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6C9F8A502070A8B6006D84A0 /* Assets.xcassets */; }; 14 | 6C9F8A542070A8B6006D84A0 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6C9F8A522070A8B6006D84A0 /* LaunchScreen.storyboard */; }; 15 | 6C9F8A5F2070A8B7006D84A0 /* CustomChannelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C9F8A5E2070A8B7006D84A0 /* CustomChannelTests.swift */; }; 16 | 6C9F8A6A2070A8B7006D84A0 /* CustomChannelUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C9F8A692070A8B7006D84A0 /* CustomChannelUITests.swift */; }; 17 | 6C9F8A812070AA02006D84A0 /* UIScreenExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C9F8A7A2070AA01006D84A0 /* UIScreenExt.swift */; }; 18 | 6C9F8A822070AA02006D84A0 /* UIColorExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C9F8A7B2070AA01006D84A0 /* UIColorExt.swift */; }; 19 | 6C9F8A832070AA02006D84A0 /* SelectedChannelCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C9F8A7C2070AA01006D84A0 /* SelectedChannelCell.swift */; }; 20 | 6C9F8A842070AA02006D84A0 /* PureTagList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C9F8A7D2070AA01006D84A0 /* PureTagList.swift */; }; 21 | 6C9F8A852070AA02006D84A0 /* UIImageExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C9F8A7E2070AA01006D84A0 /* UIImageExt.swift */; }; 22 | 6C9F8A862070AA02006D84A0 /* CustomchannelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C9F8A7F2070AA01006D84A0 /* CustomchannelView.swift */; }; 23 | 6C9F8A892070AA28006D84A0 /* SnapKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6C9F8A782070A9EB006D84A0 /* SnapKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXContainerItemProxy section */ 27 | 6C9F8A5B2070A8B7006D84A0 /* PBXContainerItemProxy */ = { 28 | isa = PBXContainerItemProxy; 29 | containerPortal = 6C9F8A3E2070A8B5006D84A0 /* Project object */; 30 | proxyType = 1; 31 | remoteGlobalIDString = 6C9F8A452070A8B5006D84A0; 32 | remoteInfo = CustomChannel; 33 | }; 34 | 6C9F8A662070A8B7006D84A0 /* PBXContainerItemProxy */ = { 35 | isa = PBXContainerItemProxy; 36 | containerPortal = 6C9F8A3E2070A8B5006D84A0 /* Project object */; 37 | proxyType = 1; 38 | remoteGlobalIDString = 6C9F8A452070A8B5006D84A0; 39 | remoteInfo = CustomChannel; 40 | }; 41 | /* End PBXContainerItemProxy section */ 42 | 43 | /* Begin PBXCopyFilesBuildPhase section */ 44 | 6C9F8A8A2070AA29006D84A0 /* Embed Frameworks */ = { 45 | isa = PBXCopyFilesBuildPhase; 46 | buildActionMask = 2147483647; 47 | dstPath = ""; 48 | dstSubfolderSpec = 10; 49 | files = ( 50 | 6C9F8A892070AA28006D84A0 /* SnapKit.framework in Embed Frameworks */, 51 | ); 52 | name = "Embed Frameworks"; 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXCopyFilesBuildPhase section */ 56 | 57 | /* Begin PBXFileReference section */ 58 | 6C9F8A462070A8B5006D84A0 /* CustomChannel.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CustomChannel.app; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | 6C9F8A492070A8B5006D84A0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 60 | 6C9F8A4B2070A8B5006D84A0 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 61 | 6C9F8A4E2070A8B5006D84A0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 62 | 6C9F8A502070A8B6006D84A0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 63 | 6C9F8A532070A8B6006D84A0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 64 | 6C9F8A552070A8B6006D84A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 65 | 6C9F8A5A2070A8B7006D84A0 /* CustomChannelTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CustomChannelTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | 6C9F8A5E2070A8B7006D84A0 /* CustomChannelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomChannelTests.swift; sourceTree = ""; }; 67 | 6C9F8A602070A8B7006D84A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 68 | 6C9F8A652070A8B7006D84A0 /* CustomChannelUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CustomChannelUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 69 | 6C9F8A692070A8B7006D84A0 /* CustomChannelUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomChannelUITests.swift; sourceTree = ""; }; 70 | 6C9F8A6B2070A8B7006D84A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 71 | 6C9F8A782070A9EB006D84A0 /* SnapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = SnapKit.framework; sourceTree = ""; }; 72 | 6C9F8A7A2070AA01006D84A0 /* UIScreenExt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIScreenExt.swift; sourceTree = ""; }; 73 | 6C9F8A7B2070AA01006D84A0 /* UIColorExt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIColorExt.swift; sourceTree = ""; }; 74 | 6C9F8A7C2070AA01006D84A0 /* SelectedChannelCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectedChannelCell.swift; sourceTree = ""; }; 75 | 6C9F8A7D2070AA01006D84A0 /* PureTagList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PureTagList.swift; sourceTree = ""; }; 76 | 6C9F8A7E2070AA01006D84A0 /* UIImageExt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageExt.swift; sourceTree = ""; }; 77 | 6C9F8A7F2070AA01006D84A0 /* CustomchannelView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomchannelView.swift; sourceTree = ""; }; 78 | /* End PBXFileReference section */ 79 | 80 | /* Begin PBXFrameworksBuildPhase section */ 81 | 6C9F8A432070A8B5006D84A0 /* Frameworks */ = { 82 | isa = PBXFrameworksBuildPhase; 83 | buildActionMask = 2147483647; 84 | files = ( 85 | ); 86 | runOnlyForDeploymentPostprocessing = 0; 87 | }; 88 | 6C9F8A572070A8B7006D84A0 /* Frameworks */ = { 89 | isa = PBXFrameworksBuildPhase; 90 | buildActionMask = 2147483647; 91 | files = ( 92 | ); 93 | runOnlyForDeploymentPostprocessing = 0; 94 | }; 95 | 6C9F8A622070A8B7006D84A0 /* Frameworks */ = { 96 | isa = PBXFrameworksBuildPhase; 97 | buildActionMask = 2147483647; 98 | files = ( 99 | ); 100 | runOnlyForDeploymentPostprocessing = 0; 101 | }; 102 | /* End PBXFrameworksBuildPhase section */ 103 | 104 | /* Begin PBXGroup section */ 105 | 6C9F8A3D2070A8B5006D84A0 = { 106 | isa = PBXGroup; 107 | children = ( 108 | 6C9F8A772070A9EB006D84A0 /* Frameworks */, 109 | 6C9F8A482070A8B5006D84A0 /* CustomChannel */, 110 | 6C9F8A5D2070A8B7006D84A0 /* CustomChannelTests */, 111 | 6C9F8A682070A8B7006D84A0 /* CustomChannelUITests */, 112 | 6C9F8A472070A8B5006D84A0 /* Products */, 113 | ); 114 | sourceTree = ""; 115 | }; 116 | 6C9F8A472070A8B5006D84A0 /* Products */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 6C9F8A462070A8B5006D84A0 /* CustomChannel.app */, 120 | 6C9F8A5A2070A8B7006D84A0 /* CustomChannelTests.xctest */, 121 | 6C9F8A652070A8B7006D84A0 /* CustomChannelUITests.xctest */, 122 | ); 123 | name = Products; 124 | sourceTree = ""; 125 | }; 126 | 6C9F8A482070A8B5006D84A0 /* CustomChannel */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 6C9F8A7F2070AA01006D84A0 /* CustomchannelView.swift */, 130 | 6C9F8A7D2070AA01006D84A0 /* PureTagList.swift */, 131 | 6C9F8A7C2070AA01006D84A0 /* SelectedChannelCell.swift */, 132 | 6C9F8A7B2070AA01006D84A0 /* UIColorExt.swift */, 133 | 6C9F8A7E2070AA01006D84A0 /* UIImageExt.swift */, 134 | 6C9F8A7A2070AA01006D84A0 /* UIScreenExt.swift */, 135 | 6C9F8A492070A8B5006D84A0 /* AppDelegate.swift */, 136 | 6C9F8A4B2070A8B5006D84A0 /* ViewController.swift */, 137 | 6C9F8A4D2070A8B5006D84A0 /* Main.storyboard */, 138 | 6C9F8A502070A8B6006D84A0 /* Assets.xcassets */, 139 | 6C9F8A522070A8B6006D84A0 /* LaunchScreen.storyboard */, 140 | 6C9F8A552070A8B6006D84A0 /* Info.plist */, 141 | ); 142 | path = CustomChannel; 143 | sourceTree = ""; 144 | }; 145 | 6C9F8A5D2070A8B7006D84A0 /* CustomChannelTests */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | 6C9F8A5E2070A8B7006D84A0 /* CustomChannelTests.swift */, 149 | 6C9F8A602070A8B7006D84A0 /* Info.plist */, 150 | ); 151 | path = CustomChannelTests; 152 | sourceTree = ""; 153 | }; 154 | 6C9F8A682070A8B7006D84A0 /* CustomChannelUITests */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | 6C9F8A692070A8B7006D84A0 /* CustomChannelUITests.swift */, 158 | 6C9F8A6B2070A8B7006D84A0 /* Info.plist */, 159 | ); 160 | path = CustomChannelUITests; 161 | sourceTree = ""; 162 | }; 163 | 6C9F8A772070A9EB006D84A0 /* Frameworks */ = { 164 | isa = PBXGroup; 165 | children = ( 166 | 6C9F8A782070A9EB006D84A0 /* SnapKit.framework */, 167 | ); 168 | path = Frameworks; 169 | sourceTree = ""; 170 | }; 171 | /* End PBXGroup section */ 172 | 173 | /* Begin PBXNativeTarget section */ 174 | 6C9F8A452070A8B5006D84A0 /* CustomChannel */ = { 175 | isa = PBXNativeTarget; 176 | buildConfigurationList = 6C9F8A6E2070A8B7006D84A0 /* Build configuration list for PBXNativeTarget "CustomChannel" */; 177 | buildPhases = ( 178 | 6C9F8A422070A8B5006D84A0 /* Sources */, 179 | 6C9F8A432070A8B5006D84A0 /* Frameworks */, 180 | 6C9F8A442070A8B5006D84A0 /* Resources */, 181 | 6C9F8A8A2070AA29006D84A0 /* Embed Frameworks */, 182 | ); 183 | buildRules = ( 184 | ); 185 | dependencies = ( 186 | ); 187 | name = CustomChannel; 188 | productName = CustomChannel; 189 | productReference = 6C9F8A462070A8B5006D84A0 /* CustomChannel.app */; 190 | productType = "com.apple.product-type.application"; 191 | }; 192 | 6C9F8A592070A8B7006D84A0 /* CustomChannelTests */ = { 193 | isa = PBXNativeTarget; 194 | buildConfigurationList = 6C9F8A712070A8B7006D84A0 /* Build configuration list for PBXNativeTarget "CustomChannelTests" */; 195 | buildPhases = ( 196 | 6C9F8A562070A8B7006D84A0 /* Sources */, 197 | 6C9F8A572070A8B7006D84A0 /* Frameworks */, 198 | 6C9F8A582070A8B7006D84A0 /* Resources */, 199 | ); 200 | buildRules = ( 201 | ); 202 | dependencies = ( 203 | 6C9F8A5C2070A8B7006D84A0 /* PBXTargetDependency */, 204 | ); 205 | name = CustomChannelTests; 206 | productName = CustomChannelTests; 207 | productReference = 6C9F8A5A2070A8B7006D84A0 /* CustomChannelTests.xctest */; 208 | productType = "com.apple.product-type.bundle.unit-test"; 209 | }; 210 | 6C9F8A642070A8B7006D84A0 /* CustomChannelUITests */ = { 211 | isa = PBXNativeTarget; 212 | buildConfigurationList = 6C9F8A742070A8B7006D84A0 /* Build configuration list for PBXNativeTarget "CustomChannelUITests" */; 213 | buildPhases = ( 214 | 6C9F8A612070A8B7006D84A0 /* Sources */, 215 | 6C9F8A622070A8B7006D84A0 /* Frameworks */, 216 | 6C9F8A632070A8B7006D84A0 /* Resources */, 217 | ); 218 | buildRules = ( 219 | ); 220 | dependencies = ( 221 | 6C9F8A672070A8B7006D84A0 /* PBXTargetDependency */, 222 | ); 223 | name = CustomChannelUITests; 224 | productName = CustomChannelUITests; 225 | productReference = 6C9F8A652070A8B7006D84A0 /* CustomChannelUITests.xctest */; 226 | productType = "com.apple.product-type.bundle.ui-testing"; 227 | }; 228 | /* End PBXNativeTarget section */ 229 | 230 | /* Begin PBXProject section */ 231 | 6C9F8A3E2070A8B5006D84A0 /* Project object */ = { 232 | isa = PBXProject; 233 | attributes = { 234 | LastSwiftUpdateCheck = 0930; 235 | LastUpgradeCheck = 0930; 236 | ORGANIZATIONNAME = "孙凯峰"; 237 | TargetAttributes = { 238 | 6C9F8A452070A8B5006D84A0 = { 239 | CreatedOnToolsVersion = 9.3; 240 | }; 241 | 6C9F8A592070A8B7006D84A0 = { 242 | CreatedOnToolsVersion = 9.3; 243 | TestTargetID = 6C9F8A452070A8B5006D84A0; 244 | }; 245 | 6C9F8A642070A8B7006D84A0 = { 246 | CreatedOnToolsVersion = 9.3; 247 | TestTargetID = 6C9F8A452070A8B5006D84A0; 248 | }; 249 | }; 250 | }; 251 | buildConfigurationList = 6C9F8A412070A8B5006D84A0 /* Build configuration list for PBXProject "CustomChannel" */; 252 | compatibilityVersion = "Xcode 9.3"; 253 | developmentRegion = en; 254 | hasScannedForEncodings = 0; 255 | knownRegions = ( 256 | en, 257 | Base, 258 | ); 259 | mainGroup = 6C9F8A3D2070A8B5006D84A0; 260 | productRefGroup = 6C9F8A472070A8B5006D84A0 /* Products */; 261 | projectDirPath = ""; 262 | projectRoot = ""; 263 | targets = ( 264 | 6C9F8A452070A8B5006D84A0 /* CustomChannel */, 265 | 6C9F8A592070A8B7006D84A0 /* CustomChannelTests */, 266 | 6C9F8A642070A8B7006D84A0 /* CustomChannelUITests */, 267 | ); 268 | }; 269 | /* End PBXProject section */ 270 | 271 | /* Begin PBXResourcesBuildPhase section */ 272 | 6C9F8A442070A8B5006D84A0 /* Resources */ = { 273 | isa = PBXResourcesBuildPhase; 274 | buildActionMask = 2147483647; 275 | files = ( 276 | 6C9F8A542070A8B6006D84A0 /* LaunchScreen.storyboard in Resources */, 277 | 6C9F8A512070A8B6006D84A0 /* Assets.xcassets in Resources */, 278 | 6C9F8A4F2070A8B5006D84A0 /* Main.storyboard in Resources */, 279 | ); 280 | runOnlyForDeploymentPostprocessing = 0; 281 | }; 282 | 6C9F8A582070A8B7006D84A0 /* Resources */ = { 283 | isa = PBXResourcesBuildPhase; 284 | buildActionMask = 2147483647; 285 | files = ( 286 | ); 287 | runOnlyForDeploymentPostprocessing = 0; 288 | }; 289 | 6C9F8A632070A8B7006D84A0 /* Resources */ = { 290 | isa = PBXResourcesBuildPhase; 291 | buildActionMask = 2147483647; 292 | files = ( 293 | ); 294 | runOnlyForDeploymentPostprocessing = 0; 295 | }; 296 | /* End PBXResourcesBuildPhase section */ 297 | 298 | /* Begin PBXSourcesBuildPhase section */ 299 | 6C9F8A422070A8B5006D84A0 /* Sources */ = { 300 | isa = PBXSourcesBuildPhase; 301 | buildActionMask = 2147483647; 302 | files = ( 303 | 6C9F8A4C2070A8B5006D84A0 /* ViewController.swift in Sources */, 304 | 6C9F8A862070AA02006D84A0 /* CustomchannelView.swift in Sources */, 305 | 6C9F8A822070AA02006D84A0 /* UIColorExt.swift in Sources */, 306 | 6C9F8A852070AA02006D84A0 /* UIImageExt.swift in Sources */, 307 | 6C9F8A842070AA02006D84A0 /* PureTagList.swift in Sources */, 308 | 6C9F8A832070AA02006D84A0 /* SelectedChannelCell.swift in Sources */, 309 | 6C9F8A4A2070A8B5006D84A0 /* AppDelegate.swift in Sources */, 310 | 6C9F8A812070AA02006D84A0 /* UIScreenExt.swift in Sources */, 311 | ); 312 | runOnlyForDeploymentPostprocessing = 0; 313 | }; 314 | 6C9F8A562070A8B7006D84A0 /* Sources */ = { 315 | isa = PBXSourcesBuildPhase; 316 | buildActionMask = 2147483647; 317 | files = ( 318 | 6C9F8A5F2070A8B7006D84A0 /* CustomChannelTests.swift in Sources */, 319 | ); 320 | runOnlyForDeploymentPostprocessing = 0; 321 | }; 322 | 6C9F8A612070A8B7006D84A0 /* Sources */ = { 323 | isa = PBXSourcesBuildPhase; 324 | buildActionMask = 2147483647; 325 | files = ( 326 | 6C9F8A6A2070A8B7006D84A0 /* CustomChannelUITests.swift in Sources */, 327 | ); 328 | runOnlyForDeploymentPostprocessing = 0; 329 | }; 330 | /* End PBXSourcesBuildPhase section */ 331 | 332 | /* Begin PBXTargetDependency section */ 333 | 6C9F8A5C2070A8B7006D84A0 /* PBXTargetDependency */ = { 334 | isa = PBXTargetDependency; 335 | target = 6C9F8A452070A8B5006D84A0 /* CustomChannel */; 336 | targetProxy = 6C9F8A5B2070A8B7006D84A0 /* PBXContainerItemProxy */; 337 | }; 338 | 6C9F8A672070A8B7006D84A0 /* PBXTargetDependency */ = { 339 | isa = PBXTargetDependency; 340 | target = 6C9F8A452070A8B5006D84A0 /* CustomChannel */; 341 | targetProxy = 6C9F8A662070A8B7006D84A0 /* PBXContainerItemProxy */; 342 | }; 343 | /* End PBXTargetDependency section */ 344 | 345 | /* Begin PBXVariantGroup section */ 346 | 6C9F8A4D2070A8B5006D84A0 /* Main.storyboard */ = { 347 | isa = PBXVariantGroup; 348 | children = ( 349 | 6C9F8A4E2070A8B5006D84A0 /* Base */, 350 | ); 351 | name = Main.storyboard; 352 | sourceTree = ""; 353 | }; 354 | 6C9F8A522070A8B6006D84A0 /* LaunchScreen.storyboard */ = { 355 | isa = PBXVariantGroup; 356 | children = ( 357 | 6C9F8A532070A8B6006D84A0 /* Base */, 358 | ); 359 | name = LaunchScreen.storyboard; 360 | sourceTree = ""; 361 | }; 362 | /* End PBXVariantGroup section */ 363 | 364 | /* Begin XCBuildConfiguration section */ 365 | 6C9F8A6C2070A8B7006D84A0 /* Debug */ = { 366 | isa = XCBuildConfiguration; 367 | buildSettings = { 368 | ALWAYS_SEARCH_USER_PATHS = NO; 369 | CLANG_ANALYZER_NONNULL = YES; 370 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 371 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 372 | CLANG_CXX_LIBRARY = "libc++"; 373 | CLANG_ENABLE_MODULES = YES; 374 | CLANG_ENABLE_OBJC_ARC = YES; 375 | CLANG_ENABLE_OBJC_WEAK = YES; 376 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 377 | CLANG_WARN_BOOL_CONVERSION = YES; 378 | CLANG_WARN_COMMA = YES; 379 | CLANG_WARN_CONSTANT_CONVERSION = YES; 380 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 381 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 382 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 383 | CLANG_WARN_EMPTY_BODY = YES; 384 | CLANG_WARN_ENUM_CONVERSION = YES; 385 | CLANG_WARN_INFINITE_RECURSION = YES; 386 | CLANG_WARN_INT_CONVERSION = YES; 387 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 388 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 389 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 390 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 391 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 392 | CLANG_WARN_STRICT_PROTOTYPES = YES; 393 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 394 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 395 | CLANG_WARN_UNREACHABLE_CODE = YES; 396 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 397 | CODE_SIGN_IDENTITY = "iPhone Developer"; 398 | COPY_PHASE_STRIP = NO; 399 | DEBUG_INFORMATION_FORMAT = dwarf; 400 | ENABLE_STRICT_OBJC_MSGSEND = YES; 401 | ENABLE_TESTABILITY = YES; 402 | GCC_C_LANGUAGE_STANDARD = gnu11; 403 | GCC_DYNAMIC_NO_PIC = NO; 404 | GCC_NO_COMMON_BLOCKS = YES; 405 | GCC_OPTIMIZATION_LEVEL = 0; 406 | GCC_PREPROCESSOR_DEFINITIONS = ( 407 | "DEBUG=1", 408 | "$(inherited)", 409 | ); 410 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 411 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 412 | GCC_WARN_UNDECLARED_SELECTOR = YES; 413 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 414 | GCC_WARN_UNUSED_FUNCTION = YES; 415 | GCC_WARN_UNUSED_VARIABLE = YES; 416 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 417 | MTL_ENABLE_DEBUG_INFO = YES; 418 | ONLY_ACTIVE_ARCH = YES; 419 | SDKROOT = iphoneos; 420 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 421 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 422 | }; 423 | name = Debug; 424 | }; 425 | 6C9F8A6D2070A8B7006D84A0 /* Release */ = { 426 | isa = XCBuildConfiguration; 427 | buildSettings = { 428 | ALWAYS_SEARCH_USER_PATHS = NO; 429 | CLANG_ANALYZER_NONNULL = YES; 430 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 431 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 432 | CLANG_CXX_LIBRARY = "libc++"; 433 | CLANG_ENABLE_MODULES = YES; 434 | CLANG_ENABLE_OBJC_ARC = YES; 435 | CLANG_ENABLE_OBJC_WEAK = YES; 436 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 437 | CLANG_WARN_BOOL_CONVERSION = YES; 438 | CLANG_WARN_COMMA = YES; 439 | CLANG_WARN_CONSTANT_CONVERSION = YES; 440 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 441 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 442 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 443 | CLANG_WARN_EMPTY_BODY = YES; 444 | CLANG_WARN_ENUM_CONVERSION = YES; 445 | CLANG_WARN_INFINITE_RECURSION = YES; 446 | CLANG_WARN_INT_CONVERSION = YES; 447 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 448 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 449 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 450 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 451 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 452 | CLANG_WARN_STRICT_PROTOTYPES = YES; 453 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 454 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 455 | CLANG_WARN_UNREACHABLE_CODE = YES; 456 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 457 | CODE_SIGN_IDENTITY = "iPhone Developer"; 458 | COPY_PHASE_STRIP = NO; 459 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 460 | ENABLE_NS_ASSERTIONS = NO; 461 | ENABLE_STRICT_OBJC_MSGSEND = YES; 462 | GCC_C_LANGUAGE_STANDARD = gnu11; 463 | GCC_NO_COMMON_BLOCKS = YES; 464 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 465 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 466 | GCC_WARN_UNDECLARED_SELECTOR = YES; 467 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 468 | GCC_WARN_UNUSED_FUNCTION = YES; 469 | GCC_WARN_UNUSED_VARIABLE = YES; 470 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 471 | MTL_ENABLE_DEBUG_INFO = NO; 472 | SDKROOT = iphoneos; 473 | SWIFT_COMPILATION_MODE = wholemodule; 474 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 475 | VALIDATE_PRODUCT = YES; 476 | }; 477 | name = Release; 478 | }; 479 | 6C9F8A6F2070A8B7006D84A0 /* Debug */ = { 480 | isa = XCBuildConfiguration; 481 | buildSettings = { 482 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 483 | CODE_SIGN_STYLE = Automatic; 484 | FRAMEWORK_SEARCH_PATHS = ( 485 | "$(inherited)", 486 | "$(PROJECT_DIR)/Frameworks", 487 | ); 488 | INFOPLIST_FILE = CustomChannel/Info.plist; 489 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 490 | LD_RUNPATH_SEARCH_PATHS = ( 491 | "$(inherited)", 492 | "@executable_path/Frameworks", 493 | ); 494 | PRODUCT_BUNDLE_IDENTIFIER = Pure.CustomChannel; 495 | PRODUCT_NAME = "$(TARGET_NAME)"; 496 | SWIFT_VERSION = 4.0; 497 | TARGETED_DEVICE_FAMILY = "1,2"; 498 | }; 499 | name = Debug; 500 | }; 501 | 6C9F8A702070A8B7006D84A0 /* Release */ = { 502 | isa = XCBuildConfiguration; 503 | buildSettings = { 504 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 505 | CODE_SIGN_STYLE = Automatic; 506 | FRAMEWORK_SEARCH_PATHS = ( 507 | "$(inherited)", 508 | "$(PROJECT_DIR)/Frameworks", 509 | ); 510 | INFOPLIST_FILE = CustomChannel/Info.plist; 511 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 512 | LD_RUNPATH_SEARCH_PATHS = ( 513 | "$(inherited)", 514 | "@executable_path/Frameworks", 515 | ); 516 | PRODUCT_BUNDLE_IDENTIFIER = Pure.CustomChannel; 517 | PRODUCT_NAME = "$(TARGET_NAME)"; 518 | SWIFT_VERSION = 4.0; 519 | TARGETED_DEVICE_FAMILY = "1,2"; 520 | }; 521 | name = Release; 522 | }; 523 | 6C9F8A722070A8B7006D84A0 /* Debug */ = { 524 | isa = XCBuildConfiguration; 525 | buildSettings = { 526 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 527 | BUNDLE_LOADER = "$(TEST_HOST)"; 528 | CODE_SIGN_STYLE = Automatic; 529 | INFOPLIST_FILE = CustomChannelTests/Info.plist; 530 | LD_RUNPATH_SEARCH_PATHS = ( 531 | "$(inherited)", 532 | "@executable_path/Frameworks", 533 | "@loader_path/Frameworks", 534 | ); 535 | PRODUCT_BUNDLE_IDENTIFIER = Pure.CustomChannelTests; 536 | PRODUCT_NAME = "$(TARGET_NAME)"; 537 | SWIFT_VERSION = 4.0; 538 | TARGETED_DEVICE_FAMILY = "1,2"; 539 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CustomChannel.app/CustomChannel"; 540 | }; 541 | name = Debug; 542 | }; 543 | 6C9F8A732070A8B7006D84A0 /* Release */ = { 544 | isa = XCBuildConfiguration; 545 | buildSettings = { 546 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 547 | BUNDLE_LOADER = "$(TEST_HOST)"; 548 | CODE_SIGN_STYLE = Automatic; 549 | INFOPLIST_FILE = CustomChannelTests/Info.plist; 550 | LD_RUNPATH_SEARCH_PATHS = ( 551 | "$(inherited)", 552 | "@executable_path/Frameworks", 553 | "@loader_path/Frameworks", 554 | ); 555 | PRODUCT_BUNDLE_IDENTIFIER = Pure.CustomChannelTests; 556 | PRODUCT_NAME = "$(TARGET_NAME)"; 557 | SWIFT_VERSION = 4.0; 558 | TARGETED_DEVICE_FAMILY = "1,2"; 559 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CustomChannel.app/CustomChannel"; 560 | }; 561 | name = Release; 562 | }; 563 | 6C9F8A752070A8B7006D84A0 /* Debug */ = { 564 | isa = XCBuildConfiguration; 565 | buildSettings = { 566 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 567 | CODE_SIGN_STYLE = Automatic; 568 | INFOPLIST_FILE = CustomChannelUITests/Info.plist; 569 | LD_RUNPATH_SEARCH_PATHS = ( 570 | "$(inherited)", 571 | "@executable_path/Frameworks", 572 | "@loader_path/Frameworks", 573 | ); 574 | PRODUCT_BUNDLE_IDENTIFIER = Pure.CustomChannelUITests; 575 | PRODUCT_NAME = "$(TARGET_NAME)"; 576 | SWIFT_VERSION = 4.0; 577 | TARGETED_DEVICE_FAMILY = "1,2"; 578 | TEST_TARGET_NAME = CustomChannel; 579 | }; 580 | name = Debug; 581 | }; 582 | 6C9F8A762070A8B7006D84A0 /* Release */ = { 583 | isa = XCBuildConfiguration; 584 | buildSettings = { 585 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 586 | CODE_SIGN_STYLE = Automatic; 587 | INFOPLIST_FILE = CustomChannelUITests/Info.plist; 588 | LD_RUNPATH_SEARCH_PATHS = ( 589 | "$(inherited)", 590 | "@executable_path/Frameworks", 591 | "@loader_path/Frameworks", 592 | ); 593 | PRODUCT_BUNDLE_IDENTIFIER = Pure.CustomChannelUITests; 594 | PRODUCT_NAME = "$(TARGET_NAME)"; 595 | SWIFT_VERSION = 4.0; 596 | TARGETED_DEVICE_FAMILY = "1,2"; 597 | TEST_TARGET_NAME = CustomChannel; 598 | }; 599 | name = Release; 600 | }; 601 | /* End XCBuildConfiguration section */ 602 | 603 | /* Begin XCConfigurationList section */ 604 | 6C9F8A412070A8B5006D84A0 /* Build configuration list for PBXProject "CustomChannel" */ = { 605 | isa = XCConfigurationList; 606 | buildConfigurations = ( 607 | 6C9F8A6C2070A8B7006D84A0 /* Debug */, 608 | 6C9F8A6D2070A8B7006D84A0 /* Release */, 609 | ); 610 | defaultConfigurationIsVisible = 0; 611 | defaultConfigurationName = Release; 612 | }; 613 | 6C9F8A6E2070A8B7006D84A0 /* Build configuration list for PBXNativeTarget "CustomChannel" */ = { 614 | isa = XCConfigurationList; 615 | buildConfigurations = ( 616 | 6C9F8A6F2070A8B7006D84A0 /* Debug */, 617 | 6C9F8A702070A8B7006D84A0 /* Release */, 618 | ); 619 | defaultConfigurationIsVisible = 0; 620 | defaultConfigurationName = Release; 621 | }; 622 | 6C9F8A712070A8B7006D84A0 /* Build configuration list for PBXNativeTarget "CustomChannelTests" */ = { 623 | isa = XCConfigurationList; 624 | buildConfigurations = ( 625 | 6C9F8A722070A8B7006D84A0 /* Debug */, 626 | 6C9F8A732070A8B7006D84A0 /* Release */, 627 | ); 628 | defaultConfigurationIsVisible = 0; 629 | defaultConfigurationName = Release; 630 | }; 631 | 6C9F8A742070A8B7006D84A0 /* Build configuration list for PBXNativeTarget "CustomChannelUITests" */ = { 632 | isa = XCConfigurationList; 633 | buildConfigurations = ( 634 | 6C9F8A752070A8B7006D84A0 /* Debug */, 635 | 6C9F8A762070A8B7006D84A0 /* Release */, 636 | ); 637 | defaultConfigurationIsVisible = 0; 638 | defaultConfigurationName = Release; 639 | }; 640 | /* End XCConfigurationList section */ 641 | }; 642 | rootObject = 6C9F8A3E2070A8B5006D84A0 /* Project object */; 643 | } 644 | -------------------------------------------------------------------------------- /CustomChannel.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CustomChannel.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CustomChannel.xcodeproj/project.xcworkspace/xcuserdata/May.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wubianxiaoxian/CustomChannel/e6fcd2945709ecee701615e5b1b6edce418bd12e/CustomChannel.xcodeproj/project.xcworkspace/xcuserdata/May.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /CustomChannel.xcodeproj/xcuserdata/May.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /CustomChannel.xcodeproj/xcuserdata/May.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | CustomChannel.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /CustomChannel/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // CustomChannel 4 | // 5 | // Created by 五月 on 2018/4/1. 6 | // Copyright © 2018年 孙凯峰. 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 | -------------------------------------------------------------------------------- /CustomChannel/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 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /CustomChannel/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /CustomChannel/Assets.xcassets/btn_close_nor.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "btn_close_nor@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "btn_close_nor@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /CustomChannel/Assets.xcassets/btn_close_nor.imageset/btn_close_nor@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wubianxiaoxian/CustomChannel/e6fcd2945709ecee701615e5b1b6edce418bd12e/CustomChannel/Assets.xcassets/btn_close_nor.imageset/btn_close_nor@2x.png -------------------------------------------------------------------------------- /CustomChannel/Assets.xcassets/btn_close_nor.imageset/btn_close_nor@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wubianxiaoxian/CustomChannel/e6fcd2945709ecee701615e5b1b6edce418bd12e/CustomChannel/Assets.xcassets/btn_close_nor.imageset/btn_close_nor@3x.png -------------------------------------------------------------------------------- /CustomChannel/Assets.xcassets/btn_delete.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "btn_delete@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /CustomChannel/Assets.xcassets/btn_delete.imageset/btn_delete@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wubianxiaoxian/CustomChannel/e6fcd2945709ecee701615e5b1b6edce418bd12e/CustomChannel/Assets.xcassets/btn_delete.imageset/btn_delete@2x.png -------------------------------------------------------------------------------- /CustomChannel/Assets.xcassets/p2184389417.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "p2184389417.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /CustomChannel/Assets.xcassets/p2184389417.imageset/p2184389417.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wubianxiaoxian/CustomChannel/e6fcd2945709ecee701615e5b1b6edce418bd12e/CustomChannel/Assets.xcassets/p2184389417.imageset/p2184389417.jpg -------------------------------------------------------------------------------- /CustomChannel/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 | -------------------------------------------------------------------------------- /CustomChannel/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 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /CustomChannel/CustomchannelView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomchannelView.swift 3 | // GenialTone 4 | // 5 | // Created by 孙凯峰 on 2017/8/17. 6 | // Copyright © 2017年 SNDA. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class CustomchannelView: UIView, UITableViewDataSource, UITableViewDelegate { 12 | var backgrouncView: UIView! 13 | var CustomTableview: UITableView! 14 | var selectTagDict = [String:AnyObject]() 15 | 16 | var customchannelViewDismissBlock: (([PureTagModel]) -> Void)? 17 | fileprivate var selectedtagList: PureTagList!//已选择的标签 18 | var selectedtitleModels:[PureTagModel] = [] 19 | fileprivate var tagList0: PureTagList!//第一个分区的标签 20 | var tagList0Models:[PureTagModel] = [] //第一个分区的数据 21 | fileprivate var playGametagList: PureTagList!//第二个分区的标签 22 | fileprivate var entertainmenttagList: PureTagList!//第三个分区的标签 23 | // MARK: - init 24 | convenience init() { 25 | self.init(frame:CGRect(x: 0, y: 0, width: UIScreen.appWidth(), height: UIScreen.appHeight()-67)) 26 | self.layer.cornerRadius = 10 27 | CustomTableview.reloadData() 28 | } 29 | 30 | override init(frame: CGRect) { 31 | super.init(frame: frame) 32 | backgrouncView = UIView(frame: .zero) 33 | backgrouncView.backgroundColor = UIColor.black.withAlphaComponent(0.3) 34 | self.backgroundColor = .white 35 | self.addSubview(cancelBtn) 36 | cancelBtn.snp.makeConstraints { (make) in 37 | make.centerX.equalToSuperview() 38 | make.bottom.equalTo(self.snp.top) 39 | make.size.equalTo(CGSize(width: 30, height: 50)) 40 | } 41 | 42 | var playGameModels:[PureTagModel] = [] 43 | var entertainmentModels:[PureTagModel] = [] 44 | 45 | // 可从外部传入 46 | let arr0 = ["夜的第七章","听妈妈的话","千里之外 ","本草纲目","退后","贻笑大方","红模仿","心雨","白色风车"," 迷迭香","菊花台"] 47 | let arr1 = ["不能说的秘密","牛仔很忙","彩虹","青花瓷","阳光宅男","蒲公英的约定","无双","我不配","扯","甜甜的","最长的电影"] 48 | let arr2 = ["龙战骑士","给我一首歌的时间","蛇舞","花海","魔术先生","说好的幸福呢","兰亭序","流浪诗人","时光机","稻香"] 49 | 50 | 51 | for (i,str) in arr0.enumerated() { 52 | let model = PureTagModel(title: str, category_id: NSNumber.init(value: i)) 53 | tagList0Models.append(model) 54 | } 55 | for (i,str) in arr1.enumerated() { 56 | let model = PureTagModel(title: str, category_id: NSNumber.init(value: i+12)) 57 | playGameModels.append(model) 58 | } 59 | for (i,str) in arr2.enumerated() { 60 | let model = PureTagModel(title: str, category_id: NSNumber.init(value: i+12)) 61 | entertainmentModels.append(model) 62 | } 63 | 64 | 65 | 66 | selectedtagList = PureTagList(frame: CGRect(x: 0, y: 34, width: UIScreen.appWidth(), height: 0)) 67 | let itemWidth2 = (UIScreen.appWidth() - 15*3-24)/4 68 | selectedtagList.tagSize = CGSize(width: itemWidth2, height: 26) 69 | selectedtagList.clickTagBlock = {[weak self] (model: PureTagModel) in 70 | self?.selectedtagclick(model) 71 | } 72 | selectedtagList.isSort = true 73 | selectedtagList.addTags(selectedtitleModels) 74 | 75 | tagList0 = PureTagList(frame: CGRect(x: 0, y: 34, width: UIScreen.appWidth(), height: 0)) 76 | tagList0.tagSize = CGSize(width: itemWidth2, height: 26) 77 | tagList0.clickTagBlock = {[weak self] (model: PureTagModel) in 78 | self?.otherTagClick(model) 79 | } 80 | tagList0.tagBackgroundColor = .white 81 | tagList0.borderColor = .gray 82 | tagList0.tagTitleColor = .gray 83 | tagList0.isAddDeleteImageView = false 84 | tagList0.addTags(tagList0Models) 85 | 86 | playGametagList = PureTagList(frame: CGRect(x: 0, y: 34, width: UIScreen.appWidth(), height: 0)) 87 | playGametagList.tagSize = CGSize(width: itemWidth2, height: 26) 88 | playGametagList.isAddDeleteImageView = false 89 | 90 | playGametagList.clickTagBlock = {[weak self] (model: PureTagModel) in 91 | self?.otherTagClick(model) 92 | } 93 | playGametagList.tagBackgroundColor = .white 94 | playGametagList.borderColor = .gray 95 | playGametagList.tagTitleColor = .gray 96 | playGametagList.addTags(playGameModels) 97 | 98 | entertainmenttagList = PureTagList(frame: CGRect(x: 0, y: 34, width: UIScreen.appWidth(), height: 0)) 99 | entertainmenttagList.isAddDeleteImageView = false 100 | entertainmenttagList.tagSize = CGSize(width: itemWidth2, height: 26) 101 | entertainmenttagList.clickTagBlock = {[weak self] (model: PureTagModel) in 102 | self?.otherTagClick(model) 103 | } 104 | entertainmenttagList.tagBackgroundColor = .white 105 | entertainmenttagList.borderColor = .gray 106 | entertainmenttagList.tagTitleColor = .gray 107 | entertainmenttagList.addTags(entertainmentModels) 108 | 109 | CustomTableview = UITableView() 110 | CustomTableview.delegate = self 111 | CustomTableview.dataSource = self 112 | CustomTableview.register(SelectedChannelCell.self, forCellReuseIdentifier: "SelectedChannelCell") 113 | CustomTableview.separatorStyle = .none 114 | self.addSubview(CustomTableview) 115 | CustomTableview.snp.makeConstraints { (make) in 116 | make.left.right.bottom.equalTo(0) 117 | make.top.equalTo(7) 118 | } 119 | } 120 | 121 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 122 | return 4 123 | } 124 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 125 | let cell = tableView.dequeueReusableCell(withIdentifier: "SelectedChannelCell") as! SelectedChannelCell 126 | if indexPath.row == 0 { 127 | cell.tagList = self.selectedtagList 128 | cell.markLabel.isHidden = false 129 | } else if indexPath.row == 1 { 130 | cell.tagList = self.tagList0 131 | cell.channelNameLabel.text = "依然范特西" 132 | } else if indexPath.row == 2 { 133 | cell.tagList = self.playGametagList 134 | cell.channelNameLabel.text = "不能说的秘密" 135 | 136 | } else if indexPath.row == 3 { 137 | cell.tagList = self.entertainmenttagList 138 | cell.channelNameLabel.text = "魔杰座" 139 | 140 | } 141 | 142 | return cell 143 | } 144 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 145 | if indexPath.row == 0 { 146 | return selectedtagList.tagListH! + 44 147 | } else if indexPath.row == 1 { 148 | return tagList0.tagListH! + 44 149 | } else if indexPath.row == 2 { 150 | return playGametagList.tagListH! + 44 151 | } else if indexPath.row == 3 { 152 | return entertainmenttagList.tagListH! + 44 153 | } 154 | return 0 155 | } 156 | 157 | // MARK: - tag 点击方法 selectedtagclick 158 | func selectedtagclick(_ model: PureTagModel) { 159 | //// // 标签最少6个 160 | // if selectedtagList.tagModelArray.count == 6 { 161 | //// BaseToast.toast(failure: "提示", message: "请至少保留6个频道!") 162 | // return 163 | // } 164 | selectedtagList.deleteTag(model.title) 165 | 166 | switch model.category_type { 167 | case 1: 168 | playGametagList.addTag(model) 169 | case 2: 170 | tagList0.addTag(model) 171 | case 3: 172 | entertainmenttagList.addTag(model) 173 | default: 174 | break 175 | } 176 | CustomTableview.reloadData() 177 | } 178 | func otherTagClick (_ model: PureTagModel) { 179 | switch model.category_type { 180 | case 1: 181 | playGametagList.deleteTag(model.title) 182 | case 2: 183 | tagList0.deleteTag(model.title) 184 | case 3: 185 | entertainmenttagList.deleteTag(model.title) 186 | default: 187 | break 188 | } 189 | selectedtagList.addTag(model) 190 | CustomTableview.reloadData() 191 | } 192 | 193 | fileprivate lazy var cancelBtn: UIButton = { 194 | let v = UIButton() 195 | v.setImage(UIImage(named: "btn_close_nor"), for: .normal) 196 | v.addTarget(self, action: #selector(cancelAction(_:)), for: .touchUpInside) 197 | return v 198 | }() 199 | 200 | required init?(coder aDecoder: NSCoder) { 201 | fatalError("init(coder:) has not been implemented") 202 | } 203 | // MARK: - show 204 | func show(inView view: UIView) { 205 | UIApplication.shared.delegate?.window??.addSubview(backgrouncView) 206 | backgrouncView.snp.makeConstraints { (make) in 207 | make.edges.equalToSuperview() 208 | } 209 | UIApplication.shared.delegate?.window??.addSubview(self) 210 | let size = frame.size 211 | frame = CGRect(x: (view.frame.size.width - size.width)/2, y: view.frame.size.height + 50, width: size.width, height: size.height) 212 | backgrouncView.alpha = 0 213 | UIView.animate(withDuration: 0.25) { 214 | self.backgrouncView.alpha = 1 215 | self.frame = CGRect(x: (view.frame.size.width - size.width)/2, y: view.frame.size.height - size.height, width: size.width, height: size.height) 216 | } 217 | 218 | } 219 | @objc func cancelAction(_ sender: UIButton) { 220 | self.customchannelViewDismissBlock!(selectedtagList.tagModelArray) 221 | dismiss() 222 | } 223 | override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 224 | let result = super.hitTest(point, with: event) 225 | let testBtnPoint = cancelBtn.convert(point, from: self) 226 | if (self.cancelBtn.point(inside: testBtnPoint, with: event)) { 227 | return self.cancelBtn 228 | } 229 | 230 | return result 231 | } 232 | // MARK: - dismiss 233 | func dismiss() { 234 | let size = frame.size 235 | UIView.animate(withDuration: 0.25, animations: { 236 | self.backgrouncView.alpha = 0 237 | self.frame = CGRect(x: (self.superview!.frame.size.width - size.width)/2, y: self.superview!.frame.size.height + 50, width: size.width, height: size.height) 238 | }) { (_) in 239 | self.backgrouncView.removeFromSuperview() 240 | self.removeFromSuperview() 241 | } 242 | } 243 | 244 | } 245 | -------------------------------------------------------------------------------- /CustomChannel/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /CustomChannel/PureTagList.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PureTagList.swift 3 | // PureTagList 4 | // 5 | // Created by 孙凯峰 on 2017/8/17. 6 | // Copyright © 2017年 孙凯峰. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class PureTagList: UIView { 12 | /* 标签删除图片 */ 13 | var tagDeleteimage: UIImage? 14 | var isAddDeleteImageView: Bool = true 15 | /* 标签间距,和距离左,上间距,默认10 */ 16 | var tagMargin: CGFloat = 10.0 17 | /* 标签颜色,默认白色 */ 18 | var tagTitleColor: UIColor = .white 19 | /* 标签背景颜色 */ 20 | var tagBackgroundColor: UIColor = .blue 21 | /* 标签背景图片 */ 22 | var tagFont: UIFont = UIFont.systemFont(ofSize: 13) 23 | /* 标签按钮内容间距,标签内容距离左上下右间距,默认5 */ 24 | var tagCornerRadius: CGFloat = 11 25 | var tagButtonMargin: CGFloat = 5 26 | var tagListH: CGFloat? { 27 | get { 28 | if self.tagButtons.count <= 0 { 29 | return 0 30 | } 31 | return (self.tagButtons.last?.frame)!.maxY + 0 32 | } 33 | } 34 | var borderWidth: CGFloat = 0.5 35 | var borderColor: UIColor = .blue 36 | /** 获取所有标签*/ 37 | lazy private(set) var tagModelArray = [PureTagModel]() 38 | /* 是否需要自定义tagList高度,默认为true*/ 39 | var isFitTagListH: Bool = true 40 | /* 是否需要排序功能*/ 41 | var isSort: Bool = false 42 | var scaleTagInSort: CGFloat = 1.3 { 43 | didSet { 44 | let scale: CGFloat = 1.0 45 | if scaleTagInSort < scale { 46 | print("scaleTagInSort必须大于1") 47 | } 48 | } 49 | } 50 | var tagClass: UIButton? 51 | var tagSize: CGSize? 52 | /*标签间距会自动计算*/ 53 | var tagListCols: Int = 4 54 | var clickTagBlock: ((PureTagModel) -> Void)? 55 | weak var tagListView: UICollectionView? 56 | lazy var tags: [String: AnyObject] = [String:AnyObject]() 57 | lazy var tagButtons: [UIButton] = [UIButton]() 58 | /* 需要移动的矩阵 */ 59 | var moveFinalRect: CGRect = CGRect(x: 0, y: 0, width: 0, height: 0) 60 | var oriCenter: CGPoint? 61 | override init(frame: CGRect) { 62 | super.init(frame: frame) 63 | self.clipsToBounds = true 64 | 65 | } 66 | required init?(coder aDecoder: NSCoder) { 67 | fatalError("init(coder:) has not been implemented") 68 | } 69 | 70 | // 添加多个标签 71 | func addTags(_ tagStrs: [PureTagModel]) { 72 | assert(self.frame.size.width != 0, "先设置标签列表的frame") 73 | for titleModel in tagStrs { 74 | self.addTag(titleModel) 75 | } 76 | } 77 | func addTag(_ titleModel: PureTagModel) { 78 | let tagStr = titleModel.title 79 | if (self.tags[tagStr] != nil) { 80 | // 重复标签不添加 81 | return 82 | } 83 | let tagButton = PureTagButton(frame:CGRect(x: 0, y: 0, width: (tagSize?.width)!, height: (tagSize?.height)!)) 84 | tagButton.setTitle("fff", for: .normal) 85 | var normalImage = UIImage.image(color: tagBackgroundColor, size: tagSize!) 86 | normalImage = normalImage?.roundCorner(radius: (tagSize?.height)!/2, borderWidth: 0.5, borderColor: borderColor) 87 | tagButton.setBackgroundImage(normalImage, for: .normal) 88 | tagButton.setTitleColor(tagTitleColor, for: .normal) 89 | tagButton.tag = self.tagButtons.count 90 | tagButton.setTitle(tagStr, for: .normal) 91 | tagButton.titleLabel?.font = tagFont 92 | tagButton.titleLabel?.adjustsFontSizeToFitWidth = true 93 | tagButton.setBackgroundImage(normalImage, for: .normal) 94 | tagButton.addTarget(self, action: #selector(clickTag(_:)), for: .touchUpInside) 95 | if isSort { 96 | let pan = UIPanGestureRecognizer.init(target: self, action: #selector(pan(_:))) 97 | tagButton .addGestureRecognizer(pan) 98 | } 99 | self.addSubview(tagButton) 100 | self.tagButtons.append(tagButton) 101 | self.tags[tagStr] = tagButton 102 | self.tagModelArray.append(titleModel) 103 | // 设置按钮的位置 104 | self.updateTagButtonFrame(tagButton.tag, extreMargin: true) 105 | if (isFitTagListH) { 106 | var frame = self.frame 107 | frame.size.height = self.tagListH! 108 | UIView.animate(withDuration: 0.25, animations: { 109 | self.frame = frame 110 | }) 111 | } 112 | if isAddDeleteImageView == false { 113 | tagButton.deleImgeView.isHidden = true 114 | } 115 | if tagStr == "推荐" { 116 | tagButton.isEnabled = false 117 | var disableImage = UIImage.image(color: .gray, size: tagSize!) 118 | disableImage = disableImage?.roundCorner(radius: (tagSize?.height)!/2, borderWidth: 0.5, borderColor: .gray) 119 | tagButton.setBackgroundImage(disableImage, for: .disabled) 120 | tagButton.setTitleColor(.gray, for: .disabled) 121 | tagButton.deleImgeView.isHidden = true 122 | } 123 | 124 | } 125 | @objc func clickTag(_ sender: UIButton) { 126 | if (clickTagBlock != nil) { 127 | let model = self.tagModelArray [sender.tag] 128 | clickTagBlock!(model) 129 | } 130 | } 131 | @objc func pan(_ sender: UIPanGestureRecognizer) { 132 | //获取偏移量 133 | let transP = sender.translation(in: self) 134 | let tagButton = sender.view as! UIButton 135 | // 开始 136 | if sender.state == .began { 137 | oriCenter = tagButton.center 138 | UIView.animate(withDuration: 0.25, animations: { 139 | tagButton.transform = CGAffineTransform(scaleX: self.scaleTagInSort, y: self.scaleTagInSort) 140 | }) 141 | self.addSubview(tagButton) 142 | } 143 | var center = tagButton.center 144 | center.x += transP.x 145 | center.y += transP.y 146 | tagButton.center = center 147 | // 改变 148 | if sender.state == .changed { 149 | let otherButton = self.buttonCenterInButtons(tagButton) 150 | if (otherButton != nil) { //插入到当前按钮的位置 151 | // 获取插入的角标 152 | let i = otherButton?.tag 153 | // 这里可以设置哪个标签不被改变 154 | // if i == 0 { 155 | // return 156 | // } 157 | //获取当前角标 158 | let curI = tagButton.tag 159 | let model = self.tagModelArray[curI] 160 | moveFinalRect = (otherButton?.frame)! 161 | //排序 162 | // 移除之前的按钮 163 | self.tagButtons.remove(at: curI) 164 | self.tagButtons.insert(tagButton, at: i!) 165 | self.tagModelArray .remove(at: curI) 166 | self.tagModelArray.insert(model, at: i!) 167 | //更新tag 168 | self.updateTag() 169 | if curI > i! {//向前插入 170 | //更新之后的标签frame 171 | UIView.animate(withDuration: 0.25, animations: { 172 | self.updateLaterTagButtonFrame(i!+1) 173 | }) 174 | } else { // 往后插入 175 | UIView.animate(withDuration: 0.25, animations: { 176 | self.updateBeforeTagButtonFrame(i!) 177 | }) 178 | } 179 | } 180 | } 181 | // 结束 182 | if sender.state == .ended { 183 | print("--self.tagArray --\(self.tagModelArray)") 184 | UIView.animate(withDuration: 0.25, animations: { 185 | tagButton.transform = CGAffineTransform.identity 186 | if self.moveFinalRect.size.width <= CGFloat(0) { 187 | tagButton.center = self.oriCenter! 188 | } else { 189 | tagButton.frame = self.moveFinalRect 190 | } 191 | }, completion: { (true) in 192 | self.moveFinalRect = .zero 193 | }) 194 | } 195 | sender.setTranslation(.zero, in: self) 196 | } 197 | //更新标签 198 | func updateTag() { 199 | for (i, button) in self.tagButtons.enumerated() { 200 | let tagButton = button 201 | tagButton.tag = i 202 | } 203 | } 204 | func deleteTag(_ tagStr: String) { 205 | // 获取对应的标签 206 | guard let button = self.tags[tagStr] as? UIButton else { 207 | return 208 | } 209 | button.removeFromSuperview() 210 | self.tagButtons.remove(at: (button.tag)) 211 | self.tags.removeValue(forKey: tagStr) 212 | self.tagModelArray.remove(at: (button.tag)) 213 | self.updateTag() 214 | UIView.animate(withDuration: 0.25) { 215 | self.updateLaterTagButtonFrame((button.tag)) 216 | } 217 | if isFitTagListH { 218 | var frame = self.frame 219 | frame.size.height = self.tagListH! 220 | UIView.animate(withDuration: 0.25, animations: { 221 | self.frame = frame 222 | }) 223 | } 224 | } 225 | 226 | func updateBeforeTagButtonFrame(_ beforeI: Int) { 227 | for i in 0..= 0 { 247 | preButton = self.tagButtons[preI] 248 | } 249 | //获取当前按钮 250 | let tagButton = self.tagButtons[i] 251 | // 判断是否设置标签的尺寸 252 | // if tagSize?.width == 0 {// 没有设置标签尺寸 253 | // 自适应标签尺寸 254 | self.setupTagButtonRegularFrame(tagButton) 255 | // } 256 | 257 | } 258 | // 看下当前按钮中心点在哪个按钮上 259 | func buttonCenterInButtons(_ curButton: UIButton) -> UIButton? { 260 | for button in self.tagButtons { 261 | if curButton == button { 262 | continue 263 | } 264 | if button.frame.contains(curButton.center) { 265 | return button 266 | } 267 | } 268 | return nil 269 | } 270 | 271 | func setupTagButtonRegularFrame(_ tagButton: UIButton) { 272 | // 获取角标 273 | let i = tagButton.tag 274 | let col = i % tagListCols 275 | let row = i / tagListCols 276 | let btnW: CGFloat = (tagSize?.width)! 277 | let btnH: CGFloat = (tagSize?.height)! 278 | let margin = (self.bounds.size.width - CGFloat(tagListCols) * btnW - 2 * tagMargin) / CGFloat(tagListCols - 1) 279 | let btnX = tagMargin + CGFloat(col) * (btnW + margin) 280 | let btnY = tagMargin + CGFloat(row) * (btnH + margin) 281 | tagButton.frame = CGRect(x: btnX, y: btnY, width: btnW, height: btnH) 282 | 283 | } 284 | } 285 | class PureTagButton: UIButton { 286 | var margin: CGFloat? 287 | var deleImgeView: UIImageView! 288 | override init(frame: CGRect) { 289 | super.init(frame: frame) 290 | deleImgeView = UIImageView(frame: CGRect(x: bounds.size.width-12, y: -5, width: 17, height: 17)) 291 | addSubview(deleImgeView!) 292 | deleImgeView.image = UIImage(named: "btn_delete") 293 | } 294 | 295 | required init?(coder aDecoder: NSCoder) { 296 | fatalError("init(coder:) has not been implemented") 297 | } 298 | 299 | } 300 | 301 | class PureTagModel: NSObject { 302 | var title: String = "" 303 | var oriImageUrl: String = "" 304 | var highlightImageUrl: String = "" 305 | 306 | var category_id: NSNumber = 0 307 | var category_type: NSNumber = 0 308 | 309 | init(title: String, category_id: NSNumber) { 310 | self.title = title 311 | self.category_id = category_id 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /CustomChannel/SelectedChannelCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SelectedChannelCell.swift 3 | // GenialTone 4 | // 5 | // Created by 五月 on 2017/8/21. 6 | // Copyright © 2017年 SNDA. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SnapKit 11 | 12 | class SelectedChannelCell: UITableViewCell { 13 | var channelNameLabel: UILabel! 14 | var markLabel: UILabel! 15 | 16 | var tagList: PureTagList! { 17 | didSet { 18 | self.contentView.addSubview(tagList) 19 | 20 | } 21 | } 22 | override init(style: UITableViewCellStyle, reuseIdentifier: String?) { 23 | super.init(style: style, reuseIdentifier: reuseIdentifier) 24 | self.selectionStyle = .none 25 | channelNameLabel = UILabel() 26 | channelNameLabel.font = UIFont.systemFont(ofSize: 12) 27 | channelNameLabel.textColor = .black 28 | channelNameLabel.textAlignment = .center 29 | self.contentView.addSubview(channelNameLabel) 30 | channelNameLabel.snp.makeConstraints { (make) in 31 | make.top.equalTo(20) 32 | make.left.equalTo(18) 33 | make.height.equalTo(14) 34 | } 35 | channelNameLabel.text = "已选歌曲标签" 36 | markLabel = UILabel() 37 | markLabel.textColor = .gray 38 | markLabel.font = UIFont.systemFont(ofSize: 12) 39 | 40 | markLabel.textAlignment = .center 41 | self.contentView.addSubview(markLabel) 42 | markLabel.snp.makeConstraints { (make) in 43 | make.left.equalTo(channelNameLabel.snp.right).offset(7) 44 | make.centerY.equalTo(channelNameLabel.snp.centerY) 45 | } 46 | markLabel.text = "按住推动调整顺序" 47 | markLabel.isHidden = true 48 | let bottomLine = UIImageView(image: UIImage.image(color: UIColor.gray)) 49 | self.contentView.addSubview(bottomLine) 50 | bottomLine.snp.makeConstraints { (make) in 51 | make.height.equalTo(0.5) 52 | make.right.left.equalTo(0) 53 | make.bottom.equalTo(0) 54 | } 55 | 56 | } 57 | required init?(coder aDecoder: NSCoder) { 58 | fatalError("init(coder:) has not been implemented") 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /CustomChannel/UIColorExt.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColorExt.swift 3 | // GenialTone 4 | // 5 | // Created by SNDA on 2017/6/21. 6 | // Copyright © 2017年 SNDA. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | extension UIColor { 13 | convenience init(rgb: UInt, alpha: CGFloat = 1) { 14 | self.init(red: CGFloat((rgb & 0xFF0000) >> 16) / 255.0, green: CGFloat((rgb & 0x00FF00) >> 8) / 255.0, blue: CGFloat(rgb & 0x0000FF) / 255.0, alpha: alpha) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /CustomChannel/UIImageExt.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageExt.swift 3 | // GenialTone 4 | // 5 | // Created by SNDA on 2017/6/21. 6 | // Copyright © 2017年 SNDA. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import QuartzCore 12 | 13 | extension UIImage { 14 | 15 | class func image(color: UIColor?, size: CGSize = CGSize(width: 10, height: 10)) -> UIImage? { 16 | guard color != nil && size.width > 0 && size.height > 0 else { 17 | return nil 18 | } 19 | let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height) 20 | UIGraphicsBeginImageContextWithOptions(rect.size, false, UIScreen.main.scale) 21 | let context = UIGraphicsGetCurrentContext() 22 | context?.setFillColor(color!.cgColor) 23 | context?.fill(rect) 24 | let image = UIGraphicsGetImageFromCurrentImageContext() 25 | UIGraphicsEndImageContext() 26 | return image 27 | } 28 | 29 | func roundCorner(radius: CGFloat, corners: UIRectCorner = .allCorners, borderWidth: CGFloat = 0, borderColor: UIColor? = nil) -> UIImage? { 30 | 31 | var aCorners = corners 32 | if aCorners != .allCorners { 33 | var tmp = 0 as UInt 34 | if (corners.rawValue & UIRectCorner.topLeft.rawValue) != 0 { 35 | tmp |= UIRectCorner.bottomLeft.rawValue 36 | } 37 | if (corners.rawValue & UIRectCorner.topRight.rawValue) != 0 { 38 | tmp |= UIRectCorner.bottomRight.rawValue 39 | } 40 | if (corners.rawValue & UIRectCorner.bottomLeft.rawValue) != 0 { 41 | tmp |= UIRectCorner.topLeft.rawValue 42 | } 43 | if (corners.rawValue & UIRectCorner.bottomRight.rawValue) != 0 { 44 | tmp |= UIRectCorner.topRight.rawValue 45 | } 46 | aCorners = UIRectCorner(rawValue: tmp) 47 | } 48 | 49 | UIGraphicsBeginImageContextWithOptions(size, false, scale) 50 | let context = UIGraphicsGetCurrentContext() 51 | let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height) 52 | context?.scaleBy(x: 1, y: -1) 53 | context?.translateBy(x: 0, y: -rect.size.height) 54 | 55 | let path = UIBezierPath.init(roundedRect: rect, byRoundingCorners: aCorners, cornerRadii: CGSize(width: radius, height: 0)) 56 | path.close() 57 | context?.saveGState() 58 | path.addClip() 59 | context?.draw(cgImage!, in: rect) 60 | context?.restoreGState() 61 | 62 | if borderColor != nil && borderWidth > 0 { 63 | let strokeRect = rect.insetBy(dx: borderWidth/2, dy: borderWidth/2) 64 | let strokeRadius = radius - borderWidth/2 65 | let path = UIBezierPath.init(roundedRect: strokeRect, byRoundingCorners: aCorners, cornerRadii: CGSize(width: strokeRadius, height: 0)) 66 | 67 | path.close() 68 | path.lineWidth = borderWidth 69 | path.lineJoinStyle = .round 70 | borderColor?.setStroke() 71 | path.stroke() 72 | } 73 | 74 | let image = UIGraphicsGetImageFromCurrentImageContext() 75 | UIGraphicsEndImageContext() 76 | return image 77 | } 78 | 79 | func cgRectFit(rect: CGRect, size: CGSize, contentMode: UIViewContentMode) -> CGRect { 80 | var rect = rect.standardized 81 | var size = size 82 | size.width = size.width < 0 ? -size.width : size.width 83 | size.height = size.height < 0 ? -size.height : size.height 84 | let center = CGPoint(x: rect.midX, y: rect.midY) 85 | switch contentMode { 86 | case .scaleAspectFit, .scaleAspectFill: 87 | if (rect.size.width < 0.01 || rect.size.height < 0.01 || size.width < 0.01 || size.height < 0.01) { 88 | rect.origin = center 89 | rect.size = CGSize.zero 90 | } else { 91 | var scale: CGFloat 92 | if (contentMode == .scaleAspectFit) { 93 | if (size.width / size.height < rect.size.width / rect.size.height) { 94 | scale = rect.size.height / size.height 95 | } else { 96 | scale = rect.size.width / size.width 97 | } 98 | } else { 99 | if (size.width / size.height < rect.size.width / rect.size.height) { 100 | scale = rect.size.width / size.width 101 | } else { 102 | scale = rect.size.height / size.height 103 | } 104 | } 105 | size.width *= scale 106 | size.height *= scale 107 | rect.size = size 108 | rect.origin = CGPoint(x:center.x - size.width * 0.5, y:center.y - size.height * 0.5) 109 | } 110 | case .center: 111 | rect.size = size 112 | rect.origin = CGPoint(x:center.x - size.width * 0.5, y:center.y - size.height * 0.5) 113 | case .top: 114 | rect.origin.x = center.x - size.width * 0.5 115 | rect.size = size 116 | case .bottom: 117 | rect.origin.x = center.x - size.width * 0.5 118 | rect.origin.y += rect.size.height - size.height 119 | rect.size = size 120 | case .left: 121 | rect.origin.y = center.y - size.height * 0.5 122 | rect.size = size 123 | case .right: 124 | rect.origin.y = center.y - size.height * 0.5 125 | rect.origin.x += rect.size.width - size.width 126 | rect.size = size 127 | case .topLeft: 128 | rect.size = size 129 | case .topRight: 130 | rect.origin.x += rect.size.width - size.width 131 | rect.size = size 132 | case .bottomLeft: 133 | rect.origin.y += rect.size.height - size.height 134 | rect.size = size 135 | case .bottomRight: 136 | rect.origin.x += rect.size.width - size.width 137 | rect.origin.y += rect.size.height - size.height 138 | rect.size = size 139 | default: 140 | rect = CGRect(origin: rect.origin, size: rect.size) 141 | } 142 | return rect 143 | } 144 | 145 | func draw(in rect: CGRect, contentMode: UIViewContentMode, clipsToBounds clips: Bool) { 146 | let drawRect = cgRectFit(rect: rect, size: size, contentMode: contentMode) 147 | guard drawRect.size.width != 0 && drawRect.size.height != 0 else { 148 | return 149 | } 150 | if clips { 151 | let context = UIGraphicsGetCurrentContext() 152 | if context != nil { 153 | context?.saveGState() 154 | context?.addRect(rect) 155 | context?.clip() 156 | draw(in: drawRect) 157 | context?.restoreGState() 158 | } 159 | } else { 160 | draw(in: drawRect) 161 | } 162 | } 163 | 164 | func resize(toSize size: CGSize) -> UIImage? { 165 | guard size.width > 0 && size.height > 0 else { 166 | return UIImage() 167 | } 168 | let scale = max(self.scale, UIScreen.main.scale) 169 | UIGraphicsBeginImageContextWithOptions(size, false, scale) 170 | draw(in: CGRect(origin: CGPoint.zero, size: size)) 171 | let image = UIGraphicsGetImageFromCurrentImageContext() 172 | UIGraphicsEndImageContext() 173 | return image 174 | } 175 | 176 | func resize(toSize size: CGSize, contentMode: UIViewContentMode) -> UIImage? { 177 | guard size.width > 0 && size.height > 0 else { 178 | return UIImage() 179 | } 180 | let scale = max(self.scale, UIScreen.main.scale) 181 | UIGraphicsBeginImageContextWithOptions(size, false, scale) 182 | draw(in: CGRect(origin: CGPoint.zero, size: size), contentMode: contentMode, clipsToBounds: false) 183 | let image = UIGraphicsGetImageFromCurrentImageContext() 184 | UIGraphicsEndImageContext() 185 | return image 186 | } 187 | 188 | /** 189 | * 返回当前图片设置alpha后的新图片 190 | * @param alpha 制定alpha值 191 | * @return 当前图片设置alpha后的新图片 192 | */ 193 | func imageWithAlpha(_ alpha: CGFloat) -> UIImage { 194 | UIGraphicsBeginImageContextWithOptions(self.size, false, UIScreen.main.scale) 195 | let ctxOpt = UIGraphicsGetCurrentContext() 196 | guard let ctx = ctxOpt else { 197 | return UIImage() 198 | } 199 | let area = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height) 200 | ctx.scaleBy(x: 1, y: -1) 201 | ctx.translateBy(x: 0, y: -area.size.height) 202 | ctx.setBlendMode(.multiply) 203 | ctx.setAlpha(alpha) 204 | ctx.draw(self.cgImage!, in: area) 205 | let newImageOpt = UIGraphicsGetImageFromCurrentImageContext() 206 | UIGraphicsEndImageContext() 207 | guard let newImage = newImageOpt else { 208 | return UIImage() 209 | } 210 | return newImage 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /CustomChannel/UIScreenExt.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIScreenExt.swift 3 | // GenialTone 4 | // 5 | // Created by SNDA on 2017/6/21. 6 | // Copyright © 2017年 SNDA. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | private struct AppScreenSize { 13 | static var screenWidth: CGFloat = 0 14 | static var screenHeight: CGFloat = 0 15 | } 16 | 17 | extension UIScreen { 18 | static func appHeight() -> CGFloat { 19 | if AppScreenSize.screenHeight > 0 { 20 | return AppScreenSize.screenHeight 21 | } 22 | 23 | AppScreenSize.screenHeight = max(main.bounds.width, main.bounds.height) 24 | return AppScreenSize.screenHeight 25 | } 26 | 27 | static func appWidth() -> CGFloat { 28 | if AppScreenSize.screenWidth > 0 { 29 | return AppScreenSize.screenWidth 30 | } 31 | 32 | AppScreenSize.screenWidth = min(main.bounds.width, main.bounds.height) 33 | return AppScreenSize.screenWidth 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /CustomChannel/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // CustomChannel 4 | // 5 | // Created by 五月 on 2018/4/1. 6 | // Copyright © 2018年 孙凯峰. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view, typically from a nib. 16 | } 17 | 18 | @IBAction func test(_ sender: Any) { 19 | let customChannelView = CustomchannelView() 20 | customChannelView.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height-77) 21 | customChannelView.show(inView: self.view) 22 | customChannelView.customchannelViewDismissBlock = {(models) in 23 | print("选择的标签 models--\(models)") 24 | for model in models { 25 | print("model.title ** \(model.title)") 26 | } 27 | } 28 | 29 | } 30 | override func didReceiveMemoryWarning() { 31 | super.didReceiveMemoryWarning() 32 | // Dispose of any resources that can be recreated. 33 | } 34 | 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /CustomChannelTests/CustomChannelTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomChannelTests.swift 3 | // CustomChannelTests 4 | // 5 | // Created by 五月 on 2018/4/1. 6 | // Copyright © 2018年 孙凯峰. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import CustomChannel 11 | 12 | class CustomChannelTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /CustomChannelTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /CustomChannelUITests/CustomChannelUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomChannelUITests.swift 3 | // CustomChannelUITests 4 | // 5 | // Created by 五月 on 2018/4/1. 6 | // Copyright © 2018年 孙凯峰. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class CustomChannelUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /CustomChannelUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Frameworks/SnapKit.framework/Headers/SnapKit-Swift.h: -------------------------------------------------------------------------------- 1 | // Generated by Apple Swift version 4.1 (swiftlang-902.0.48 clang-902.0.37.1) 2 | #pragma clang diagnostic push 3 | #pragma clang diagnostic ignored "-Wgcc-compat" 4 | 5 | #if !defined(__has_include) 6 | # define __has_include(x) 0 7 | #endif 8 | #if !defined(__has_attribute) 9 | # define __has_attribute(x) 0 10 | #endif 11 | #if !defined(__has_feature) 12 | # define __has_feature(x) 0 13 | #endif 14 | #if !defined(__has_warning) 15 | # define __has_warning(x) 0 16 | #endif 17 | 18 | #if __has_include() 19 | # include 20 | #endif 21 | 22 | #pragma clang diagnostic ignored "-Wauto-import" 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #if !defined(SWIFT_TYPEDEFS) 29 | # define SWIFT_TYPEDEFS 1 30 | # if __has_include() 31 | # include 32 | # elif !defined(__cplusplus) 33 | typedef uint_least16_t char16_t; 34 | typedef uint_least32_t char32_t; 35 | # endif 36 | typedef float swift_float2 __attribute__((__ext_vector_type__(2))); 37 | typedef float swift_float3 __attribute__((__ext_vector_type__(3))); 38 | typedef float swift_float4 __attribute__((__ext_vector_type__(4))); 39 | typedef double swift_double2 __attribute__((__ext_vector_type__(2))); 40 | typedef double swift_double3 __attribute__((__ext_vector_type__(3))); 41 | typedef double swift_double4 __attribute__((__ext_vector_type__(4))); 42 | typedef int swift_int2 __attribute__((__ext_vector_type__(2))); 43 | typedef int swift_int3 __attribute__((__ext_vector_type__(3))); 44 | typedef int swift_int4 __attribute__((__ext_vector_type__(4))); 45 | typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2))); 46 | typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3))); 47 | typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4))); 48 | #endif 49 | 50 | #if !defined(SWIFT_PASTE) 51 | # define SWIFT_PASTE_HELPER(x, y) x##y 52 | # define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y) 53 | #endif 54 | #if !defined(SWIFT_METATYPE) 55 | # define SWIFT_METATYPE(X) Class 56 | #endif 57 | #if !defined(SWIFT_CLASS_PROPERTY) 58 | # if __has_feature(objc_class_property) 59 | # define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__ 60 | # else 61 | # define SWIFT_CLASS_PROPERTY(...) 62 | # endif 63 | #endif 64 | 65 | #if __has_attribute(objc_runtime_name) 66 | # define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X))) 67 | #else 68 | # define SWIFT_RUNTIME_NAME(X) 69 | #endif 70 | #if __has_attribute(swift_name) 71 | # define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X))) 72 | #else 73 | # define SWIFT_COMPILE_NAME(X) 74 | #endif 75 | #if __has_attribute(objc_method_family) 76 | # define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X))) 77 | #else 78 | # define SWIFT_METHOD_FAMILY(X) 79 | #endif 80 | #if __has_attribute(noescape) 81 | # define SWIFT_NOESCAPE __attribute__((noescape)) 82 | #else 83 | # define SWIFT_NOESCAPE 84 | #endif 85 | #if __has_attribute(warn_unused_result) 86 | # define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) 87 | #else 88 | # define SWIFT_WARN_UNUSED_RESULT 89 | #endif 90 | #if __has_attribute(noreturn) 91 | # define SWIFT_NORETURN __attribute__((noreturn)) 92 | #else 93 | # define SWIFT_NORETURN 94 | #endif 95 | #if !defined(SWIFT_CLASS_EXTRA) 96 | # define SWIFT_CLASS_EXTRA 97 | #endif 98 | #if !defined(SWIFT_PROTOCOL_EXTRA) 99 | # define SWIFT_PROTOCOL_EXTRA 100 | #endif 101 | #if !defined(SWIFT_ENUM_EXTRA) 102 | # define SWIFT_ENUM_EXTRA 103 | #endif 104 | #if !defined(SWIFT_CLASS) 105 | # if __has_attribute(objc_subclassing_restricted) 106 | # define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA 107 | # define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA 108 | # else 109 | # define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA 110 | # define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA 111 | # endif 112 | #endif 113 | 114 | #if !defined(SWIFT_PROTOCOL) 115 | # define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA 116 | # define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA 117 | #endif 118 | 119 | #if !defined(SWIFT_EXTENSION) 120 | # define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__) 121 | #endif 122 | 123 | #if !defined(OBJC_DESIGNATED_INITIALIZER) 124 | # if __has_attribute(objc_designated_initializer) 125 | # define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) 126 | # else 127 | # define OBJC_DESIGNATED_INITIALIZER 128 | # endif 129 | #endif 130 | #if !defined(SWIFT_ENUM_ATTR) 131 | # if defined(__has_attribute) && __has_attribute(enum_extensibility) 132 | # define SWIFT_ENUM_ATTR __attribute__((enum_extensibility(open))) 133 | # else 134 | # define SWIFT_ENUM_ATTR 135 | # endif 136 | #endif 137 | #if !defined(SWIFT_ENUM) 138 | # define SWIFT_ENUM(_type, _name) enum _name : _type _name; enum SWIFT_ENUM_ATTR SWIFT_ENUM_EXTRA _name : _type 139 | # if __has_feature(generalized_swift_name) 140 | # define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR SWIFT_ENUM_EXTRA _name : _type 141 | # else 142 | # define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME) SWIFT_ENUM(_type, _name) 143 | # endif 144 | #endif 145 | #if !defined(SWIFT_UNAVAILABLE) 146 | # define SWIFT_UNAVAILABLE __attribute__((unavailable)) 147 | #endif 148 | #if !defined(SWIFT_UNAVAILABLE_MSG) 149 | # define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg))) 150 | #endif 151 | #if !defined(SWIFT_AVAILABILITY) 152 | # define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__))) 153 | #endif 154 | #if !defined(SWIFT_DEPRECATED) 155 | # define SWIFT_DEPRECATED __attribute__((deprecated)) 156 | #endif 157 | #if !defined(SWIFT_DEPRECATED_MSG) 158 | # define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__))) 159 | #endif 160 | #if __has_feature(attribute_diagnose_if_objc) 161 | # define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning"))) 162 | #else 163 | # define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg) 164 | #endif 165 | #if __has_feature(modules) 166 | @import UIKit; 167 | #endif 168 | 169 | #pragma clang diagnostic ignored "-Wproperty-attribute-mismatch" 170 | #pragma clang diagnostic ignored "-Wduplicate-method-arg" 171 | #if __has_warning("-Wpragma-clang-attribute") 172 | # pragma clang diagnostic ignored "-Wpragma-clang-attribute" 173 | #endif 174 | #pragma clang diagnostic ignored "-Wunknown-pragmas" 175 | #pragma clang diagnostic ignored "-Wnullability" 176 | 177 | #if __has_attribute(external_source_symbol) 178 | # pragma push_macro("any") 179 | # undef any 180 | # pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="SnapKit",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol)) 181 | # pragma pop_macro("any") 182 | #endif 183 | 184 | 185 | SWIFT_CLASS("_TtC7SnapKit16LayoutConstraint") 186 | @interface LayoutConstraint : NSLayoutConstraint 187 | - (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER; 188 | @end 189 | 190 | 191 | @interface LayoutConstraint (SWIFT_EXTENSION(SnapKit)) 192 | @property (nonatomic, readonly, copy) NSString * _Nonnull description; 193 | @end 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | #if __has_attribute(external_source_symbol) 208 | # pragma clang attribute pop 209 | #endif 210 | #pragma clang diagnostic pop 211 | -------------------------------------------------------------------------------- /Frameworks/SnapKit.framework/Headers/SnapKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import 25 | 26 | FOUNDATION_EXPORT double SnapKitVersionNumber; 27 | FOUNDATION_EXPORT const unsigned char SnapKitVersionString[]; -------------------------------------------------------------------------------- /Frameworks/SnapKit.framework/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wubianxiaoxian/CustomChannel/e6fcd2945709ecee701615e5b1b6edce418bd12e/Frameworks/SnapKit.framework/Info.plist -------------------------------------------------------------------------------- /Frameworks/SnapKit.framework/Modules/SnapKit.swiftmodule/arm.swiftdoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wubianxiaoxian/CustomChannel/e6fcd2945709ecee701615e5b1b6edce418bd12e/Frameworks/SnapKit.framework/Modules/SnapKit.swiftmodule/arm.swiftdoc -------------------------------------------------------------------------------- /Frameworks/SnapKit.framework/Modules/SnapKit.swiftmodule/arm.swiftmodule: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wubianxiaoxian/CustomChannel/e6fcd2945709ecee701615e5b1b6edce418bd12e/Frameworks/SnapKit.framework/Modules/SnapKit.swiftmodule/arm.swiftmodule -------------------------------------------------------------------------------- /Frameworks/SnapKit.framework/Modules/SnapKit.swiftmodule/arm64.swiftdoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wubianxiaoxian/CustomChannel/e6fcd2945709ecee701615e5b1b6edce418bd12e/Frameworks/SnapKit.framework/Modules/SnapKit.swiftmodule/arm64.swiftdoc -------------------------------------------------------------------------------- /Frameworks/SnapKit.framework/Modules/SnapKit.swiftmodule/arm64.swiftmodule: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wubianxiaoxian/CustomChannel/e6fcd2945709ecee701615e5b1b6edce418bd12e/Frameworks/SnapKit.framework/Modules/SnapKit.swiftmodule/arm64.swiftmodule -------------------------------------------------------------------------------- /Frameworks/SnapKit.framework/Modules/SnapKit.swiftmodule/i386.swiftdoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wubianxiaoxian/CustomChannel/e6fcd2945709ecee701615e5b1b6edce418bd12e/Frameworks/SnapKit.framework/Modules/SnapKit.swiftmodule/i386.swiftdoc -------------------------------------------------------------------------------- /Frameworks/SnapKit.framework/Modules/SnapKit.swiftmodule/i386.swiftmodule: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wubianxiaoxian/CustomChannel/e6fcd2945709ecee701615e5b1b6edce418bd12e/Frameworks/SnapKit.framework/Modules/SnapKit.swiftmodule/i386.swiftmodule -------------------------------------------------------------------------------- /Frameworks/SnapKit.framework/Modules/SnapKit.swiftmodule/x86_64.swiftdoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wubianxiaoxian/CustomChannel/e6fcd2945709ecee701615e5b1b6edce418bd12e/Frameworks/SnapKit.framework/Modules/SnapKit.swiftmodule/x86_64.swiftdoc -------------------------------------------------------------------------------- /Frameworks/SnapKit.framework/Modules/SnapKit.swiftmodule/x86_64.swiftmodule: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wubianxiaoxian/CustomChannel/e6fcd2945709ecee701615e5b1b6edce418bd12e/Frameworks/SnapKit.framework/Modules/SnapKit.swiftmodule/x86_64.swiftmodule -------------------------------------------------------------------------------- /Frameworks/SnapKit.framework/Modules/module.modulemap: -------------------------------------------------------------------------------- 1 | framework module SnapKit { 2 | umbrella header "SnapKit.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | 8 | module SnapKit.Swift { 9 | header "SnapKit-Swift.h" 10 | requires objc 11 | } 12 | -------------------------------------------------------------------------------- /Frameworks/SnapKit.framework/SnapKit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wubianxiaoxian/CustomChannel/e6fcd2945709ecee701615e5b1b6edce418bd12e/Frameworks/SnapKit.framework/SnapKit -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CustomChannel 2 | ======= 3 | 4 | * 模仿今日头条选择标签 5 | 6 | * 自定义选择标签,支持拖拽排序、选中取消,支持自定义标签颜色、字体颜色 7 | 8 | 9 | 10 | ![https://raw.githubusercontent.com/wubianxiaoxian/CustomChannel/master/selectTag.gif](https://raw.githubusercontent.com/wubianxiaoxian/CustomChannel/master/selectTag.gif) 11 | -------------------------------------------------------------------------------- /selectTag.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wubianxiaoxian/CustomChannel/e6fcd2945709ecee701615e5b1b6edce418bd12e/selectTag.gif --------------------------------------------------------------------------------