├── ChannelTag.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── Shin.xcuserdatad │ │ └── xcdebugger │ │ └── Expressions.xcexplist └── xcuserdata │ └── Shin.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── ChannelTag ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── Exit.imageset │ │ ├── Contents.json │ │ └── Exit.png │ ├── LaunchImage.launchimage │ │ └── Contents.json │ ├── back.imageset │ │ ├── Contents.json │ │ └── back.png │ └── del.imageset │ │ ├── Contents.json │ │ └── del.png ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Channel.h ├── Channel.m ├── ChannelCell.h ├── ChannelCell.m ├── ChannelTags.h ├── ChannelTags.m ├── Info.plist ├── ViewController.h ├── ViewController.m ├── en.lproj │ └── LaunchScreen.strings └── main.m ├── ChannelTagTests ├── ChannelTagTests.m └── Info.plist ├── ChannelTagUITests ├── ChannelTagUITests.m └── Info.plist ├── README.md └── taggif.gif /ChannelTag.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 54F134BD1FCAFD200054E52C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 54F134BC1FCAFD200054E52C /* AppDelegate.m */; }; 11 | 54F134C01FCAFD200054E52C /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 54F134BF1FCAFD200054E52C /* ViewController.m */; }; 12 | 54F134C31FCAFD200054E52C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 54F134C11FCAFD200054E52C /* Main.storyboard */; }; 13 | 54F134C51FCAFD200054E52C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 54F134C41FCAFD200054E52C /* Assets.xcassets */; }; 14 | 54F134C81FCAFD200054E52C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 54F134C61FCAFD200054E52C /* LaunchScreen.storyboard */; }; 15 | 54F134CB1FCAFD200054E52C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 54F134CA1FCAFD200054E52C /* main.m */; }; 16 | 54F134D51FCAFD200054E52C /* ChannelTagTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 54F134D41FCAFD200054E52C /* ChannelTagTests.m */; }; 17 | 54F134E01FCAFD200054E52C /* ChannelTagUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 54F134DF1FCAFD200054E52C /* ChannelTagUITests.m */; }; 18 | 54F134EF1FCAFFC40054E52C /* ChannelTags.m in Sources */ = {isa = PBXBuildFile; fileRef = 54F134EE1FCAFFC40054E52C /* ChannelTags.m */; }; 19 | 54F134F31FCB00710054E52C /* ChannelCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 54F134F21FCB00710054E52C /* ChannelCell.m */; }; 20 | 54F134F61FCB06D40054E52C /* Channel.m in Sources */ = {isa = PBXBuildFile; fileRef = 54F134F51FCB06D40054E52C /* Channel.m */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | 54F134D11FCAFD200054E52C /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = 54F134B01FCAFD200054E52C /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = 54F134B71FCAFD200054E52C; 29 | remoteInfo = ChannelTag; 30 | }; 31 | 54F134DC1FCAFD200054E52C /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = 54F134B01FCAFD200054E52C /* Project object */; 34 | proxyType = 1; 35 | remoteGlobalIDString = 54F134B71FCAFD200054E52C; 36 | remoteInfo = ChannelTag; 37 | }; 38 | /* End PBXContainerItemProxy section */ 39 | 40 | /* Begin PBXFileReference section */ 41 | 54F134B81FCAFD200054E52C /* ChannelTag.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ChannelTag.app; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 54F134BB1FCAFD200054E52C /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 43 | 54F134BC1FCAFD200054E52C /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 44 | 54F134BE1FCAFD200054E52C /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 45 | 54F134BF1FCAFD200054E52C /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 46 | 54F134C21FCAFD200054E52C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | 54F134C41FCAFD200054E52C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 54F134C71FCAFD200054E52C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | 54F134C91FCAFD200054E52C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | 54F134CA1FCAFD200054E52C /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 51 | 54F134D01FCAFD200054E52C /* ChannelTagTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ChannelTagTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | 54F134D41FCAFD200054E52C /* ChannelTagTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ChannelTagTests.m; sourceTree = ""; }; 53 | 54F134D61FCAFD200054E52C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 54 | 54F134DB1FCAFD200054E52C /* ChannelTagUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ChannelTagUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | 54F134DF1FCAFD200054E52C /* ChannelTagUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ChannelTagUITests.m; sourceTree = ""; }; 56 | 54F134E11FCAFD200054E52C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 57 | 54F134ED1FCAFFC40054E52C /* ChannelTags.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ChannelTags.h; sourceTree = ""; }; 58 | 54F134EE1FCAFFC40054E52C /* ChannelTags.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ChannelTags.m; sourceTree = ""; }; 59 | 54F134F11FCB00710054E52C /* ChannelCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ChannelCell.h; sourceTree = ""; }; 60 | 54F134F21FCB00710054E52C /* ChannelCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ChannelCell.m; sourceTree = ""; }; 61 | 54F134F41FCB06D40054E52C /* Channel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Channel.h; sourceTree = ""; }; 62 | 54F134F51FCB06D40054E52C /* Channel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Channel.m; sourceTree = ""; }; 63 | /* End PBXFileReference section */ 64 | 65 | /* Begin PBXFrameworksBuildPhase section */ 66 | 54F134B51FCAFD200054E52C /* Frameworks */ = { 67 | isa = PBXFrameworksBuildPhase; 68 | buildActionMask = 2147483647; 69 | files = ( 70 | ); 71 | runOnlyForDeploymentPostprocessing = 0; 72 | }; 73 | 54F134CD1FCAFD200054E52C /* Frameworks */ = { 74 | isa = PBXFrameworksBuildPhase; 75 | buildActionMask = 2147483647; 76 | files = ( 77 | ); 78 | runOnlyForDeploymentPostprocessing = 0; 79 | }; 80 | 54F134D81FCAFD200054E52C /* Frameworks */ = { 81 | isa = PBXFrameworksBuildPhase; 82 | buildActionMask = 2147483647; 83 | files = ( 84 | ); 85 | runOnlyForDeploymentPostprocessing = 0; 86 | }; 87 | /* End PBXFrameworksBuildPhase section */ 88 | 89 | /* Begin PBXGroup section */ 90 | 54F134AF1FCAFD200054E52C = { 91 | isa = PBXGroup; 92 | children = ( 93 | 54F134BA1FCAFD200054E52C /* ChannelTag */, 94 | 54F134D31FCAFD200054E52C /* ChannelTagTests */, 95 | 54F134DE1FCAFD200054E52C /* ChannelTagUITests */, 96 | 54F134B91FCAFD200054E52C /* Products */, 97 | ); 98 | sourceTree = ""; 99 | }; 100 | 54F134B91FCAFD200054E52C /* Products */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 54F134B81FCAFD200054E52C /* ChannelTag.app */, 104 | 54F134D01FCAFD200054E52C /* ChannelTagTests.xctest */, 105 | 54F134DB1FCAFD200054E52C /* ChannelTagUITests.xctest */, 106 | ); 107 | name = Products; 108 | sourceTree = ""; 109 | }; 110 | 54F134BA1FCAFD200054E52C /* ChannelTag */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 54F134F41FCB06D40054E52C /* Channel.h */, 114 | 54F134F51FCB06D40054E52C /* Channel.m */, 115 | 54F134F11FCB00710054E52C /* ChannelCell.h */, 116 | 54F134F21FCB00710054E52C /* ChannelCell.m */, 117 | 54F134ED1FCAFFC40054E52C /* ChannelTags.h */, 118 | 54F134EE1FCAFFC40054E52C /* ChannelTags.m */, 119 | 54F134BB1FCAFD200054E52C /* AppDelegate.h */, 120 | 54F134BC1FCAFD200054E52C /* AppDelegate.m */, 121 | 54F134BE1FCAFD200054E52C /* ViewController.h */, 122 | 54F134BF1FCAFD200054E52C /* ViewController.m */, 123 | 54F134C11FCAFD200054E52C /* Main.storyboard */, 124 | 54F134C41FCAFD200054E52C /* Assets.xcassets */, 125 | 54F134C61FCAFD200054E52C /* LaunchScreen.storyboard */, 126 | 54F134C91FCAFD200054E52C /* Info.plist */, 127 | 54F134CA1FCAFD200054E52C /* main.m */, 128 | ); 129 | path = ChannelTag; 130 | sourceTree = ""; 131 | }; 132 | 54F134D31FCAFD200054E52C /* ChannelTagTests */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | 54F134D41FCAFD200054E52C /* ChannelTagTests.m */, 136 | 54F134D61FCAFD200054E52C /* Info.plist */, 137 | ); 138 | path = ChannelTagTests; 139 | sourceTree = ""; 140 | }; 141 | 54F134DE1FCAFD200054E52C /* ChannelTagUITests */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | 54F134DF1FCAFD200054E52C /* ChannelTagUITests.m */, 145 | 54F134E11FCAFD200054E52C /* Info.plist */, 146 | ); 147 | path = ChannelTagUITests; 148 | sourceTree = ""; 149 | }; 150 | /* End PBXGroup section */ 151 | 152 | /* Begin PBXNativeTarget section */ 153 | 54F134B71FCAFD200054E52C /* ChannelTag */ = { 154 | isa = PBXNativeTarget; 155 | buildConfigurationList = 54F134E41FCAFD200054E52C /* Build configuration list for PBXNativeTarget "ChannelTag" */; 156 | buildPhases = ( 157 | 54F134B41FCAFD200054E52C /* Sources */, 158 | 54F134B51FCAFD200054E52C /* Frameworks */, 159 | 54F134B61FCAFD200054E52C /* Resources */, 160 | ); 161 | buildRules = ( 162 | ); 163 | dependencies = ( 164 | ); 165 | name = ChannelTag; 166 | productName = ChannelTag; 167 | productReference = 54F134B81FCAFD200054E52C /* ChannelTag.app */; 168 | productType = "com.apple.product-type.application"; 169 | }; 170 | 54F134CF1FCAFD200054E52C /* ChannelTagTests */ = { 171 | isa = PBXNativeTarget; 172 | buildConfigurationList = 54F134E71FCAFD200054E52C /* Build configuration list for PBXNativeTarget "ChannelTagTests" */; 173 | buildPhases = ( 174 | 54F134CC1FCAFD200054E52C /* Sources */, 175 | 54F134CD1FCAFD200054E52C /* Frameworks */, 176 | 54F134CE1FCAFD200054E52C /* Resources */, 177 | ); 178 | buildRules = ( 179 | ); 180 | dependencies = ( 181 | 54F134D21FCAFD200054E52C /* PBXTargetDependency */, 182 | ); 183 | name = ChannelTagTests; 184 | productName = ChannelTagTests; 185 | productReference = 54F134D01FCAFD200054E52C /* ChannelTagTests.xctest */; 186 | productType = "com.apple.product-type.bundle.unit-test"; 187 | }; 188 | 54F134DA1FCAFD200054E52C /* ChannelTagUITests */ = { 189 | isa = PBXNativeTarget; 190 | buildConfigurationList = 54F134EA1FCAFD200054E52C /* Build configuration list for PBXNativeTarget "ChannelTagUITests" */; 191 | buildPhases = ( 192 | 54F134D71FCAFD200054E52C /* Sources */, 193 | 54F134D81FCAFD200054E52C /* Frameworks */, 194 | 54F134D91FCAFD200054E52C /* Resources */, 195 | ); 196 | buildRules = ( 197 | ); 198 | dependencies = ( 199 | 54F134DD1FCAFD200054E52C /* PBXTargetDependency */, 200 | ); 201 | name = ChannelTagUITests; 202 | productName = ChannelTagUITests; 203 | productReference = 54F134DB1FCAFD200054E52C /* ChannelTagUITests.xctest */; 204 | productType = "com.apple.product-type.bundle.ui-testing"; 205 | }; 206 | /* End PBXNativeTarget section */ 207 | 208 | /* Begin PBXProject section */ 209 | 54F134B01FCAFD200054E52C /* Project object */ = { 210 | isa = PBXProject; 211 | attributes = { 212 | LastUpgradeCheck = 0910; 213 | ORGANIZATIONNAME = Shin; 214 | TargetAttributes = { 215 | 54F134B71FCAFD200054E52C = { 216 | CreatedOnToolsVersion = 9.1; 217 | ProvisioningStyle = Automatic; 218 | }; 219 | 54F134CF1FCAFD200054E52C = { 220 | CreatedOnToolsVersion = 9.1; 221 | ProvisioningStyle = Automatic; 222 | TestTargetID = 54F134B71FCAFD200054E52C; 223 | }; 224 | 54F134DA1FCAFD200054E52C = { 225 | CreatedOnToolsVersion = 9.1; 226 | ProvisioningStyle = Automatic; 227 | TestTargetID = 54F134B71FCAFD200054E52C; 228 | }; 229 | }; 230 | }; 231 | buildConfigurationList = 54F134B31FCAFD200054E52C /* Build configuration list for PBXProject "ChannelTag" */; 232 | compatibilityVersion = "Xcode 8.0"; 233 | developmentRegion = en; 234 | hasScannedForEncodings = 0; 235 | knownRegions = ( 236 | en, 237 | Base, 238 | ); 239 | mainGroup = 54F134AF1FCAFD200054E52C; 240 | productRefGroup = 54F134B91FCAFD200054E52C /* Products */; 241 | projectDirPath = ""; 242 | projectRoot = ""; 243 | targets = ( 244 | 54F134B71FCAFD200054E52C /* ChannelTag */, 245 | 54F134CF1FCAFD200054E52C /* ChannelTagTests */, 246 | 54F134DA1FCAFD200054E52C /* ChannelTagUITests */, 247 | ); 248 | }; 249 | /* End PBXProject section */ 250 | 251 | /* Begin PBXResourcesBuildPhase section */ 252 | 54F134B61FCAFD200054E52C /* Resources */ = { 253 | isa = PBXResourcesBuildPhase; 254 | buildActionMask = 2147483647; 255 | files = ( 256 | 54F134C81FCAFD200054E52C /* LaunchScreen.storyboard in Resources */, 257 | 54F134C51FCAFD200054E52C /* Assets.xcassets in Resources */, 258 | 54F134C31FCAFD200054E52C /* Main.storyboard in Resources */, 259 | ); 260 | runOnlyForDeploymentPostprocessing = 0; 261 | }; 262 | 54F134CE1FCAFD200054E52C /* Resources */ = { 263 | isa = PBXResourcesBuildPhase; 264 | buildActionMask = 2147483647; 265 | files = ( 266 | ); 267 | runOnlyForDeploymentPostprocessing = 0; 268 | }; 269 | 54F134D91FCAFD200054E52C /* Resources */ = { 270 | isa = PBXResourcesBuildPhase; 271 | buildActionMask = 2147483647; 272 | files = ( 273 | ); 274 | runOnlyForDeploymentPostprocessing = 0; 275 | }; 276 | /* End PBXResourcesBuildPhase section */ 277 | 278 | /* Begin PBXSourcesBuildPhase section */ 279 | 54F134B41FCAFD200054E52C /* Sources */ = { 280 | isa = PBXSourcesBuildPhase; 281 | buildActionMask = 2147483647; 282 | files = ( 283 | 54F134EF1FCAFFC40054E52C /* ChannelTags.m in Sources */, 284 | 54F134F61FCB06D40054E52C /* Channel.m in Sources */, 285 | 54F134C01FCAFD200054E52C /* ViewController.m in Sources */, 286 | 54F134CB1FCAFD200054E52C /* main.m in Sources */, 287 | 54F134BD1FCAFD200054E52C /* AppDelegate.m in Sources */, 288 | 54F134F31FCB00710054E52C /* ChannelCell.m in Sources */, 289 | ); 290 | runOnlyForDeploymentPostprocessing = 0; 291 | }; 292 | 54F134CC1FCAFD200054E52C /* Sources */ = { 293 | isa = PBXSourcesBuildPhase; 294 | buildActionMask = 2147483647; 295 | files = ( 296 | 54F134D51FCAFD200054E52C /* ChannelTagTests.m in Sources */, 297 | ); 298 | runOnlyForDeploymentPostprocessing = 0; 299 | }; 300 | 54F134D71FCAFD200054E52C /* Sources */ = { 301 | isa = PBXSourcesBuildPhase; 302 | buildActionMask = 2147483647; 303 | files = ( 304 | 54F134E01FCAFD200054E52C /* ChannelTagUITests.m in Sources */, 305 | ); 306 | runOnlyForDeploymentPostprocessing = 0; 307 | }; 308 | /* End PBXSourcesBuildPhase section */ 309 | 310 | /* Begin PBXTargetDependency section */ 311 | 54F134D21FCAFD200054E52C /* PBXTargetDependency */ = { 312 | isa = PBXTargetDependency; 313 | target = 54F134B71FCAFD200054E52C /* ChannelTag */; 314 | targetProxy = 54F134D11FCAFD200054E52C /* PBXContainerItemProxy */; 315 | }; 316 | 54F134DD1FCAFD200054E52C /* PBXTargetDependency */ = { 317 | isa = PBXTargetDependency; 318 | target = 54F134B71FCAFD200054E52C /* ChannelTag */; 319 | targetProxy = 54F134DC1FCAFD200054E52C /* PBXContainerItemProxy */; 320 | }; 321 | /* End PBXTargetDependency section */ 322 | 323 | /* Begin PBXVariantGroup section */ 324 | 54F134C11FCAFD200054E52C /* Main.storyboard */ = { 325 | isa = PBXVariantGroup; 326 | children = ( 327 | 54F134C21FCAFD200054E52C /* Base */, 328 | ); 329 | name = Main.storyboard; 330 | sourceTree = ""; 331 | }; 332 | 54F134C61FCAFD200054E52C /* LaunchScreen.storyboard */ = { 333 | isa = PBXVariantGroup; 334 | children = ( 335 | 54F134C71FCAFD200054E52C /* Base */, 336 | ); 337 | name = LaunchScreen.storyboard; 338 | sourceTree = ""; 339 | }; 340 | /* End PBXVariantGroup section */ 341 | 342 | /* Begin XCBuildConfiguration section */ 343 | 54F134E21FCAFD200054E52C /* Debug */ = { 344 | isa = XCBuildConfiguration; 345 | buildSettings = { 346 | ALWAYS_SEARCH_USER_PATHS = NO; 347 | CLANG_ANALYZER_NONNULL = YES; 348 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 349 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 350 | CLANG_CXX_LIBRARY = "libc++"; 351 | CLANG_ENABLE_MODULES = YES; 352 | CLANG_ENABLE_OBJC_ARC = YES; 353 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 354 | CLANG_WARN_BOOL_CONVERSION = YES; 355 | CLANG_WARN_COMMA = YES; 356 | CLANG_WARN_CONSTANT_CONVERSION = YES; 357 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 358 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 359 | CLANG_WARN_EMPTY_BODY = YES; 360 | CLANG_WARN_ENUM_CONVERSION = YES; 361 | CLANG_WARN_INFINITE_RECURSION = YES; 362 | CLANG_WARN_INT_CONVERSION = YES; 363 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 364 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 365 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 366 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 367 | CLANG_WARN_STRICT_PROTOTYPES = YES; 368 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 369 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 370 | CLANG_WARN_UNREACHABLE_CODE = YES; 371 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 372 | CODE_SIGN_IDENTITY = "iPhone Developer"; 373 | COPY_PHASE_STRIP = NO; 374 | DEBUG_INFORMATION_FORMAT = dwarf; 375 | ENABLE_STRICT_OBJC_MSGSEND = YES; 376 | ENABLE_TESTABILITY = YES; 377 | GCC_C_LANGUAGE_STANDARD = gnu11; 378 | GCC_DYNAMIC_NO_PIC = NO; 379 | GCC_NO_COMMON_BLOCKS = YES; 380 | GCC_OPTIMIZATION_LEVEL = 0; 381 | GCC_PREPROCESSOR_DEFINITIONS = ( 382 | "DEBUG=1", 383 | "$(inherited)", 384 | ); 385 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 386 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 387 | GCC_WARN_UNDECLARED_SELECTOR = YES; 388 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 389 | GCC_WARN_UNUSED_FUNCTION = YES; 390 | GCC_WARN_UNUSED_VARIABLE = YES; 391 | IPHONEOS_DEPLOYMENT_TARGET = 11.1; 392 | MTL_ENABLE_DEBUG_INFO = YES; 393 | ONLY_ACTIVE_ARCH = YES; 394 | SDKROOT = iphoneos; 395 | }; 396 | name = Debug; 397 | }; 398 | 54F134E31FCAFD200054E52C /* Release */ = { 399 | isa = XCBuildConfiguration; 400 | buildSettings = { 401 | ALWAYS_SEARCH_USER_PATHS = NO; 402 | CLANG_ANALYZER_NONNULL = YES; 403 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 404 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 405 | CLANG_CXX_LIBRARY = "libc++"; 406 | CLANG_ENABLE_MODULES = YES; 407 | CLANG_ENABLE_OBJC_ARC = YES; 408 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 409 | CLANG_WARN_BOOL_CONVERSION = YES; 410 | CLANG_WARN_COMMA = YES; 411 | CLANG_WARN_CONSTANT_CONVERSION = YES; 412 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 413 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 414 | CLANG_WARN_EMPTY_BODY = YES; 415 | CLANG_WARN_ENUM_CONVERSION = YES; 416 | CLANG_WARN_INFINITE_RECURSION = YES; 417 | CLANG_WARN_INT_CONVERSION = YES; 418 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 419 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 420 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 421 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 422 | CLANG_WARN_STRICT_PROTOTYPES = YES; 423 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 424 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 425 | CLANG_WARN_UNREACHABLE_CODE = YES; 426 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 427 | CODE_SIGN_IDENTITY = "iPhone Developer"; 428 | COPY_PHASE_STRIP = NO; 429 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 430 | ENABLE_NS_ASSERTIONS = NO; 431 | ENABLE_STRICT_OBJC_MSGSEND = YES; 432 | GCC_C_LANGUAGE_STANDARD = gnu11; 433 | GCC_NO_COMMON_BLOCKS = YES; 434 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 435 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 436 | GCC_WARN_UNDECLARED_SELECTOR = YES; 437 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 438 | GCC_WARN_UNUSED_FUNCTION = YES; 439 | GCC_WARN_UNUSED_VARIABLE = YES; 440 | IPHONEOS_DEPLOYMENT_TARGET = 11.1; 441 | MTL_ENABLE_DEBUG_INFO = NO; 442 | SDKROOT = iphoneos; 443 | VALIDATE_PRODUCT = YES; 444 | }; 445 | name = Release; 446 | }; 447 | 54F134E51FCAFD200054E52C /* Debug */ = { 448 | isa = XCBuildConfiguration; 449 | buildSettings = { 450 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 451 | CODE_SIGN_STYLE = Automatic; 452 | DEVELOPMENT_TEAM = 5DVPT5R6X2; 453 | INFOPLIST_FILE = ChannelTag/Info.plist; 454 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 455 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 456 | PRODUCT_BUNDLE_IDENTIFIER = com.Shin.ChannelTag; 457 | PRODUCT_NAME = "$(TARGET_NAME)"; 458 | TARGETED_DEVICE_FAMILY = "1,2"; 459 | }; 460 | name = Debug; 461 | }; 462 | 54F134E61FCAFD200054E52C /* Release */ = { 463 | isa = XCBuildConfiguration; 464 | buildSettings = { 465 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 466 | CODE_SIGN_STYLE = Automatic; 467 | DEVELOPMENT_TEAM = 5DVPT5R6X2; 468 | INFOPLIST_FILE = ChannelTag/Info.plist; 469 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 470 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 471 | PRODUCT_BUNDLE_IDENTIFIER = com.Shin.ChannelTag; 472 | PRODUCT_NAME = "$(TARGET_NAME)"; 473 | TARGETED_DEVICE_FAMILY = "1,2"; 474 | }; 475 | name = Release; 476 | }; 477 | 54F134E81FCAFD200054E52C /* Debug */ = { 478 | isa = XCBuildConfiguration; 479 | buildSettings = { 480 | BUNDLE_LOADER = "$(TEST_HOST)"; 481 | CODE_SIGN_STYLE = Automatic; 482 | DEVELOPMENT_TEAM = 5DVPT5R6X2; 483 | INFOPLIST_FILE = ChannelTagTests/Info.plist; 484 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 485 | PRODUCT_BUNDLE_IDENTIFIER = com.Shin.ChannelTagTests; 486 | PRODUCT_NAME = "$(TARGET_NAME)"; 487 | TARGETED_DEVICE_FAMILY = "1,2"; 488 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ChannelTag.app/ChannelTag"; 489 | }; 490 | name = Debug; 491 | }; 492 | 54F134E91FCAFD200054E52C /* Release */ = { 493 | isa = XCBuildConfiguration; 494 | buildSettings = { 495 | BUNDLE_LOADER = "$(TEST_HOST)"; 496 | CODE_SIGN_STYLE = Automatic; 497 | DEVELOPMENT_TEAM = 5DVPT5R6X2; 498 | INFOPLIST_FILE = ChannelTagTests/Info.plist; 499 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 500 | PRODUCT_BUNDLE_IDENTIFIER = com.Shin.ChannelTagTests; 501 | PRODUCT_NAME = "$(TARGET_NAME)"; 502 | TARGETED_DEVICE_FAMILY = "1,2"; 503 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ChannelTag.app/ChannelTag"; 504 | }; 505 | name = Release; 506 | }; 507 | 54F134EB1FCAFD200054E52C /* Debug */ = { 508 | isa = XCBuildConfiguration; 509 | buildSettings = { 510 | CODE_SIGN_STYLE = Automatic; 511 | DEVELOPMENT_TEAM = 5DVPT5R6X2; 512 | INFOPLIST_FILE = ChannelTagUITests/Info.plist; 513 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 514 | PRODUCT_BUNDLE_IDENTIFIER = com.Shin.ChannelTagUITests; 515 | PRODUCT_NAME = "$(TARGET_NAME)"; 516 | TARGETED_DEVICE_FAMILY = "1,2"; 517 | TEST_TARGET_NAME = ChannelTag; 518 | }; 519 | name = Debug; 520 | }; 521 | 54F134EC1FCAFD200054E52C /* Release */ = { 522 | isa = XCBuildConfiguration; 523 | buildSettings = { 524 | CODE_SIGN_STYLE = Automatic; 525 | DEVELOPMENT_TEAM = 5DVPT5R6X2; 526 | INFOPLIST_FILE = ChannelTagUITests/Info.plist; 527 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 528 | PRODUCT_BUNDLE_IDENTIFIER = com.Shin.ChannelTagUITests; 529 | PRODUCT_NAME = "$(TARGET_NAME)"; 530 | TARGETED_DEVICE_FAMILY = "1,2"; 531 | TEST_TARGET_NAME = ChannelTag; 532 | }; 533 | name = Release; 534 | }; 535 | /* End XCBuildConfiguration section */ 536 | 537 | /* Begin XCConfigurationList section */ 538 | 54F134B31FCAFD200054E52C /* Build configuration list for PBXProject "ChannelTag" */ = { 539 | isa = XCConfigurationList; 540 | buildConfigurations = ( 541 | 54F134E21FCAFD200054E52C /* Debug */, 542 | 54F134E31FCAFD200054E52C /* Release */, 543 | ); 544 | defaultConfigurationIsVisible = 0; 545 | defaultConfigurationName = Release; 546 | }; 547 | 54F134E41FCAFD200054E52C /* Build configuration list for PBXNativeTarget "ChannelTag" */ = { 548 | isa = XCConfigurationList; 549 | buildConfigurations = ( 550 | 54F134E51FCAFD200054E52C /* Debug */, 551 | 54F134E61FCAFD200054E52C /* Release */, 552 | ); 553 | defaultConfigurationIsVisible = 0; 554 | defaultConfigurationName = Release; 555 | }; 556 | 54F134E71FCAFD200054E52C /* Build configuration list for PBXNativeTarget "ChannelTagTests" */ = { 557 | isa = XCConfigurationList; 558 | buildConfigurations = ( 559 | 54F134E81FCAFD200054E52C /* Debug */, 560 | 54F134E91FCAFD200054E52C /* Release */, 561 | ); 562 | defaultConfigurationIsVisible = 0; 563 | defaultConfigurationName = Release; 564 | }; 565 | 54F134EA1FCAFD200054E52C /* Build configuration list for PBXNativeTarget "ChannelTagUITests" */ = { 566 | isa = XCConfigurationList; 567 | buildConfigurations = ( 568 | 54F134EB1FCAFD200054E52C /* Debug */, 569 | 54F134EC1FCAFD200054E52C /* Release */, 570 | ); 571 | defaultConfigurationIsVisible = 0; 572 | defaultConfigurationName = Release; 573 | }; 574 | /* End XCConfigurationList section */ 575 | }; 576 | rootObject = 54F134B01FCAFD200054E52C /* Project object */; 577 | } 578 | -------------------------------------------------------------------------------- /ChannelTag.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ChannelTag.xcodeproj/project.xcworkspace/xcuserdata/Shin.xcuserdatad/xcdebugger/Expressions.xcexplist: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ChannelTag.xcodeproj/xcuserdata/Shin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ChannelTag.xcodeproj/xcuserdata/Shin.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ChannelTag.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ChannelTag/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // ChannelTag 4 | // 5 | // Created by Shin on 2017/11/26. 6 | // Copyright © 2017年 Shin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /ChannelTag/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // ChannelTag 4 | // 5 | // Created by Shin on 2017/11/26. 6 | // Copyright © 2017年 Shin. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // 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. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // 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. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // 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. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // 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. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /ChannelTag/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 | } -------------------------------------------------------------------------------- /ChannelTag/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ChannelTag/Assets.xcassets/Exit.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "Exit.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ChannelTag/Assets.xcassets/Exit.imageset/Exit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin1122/YZChannelTag/9cb3ca0a7ac24362b98158bf4db0b1fbad1c027d/ChannelTag/Assets.xcassets/Exit.imageset/Exit.png -------------------------------------------------------------------------------- /ChannelTag/Assets.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "ipad", 6 | "minimum-system-version" : "7.0", 7 | "extent" : "full-screen", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "landscape", 12 | "idiom" : "ipad", 13 | "minimum-system-version" : "7.0", 14 | "extent" : "full-screen", 15 | "scale" : "1x" 16 | }, 17 | { 18 | "orientation" : "landscape", 19 | "idiom" : "ipad", 20 | "minimum-system-version" : "7.0", 21 | "extent" : "full-screen", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "orientation" : "portrait", 26 | "idiom" : "iphone", 27 | "minimum-system-version" : "7.0", 28 | "scale" : "2x" 29 | }, 30 | { 31 | "orientation" : "portrait", 32 | "idiom" : "iphone", 33 | "minimum-system-version" : "7.0", 34 | "subtype" : "retina4", 35 | "scale" : "2x" 36 | }, 37 | { 38 | "orientation" : "portrait", 39 | "idiom" : "ipad", 40 | "minimum-system-version" : "7.0", 41 | "extent" : "full-screen", 42 | "scale" : "1x" 43 | } 44 | ], 45 | "info" : { 46 | "version" : 1, 47 | "author" : "xcode" 48 | } 49 | } -------------------------------------------------------------------------------- /ChannelTag/Assets.xcassets/back.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "back.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ChannelTag/Assets.xcassets/back.imageset/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin1122/YZChannelTag/9cb3ca0a7ac24362b98158bf4db0b1fbad1c027d/ChannelTag/Assets.xcassets/back.imageset/back.png -------------------------------------------------------------------------------- /ChannelTag/Assets.xcassets/del.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "del.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ChannelTag/Assets.xcassets/del.imageset/del.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin1122/YZChannelTag/9cb3ca0a7ac24362b98158bf4db0b1fbad1c027d/ChannelTag/Assets.xcassets/del.imageset/del.png -------------------------------------------------------------------------------- /ChannelTag/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 | 29 | 30 | -------------------------------------------------------------------------------- /ChannelTag/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 | 28 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /ChannelTag/Channel.h: -------------------------------------------------------------------------------- 1 | // 2 | // Channel.h 3 | // ChannelTag 4 | // 5 | // Created by Shin on 2017/11/26. 6 | // Copyright © 2017年 Shin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | cell类型 13 | 14 | - MyChannel: 我的频道样式 不带“+”的 15 | - RecommandChannel: 推荐频道样式 带“+”的 16 | */ 17 | typedef NS_ENUM(NSUInteger, ChannelType) { 18 | MyChannel, 19 | RecommandChannel, 20 | }; 21 | 22 | @interface Channel : NSObject 23 | 24 | @property (nonatomic, assign) BOOL resident ; 25 | @property (nonatomic, assign) BOOL editable ; 26 | 27 | @property (nonatomic, strong) NSString *title ; 28 | 29 | @property (nonatomic, assign) BOOL selected ; 30 | 31 | @property (nonatomic, assign) ChannelType tagType ; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /ChannelTag/Channel.m: -------------------------------------------------------------------------------- 1 | // 2 | // Channel.m 3 | // ChannelTag 4 | // 5 | // Created by Shin on 2017/11/26. 6 | // Copyright © 2017年 Shin. All rights reserved. 7 | // 8 | 9 | #import "Channel.h" 10 | 11 | @implementation Channel 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ChannelTag/ChannelCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelCell.h 3 | // ChannelTag 4 | // 5 | // Created by Shin on 2017/11/26. 6 | // Copyright © 2017年 Shin. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "Channel.h" 11 | 12 | @protocol ChannelCellDeleteDelegate 13 | 14 | - (void)deleteCell:(UIButton *)sender; 15 | 16 | @end 17 | 18 | 19 | @interface ChannelCell : UICollectionViewCell 20 | 21 | /** 22 | 样式 23 | */ 24 | @property (nonatomic, assign) ChannelType style ; 25 | 26 | /** 27 | 标题 28 | */ 29 | @property (nonatomic, strong) UILabel *title ; 30 | /** 右上角的删除按钮 */ 31 | @property (nonatomic, strong) UIButton * delBtn ; 32 | 33 | 34 | @property (nonatomic, assign) id delegate ; 35 | 36 | @property (nonatomic, strong) Channel *model ; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /ChannelTag/ChannelCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelCell.m 3 | // ChannelTag 4 | // 5 | // Created by Shin on 2017/11/26. 6 | // Copyright © 2017年 Shin. All rights reserved. 7 | // 8 | 9 | #import "ChannelCell.h" 10 | 11 | @interface ChannelCell (){ 12 | 13 | } 14 | 15 | 16 | 17 | @end 18 | 19 | @implementation ChannelCell 20 | 21 | - (instancetype)initWithFrame:(CGRect)frame 22 | { 23 | self = [super initWithFrame:frame]; 24 | if (self) { 25 | 26 | //title 27 | _title = [[UILabel alloc]init]; 28 | [self.contentView addSubview:_title]; 29 | _title.frame = CGRectMake(5, 5, frame.size.width-10, frame.size.height-10); 30 | _title.backgroundColor = [UIColor colorWithRed:0.08 green:0.08 blue:0.08 alpha:1.0]; 31 | _title.layer.masksToBounds = YES; 32 | _title.layer.cornerRadius = M_PI; 33 | _title.font = [UIFont systemFontOfSize:16]; 34 | _title.textAlignment = NSTextAlignmentCenter; 35 | _title.numberOfLines = 0; 36 | _title.textColor = [UIColor colorWithRed:0.36 green:0.36 blue:0.36 alpha:1.00]; 37 | 38 | _delBtn = [[UIButton alloc]init]; 39 | [self.contentView addSubview:_delBtn]; 40 | _delBtn.frame = CGRectMake(frame.size.width-18, 0, 18, 18); 41 | [_delBtn setImage:[UIImage imageNamed:@"del"] forState:UIControlStateNormal]; 42 | [_delBtn addTarget:self action:@selector(delete:) forControlEvents:UIControlEventTouchUpInside]; 43 | } 44 | return self; 45 | } 46 | 47 | -(void)setModel:(Channel *)model{ 48 | 49 | _model = model; 50 | 51 | if (model.tagType == MyChannel) { 52 | if ([model.title containsString:@"+"]) { 53 | model.title = [model.title substringFromIndex:1]; 54 | } 55 | if (model.editable) { 56 | }else{ 57 | model.editable = YES; 58 | } 59 | if (model.resident) { 60 | _delBtn.hidden = YES; 61 | }else{ 62 | _delBtn.hidden = NO; 63 | } 64 | 65 | //选择出来的tag高亮显示 66 | if (model.selected) { 67 | _title.textColor = [UIColor colorWithRed:0.5 green:0.26 blue:0.27 alpha:1.0]; 68 | }else{ 69 | _title.textColor = [UIColor colorWithRed:0.36 green:0.36 blue:0.36 alpha:1.0]; 70 | } 71 | 72 | }else if (model.tagType == RecommandChannel){ 73 | if (![model.title containsString:@"+"]) { 74 | model.title = [@"+" stringByAppendingString:model.title]; 75 | } 76 | if (model.editable) { 77 | model.editable = NO; 78 | }else{ 79 | } 80 | // if (model.resident) { 81 | // _delBtn.hidden = YES; 82 | // }else{ 83 | // _delBtn.hidden = NO; 84 | // } 85 | _delBtn.hidden = YES; 86 | } 87 | _title.text = model.title; 88 | 89 | } 90 | 91 | 92 | 93 | - (void)delete:(UIButton *)sender{ 94 | 95 | [_delegate deleteCell:sender]; 96 | } 97 | 98 | 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /ChannelTag/ChannelTags.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelTags.h 3 | // ChannelTag 4 | // 5 | // Created by Shin on 2017/11/26. 6 | // Copyright © 2017年 Shin. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ChannelCell.h" 11 | 12 | 13 | /** 14 | 选择出来的tags们 15 | 16 | @param tags 数组里都是对象 17 | */ 18 | typedef void(^ChoosedTags)(NSArray *chooseTags,NSArray *recommandTags); 19 | 20 | 21 | /** 22 | 单独选择的tag 23 | 24 | @param channel channel对象 25 | */ 26 | typedef void(^SelectedTag)(Channel *channel); 27 | 28 | @interface ChannelTags : UIViewController 29 | 30 | @property (nonatomic, strong) UICollectionView *mainView ; 31 | 32 | @property (nonatomic, strong) NSMutableArray *myChannels ; 33 | 34 | @property (nonatomic, strong) NSMutableArray *recommandChannels ; 35 | 36 | @property (nonatomic, copy) ChoosedTags choosedTags ; 37 | 38 | @property (nonatomic, copy) SelectedTag selectedTag ; 39 | 40 | 41 | /** 42 | 初始化器 43 | 44 | @param myTags 已选tag 45 | @param recommandTags 推荐tag 46 | @return id 47 | */ 48 | -(instancetype)initWithMyTags:(NSArray *)myTags andRecommandTags:(NSArray *)recommandTags; 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /ChannelTag/ChannelTags.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelTags.m 3 | // ChannelTag 4 | // 5 | // Created by Shin on 2017/11/26. 6 | // Copyright © 2017年 Shin. All rights reserved. 7 | // 8 | 9 | #import "ChannelTags.h" 10 | 11 | #define SCREENWIDTH self.view.frame.size.width 12 | 13 | @interface ChannelTags (){ 14 | 15 | NSMutableArray *_myTags; 16 | NSMutableArray *_recommandTags; 17 | 18 | UIButton *_editBtn;//编辑按钮 19 | 20 | BOOL _onEdit;//tag处在编辑状态 21 | BOOL _tagDeletable;//在长按tag的时候是否可以删除该tag 22 | } 23 | 24 | @end 25 | 26 | @implementation ChannelTags 27 | 28 | -(instancetype)initWithMyTags:(NSArray *)myTags andRecommandTags:(NSArray *)recommandTags{ 29 | 30 | self = [super init]; 31 | if (self) { 32 | _myChannels = myTags.mutableCopy; 33 | _recommandChannels = recommandTags.mutableCopy; 34 | } 35 | return self; 36 | } 37 | 38 | - (void)viewDidLoad { 39 | [super viewDidLoad]; 40 | self.view.backgroundColor = [UIColor colorWithRed:0.11 green:0.11 blue:0.11 alpha:1.00]; 41 | //加载数据 42 | [self makeTags]; 43 | //视图 44 | [self setupViews]; 45 | 46 | _onEdit = NO; 47 | 48 | } 49 | 50 | - (void)makeTags{ 51 | _myTags = @[].mutableCopy; 52 | _recommandTags = @[].mutableCopy; 53 | for (NSString *title in _myChannels) { 54 | Channel *mod = [[Channel alloc]init]; 55 | mod.title = title; 56 | if ([title isEqualToString:@"关注"]||[title isEqualToString:@"推荐"]) { 57 | mod.resident = YES;//常驻 58 | } 59 | mod.editable = YES; 60 | mod.selected = NO; 61 | mod.tagType = MyChannel; 62 | //demo默认选择第一个 63 | if ([title isEqualToString:@"关注"]) { 64 | mod.selected = YES; 65 | } 66 | [_myTags addObject:mod]; 67 | } 68 | for (NSString *title in _recommandChannels) { 69 | Channel *mod = [[Channel alloc]init]; 70 | mod.title = title; 71 | if ([title isEqualToString:@"关注"]||[title isEqualToString:@"推荐"]) { 72 | mod.resident = YES;//常驻 73 | } 74 | mod.editable = NO; 75 | mod.tagType = RecommandChannel; 76 | [_recommandTags addObject:mod]; 77 | } 78 | } 79 | 80 | - (void)setupViews{ 81 | 82 | UIButton *exit = [[UIButton alloc]init]; 83 | [self.view addSubview:exit]; 84 | exit.frame = CGRectMake(15, 30, 20, 20); 85 | [exit setImage:[UIImage imageNamed:@"Exit"] forState:UIControlStateNormal]; 86 | exit.imageView.contentMode = UIViewContentModeScaleAspectFit; 87 | [exit addTarget:self action:@selector(returnLast) forControlEvents:UIControlEventTouchUpInside]; 88 | 89 | UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc]init]; 90 | _mainView = [[UICollectionView alloc]initWithFrame:CGRectMake(0, exit.frame.origin.y+exit.frame.size.height, self.view.frame.size.width, self.view.frame.size.height-40) collectionViewLayout:layout]; 91 | [self.view addSubview:_mainView]; 92 | _mainView.backgroundColor = [UIColor colorWithRed:0.11 green:0.11 blue:0.11 alpha:1.00]; 93 | [_mainView registerClass:[ChannelCell class] forCellWithReuseIdentifier:@"cellIdentifier"]; 94 | [_mainView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"head1"]; 95 | [_mainView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"head2"]; 96 | _mainView.delegate = self; 97 | _mainView.dataSource = self; 98 | //添加长按的手势 99 | UILongPressGestureRecognizer *longPress=[[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPress:)]; 100 | [_mainView addGestureRecognizer:longPress]; 101 | } 102 | 103 | - (void)longPress:(UIGestureRecognizer *)longPress { 104 | //获取点击在collectionView的坐标 105 | CGPoint point=[longPress locationInView:_mainView]; 106 | //从长按开始 107 | NSIndexPath *indexPath=[_mainView indexPathForItemAtPoint:point]; 108 | if (longPress.state == UIGestureRecognizerStateBegan) { 109 | [_mainView beginInteractiveMovementForItemAtIndexPath:indexPath]; 110 | if (_onEdit) { 111 | }else{ 112 | [self editTags:_editBtn]; 113 | } 114 | _tagDeletable = NO; 115 | //长按手势状态改变 116 | } else if(longPress.state==UIGestureRecognizerStateChanged) { 117 | [_mainView updateInteractiveMovementTargetPosition:point]; 118 | //长按手势结束 119 | } else if (longPress.state==UIGestureRecognizerStateEnded) { 120 | [_mainView endInteractiveMovement]; 121 | _tagDeletable = YES; 122 | //其他情况 123 | } else { 124 | [_mainView cancelInteractiveMovement]; 125 | } 126 | } 127 | 128 | #pragma mark- collection datasource 129 | -(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{ 130 | if (section == 0) { 131 | return _myTags.count; 132 | }else{ 133 | return _recommandTags.count; 134 | } 135 | } 136 | 137 | -(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ 138 | 139 | static NSString * CellIdentifier = @"cellIdentifier"; 140 | ChannelCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath]; 141 | if (indexPath.section == 0) { 142 | if (_myTags.count>indexPath.item) { 143 | cell.model = _myTags[indexPath.item]; 144 | cell.delBtn.tag = indexPath.item; 145 | cell.delegate = self; 146 | if (_onEdit) { 147 | if (cell.model.resident) { 148 | cell.delBtn.hidden = YES; 149 | }else{ 150 | if (!cell.model.editable) { 151 | cell.delBtn.hidden = YES; 152 | }else{ 153 | cell.delBtn.hidden = NO; 154 | } 155 | } 156 | }else{ 157 | cell.delBtn.hidden = YES; 158 | } 159 | } 160 | }else if (indexPath.section == 1){ 161 | if (_recommandTags.count>indexPath.item) { 162 | cell.model = _recommandTags[indexPath.item]; 163 | if (_onEdit) { 164 | cell.delBtn.hidden = NO; 165 | }else{ 166 | cell.delBtn.hidden = YES; 167 | } 168 | } 169 | } 170 | return cell; 171 | } 172 | 173 | #pragma mark- collection delegate 174 | -(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{ 175 | return 2; 176 | } 177 | 178 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{ 179 | 180 | return CGSizeMake(SCREENWIDTH/4-10, 53); 181 | } 182 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{ 183 | return UIEdgeInsetsMake(0, 10, 4, 10); 184 | } 185 | - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section{ 186 | return 4; 187 | } 188 | - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section{ 189 | return 5; 190 | } 191 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section{ 192 | return CGSizeMake(collectionView.bounds.size.width, 50); 193 | } 194 | 195 | -(UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath{ 196 | UICollectionReusableView *header = nil; 197 | if (indexPath.section == 0) { 198 | if (kind == UICollectionElementKindSectionHeader){ 199 | NSString *CellIdentifier = @"head1"; 200 | header=[collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:CellIdentifier forIndexPath:indexPath]; 201 | UILabel *lab1 = [[UILabel alloc]init]; 202 | [header addSubview:lab1]; 203 | lab1.text = @"我的频道"; 204 | lab1.frame = CGRectMake(20, 0, 100, 60); 205 | lab1.textColor = [UIColor colorWithRed:0.36 green:0.36 blue:0.36 alpha:1.00]; 206 | 207 | UILabel *lab2 = [[UILabel alloc]init]; 208 | [header addSubview:lab2]; 209 | lab2.text = @"点击进入频道"; 210 | lab2.font = [UIFont systemFontOfSize:13]; 211 | lab2.frame = CGRectMake(100, 2, 200, 60); 212 | lab2.textColor = [UIColor colorWithRed:0.36 green:0.36 blue:0.36 alpha:1.00]; 213 | 214 | _editBtn = [[UIButton alloc]init]; 215 | _editBtn.frame = CGRectMake(collectionView.frame.size.width-60, 13, 44, 28); 216 | [header addSubview:_editBtn]; 217 | [_editBtn setTitle:@"编辑" forState:UIControlStateNormal]; 218 | _editBtn.titleLabel.font = [UIFont systemFontOfSize:13]; 219 | [_editBtn setTitleColor:[UIColor colorWithRed:0.5 green:0.26 blue:0.27 alpha:1.0] forState:UIControlStateNormal]; 220 | _editBtn.layer.borderColor = [UIColor colorWithRed:0.5 green:0.26 blue:0.27 alpha:1.0].CGColor; 221 | _editBtn.layer.masksToBounds = YES; 222 | _editBtn.layer.cornerRadius = 14; 223 | _editBtn.layer.borderWidth = 0.8; 224 | [_editBtn addTarget:self action:@selector(editTags:) forControlEvents:UIControlEventTouchUpInside]; 225 | } 226 | }else if (indexPath.section == 1){ 227 | if (kind == UICollectionElementKindSectionHeader){ 228 | NSString *CellIdentifier = @"head2"; 229 | header=[collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:CellIdentifier forIndexPath:indexPath]; 230 | UILabel *lab1 = [[UILabel alloc]init]; 231 | [header addSubview:lab1]; 232 | lab1.text = @"频道推荐"; 233 | lab1.frame = CGRectMake(20, 0, 100, 60); 234 | lab1.textColor = [UIColor colorWithRed:0.36 green:0.36 blue:0.36 alpha:1.00]; 235 | 236 | UILabel *lab2 = [[UILabel alloc]init]; 237 | [header addSubview:lab2]; 238 | lab2.text = @"点击添加频道"; 239 | lab2.font = [UIFont systemFontOfSize:13]; 240 | lab2.frame = CGRectMake(100, 2, 200, 60); 241 | lab2.textColor = [UIColor colorWithRed:0.36 green:0.36 blue:0.36 alpha:1.00]; 242 | } 243 | } 244 | return header; 245 | } 246 | 247 | /** 进入编辑状态 */ 248 | - (void)editTags:(UIButton *)sender{ 249 | 250 | if (!_onEdit) { 251 | for (ChannelCell *items in _mainView.visibleCells) { 252 | if (items.model.tagType == MyChannel) { 253 | if (items.model.resident) { 254 | items.delBtn.hidden = YES; 255 | }else{ 256 | items.delBtn.hidden = NO; 257 | } 258 | } 259 | } 260 | [sender setTitle:@"完成" forState:UIControlStateNormal]; 261 | }else{ 262 | for (ChannelCell *items in _mainView.visibleCells) { 263 | if (items.model.tagType == MyChannel) { 264 | items.delBtn.hidden = YES; 265 | } 266 | } 267 | [sender setTitle:@"编辑" forState:UIControlStateNormal]; 268 | } 269 | _onEdit = !_onEdit; 270 | 271 | } 272 | 273 | - (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath{ 274 | ChannelCell *cell = (ChannelCell *)[collectionView cellForItemAtIndexPath:indexPath]; 275 | if (indexPath.section == 0) { 276 | if (cell.model.resident) { 277 | return NO; 278 | }else{ 279 | return YES; 280 | } 281 | } 282 | return NO; 283 | } 284 | 285 | -(void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{ 286 | Channel *object= _myTags[sourceIndexPath.item]; 287 | [_myTags removeObjectAtIndex:sourceIndexPath.item]; 288 | if (destinationIndexPath.section == 0) { 289 | [_myTags insertObject:object atIndex:destinationIndexPath.item]; 290 | }else if (destinationIndexPath.section == 1) { 291 | object.tagType = RecommandChannel; 292 | object.editable = NO; 293 | object.selected = NO; 294 | [_recommandTags insertObject:object atIndex:destinationIndexPath.item]; 295 | [collectionView reloadItemsAtIndexPaths:@[destinationIndexPath]]; 296 | } 297 | 298 | [self refreshDelBtnsTag]; 299 | } 300 | 301 | -(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{ 302 | 303 | if (indexPath.section == 0) { 304 | NSInteger item = 0; 305 | for (Channel *mod in _myTags) { 306 | if (mod.selected) { 307 | mod.selected = NO; 308 | [collectionView reloadItemsAtIndexPaths:@[[NSIndexPath indexPathForRow:item inSection:0]]]; 309 | } 310 | item++; 311 | } 312 | Channel *object = _myTags[indexPath.item]; 313 | object.selected = YES; 314 | [collectionView reloadItemsAtIndexPaths:@[indexPath]]; 315 | typeof(self) __weak weakSelf = self; 316 | [self dismissViewControllerAnimated:YES completion:^{ 317 | //单选某个tag 318 | if (weakSelf.selectedTag) { 319 | weakSelf.selectedTag(object); 320 | } 321 | }]; 322 | }else if (indexPath.section == 1) { 323 | ChannelCell *cell = (ChannelCell *)[collectionView cellForItemAtIndexPath:indexPath]; 324 | cell.model.editable = YES; 325 | cell.model.tagType = MyChannel; 326 | cell.delBtn.hidden = YES; 327 | [_mainView reloadItemsAtIndexPaths:@[indexPath]]; 328 | [_recommandTags removeObjectAtIndex:indexPath.item]; 329 | [_myTags addObject:cell.model]; 330 | NSIndexPath *targetIndexPage = [NSIndexPath indexPathForItem:_myTags.count-1 inSection:0]; 331 | cell.delBtn.tag = targetIndexPage.item; 332 | [_mainView moveItemAtIndexPath:indexPath toIndexPath:targetIndexPage]; 333 | } 334 | 335 | [self refreshDelBtnsTag]; 336 | } 337 | 338 | -(void)deleteCell:(UIButton *)sender{ 339 | NSIndexPath *indexPath = [NSIndexPath indexPathForItem:sender.tag inSection:0]; 340 | ChannelCell *cell = (ChannelCell *)[_mainView cellForItemAtIndexPath:indexPath]; 341 | cell.model.editable = NO; 342 | cell.model.tagType = RecommandChannel; 343 | cell.model.selected = NO; 344 | cell.delBtn.hidden = YES; 345 | [_mainView reloadItemsAtIndexPaths:@[indexPath]]; 346 | 347 | id object = _myTags[indexPath.item]; 348 | [_myTags removeObjectAtIndex:indexPath.item]; 349 | [_recommandTags insertObject:object atIndex:0]; 350 | NSIndexPath *targetIndexPage = [NSIndexPath indexPathForItem:0 inSection:1]; 351 | [_mainView moveItemAtIndexPath:indexPath toIndexPath:targetIndexPage]; 352 | [self refreshDelBtnsTag]; 353 | } 354 | 355 | /** 刷新删除按钮的tag */ 356 | - (void)refreshDelBtnsTag{ 357 | 358 | for (ChannelCell *cell in _mainView.visibleCells) { 359 | NSIndexPath *indexpath = [_mainView indexPathForCell:cell]; 360 | cell.delBtn.tag = indexpath.item; 361 | } 362 | } 363 | 364 | 365 | - (UIStatusBarStyle)preferredStatusBarStyle{ 366 | return UIStatusBarStyleLightContent; 367 | } 368 | 369 | - (void)returnLast{ 370 | typeof(self) __weak weakSelf = self; 371 | [self dismissViewControllerAnimated:YES completion:^{ 372 | if (weakSelf.choosedTags) { 373 | weakSelf.choosedTags(_myTags,_recommandTags); 374 | } 375 | }]; 376 | } 377 | 378 | - (void)didReceiveMemoryWarning { 379 | [super didReceiveMemoryWarning]; 380 | // Dispose of any resources that can be recreated. 381 | } 382 | 383 | /* 384 | #pragma mark - Navigation 385 | 386 | // In a storyboard-based application, you will often want to do a little preparation before navigation 387 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 388 | // Get the new view controller using [segue destinationViewController]. 389 | // Pass the selected object to the new view controller. 390 | } 391 | */ 392 | 393 | @end 394 | -------------------------------------------------------------------------------- /ChannelTag/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.2 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 | -------------------------------------------------------------------------------- /ChannelTag/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // ChannelTag 4 | // 5 | // Created by Shin on 2017/11/26. 6 | // Copyright © 2017年 Shin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /ChannelTag/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // ChannelTag 4 | // 5 | // Created by Shin on 2017/11/26. 6 | // Copyright © 2017年 Shin. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "ChannelTags.h" 11 | 12 | @interface ViewController (){ 13 | 14 | __block NSMutableArray *_myTags; 15 | __block NSMutableArray *_recommandTags; 16 | 17 | } 18 | 19 | @property (weak, nonatomic) IBOutlet UILabel *tagLabel; 20 | 21 | @end 22 | 23 | @implementation ViewController 24 | 25 | - (void)viewWillAppear:(BOOL)animated{ 26 | [super viewWillAppear:animated]; 27 | 28 | } 29 | 30 | - (void)viewDidLoad { 31 | [super viewDidLoad]; 32 | 33 | _myTags = @[@"关注",@"推荐",@"热点",@"北京",@"视频",@"社会",@"图片",@"娱乐",@"问答",@"科技",@"汽车",@"财经",@"军事",@"体育",@"段子",@"国际",@"趣图",@"健康",@"特卖",@"房产",@"美食"].mutableCopy; 34 | _recommandTags = @[@"小说",@"时尚",@"历史",@"育儿",@"直播",@"搞笑",@"数码",@"养生",@"电影",@"手机",@"旅游",@"宠物",@"情感",@"家居",@"教育",@"三农"].mutableCopy; 35 | 36 | } 37 | 38 | - (IBAction)chooseBtn:(id)sender { 39 | //秀出来选择框 40 | ChannelTags *controller = [[ChannelTags alloc]initWithMyTags:_myTags andRecommandTags:_recommandTags]; 41 | [self presentViewController:controller animated:YES completion:^{}]; 42 | 43 | //所有的已选的tags 44 | __block NSMutableString *_str = @"已选:\n".mutableCopy; 45 | controller.choosedTags = ^(NSArray *chooseTags, NSArray *recommandTags) { 46 | _myTags = @[].mutableCopy; 47 | _recommandTags = @[].mutableCopy; 48 | for (Channel *mod in recommandTags) { 49 | [_recommandTags addObject:mod.title]; 50 | } 51 | for (Channel *mod in chooseTags) { 52 | [_myTags addObject:mod.title]; 53 | [_str appendString:mod.title]; 54 | [_str appendString:@"、"]; 55 | } 56 | _tagLabel.text = _str; 57 | }; 58 | 59 | //单选tag 60 | controller.selectedTag = ^(Channel *channel) { 61 | [_str appendString:channel.title]; 62 | _tagLabel.text = _str; 63 | }; 64 | } 65 | 66 | - (UIStatusBarStyle)preferredStatusBarStyle{ 67 | return UIStatusBarStyleLightContent; 68 | } 69 | 70 | 71 | - (void)didReceiveMemoryWarning { 72 | [super didReceiveMemoryWarning]; 73 | // Dispose of any resources that can be recreated. 74 | } 75 | 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /ChannelTag/en.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /ChannelTag/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ChannelTag 4 | // 5 | // Created by Shin on 2017/11/26. 6 | // Copyright © 2017年 Shin. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ChannelTagTests/ChannelTagTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelTagTests.m 3 | // ChannelTagTests 4 | // 5 | // Created by Shin on 2017/11/26. 6 | // Copyright © 2017年 Shin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ChannelTagTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation ChannelTagTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /ChannelTagTests/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 | -------------------------------------------------------------------------------- /ChannelTagUITests/ChannelTagUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelTagUITests.m 3 | // ChannelTagUITests 4 | // 5 | // Created by Shin on 2017/11/26. 6 | // Copyright © 2017年 Shin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ChannelTagUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation ChannelTagUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // 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. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /ChannelTagUITests/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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # YZChannelTag 2 | ### 仿写《今日头条》的tag选择页面 3 | 4 | item可以移动的collectionview实际应用 5 | 6 | 图片名称 7 | 8 | 在《今日头条》中,该页面是用来选择自己感兴趣的频道标签从而改变segment的。标签功能应用的需求现在也比较多,主要使用collectionview中item可以移动的方法和思路来写这样的页面。 9 | 10 | #### Version & issues 11 | 12 | ###### 1.2 增加长按进入编辑状态 [#1](https://github.com/Shin1122/YZChannelTag/issues/1) 13 | 14 | ###### 1.0 仿写今日头条当前版本功能 15 | 16 | #### 用法 17 | 18 | ``` 19 | ChannelTags *controller = 20 | [[ChannelTags alloc]initWithMyTags:_myTags andRecommandTags:_recommandTags]; 21 | 22 | [self presentViewController:controller animated:YES completion:^{}]; 23 | ``` 24 | 可以直接加载出来该Controller,可自定义修改模态。 25 | 26 | #### 初始化 27 | ``` 28 | /** 29 | 初始化器 30 | 31 | @param myTags 已选tag 32 | @param recommandTags 推荐tag 33 | @return id 34 | */ 35 | -(instancetype)initWithMyTags:(NSArray *)myTags andRecommandTags:(NSArray *)recommandTags; 36 | 37 | ``` 38 | 初始化器传入两个字符类型元素的数组,做为两组不同的数据源,可以是NSMutableArray类型。在设置完成后数组元素会发生变化,再次进入页面后会加载新的数据源。 39 | 40 | #### 应用点 41 | * 在collectionview上添加的长按手势 42 | ``` 43 | //添加长按的手势 44 | UILongPressGestureRecognizer *longPress = 45 | [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPress:)]; 46 | 47 | [_mainView addGestureRecognizer:longPress]; 48 | ``` 49 | * 手势状态的变化和操作 50 | ``` 51 | - (void)longPress:(UIGestureRecognizer *)longPress { 52 | 53 | //获取点击在collectionView的坐标 54 | CGPoint point=[longPress locationInView:_mainView]; 55 | 56 | //从长按开始 57 | NSIndexPath *indexPath=[_mainView indexPathForItemAtPoint:point]; 58 | 59 | if (longPress.state == UIGestureRecognizerStateBegan) { 60 | [_mainView beginInteractiveMovementForItemAtIndexPath:indexPath]; 61 | 62 | //长按手势状态改变 63 | } else if(longPress.state==UIGestureRecognizerStateChanged) { 64 | [_mainView updateInteractiveMovementTargetPosition:point]; 65 | 66 | //长按手势结束 67 | } else if (longPress.state==UIGestureRecognizerStateEnded) { 68 | [_mainView endInteractiveMovement]; 69 | 70 | //其他情况 71 | } else { 72 | [_mainView cancelInteractiveMovement]; 73 | } 74 | } 75 | ``` 76 | * 实现回调方法 77 | ``` 78 | - (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath; 79 | 80 | -(void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath; 81 | ``` 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /taggif.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin1122/YZChannelTag/9cb3ca0a7ac24362b98158bf4db0b1fbad1c027d/taggif.gif --------------------------------------------------------------------------------