├── .github └── FUNDING.yml ├── .gitignore ├── .swiftpm └── xcode │ └── package.xcworkspace │ └── contents.xcworkspacedata ├── .travis.yml ├── Example ├── Podfile ├── TTGTagCollectionView.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ ├── TTGTagCollectionView_Example.xcscheme │ │ └── TTGTagCollectionView_Tests.xcscheme ├── TTGTagCollectionView.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── TTGTagCollectionView │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-60@2x.png │ │ │ ├── Icon-60@3x.png │ │ │ ├── Icon-76.png │ │ │ ├── Icon-76@2x.png │ │ │ ├── Icon-83.5@2x.png │ │ │ ├── Icon-Notification.png │ │ │ ├── Icon-Notification@2x.png │ │ │ ├── Icon-Notification@3x.png │ │ │ ├── Icon-Small-40.png │ │ │ ├── Icon-Small-40@2x.png │ │ │ ├── Icon-Small-40@3x.png │ │ │ ├── Icon-Small.png │ │ │ ├── Icon-Small@2x.png │ │ │ └── Icon-Small@3x.png │ │ ├── Contents.json │ │ ├── bluefaces_1.imageset │ │ │ ├── Contents.json │ │ │ └── bluefaces_1.png │ │ ├── bluefaces_2.imageset │ │ │ ├── Contents.json │ │ │ └── bluefaces_2.png │ │ ├── bluefaces_3.imageset │ │ │ ├── Contents.json │ │ │ └── bluefaces_3.png │ │ ├── bluefaces_4.imageset │ │ │ ├── Contents.json │ │ │ └── bluefaces_4.png │ │ └── logo.imageset │ │ │ ├── Contents.json │ │ │ └── Icon-60@2x.png │ ├── Launch Screen.storyboard │ ├── Main.storyboard │ ├── TTGAppDelegate.h │ ├── TTGAppDelegate.m │ ├── TTGExample1ViewController.h │ ├── TTGExample1ViewController.m │ ├── TTGExample2ViewController.h │ ├── TTGExample2ViewController.m │ ├── TTGExample3ViewController.h │ ├── TTGExample3ViewController.m │ ├── TTGExample4TableViewCell.h │ ├── TTGExample4TableViewCell.m │ ├── TTGExample4ViewController.h │ ├── TTGExample4ViewController.m │ ├── TTGExample5ViewController.h │ ├── TTGExample5ViewController.m │ ├── TTGExample6ViewController.h │ ├── TTGExample6ViewController.m │ ├── TTGExample7ViewController.h │ ├── TTGExample7ViewController.m │ ├── TTGExample8ViewController.h │ ├── TTGExample8ViewController.m │ ├── TTGExample9ViewController.h │ ├── TTGExample9ViewController.m │ ├── TTGTagCollectionView-Info.plist │ ├── TTGTagCollectionView-Prefix.pch │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m └── Tests │ ├── Tests-Info.plist │ ├── Tests-Prefix.pch │ ├── Tests.m │ └── en.lproj │ └── InfoPlist.strings ├── ExampleSwift ├── Podfile ├── TTGTagSwiftExample.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── TTGTagSwiftExample.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── TTGTagSwiftExample │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── SceneDelegate.swift │ └── ViewController.swift ├── LICENSE ├── Package.swift ├── README.md ├── Resources ├── alignment_type.png ├── code_structure.png ├── demo_example.jpeg └── screen_shot.png ├── Sources ├── TTGTagCollectionView-Bridging-Header.h ├── TTGTagCollectionView.h ├── TTGTagCollectionView.m ├── TTGTextTag.h ├── TTGTextTag.m ├── TTGTextTagAttributedStringContent.h ├── TTGTextTagAttributedStringContent.m ├── TTGTextTagCollectionView.h ├── TTGTextTagCollectionView.m ├── TTGTextTagContent.h ├── TTGTextTagContent.m ├── TTGTextTagStringContent.h ├── TTGTextTagStringContent.m ├── TTGTextTagStyle.h └── TTGTextTagStyle.m ├── TTGTagCollectionView.podspec └── TTGTagCollectionView ├── TTGTagCollectionView.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist └── TTGTagCollectionView └── Info.plist /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [zekunyan] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | 6 | ## Build generated 7 | build/ 8 | DerivedData 9 | 10 | ## Various settings 11 | *.pbxuser 12 | !default.pbxuser 13 | *.mode1v3 14 | !default.mode1v3 15 | *.mode2v3 16 | !default.mode2v3 17 | *.perspectivev3 18 | !default.perspectivev3 19 | xcuserdata 20 | 21 | ## Other 22 | *.xccheckout 23 | *.moved-aside 24 | *.xcuserstate 25 | *.xcscmblueprint 26 | 27 | ## Obj-C/Swift specific 28 | *.hmap 29 | *.ipa 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | .build/ 37 | 38 | # Carthage 39 | Carthage/Build 40 | 41 | # AppCode 42 | .idea/ 43 | 44 | # CocoaPods 45 | Pods/ 46 | Podfile.lock 47 | TaobaoEnv -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | osx_image: xcode12.4 2 | language: objective-c 3 | script: 4 | - set -o pipefail 5 | - xcodebuild -version 6 | - xcodebuild -showsdks 7 | - xcodebuild clean build -project TTGTagCollectionView/TTGTagCollectionView.xcodeproj -alltargets ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO PROVISIONING_PROFILE_SPECIFIER="" PROVISIONING_PROFILE="" CODE_SIGNING_ALLOWED=NO | xcpretty 8 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | 3 | use_frameworks! 4 | 5 | platform :ios, '11.0' 6 | 7 | target 'TTGTagCollectionView_Example' do 8 | pod 'TTGTagCollectionView', :path => '../' 9 | pod 'Masonry', :git => 'git@github.com:SnapKit/Masonry.git', :tag => 'v1.1.0' 10 | pod 'SVPullToRefresh', :git => 'git@github.com:samvermette/SVPullToRefresh.git', :tag => '0.4.1' 11 | 12 | target 'TTGTagCollectionView_Tests' do 13 | inherit! :search_paths 14 | end 15 | end 16 | 17 | post_install do |installer| 18 | installer.generated_projects.each do |project| 19 | project.targets.each do |target| 20 | target.build_configurations.each do |config| 21 | config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0' 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView.xcodeproj/xcshareddata/xcschemes/TTGTagCollectionView_Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView.xcodeproj/xcshareddata/xcschemes/TTGTagCollectionView_Tests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 39 | 40 | 41 | 42 | 48 | 49 | 51 | 52 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-Notification@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-Notification@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-Small@2x.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-Small@3x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-Small-40@2x.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-Small-40@3x.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-60@2x.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-60@3x.png", 49 | "scale" : "3x" 50 | }, 51 | { 52 | "size" : "20x20", 53 | "idiom" : "ipad", 54 | "filename" : "Icon-Notification.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-Notification@2x.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "29x29", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-Small.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-Small@2x.png", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "40x40", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-Small-40.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-Small-40@2x.png", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "size" : "76x76", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-76.png", 91 | "scale" : "1x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-76@2x.png", 97 | "scale" : "2x" 98 | }, 99 | { 100 | "size" : "83.5x83.5", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-83.5@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "idiom" : "ios-marketing", 107 | "size" : "1024x1024", 108 | "scale" : "1x" 109 | } 110 | ], 111 | "info" : { 112 | "version" : 1, 113 | "author" : "xcode" 114 | } 115 | } -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-76.png -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-83.5@2x.png -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-Notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-Notification.png -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-Notification@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-Notification@2x.png -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-Notification@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-Notification@3x.png -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-Small-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-Small-40.png -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-Small.png -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Example/TTGTagCollectionView/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/bluefaces_1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "bluefaces_1.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/bluefaces_1.imageset/bluefaces_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Example/TTGTagCollectionView/Images.xcassets/bluefaces_1.imageset/bluefaces_1.png -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/bluefaces_2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "bluefaces_2.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/bluefaces_2.imageset/bluefaces_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Example/TTGTagCollectionView/Images.xcassets/bluefaces_2.imageset/bluefaces_2.png -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/bluefaces_3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "bluefaces_3.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/bluefaces_3.imageset/bluefaces_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Example/TTGTagCollectionView/Images.xcassets/bluefaces_3.imageset/bluefaces_3.png -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/bluefaces_4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "bluefaces_4.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/bluefaces_4.imageset/bluefaces_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Example/TTGTagCollectionView/Images.xcassets/bluefaces_4.imageset/bluefaces_4.png -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Icon-60@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 | } -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Images.xcassets/logo.imageset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Example/TTGTagCollectionView/Images.xcassets/logo.imageset/Icon-60@2x.png -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/Launch Screen.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 | 30 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // TTGAppDelegate.h 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 12/11/2015. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface TTGAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // TTGAppDelegate.m 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 12/11/2015. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | #import "TTGAppDelegate.h" 10 | 11 | @implementation TTGAppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 14 | return YES; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGExample1ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TTGExample1ViewController.h 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 15/12/29. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TTGExample1ViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGExample1ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TTGExample1ViewController.m 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 15/12/29. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "TTGExample1ViewController.h" 11 | 12 | @interface TTGExample1ViewController () 13 | @property (weak, nonatomic) IBOutlet TTGTextTagCollectionView *textTagCollectionView1; 14 | @property (weak, nonatomic) IBOutlet TTGTextTagCollectionView *textTagCollectionView2; 15 | @property (weak, nonatomic) IBOutlet UILabel *logLabel; 16 | 17 | @property (strong, nonatomic) NSArray *tags; 18 | @end 19 | 20 | @implementation TTGExample1ViewController 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | 25 | // Remember to set this to NO 26 | self.automaticallyAdjustsScrollViewInsets = NO; 27 | 28 | // Init Tags 29 | _tags = @[ 30 | @"AutoLayoutAutoLayoutAutoLayoutAutoLayoutAutoLayoutAutoLayoutAutoLayoutAutoLayoutAutoLayout", 31 | @"dynamically", @"calculates", @"the", @"size", @"and", @"position", 32 | @"of", @"all", @"the", @"views", @"in", @"your", @"view", @"hierarchy", @"based", 33 | @"on", @"constraints", @"placed", @"on", @"those", @"views", 34 | @"For", @"example", @"you", @"can", @"constrain", @"a", @"button", 35 | @"so", @"that", @"it", @"is", @"horizontally", @"centered", @"with", 36 | @"an", @"Image", @"view", @"and", @"so", @"that", @"the", @"button’s", 37 | @"top", @"edge", @"always", @"remains", @"8", @"points", @"below", @"the", 38 | @"image’s", @"bottom", @"If", @"the", @"image", @"view’s", @"size", @"or", 39 | @"position", @"changes", @"the", @"button’s", @"position", @"automatically", @"adjusts", @"to", @"match" 40 | ]; 41 | 42 | _logLabel.adjustsFontSizeToFitWidth = YES; 43 | _textTagCollectionView1.delegate = self; 44 | _textTagCollectionView2.delegate = self; 45 | 46 | _textTagCollectionView1.showsVerticalScrollIndicator = NO; 47 | _textTagCollectionView2.showsVerticalScrollIndicator = NO; 48 | 49 | _textTagCollectionView1.horizontalSpacing = 6.0; 50 | _textTagCollectionView1.verticalSpacing = 8.0; 51 | 52 | _textTagCollectionView2.horizontalSpacing = 8; 53 | _textTagCollectionView2.verticalSpacing = 8; 54 | 55 | // Change alignment 56 | _textTagCollectionView1.alignment = TTGTagCollectionAlignmentFillByExpandingWidth; 57 | _textTagCollectionView2.alignment = TTGTagCollectionAlignmentFillByExpandingWidthExceptLastLine; 58 | 59 | // Style1 60 | TTGTextTagStringContent *content = [TTGTextTagStringContent new]; 61 | TTGTextTagStringContent *selectedContent = [TTGTextTagStringContent new]; 62 | TTGTextTagStyle *style = [TTGTextTagStyle new]; 63 | TTGTextTagStyle *selectedStyle = [TTGTextTagStyle new]; 64 | 65 | content.textFont = [UIFont boldSystemFontOfSize:18.0f]; 66 | selectedContent.textFont = content.textFont; 67 | 68 | content.textColor = [UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1.00]; 69 | selectedContent.textColor = [UIColor whiteColor]; 70 | 71 | style.backgroundColor = [UIColor colorWithRed:0.31 green:0.70 blue:0.80 alpha:1.00]; 72 | selectedStyle.backgroundColor = [UIColor colorWithRed:0.38 green:0.36 blue:0.63 alpha:1.00]; 73 | 74 | style.borderColor = [UIColor colorWithRed:0.18 green:0.19 blue:0.22 alpha:1.00]; 75 | style.borderWidth = 1; 76 | 77 | selectedStyle.borderColor = [UIColor colorWithRed:0.18 green:0.19 blue:0.22 alpha:1.00]; 78 | selectedStyle.borderWidth = 1; 79 | 80 | style.shadowColor = [UIColor grayColor]; 81 | style.shadowOffset = CGSizeMake(0, 1); 82 | style.shadowOpacity = 0.5f; 83 | style.shadowRadius = 2; 84 | 85 | selectedStyle.shadowColor = [UIColor greenColor]; 86 | selectedStyle.shadowOffset = CGSizeMake(0, 2); 87 | selectedStyle.shadowOpacity = 0.5f; 88 | selectedStyle.shadowRadius = 1; 89 | 90 | style.cornerRadius = 2; 91 | selectedStyle.cornerRadius = 4; 92 | 93 | style.extraSpace = CGSizeMake(4, 4); 94 | selectedStyle.extraSpace = style.extraSpace; 95 | 96 | NSMutableArray *tags = [NSMutableArray new]; 97 | for (NSString *string in _tags) { 98 | TTGTextTagStringContent *stringContent = [content copy]; 99 | stringContent.text = string; 100 | TTGTextTagStringContent *selectedStringContent = [selectedContent copy]; 101 | selectedStringContent.text = string; 102 | TTGTextTag *tag = [TTGTextTag new]; 103 | tag.content = stringContent; 104 | tag.selectedContent = selectedStringContent; 105 | tag.style = style; 106 | tag.selectedStyle = selectedStyle; 107 | [tags addObject:tag.copy]; 108 | } 109 | [_textTagCollectionView1 addTags:tags]; 110 | 111 | // Style2 112 | content.textFont = [UIFont systemFontOfSize:18.0f]; 113 | selectedContent.textFont = [UIFont systemFontOfSize:20.0f]; 114 | 115 | content.textColor = [UIColor whiteColor]; 116 | selectedContent.textColor = [UIColor greenColor]; 117 | 118 | style.extraSpace = CGSizeMake(12, 12); 119 | selectedStyle.extraSpace = CGSizeMake(12, 12); 120 | 121 | style.backgroundColor = [UIColor colorWithRed:0.10 green:0.53 blue:0.85 alpha:1.00]; 122 | selectedStyle.backgroundColor = [UIColor colorWithRed:0.21 green:0.29 blue:0.36 alpha:1.00]; 123 | 124 | style.cornerRadius = 12.0f; 125 | style.cornerBottomRight = true; 126 | style.cornerBottomLeft = false; 127 | style.cornerTopRight = false; 128 | style.cornerTopLeft = true; 129 | 130 | selectedStyle.cornerRadius = 8.0f; 131 | selectedStyle.cornerBottomRight = true; 132 | selectedStyle.cornerBottomLeft = false; 133 | selectedStyle.cornerTopRight = true; 134 | selectedStyle.cornerTopLeft = false; 135 | 136 | style.borderWidth = 1; 137 | selectedStyle.borderWidth = 4; 138 | 139 | style.borderColor = [UIColor redColor]; 140 | selectedStyle.borderColor = [UIColor orangeColor]; 141 | 142 | style.shadowColor = [UIColor blackColor]; 143 | style.shadowOffset = CGSizeMake(0, 4); 144 | style.shadowOpacity = 0.3f; 145 | style.shadowRadius = 4; 146 | 147 | selectedStyle.shadowColor = [UIColor redColor]; 148 | selectedStyle.shadowOffset = CGSizeMake(0, 1); 149 | selectedStyle.shadowOpacity = 0.3f; 150 | selectedStyle.shadowRadius = 2; 151 | 152 | tags = [NSMutableArray new]; 153 | for (NSString *string in _tags) { 154 | TTGTextTagStringContent *stringContent = [content copy]; 155 | stringContent.text = string; 156 | TTGTextTagStringContent *selectedStringContent = [selectedContent copy]; 157 | selectedStringContent.text = [string stringByAppendingString:@"!"]; 158 | TTGTextTag *tag = [TTGTextTag new]; 159 | tag.content = stringContent; 160 | tag.selectedContent = selectedStringContent; 161 | tag.style = style; 162 | tag.selectedStyle = selectedStyle; 163 | [tags addObject:tag.copy]; 164 | } 165 | [_textTagCollectionView2 addTags:tags]; 166 | 167 | // Init selection 168 | [_textTagCollectionView1 updateTagAtIndex:0 selected:YES]; 169 | [_textTagCollectionView1 updateTagAtIndex:4 selected:YES]; 170 | [_textTagCollectionView1 updateTagAtIndex:6 selected:YES]; 171 | [_textTagCollectionView1 updateTagAtIndex:17 selected:YES]; 172 | 173 | [_textTagCollectionView2 updateTagAtIndex:0 selected:YES]; 174 | [_textTagCollectionView2 updateTagAtIndex:4 selected:YES]; 175 | [_textTagCollectionView2 updateTagAtIndex:6 selected:YES]; 176 | [_textTagCollectionView2 updateTagAtIndex:17 selected:YES]; 177 | 178 | // Load data 179 | [_textTagCollectionView1 reload]; 180 | [_textTagCollectionView2 reload]; 181 | } 182 | 183 | #pragma mark - TTGTextTagCollectionViewDelegate 184 | 185 | - (void)textTagCollectionView:(TTGTextTagCollectionView *)textTagCollectionView didTapTag:(TTGTextTag *)tag atIndex:(NSUInteger)index { 186 | _logLabel.text = [NSString stringWithFormat:@"Tap tag: %@, at: %ld, selected: %d", tag.content.getContentAttributedString.string, (long) index, tag.selected]; 187 | } 188 | 189 | - (void)textTagCollectionView:(TTGTextTagCollectionView *)textTagCollectionView updateContentSize:(CGSize)contentSize { 190 | NSLog(@"text tag collection: %@ new content size: %@", textTagCollectionView, NSStringFromCGSize(contentSize)); 191 | } 192 | 193 | @end 194 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGExample2ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TTGExample2ViewController.h 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 15/12/29. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TTGExample2ViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGExample2ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TTGExample2ViewController.m 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 15/12/29. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "TTGExample2ViewController.h" 11 | 12 | @interface TTGExample2ViewController () 13 | @property (weak, nonatomic) IBOutlet TTGTagCollectionView *tagCollectionView; 14 | @property (weak, nonatomic) IBOutlet UILabel *logLabel; 15 | 16 | @property (strong, nonatomic) NSMutableArray *tagViews; 17 | @end 18 | 19 | @implementation TTGExample2ViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | 24 | _tagCollectionView.delegate = self; 25 | _tagCollectionView.dataSource = self; 26 | _logLabel.adjustsFontSizeToFitWidth = YES; 27 | _tagViews = [NSMutableArray new]; 28 | 29 | UIColor *backgroundColor1 = [UIColor colorWithRed:0.30 green:0.72 blue:0.53 alpha:1.00]; 30 | UIColor *backgroundColor2 = [UIColor colorWithRed:0.10 green:0.53 blue:0.85 alpha:1.00]; 31 | UIColor *backgroundColor3 = [UIColor colorWithRed:0.97 green:0.64 blue:0.27 alpha:1.00]; 32 | 33 | [_tagViews addObject:[self newLabelWithText:@"AutoLayout" fontSize:14.0f textColor:[UIColor whiteColor] backgroundColor:backgroundColor1]]; 34 | [_tagViews addObject:[self newButtonWithTitle:@"Button1" fontSize:18.0f backgroundColor:backgroundColor2]]; 35 | [_tagViews addObject:[self newImageViewWithImage:[UIImage imageNamed:@"bluefaces_1"]]]; 36 | [_tagViews addObject:[self newLabelWithText:@"dynamically" fontSize:20.0f textColor:[UIColor whiteColor] backgroundColor:backgroundColor1]]; 37 | [_tagViews addObject:[self newButtonWithTitle:@"Button2" fontSize:16.0f backgroundColor:backgroundColor3]]; 38 | [_tagViews addObject:[self newButtonWithTitle:@"Button3" fontSize:15.0f backgroundColor:backgroundColor2]]; 39 | [_tagViews addObject:[self newImageViewWithImage:[UIImage imageNamed:@"bluefaces_2"]]]; 40 | [_tagViews addObject:[self newLabelWithText:@"the" fontSize:16.0f textColor:[UIColor blackColor] backgroundColor:backgroundColor1]]; 41 | [_tagViews addObject:[self newButtonWithTitle:@"Button4" fontSize:22.0f backgroundColor:backgroundColor2]]; 42 | [_tagViews addObject:[self newImageViewWithImage:[UIImage imageNamed:@"bluefaces_3"]]]; 43 | [_tagViews addObject:[self newLabelWithText:@"views" fontSize:12.0f 44 | textColor:[UIColor colorWithRed:0.21 green:0.29 blue:0.36 alpha:1.00] 45 | backgroundColor:backgroundColor3]]; 46 | [_tagViews addObject:[self newButtonWithTitle:@"Button5" fontSize:15.0f backgroundColor:backgroundColor1]]; 47 | [_tagViews addObject:[self newImageViewWithImage:[UIImage imageNamed:@"bluefaces_4"]]]; 48 | [_tagViews addObject:[self newImageViewWithImage:[UIImage imageNamed:@"bluefaces_4"]]]; 49 | 50 | [_tagCollectionView reload]; 51 | } 52 | 53 | #pragma mark - TTGTagCollectionViewDelegate 54 | 55 | - (CGSize)tagCollectionView:(TTGTagCollectionView *)tagCollectionView sizeForTagAtIndex:(NSUInteger)index { 56 | return _tagViews[index].frame.size; 57 | } 58 | 59 | - (void)tagCollectionView:(TTGTagCollectionView *)tagCollectionView didSelectTag:(UIView *)tagView atIndex:(NSUInteger)index { 60 | _logLabel.text = [NSString stringWithFormat:@"Tap tag: %@, at: %ld", tagView.class, (long) index]; 61 | } 62 | 63 | #pragma mark - TTGTagCollectionViewDataSource 64 | 65 | - (NSUInteger)numberOfTagsInTagCollectionView:(TTGTagCollectionView *)tagCollectionView { 66 | return _tagViews.count; 67 | } 68 | 69 | - (UIView *)tagCollectionView:(TTGTagCollectionView *)tagCollectionView tagViewForIndex:(NSUInteger)index { 70 | return _tagViews[index]; 71 | } 72 | 73 | #pragma mark - Private methods 74 | 75 | - (UILabel *)newLabelWithText:(NSString *)text fontSize:(CGFloat)fontSize textColor:(UIColor *)textColor backgroundColor:(UIColor *)backgroudColor { 76 | UILabel *label = [UILabel new]; 77 | 78 | label.font = [UIFont systemFontOfSize:fontSize]; 79 | label.textAlignment = NSTextAlignmentCenter; 80 | label.text = text; 81 | label.textColor = textColor; 82 | label.backgroundColor = backgroudColor; 83 | [label sizeToFit]; 84 | label.layer.masksToBounds = YES; 85 | label.layer.cornerRadius = 4.0f; 86 | 87 | [self expandSizeForView:label extraWidth:12 extraHeight:8]; 88 | 89 | return label; 90 | } 91 | 92 | - (UIButton *)newButtonWithTitle:(NSString *)title fontSize:(CGFloat)fontSize backgroundColor:(UIColor *)backgroudColor { 93 | UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; 94 | 95 | button.titleLabel.font = [UIFont systemFontOfSize:fontSize]; 96 | [button setTitle:title forState:UIControlStateNormal]; 97 | button.backgroundColor = backgroudColor; 98 | [button sizeToFit]; 99 | button.layer.masksToBounds = YES; 100 | button.layer.cornerRadius = 4.0f; 101 | 102 | [self expandSizeForView:button extraWidth:12 extraHeight:8]; 103 | 104 | [button addTarget:self action:@selector(onTap:) forControlEvents:UIControlEventTouchUpInside]; 105 | 106 | return button; 107 | } 108 | 109 | - (UIImageView *)newImageViewWithImage:(UIImage *)image { 110 | UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; 111 | [imageView sizeToFit]; 112 | return imageView; 113 | } 114 | 115 | - (void)expandSizeForView:(UIView *)view extraWidth:(CGFloat)extraWidth extraHeight:(CGFloat)extraHeight { 116 | CGRect frame = view.frame; 117 | frame.size.width += extraWidth; 118 | frame.size.height += extraHeight; 119 | view.frame = frame; 120 | } 121 | 122 | #pragma mark - Action 123 | 124 | - (void)onTap:(UIButton *)button { 125 | _logLabel.text = @"Tap tag button !"; 126 | } 127 | 128 | @end 129 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGExample3ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TTGExample3ViewController.h 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2016/9/29. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TTGExample3ViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGExample3ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TTGExample3ViewController.m 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2016/9/29. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | #import "TTGExample3ViewController.h" 10 | #import 11 | 12 | @interface TTGExample3ViewController () 13 | @property (nonatomic, strong) TTGTextTagCollectionView *tagView; 14 | @end 15 | 16 | @implementation TTGExample3ViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | 21 | NSArray *tags = @[@"AutoLayout", @"dynamically", @"calculates", @"the", @"size", @"and", @"position", 22 | @"of", @"all", @"the", @"views", @"in", @"your", @"view", @"hierarchy", @"based", 23 | @"on", @"constraints", @"placed", @"on", @"those", @"views"]; 24 | 25 | _tagView = [TTGTextTagCollectionView new]; 26 | _tagView.alignment = TTGTagCollectionAlignmentFillByExpandingWidth; 27 | _tagView.layer.borderColor = [UIColor grayColor].CGColor; 28 | _tagView.layer.borderWidth = 1; 29 | _tagView.translatesAutoresizingMaskIntoConstraints = NO; 30 | _tagView.onTapBlankArea = ^(CGPoint location) { 31 | NSLog(@"Blank: %@", NSStringFromCGPoint(location)); 32 | }; 33 | _tagView.onTapAllArea = ^(CGPoint location) { 34 | NSLog(@"All: %@", NSStringFromCGPoint(location)); 35 | }; 36 | [self.view addSubview:_tagView]; 37 | 38 | NSArray *hConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-20-[tagView]-20-|" 39 | options:(NSLayoutFormatOptions) 0 metrics:nil 40 | views:@{@"tagView": _tagView}]; 41 | NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:_tagView 42 | attribute:NSLayoutAttributeTop 43 | relatedBy:NSLayoutRelationEqual 44 | toItem:self.view 45 | attribute:NSLayoutAttributeTop 46 | multiplier:1 constant:180]; 47 | [self.view addConstraint:topConstraint]; 48 | [self.view addConstraints:hConstraints]; 49 | 50 | NSMutableArray *textTags = [NSMutableArray new]; 51 | for (NSString *string in tags) { 52 | TTGTextTag *textTag = [TTGTextTag tagWithContent:[TTGTextTagStringContent contentWithText:string] style:[TTGTextTagStyle new]]; 53 | textTag.selectedStyle.backgroundColor = [UIColor greenColor]; 54 | [textTags addObject:textTag]; 55 | } 56 | [_tagView addTags:textTags]; 57 | 58 | for (NSInteger i = 0; i < 5; i++) { 59 | [_tagView updateTagAtIndex:arc4random_uniform((uint32_t)tags.count) selected:YES]; 60 | } 61 | 62 | _tagView.onTapAllArea = ^(CGPoint location) { 63 | NSLog(@"onTapAllArea: %@", NSStringFromCGPoint(location)); 64 | }; 65 | _tagView.onTapBlankArea = ^(CGPoint location) { 66 | NSLog(@"onTapBlankArea: %@", NSStringFromCGPoint(location)); 67 | }; 68 | 69 | [_tagView reload]; 70 | } 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGExample4TableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // TTGExample4TableViewCell.h 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2016/10/1. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class TTGTextTagCollectionView; 12 | 13 | @interface TTGExample4TableViewCell : UITableViewCell 14 | @property (weak, nonatomic) IBOutlet TTGTextTagCollectionView *tagView; 15 | @property (weak, nonatomic) IBOutlet UILabel *label; 16 | 17 | - (void)setTags:(NSArray *)tags; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGExample4TableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // TTGExample4TableViewCell.m 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2016/10/1. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | #import "TTGExample4TableViewCell.h" 10 | #import 11 | 12 | @interface TTGExample4TableViewCell () 13 | @end 14 | 15 | @implementation TTGExample4TableViewCell 16 | 17 | - (void)awakeFromNib { 18 | [super awakeFromNib]; 19 | 20 | // Alignment 21 | _tagView.alignment = TTGTagCollectionAlignmentFillByExpandingWidth; 22 | 23 | // Use manual calculate height 24 | _tagView.manualCalculateHeight = YES; 25 | } 26 | 27 | - (void)setTags:(NSArray *)tags { 28 | [_tagView removeAllTags]; 29 | 30 | NSMutableArray *textTags = [NSMutableArray new]; 31 | for (NSString *string in tags) { 32 | TTGTextTag *textTag = [TTGTextTag tagWithContent:[TTGTextTagStringContent contentWithText:string] style:[TTGTextTagStyle new]]; 33 | textTag.selectedStyle.backgroundColor = [UIColor greenColor]; 34 | [textTags addObject:textTag]; 35 | } 36 | [_tagView addTags:textTags]; 37 | 38 | // Use manual height, update preferredMaxLayoutWidth 39 | _tagView.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 16; 40 | 41 | // Random selected 42 | for (NSInteger i = 0; i < 3; i++) { 43 | [_tagView updateTagAtIndex:arc4random_uniform((uint32_t)tags.count) selected:YES]; 44 | } 45 | [_tagView reload]; 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGExample4ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TTGExample4ViewController.h 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2016/10/1. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TTGExample4ViewController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGExample4ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TTGExample4ViewController.m 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2016/10/1. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | #import "TTGExample4ViewController.h" 10 | #import "TTGExample4TableViewCell.h" 11 | 12 | @interface TTGExample4ViewController () 13 | @property (nonatomic, strong) NSArray *allTags; 14 | @property (nonatomic, strong) NSMutableArray *cellInfos; 15 | @end 16 | 17 | @implementation TTGExample4ViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | 22 | self.tableView.rowHeight = UITableViewAutomaticDimension; 23 | self.tableView.estimatedRowHeight = 80; 24 | 25 | _allTags = @[ 26 | @"AutoLayout", @"dynamically", @"calculates", @"the", @"size", @"and", @"position", 27 | @"of", @"all", @"the", @"views", @"in", @"your", @"view", @"hierarchy", @"based", 28 | @"on", @"constraints", @"placed", @"on", @"those", @"views" 29 | ]; 30 | _cellInfos = [NSMutableArray new]; 31 | for (NSInteger i = 0; i < 50; i++) { 32 | [_cellInfos addObject:[_allTags subarrayWithRange:NSMakeRange(0, i % (_allTags.count + 1))]]; 33 | } 34 | } 35 | 36 | #pragma mark - Table view data source 37 | 38 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 39 | return _cellInfos.count; 40 | } 41 | 42 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 43 | TTGExample4TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([TTGExample4TableViewCell class]) 44 | forIndexPath:indexPath]; 45 | [cell setTags:_cellInfos[(NSUInteger) indexPath.row]]; 46 | cell.label.text = [NSString stringWithFormat:@"Cell: %ld", (long)indexPath.row]; 47 | 48 | return cell; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGExample5ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TTGExample5ViewController.h 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2016/10/16. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TTGExample5ViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGExample5ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TTGExample5ViewController.m 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2016/10/16. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | #import "TTGExample5ViewController.h" 10 | #import 11 | 12 | @interface TTGExample5ViewController () 13 | @property (weak, nonatomic) IBOutlet TTGTextTagCollectionView *oneLineTagView; 14 | @property (weak, nonatomic) IBOutlet TTGTextTagCollectionView *twoLineTagView; 15 | @property (weak, nonatomic) IBOutlet TTGTextTagCollectionView *threeLineTagView; 16 | 17 | @end 18 | 19 | @implementation TTGExample5ViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | 24 | _oneLineTagView.scrollDirection = TTGTagCollectionScrollDirectionHorizontal; 25 | _twoLineTagView.scrollDirection = TTGTagCollectionScrollDirectionHorizontal; 26 | _threeLineTagView.scrollDirection = TTGTagCollectionScrollDirectionHorizontal; 27 | 28 | _oneLineTagView.alignment = TTGTagCollectionAlignmentFillByExpandingWidth; 29 | _twoLineTagView.alignment = TTGTagCollectionAlignmentFillByExpandingWidth; 30 | _threeLineTagView.alignment = TTGTagCollectionAlignmentFillByExpandingWidth; 31 | 32 | _oneLineTagView.numberOfLines = 1; 33 | _twoLineTagView.numberOfLines = 2; 34 | _threeLineTagView.numberOfLines = 3; 35 | 36 | [_oneLineTagView addTags:[self generateTags]]; 37 | [_oneLineTagView reload]; 38 | 39 | [_twoLineTagView addTags:[self generateTags]]; 40 | [_twoLineTagView reload]; 41 | 42 | [_threeLineTagView addTags:[self generateTags]]; 43 | [_threeLineTagView reload]; 44 | } 45 | 46 | - (NSArray *)generateTags { 47 | NSArray *tags = @[@"AutoLayout", @"dynamically", @"calculates", @"the", @"size", @"and", @"position", 48 | @"of", @"all", @"the", @"views", @"in", @"your", @"view", @"hierarchy", @"based", 49 | @"on", @"constraints", @"placed", @"on", @"those", @"views"]; 50 | 51 | NSMutableArray *textTags = [NSMutableArray new]; 52 | for (NSString *string in tags) { 53 | TTGTextTag *textTag = [TTGTextTag tagWithContent:[TTGTextTagStringContent contentWithText:string] style:[TTGTextTagStyle new]]; 54 | textTag.selectedStyle.backgroundColor = [UIColor greenColor]; 55 | [textTags addObject:textTag]; 56 | } 57 | 58 | return textTags; 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGExample6ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TTGExample6ViewController.h 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2017/3/2. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TTGExample6ViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGExample6ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TTGExample6ViewController.m 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2017/3/2. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | #import "TTGExample6ViewController.h" 10 | #import 11 | #import 12 | 13 | @interface TTGExample6ViewController () 14 | @property (weak, nonatomic) IBOutlet TTGTextTagCollectionView *tagView; 15 | 16 | @end 17 | 18 | @implementation TTGExample6ViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | 23 | __weak typeof(self) weakSelf = self; 24 | [_tagView.scrollView addPullToRefreshWithActionHandler:^{ 25 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 26 | [weakSelf.tagView.scrollView.pullToRefreshView stopAnimating]; 27 | [weakSelf.tagView removeAllTags]; 28 | [weakSelf.tagView addTags:[weakSelf generateTags]]; 29 | [weakSelf.tagView reload]; 30 | }); 31 | }]; 32 | 33 | [_tagView.scrollView addInfiniteScrollingWithActionHandler:^{ 34 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 35 | [weakSelf.tagView.scrollView.infiniteScrollingView stopAnimating]; 36 | [weakSelf.tagView addTags:[weakSelf generateTags]]; 37 | [weakSelf.tagView reload]; 38 | }); 39 | }]; 40 | 41 | _tagView.scrollView.infiniteScrollingView.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray; 42 | _tagView.alignment = TTGTagCollectionAlignmentFillByExpandingWidth; 43 | _tagView.scrollView.alwaysBounceVertical = YES; // Very important 44 | [_tagView.scrollView triggerPullToRefresh]; 45 | } 46 | 47 | - (NSArray *)generateTags { 48 | NSArray *tags = @[@"AutoLayout", @"dynamically", @"calculates", @"the", @"size", @"and", @"position", 49 | @"of", @"all", @"the", @"views", @"in", @"your", @"view", @"hierarchy", @"based", 50 | @"on", @"constraints", @"placed", @"on", @"those", @"views"]; 51 | 52 | NSMutableArray *textTags = [NSMutableArray new]; 53 | for (NSString *string in tags) { 54 | TTGTextTag *textTag = [TTGTextTag tagWithContent:[TTGTextTagStringContent contentWithText:string] style:[TTGTextTagStyle new]]; 55 | textTag.selectedStyle.backgroundColor = [UIColor greenColor]; 56 | [textTags addObject:textTag]; 57 | } 58 | 59 | return textTags; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGExample7ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TTGExample7ViewController.h 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2017/3/4. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TTGExample7ViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGExample7ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TTGExample7ViewController.m 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2017/3/4. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | #import "TTGExample7ViewController.h" 10 | #import 11 | 12 | @interface TTGExample7ViewController () 13 | @property (weak, nonatomic) IBOutlet TTGTextTagCollectionView *tagView; 14 | @end 15 | 16 | @implementation TTGExample7ViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | 21 | NSArray *tags = @[@"AutoLayout", @"dynamically", @"calculates", @"the", @"size", @"and", @"position", 22 | @"of", @"all", @"the", @"views", @"in", @"your", @"view", @"hierarchy", @"based", 23 | @"on", @"constraints", @"placed", @"on", @"those", @"views", 24 | @"AutoLayout", @"dynamically", @"calculates", @"the", @"size", @"and", @"position", 25 | @"of", @"all", @"the", @"views", @"in", @"your", @"view", @"hierarchy", @"based", 26 | @"on", @"constraints", @"placed", @"on", @"those", @"views", 27 | @"AutoLayout", @"dynamically", @"calculates", @"the", @"size", @"and", @"position", 28 | @"of", @"all", @"the", @"views", @"in", @"your", @"view", @"hierarchy", @"based", 29 | @"on", @"constraints", @"placed", @"on", @"those", @"views", 30 | @"AutoLayout", @"dynamically", @"calculates", @"the", @"size", @"and", @"position", 31 | @"of", @"all", @"the", @"views", @"in", @"your", @"view", @"hierarchy", @"based", 32 | @"on", @"constraints", @"placed", @"on", @"those", @"views", 33 | @"AutoLayout", @"dynamically", @"calculates", @"the", @"size", @"and", @"position", 34 | @"of", @"all", @"the", @"views", @"in", @"your", @"view", @"hierarchy", @"based", 35 | @"on", @"constraints", @"placed", @"on", @"those", @"views", 36 | @"AutoLayout", @"dynamically", @"calculates", @"the", @"size", @"and", @"position", 37 | @"of", @"all", @"the", @"views", @"in", @"your", @"view", @"hierarchy", @"based", 38 | @"on", @"constraints", @"placed", @"on", @"those", @"views", 39 | @"AutoLayout", @"dynamically", @"calculates", @"the", @"size", @"and", @"position", 40 | @"of", @"all", @"the", @"views", @"in", @"your", @"view", @"hierarchy", @"based", 41 | @"on", @"constraints", @"placed", @"on", @"those", @"views", 42 | @"AutoLayout", @"dynamically", @"calculates", @"the", @"size", @"and", @"position", 43 | @"of", @"all", @"the", @"views", @"in", @"your", @"view", @"hierarchy", @"based", 44 | @"on", @"constraints", @"placed", @"on", @"those", @"views", 45 | @"AutoLayout", @"dynamically", @"calculates", @"the", @"size", @"and", @"position", 46 | @"of", @"all", @"the", @"views", @"in", @"your", @"view", @"hierarchy", @"based", 47 | @"on", @"constraints", @"placed", @"on", @"those", @"views", 48 | @"AutoLayout", @"dynamically", @"calculates", @"the", @"size", @"and", @"position", 49 | @"of", @"all", @"the", @"views", @"in", @"your", @"view", @"hierarchy", @"based", 50 | @"on", @"constraints", @"placed", @"on", @"those", @"views"]; 51 | 52 | _tagView.alignment = TTGTagCollectionAlignmentFillByExpandingWidth; 53 | 54 | NSUInteger location = 0; 55 | NSUInteger length = 8; 56 | 57 | [self.class addBatchTagsWithStrings:tags 58 | range:NSMakeRange(location, length) 59 | toTagView:_tagView 60 | backgroundColor:[UIColor colorWithRed:0.24 green:0.72 blue:0.94 alpha:1.00] 61 | attachment:@{@"key": @"1"}]; 62 | 63 | location += length; 64 | [self.class addBatchTagsWithStrings:tags 65 | range:NSMakeRange(location, length) 66 | toTagView:_tagView 67 | backgroundColor:[UIColor colorWithRed:0.30 green:0.72 blue:0.53 alpha:1.00] 68 | attachment:@{@"key": @"2"}]; 69 | 70 | location += length; 71 | [self.class addBatchTagsWithStrings:tags 72 | range:NSMakeRange(location, length) 73 | toTagView:_tagView 74 | backgroundColor:[UIColor colorWithRed:0.97 green:0.64 blue:0.27 alpha:1.00] 75 | attachment:@{@"key": @"3"}]; 76 | 77 | location += length; 78 | [self.class addBatchTagsWithStrings:tags 79 | range:NSMakeRange(location, length) 80 | toTagView:_tagView 81 | backgroundColor:[UIColor colorWithRed:0.73 green:0.91 blue:0.41 alpha:1.00] 82 | attachment:@{@"key": @"4"}]; 83 | 84 | location += length; 85 | [self.class addBatchTagsWithStrings:tags 86 | range:NSMakeRange(location, length) 87 | toTagView:_tagView 88 | backgroundColor:[UIColor colorWithRed:0.35 green:0.35 blue:0.36 alpha:1.00] 89 | attachment:@{@"key": @"5"}]; 90 | 91 | location += length; 92 | [self.class addBatchTagsWithStrings:tags 93 | range:NSMakeRange(location, length) 94 | toTagView:_tagView 95 | backgroundColor:[UIColor colorWithRed:1.00 green:0.41 blue:0.42 alpha:1.00] 96 | attachment:@{@"key": @"6"}]; 97 | 98 | location += length; 99 | [self.class addBatchTagsWithStrings:tags 100 | range:NSMakeRange(location, length) 101 | toTagView:_tagView 102 | backgroundColor:[UIColor colorWithRed:0.50 green:0.86 blue:0.90 alpha:1.00] 103 | attachment:@{@"key": @"7"}]; 104 | 105 | location += length; 106 | [self.class addBatchTagsWithStrings:tags 107 | range:NSMakeRange(location, length) 108 | toTagView:_tagView 109 | backgroundColor:[UIColor colorWithRed:0.33 green:0.23 blue:0.34 alpha:1.00] 110 | attachment:@{@"key": @"8"}]; 111 | 112 | _tagView.delegate = self; 113 | [_tagView reload]; 114 | } 115 | 116 | #pragma mark - TTGTextTagCollectionViewDelegate 117 | 118 | - (void)textTagCollectionView:(TTGTextTagCollectionView *)textTagCollectionView 119 | didTapTag:(TTGTextTag *)tag 120 | atIndex:(NSUInteger)index { 121 | NSLog(@"Did tap: %@, config extra: %@", tag.content, tag.attachment); 122 | } 123 | 124 | + (void)addBatchTagsWithStrings:(NSArray *)strings 125 | range:(NSRange)range 126 | toTagView:(TTGTextTagCollectionView *)tagView 127 | backgroundColor:(UIColor *)backgroundColor 128 | attachment:(id)attachment { 129 | 130 | static TTGTextTagStyle *defaultStyle = nil; 131 | static TTGTextTagStringContent *defaultContent = nil; 132 | 133 | static dispatch_once_t onceToken; 134 | dispatch_once(&onceToken, ^{ 135 | defaultStyle = [TTGTextTagStyle new]; 136 | defaultStyle.backgroundColor = [UIColor whiteColor]; 137 | defaultStyle.borderColor = [UIColor whiteColor]; 138 | defaultStyle.borderWidth = 1; 139 | defaultStyle.cornerRadius = 4; 140 | defaultStyle.extraSpace = CGSizeMake(8, 8); 141 | defaultStyle.shadowColor = [UIColor blackColor]; 142 | defaultStyle.shadowOpacity = 0.3; 143 | defaultStyle.shadowRadius = 2; 144 | defaultStyle.shadowOffset = CGSizeMake(1, 1); 145 | 146 | defaultContent = [TTGTextTagStringContent new]; 147 | defaultContent.textFont = [UIFont systemFontOfSize:20]; 148 | defaultContent.textColor = [UIColor whiteColor]; 149 | }); 150 | 151 | for (NSString *text in [strings subarrayWithRange:range]) { 152 | TTGTextTag *tag = [TTGTextTag new]; 153 | 154 | // tag.enableAutoDetectAccessibility = YES; 155 | 156 | tag.isAccessibilityElement = YES; 157 | tag.accessibilityLabel = text; 158 | tag.accessibilityIdentifier = [NSString stringWithFormat:@"identifier: %@", text]; 159 | tag.accessibilityHint = [NSString stringWithFormat:@"hint: %@", text]; 160 | tag.accessibilityValue = [NSString stringWithFormat:@"value: %@", text]; 161 | 162 | TTGTextTagStyle *style = [defaultStyle copy]; 163 | style.backgroundColor = backgroundColor; 164 | 165 | TTGTextTagStyle *selectedStyle = [style copy]; 166 | selectedStyle.backgroundColor = [self.class getRevertColor:style.backgroundColor]; 167 | selectedStyle.borderColor = [UIColor blackColor]; 168 | selectedStyle.cornerRadius = 8; 169 | selectedStyle.shadowColor = [UIColor greenColor]; 170 | 171 | TTGTextTagStringContent *content = [defaultContent copy]; 172 | content.text = text; 173 | 174 | tag.style = style; 175 | tag.selectedStyle = selectedStyle; 176 | tag.content = content; 177 | tag.attachment = attachment; 178 | [tagView addTag:tag]; 179 | } 180 | 181 | [tagView updateTagAtIndex:range.location + arc4random_uniform((uint32_t)range.length) selected:YES]; 182 | } 183 | 184 | + (UIColor *)getRevertColor:(UIColor *)color { 185 | CGFloat red = 0; 186 | CGFloat green = 0; 187 | CGFloat blue = 0; 188 | [color getRed:&red green:&green blue:&blue alpha:nil]; 189 | return [UIColor colorWithRed:1 - red green:1 - green blue:1 - blue alpha:1]; 190 | } 191 | 192 | @end 193 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGExample8ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TTGExample8ViewController.h 3 | // TTGTagCollectionView_Example 4 | // 5 | // Created by zekunyan on 24/03/2018. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TTGExample8ViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGExample8ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TTGExample8ViewController.m 3 | // TTGTagCollectionView_Example 4 | // 5 | // Created by zekunyan on 24/03/2018. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | #import "TTGExample8ViewController.h" 10 | #import 11 | 12 | #pragma mark - CustomTagData 13 | 14 | @interface CustomTagData: NSObject 15 | @property (nonatomic, strong) NSString *info; 16 | @end 17 | 18 | @implementation CustomTagData 19 | - (NSString *)description { 20 | return [NSString stringWithFormat:@"I am CustomTagData, info is: %@", _info]; 21 | } 22 | @end 23 | 24 | #pragma mark - TTGExample8ViewController 25 | 26 | @interface TTGExample8ViewController () 27 | @property (nonatomic, strong) TTGTextTagCollectionView *tagView; 28 | @property (nonatomic, strong) UITextView *infoTextView; 29 | @end 30 | 31 | @implementation TTGExample8ViewController 32 | 33 | - (void)viewDidLoad { 34 | [super viewDidLoad]; 35 | 36 | // Create view 37 | _tagView = [TTGTextTagCollectionView new]; 38 | _tagView.alignment = TTGTagCollectionAlignmentFillByExpandingWidth; 39 | _tagView.layer.borderColor = [UIColor grayColor].CGColor; 40 | _tagView.layer.borderWidth = 1; 41 | _tagView.translatesAutoresizingMaskIntoConstraints = NO; 42 | [self.view addSubview:_tagView]; 43 | 44 | _infoTextView = [UITextView new]; 45 | _infoTextView.layer.borderColor = [UIColor grayColor].CGColor; 46 | _infoTextView.layer.borderWidth = 1; 47 | _infoTextView.textColor = [UIColor grayColor]; 48 | _infoTextView.font = [UIFont systemFontOfSize:12]; 49 | _infoTextView.translatesAutoresizingMaskIntoConstraints = NO; 50 | _infoTextView.contentInset = UIEdgeInsetsZero; 51 | _infoTextView.textContainerInset = UIEdgeInsetsZero; 52 | _infoTextView.showsHorizontalScrollIndicator = YES; 53 | [self.view addSubview:_infoTextView]; 54 | 55 | // TagView Layout 56 | NSArray *hConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-20-[tagView]-20-|" 57 | options:(NSLayoutFormatOptions) 0 metrics:nil 58 | views:@{@"tagView": _tagView}]; 59 | NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:_tagView 60 | attribute:NSLayoutAttributeTop 61 | relatedBy:NSLayoutRelationEqual 62 | toItem:self.topLayoutGuide 63 | attribute:NSLayoutAttributeBottom 64 | multiplier:1 constant:20]; 65 | [self.view addConstraint:topConstraint]; 66 | [self.view addConstraints:hConstraints]; 67 | 68 | // Info textView layout 69 | hConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-20-[infoView]-20-|" 70 | options:(NSLayoutFormatOptions) 0 metrics:nil 71 | views:@{@"infoView": _infoTextView}]; 72 | topConstraint = [NSLayoutConstraint constraintWithItem:_infoTextView 73 | attribute:NSLayoutAttributeTop 74 | relatedBy:NSLayoutRelationEqual 75 | toItem:_tagView 76 | attribute:NSLayoutAttributeBottom 77 | multiplier:1 constant:20]; 78 | NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:_infoTextView 79 | attribute:NSLayoutAttributeBottom 80 | relatedBy:NSLayoutRelationEqual 81 | toItem:self.view 82 | attribute:NSLayoutAttributeBottom 83 | multiplier:1 constant:-20]; 84 | [self.view addConstraint:topConstraint]; 85 | [self.view addConstraints:hConstraints]; 86 | [self.view addConstraint:bottomConstraint]; 87 | 88 | TTGTextTagStringContent *defaultContent = [TTGTextTagStringContent new]; 89 | 90 | TTGTextTagStyle *defaultStyle = [TTGTextTagStyle new]; 91 | defaultStyle.backgroundColor = [UIColor colorWithRed:0.24 green:0.72 blue:0.94 alpha:1.00]; 92 | defaultStyle.borderColor = [UIColor whiteColor]; 93 | defaultStyle.borderWidth = 1; 94 | defaultStyle.cornerRadius = 4; 95 | defaultStyle.extraSpace = CGSizeMake(8, 8); 96 | defaultStyle.shadowColor = [UIColor blackColor]; 97 | defaultStyle.shadowOpacity = 0.3; 98 | defaultStyle.shadowRadius = 2; 99 | defaultStyle.shadowOffset = CGSizeMake(1, 1); 100 | 101 | // Bind CustomTagData 102 | CustomTagData *customTagData = [CustomTagData new]; 103 | customTagData.info = @"I am TTGTag custom data."; 104 | 105 | TTGTextTag *tag1 = [TTGTextTag new]; 106 | tag1.attachment = customTagData; 107 | tag1.style = defaultStyle; 108 | tag1.content = defaultContent; 109 | ((TTGTextTagStringContent *)tag1.content).text = @"Bind CustomTagData"; 110 | [_tagView addTag:tag1]; 111 | 112 | // Bind NSDictionary 113 | NSDictionary *dict = @{@"info": @"I am TTGTag NSDictionary data"}; 114 | 115 | TTGTextTag *tag2 = [TTGTextTag new]; 116 | tag2.attachment = dict; 117 | tag2.style = defaultStyle; 118 | tag2.content = defaultContent; 119 | ((TTGTextTagStringContent *)tag2.content).text = @"Bind NSDictionary"; 120 | [_tagView addTag:tag2]; 121 | 122 | // Bind String1 123 | TTGTextTag *tag3 = [TTGTextTag new]; 124 | tag3.attachment = @"String1"; 125 | tag3.style = defaultStyle; 126 | tag3.content = defaultContent; 127 | ((TTGTextTagStringContent *)tag3.content).text = @"Bind String1"; 128 | [_tagView addTag:tag3]; 129 | 130 | // Bind String2 131 | TTGTextTag *tag4 = [TTGTextTag new]; 132 | tag4.attachment = @"String2"; 133 | tag4.style = defaultStyle; 134 | tag4.content = defaultContent; 135 | ((TTGTextTagStringContent *)tag4.content).text = @"Bind String2"; 136 | [_tagView addTag:tag4]; 137 | 138 | // Reload 139 | [_tagView reload]; 140 | 141 | // Set delegate 142 | _tagView.delegate = self; 143 | } 144 | 145 | #pragma mark - TTGTextTagCollectionViewDelegate 146 | 147 | - (void)textTagCollectionView:(TTGTextTagCollectionView *)textTagCollectionView 148 | didTapTag:(TTGTextTag *)tag 149 | atIndex:(NSUInteger)index { 150 | _infoTextView.text = [NSString stringWithFormat:@"%@Did Tap:\n%@\n\n", _infoTextView.text, tag.attachment]; 151 | } 152 | 153 | @end 154 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGExample9ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TTGExample9ViewController.h 3 | // TTGTagCollectionView_Example 4 | // 5 | // Created by zekunyan on 2021/4/16. 6 | // Copyright © 2021 zekunyan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TTGExample9ViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGExample9ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TTGExample9ViewController.m 3 | // TTGTagCollectionView_Example 4 | // 5 | // Created by zekunyan on 2021/4/16. 6 | // Copyright © 2021 zekunyan. All rights reserved. 7 | // 8 | 9 | #import "TTGExample9ViewController.h" 10 | #import 11 | 12 | @interface TTGExample9ViewController () 13 | @property (weak, nonatomic) IBOutlet TTGTextTagCollectionView *textTag; 14 | @end 15 | 16 | @implementation TTGExample9ViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | 21 | // Tag1 22 | NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@"Auto Layout dynamically "]; 23 | 24 | NSUInteger location = 0; 25 | NSUInteger length = 8; 26 | 27 | [attributedString addAttributes:@{ 28 | NSFontAttributeName: [UIFont boldSystemFontOfSize:14], 29 | NSForegroundColorAttributeName: [UIColor blueColor] 30 | } range:NSMakeRange(location, length)]; 31 | 32 | location += length; 33 | [attributedString addAttributes:@{ 34 | NSFontAttributeName: [UIFont boldSystemFontOfSize:20], 35 | NSForegroundColorAttributeName: [UIColor greenColor], 36 | NSBackgroundColorAttributeName: [UIColor blackColor], 37 | NSStrikethroughStyleAttributeName: @(1) 38 | } range:NSMakeRange(location, length)]; 39 | 40 | location += length; 41 | [attributedString addAttributes:@{ 42 | NSFontAttributeName: [UIFont italicSystemFontOfSize:16], 43 | NSForegroundColorAttributeName: [UIColor redColor], 44 | NSBackgroundColorAttributeName: [UIColor yellowColor] 45 | } range:NSMakeRange(location, length)]; 46 | 47 | TTGTextTag *tag = [TTGTextTag new]; 48 | tag.content = [TTGTextTagAttributedStringContent contentWithAttributedText:attributedString]; 49 | tag.style = [TTGTextTagStyle new]; 50 | [_textTag addTag:tag]; 51 | 52 | // Tag2 53 | attributedString = [[NSMutableAttributedString alloc] initWithString:@" calculates the size "]; 54 | 55 | location = 0; 56 | [attributedString addAttributes:@{ 57 | NSFontAttributeName: [UIFont boldSystemFontOfSize:32], 58 | NSForegroundColorAttributeName: [UIColor orangeColor], 59 | NSBackgroundColorAttributeName: [UIColor darkGrayColor], 60 | NSStrikethroughStyleAttributeName: @(1), 61 | NSStrikethroughColorAttributeName: [UIColor redColor] 62 | } range:NSMakeRange(location, length)]; 63 | 64 | location += length; 65 | [attributedString addAttributes:@{ 66 | NSFontAttributeName: [UIFont italicSystemFontOfSize:12], 67 | NSForegroundColorAttributeName: [UIColor purpleColor], 68 | NSBackgroundColorAttributeName: [UIColor whiteColor], 69 | NSUnderlineStyleAttributeName: @(1), 70 | NSUnderlineColorAttributeName: [UIColor systemPinkColor] 71 | } range:NSMakeRange(location, length)]; 72 | 73 | tag = [TTGTextTag new]; 74 | tag.content = [TTGTextTagAttributedStringContent contentWithAttributedText:attributedString]; 75 | tag.style = [TTGTextTagStyle new]; 76 | [_textTag addTag:tag]; 77 | 78 | [_textTag reload]; 79 | } 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGTagCollectionView-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | Launch Screen 29 | UIMainStoryboardFile 30 | Main 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/TTGTagCollectionView-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | @import UIKit; 15 | @import Foundation; 16 | #endif 17 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/TTGTagCollectionView/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 12/11/2015. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | #import "TTGAppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) 13 | { 14 | @autoreleasepool { 15 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([TTGAppDelegate class])); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Example/Tests/Tests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/Tests/Tests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // The contents of this file are implicitly included at the beginning of every test case source file. 2 | 3 | #ifdef __OBJC__ 4 | 5 | 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /Example/Tests/Tests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TTGTagCollectionViewTests.m 3 | // TTGTagCollectionViewTests 4 | // 5 | // Created by zekunyan on 12/11/2015. 6 | // Copyright (c) 2019 zekunyan. All rights reserved. 7 | // 8 | 9 | @import XCTest; 10 | #import 11 | 12 | @interface Tests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation Tests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | } 21 | 22 | - (void)tearDown { 23 | [super tearDown]; 24 | } 25 | 26 | - (TTGTextTagCollectionView *)getTextCaseTextTagView { 27 | TTGTextTagCollectionView *textTagView = [TTGTextTagCollectionView new]; 28 | 29 | for (NSInteger i = 1; i < 10; i++) { 30 | [textTagView addTag:@(i).description]; 31 | } 32 | 33 | return textTagView; 34 | } 35 | 36 | - (void)testAddTagAndGetTag { 37 | TTGTextTagCollectionView *textTagView = [TTGTextTagCollectionView new]; 38 | 39 | // addTag: 40 | [textTagView addTag:@"1"]; 41 | [textTagView addTag:@"2"]; 42 | 43 | // addTags: 44 | [textTagView addTags:@[@"3", @"4"]]; 45 | [textTagView addTags:@[@"5", @"6"]]; 46 | 47 | // addTag:withConfig 48 | TTGTextTag *config = [TTGTextTag new]; 49 | config.textFont = [UIFont systemFontOfSize:14]; 50 | [textTagView addTag:@"7" withConfig:config]; 51 | 52 | // addTags:withConfig: 53 | config.textFont = [UIFont systemFontOfSize:16]; 54 | [textTagView addTags:@[@"8", @"9"] withConfig:config]; 55 | 56 | // Check 57 | XCTAssert([textTagView allTags].count == 9); 58 | XCTAssert([textTagView allNotSelectedTags].count == 9); 59 | XCTAssert([textTagView allSelectedTags].count == 0); 60 | 61 | XCTAssert([[textTagView getTagAtIndex:1] isEqualToString:@"2"]); 62 | XCTAssert([[textTagView getTagAtIndex:4] isEqualToString:@"5"]); 63 | XCTAssert([textTagView getTagAtIndex:100] == nil); 64 | XCTAssert([textTagView getTagAtIndex:-1] == nil); 65 | 66 | NSArray *tags = [textTagView getTagsInRange:NSMakeRange(1, 2)]; 67 | XCTAssert(tags.count == 2); 68 | XCTAssert([tags[0] isEqualToString:@"2"]); 69 | XCTAssert([tags[1] isEqualToString:@"3"]); 70 | 71 | tags = [textTagView getTagsInRange:NSMakeRange(100, 100)]; 72 | XCTAssert(tags == nil); 73 | 74 | config = [textTagView getConfigAtIndex:6]; 75 | XCTAssert(config.textFont.pointSize == 14); 76 | 77 | config = [textTagView getConfigAtIndex:100]; 78 | XCTAssert(config == nil); 79 | 80 | NSArray *configs = [textTagView getConfigsInRange:NSMakeRange(7, 2)]; 81 | XCTAssert(configs.count == 2); 82 | XCTAssert(configs[0].textFont.pointSize == 16); 83 | XCTAssert(configs[1].textFont.pointSize == 16); 84 | 85 | configs = [textTagView getConfigsInRange:NSMakeRange(100, 100)]; 86 | XCTAssert(configs == nil); 87 | } 88 | 89 | - (void)testInsertTag { 90 | TTGTextTagCollectionView *textTagView = [self getTextCaseTextTagView]; 91 | XCTAssert([textTagView allTags].count == 9); 92 | 93 | [textTagView insertTag:@"10" atIndex:0]; 94 | XCTAssert([[textTagView getTagAtIndex:0] isEqualToString:@"10"]); 95 | 96 | [textTagView insertTag:@"11" atIndex:100]; 97 | [textTagView insertTag:@"11" atIndex:-1]; 98 | XCTAssert([textTagView allTags].count == 10); 99 | 100 | [textTagView insertTag:@"12" atIndex:[textTagView allTags].count]; 101 | XCTAssert([[textTagView getTagAtIndex:textTagView.allTags.count - 1] isEqualToString:@"12"]); 102 | 103 | textTagView = [self getTextCaseTextTagView]; 104 | [textTagView insertTags:@[@"13", @"14"] atIndex:2]; 105 | XCTAssert([textTagView allTags].count == 11); 106 | XCTAssert([[textTagView getTagAtIndex:2] isEqualToString:@"13"]); 107 | XCTAssert([[textTagView getTagAtIndex:3] isEqualToString:@"14"]); 108 | XCTAssert([[textTagView getTagAtIndex:4] isEqualToString:@"3"]); 109 | 110 | TTGTextTag *config = [TTGTextTag new]; 111 | config.textFont = [UIFont systemFontOfSize:32]; 112 | textTagView = [self getTextCaseTextTagView]; 113 | [textTagView insertTag:@"15" atIndex:6 withConfig:config]; 114 | XCTAssert([textTagView allTags].count == 10); 115 | XCTAssert([textTagView getConfigAtIndex:6].textFont.pointSize == 32); 116 | 117 | textTagView = [self getTextCaseTextTagView]; 118 | config.textFont = [UIFont systemFontOfSize:24]; 119 | [textTagView insertTags:@[@"16", @"17"] atIndex:5 withConfig:config]; 120 | XCTAssert([textTagView getConfigAtIndex:5].textFont.pointSize == [textTagView getConfigAtIndex:6].textFont.pointSize); 121 | } 122 | 123 | - (void)testUpdateTag { 124 | TTGTextTagCollectionView *textTagView = [self getTextCaseTextTagView]; 125 | XCTAssert([textTagView allTags].count == 9); 126 | 127 | [textTagView setTagAtIndex:2 selected:YES]; 128 | XCTAssert([textTagView allSelectedTags].count == 1); 129 | XCTAssert([[textTagView allSelectedTags].lastObject isEqualToString:@"3"]); 130 | 131 | TTGTextTag *config = [TTGTextTag new]; 132 | config.textFont = [UIFont systemFontOfSize:40]; 133 | [textTagView setTagAtIndex:2 withConfig:config]; 134 | XCTAssert([textTagView getConfigAtIndex:2].textFont.pointSize == 40); 135 | 136 | config.textFont = [UIFont systemFontOfSize:10]; 137 | [textTagView setTagsInRange:NSMakeRange(4, 2) withConfig:config]; 138 | XCTAssert([textTagView getConfigAtIndex:4].textFont.pointSize == 10); 139 | XCTAssert([textTagView getConfigAtIndex:5].textFont.pointSize == 10); 140 | } 141 | 142 | - (void)testRemoveTag { 143 | TTGTextTagCollectionView *textTagView = [self getTextCaseTextTagView]; 144 | XCTAssert([textTagView allTags].count == 9); 145 | 146 | [textTagView addTag:@"1"]; 147 | [textTagView addTag:@"1"]; 148 | [textTagView addTag:@"1"]; 149 | [textTagView addTag:@"1"]; 150 | XCTAssert([textTagView allTags].count == 13); 151 | 152 | [textTagView removeTag:@"1"]; 153 | XCTAssert([textTagView allTags].count == 8); 154 | 155 | [textTagView removeTagAtIndex:-1]; 156 | [textTagView removeTagAtIndex:100]; 157 | [textTagView removeTag:@"haha"]; 158 | XCTAssert([textTagView allTags].count == 8); 159 | 160 | [textTagView removeTagAtIndex:2]; 161 | XCTAssert([textTagView allTags].count == 7); 162 | XCTAssert([[textTagView getTagAtIndex:2] isEqualToString:@"5"]); 163 | 164 | [textTagView removeAllTags]; 165 | XCTAssert([textTagView allTags].count == [textTagView allSelectedTags].count); 166 | } 167 | 168 | @end 169 | 170 | -------------------------------------------------------------------------------- /Example/Tests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /ExampleSwift/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '11.0' 2 | 3 | target 'TTGTagSwiftExample' do 4 | use_frameworks! 5 | pod 'TTGTagCollectionView', :path => '../' 6 | end 7 | -------------------------------------------------------------------------------- /ExampleSwift/TTGTagSwiftExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | BF6A36C5263BFF81000CB844 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6A36C4263BFF81000CB844 /* AppDelegate.swift */; }; 11 | BF6A36C7263BFF81000CB844 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6A36C6263BFF81000CB844 /* SceneDelegate.swift */; }; 12 | BF6A36C9263BFF81000CB844 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6A36C8263BFF81000CB844 /* ViewController.swift */; }; 13 | BF6A36CC263BFF81000CB844 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF6A36CA263BFF81000CB844 /* Main.storyboard */; }; 14 | BF6A36CE263BFF84000CB844 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BF6A36CD263BFF84000CB844 /* Assets.xcassets */; }; 15 | BF6A36D1263BFF84000CB844 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF6A36CF263BFF84000CB844 /* LaunchScreen.storyboard */; }; 16 | C63AADBAC89AF2627CDBE1E0 /* Pods_TTGTagSwiftExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F9ABBC9733CA17B850DB303 /* Pods_TTGTagSwiftExample.framework */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 18FE1A9005B5D818D3FB6510 /* Pods-TTGTagSwiftExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TTGTagSwiftExample.debug.xcconfig"; path = "Target Support Files/Pods-TTGTagSwiftExample/Pods-TTGTagSwiftExample.debug.xcconfig"; sourceTree = ""; }; 21 | 6F9ABBC9733CA17B850DB303 /* Pods_TTGTagSwiftExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TTGTagSwiftExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | BB73C78F0F625132A98AF918 /* Pods-TTGTagSwiftExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TTGTagSwiftExample.release.xcconfig"; path = "Target Support Files/Pods-TTGTagSwiftExample/Pods-TTGTagSwiftExample.release.xcconfig"; sourceTree = ""; }; 23 | BF6A36C1263BFF81000CB844 /* TTGTagSwiftExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TTGTagSwiftExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 24 | BF6A36C4263BFF81000CB844 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 25 | BF6A36C6263BFF81000CB844 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 26 | BF6A36C8263BFF81000CB844 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 27 | BF6A36CB263BFF81000CB844 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 28 | BF6A36CD263BFF84000CB844 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 29 | BF6A36D0263BFF84000CB844 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 30 | BF6A36D2263BFF84000CB844 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | BF6A36BE263BFF81000CB844 /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | C63AADBAC89AF2627CDBE1E0 /* Pods_TTGTagSwiftExample.framework in Frameworks */, 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXFrameworksBuildPhase section */ 43 | 44 | /* Begin PBXGroup section */ 45 | 00DBBB0F1645DB9D63D3DA88 /* Frameworks */ = { 46 | isa = PBXGroup; 47 | children = ( 48 | 6F9ABBC9733CA17B850DB303 /* Pods_TTGTagSwiftExample.framework */, 49 | ); 50 | name = Frameworks; 51 | sourceTree = ""; 52 | }; 53 | BF6A36B8263BFF80000CB844 = { 54 | isa = PBXGroup; 55 | children = ( 56 | BF6A36C3263BFF81000CB844 /* TTGTagSwiftExample */, 57 | BF6A36C2263BFF81000CB844 /* Products */, 58 | FD59D0F953286C8B13FAE4EA /* Pods */, 59 | 00DBBB0F1645DB9D63D3DA88 /* Frameworks */, 60 | ); 61 | sourceTree = ""; 62 | }; 63 | BF6A36C2263BFF81000CB844 /* Products */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | BF6A36C1263BFF81000CB844 /* TTGTagSwiftExample.app */, 67 | ); 68 | name = Products; 69 | sourceTree = ""; 70 | }; 71 | BF6A36C3263BFF81000CB844 /* TTGTagSwiftExample */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | BF6A36C4263BFF81000CB844 /* AppDelegate.swift */, 75 | BF6A36C6263BFF81000CB844 /* SceneDelegate.swift */, 76 | BF6A36C8263BFF81000CB844 /* ViewController.swift */, 77 | BF6A36CA263BFF81000CB844 /* Main.storyboard */, 78 | BF6A36CD263BFF84000CB844 /* Assets.xcassets */, 79 | BF6A36CF263BFF84000CB844 /* LaunchScreen.storyboard */, 80 | BF6A36D2263BFF84000CB844 /* Info.plist */, 81 | ); 82 | path = TTGTagSwiftExample; 83 | sourceTree = ""; 84 | }; 85 | FD59D0F953286C8B13FAE4EA /* Pods */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 18FE1A9005B5D818D3FB6510 /* Pods-TTGTagSwiftExample.debug.xcconfig */, 89 | BB73C78F0F625132A98AF918 /* Pods-TTGTagSwiftExample.release.xcconfig */, 90 | ); 91 | name = Pods; 92 | path = Pods; 93 | sourceTree = ""; 94 | }; 95 | /* End PBXGroup section */ 96 | 97 | /* Begin PBXNativeTarget section */ 98 | BF6A36C0263BFF81000CB844 /* TTGTagSwiftExample */ = { 99 | isa = PBXNativeTarget; 100 | buildConfigurationList = BF6A36D5263BFF84000CB844 /* Build configuration list for PBXNativeTarget "TTGTagSwiftExample" */; 101 | buildPhases = ( 102 | 3D80F5748BCA2BA0E353F305 /* [CP] Check Pods Manifest.lock */, 103 | BF6A36BD263BFF81000CB844 /* Sources */, 104 | BF6A36BE263BFF81000CB844 /* Frameworks */, 105 | BF6A36BF263BFF81000CB844 /* Resources */, 106 | 54F7AD8D4F62063FD18B9BE6 /* [CP] Embed Pods Frameworks */, 107 | ); 108 | buildRules = ( 109 | ); 110 | dependencies = ( 111 | ); 112 | name = TTGTagSwiftExample; 113 | productName = TTGTagSwiftExample; 114 | productReference = BF6A36C1263BFF81000CB844 /* TTGTagSwiftExample.app */; 115 | productType = "com.apple.product-type.application"; 116 | }; 117 | /* End PBXNativeTarget section */ 118 | 119 | /* Begin PBXProject section */ 120 | BF6A36B9263BFF80000CB844 /* Project object */ = { 121 | isa = PBXProject; 122 | attributes = { 123 | LastSwiftUpdateCheck = 1250; 124 | LastUpgradeCheck = 1250; 125 | TargetAttributes = { 126 | BF6A36C0263BFF81000CB844 = { 127 | CreatedOnToolsVersion = 12.5; 128 | }; 129 | }; 130 | }; 131 | buildConfigurationList = BF6A36BC263BFF80000CB844 /* Build configuration list for PBXProject "TTGTagSwiftExample" */; 132 | compatibilityVersion = "Xcode 9.3"; 133 | developmentRegion = en; 134 | hasScannedForEncodings = 0; 135 | knownRegions = ( 136 | en, 137 | Base, 138 | ); 139 | mainGroup = BF6A36B8263BFF80000CB844; 140 | productRefGroup = BF6A36C2263BFF81000CB844 /* Products */; 141 | projectDirPath = ""; 142 | projectRoot = ""; 143 | targets = ( 144 | BF6A36C0263BFF81000CB844 /* TTGTagSwiftExample */, 145 | ); 146 | }; 147 | /* End PBXProject section */ 148 | 149 | /* Begin PBXResourcesBuildPhase section */ 150 | BF6A36BF263BFF81000CB844 /* Resources */ = { 151 | isa = PBXResourcesBuildPhase; 152 | buildActionMask = 2147483647; 153 | files = ( 154 | BF6A36D1263BFF84000CB844 /* LaunchScreen.storyboard in Resources */, 155 | BF6A36CE263BFF84000CB844 /* Assets.xcassets in Resources */, 156 | BF6A36CC263BFF81000CB844 /* Main.storyboard in Resources */, 157 | ); 158 | runOnlyForDeploymentPostprocessing = 0; 159 | }; 160 | /* End PBXResourcesBuildPhase section */ 161 | 162 | /* Begin PBXShellScriptBuildPhase section */ 163 | 3D80F5748BCA2BA0E353F305 /* [CP] Check Pods Manifest.lock */ = { 164 | isa = PBXShellScriptBuildPhase; 165 | buildActionMask = 2147483647; 166 | files = ( 167 | ); 168 | inputFileListPaths = ( 169 | ); 170 | inputPaths = ( 171 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 172 | "${PODS_ROOT}/Manifest.lock", 173 | ); 174 | name = "[CP] Check Pods Manifest.lock"; 175 | outputFileListPaths = ( 176 | ); 177 | outputPaths = ( 178 | "$(DERIVED_FILE_DIR)/Pods-TTGTagSwiftExample-checkManifestLockResult.txt", 179 | ); 180 | runOnlyForDeploymentPostprocessing = 0; 181 | shellPath = /bin/sh; 182 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 183 | showEnvVarsInLog = 0; 184 | }; 185 | 54F7AD8D4F62063FD18B9BE6 /* [CP] Embed Pods Frameworks */ = { 186 | isa = PBXShellScriptBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | ); 190 | inputFileListPaths = ( 191 | "${PODS_ROOT}/Target Support Files/Pods-TTGTagSwiftExample/Pods-TTGTagSwiftExample-frameworks-${CONFIGURATION}-input-files.xcfilelist", 192 | ); 193 | name = "[CP] Embed Pods Frameworks"; 194 | outputFileListPaths = ( 195 | "${PODS_ROOT}/Target Support Files/Pods-TTGTagSwiftExample/Pods-TTGTagSwiftExample-frameworks-${CONFIGURATION}-output-files.xcfilelist", 196 | ); 197 | runOnlyForDeploymentPostprocessing = 0; 198 | shellPath = /bin/sh; 199 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-TTGTagSwiftExample/Pods-TTGTagSwiftExample-frameworks.sh\"\n"; 200 | showEnvVarsInLog = 0; 201 | }; 202 | /* End PBXShellScriptBuildPhase section */ 203 | 204 | /* Begin PBXSourcesBuildPhase section */ 205 | BF6A36BD263BFF81000CB844 /* Sources */ = { 206 | isa = PBXSourcesBuildPhase; 207 | buildActionMask = 2147483647; 208 | files = ( 209 | BF6A36C9263BFF81000CB844 /* ViewController.swift in Sources */, 210 | BF6A36C5263BFF81000CB844 /* AppDelegate.swift in Sources */, 211 | BF6A36C7263BFF81000CB844 /* SceneDelegate.swift in Sources */, 212 | ); 213 | runOnlyForDeploymentPostprocessing = 0; 214 | }; 215 | /* End PBXSourcesBuildPhase section */ 216 | 217 | /* Begin PBXVariantGroup section */ 218 | BF6A36CA263BFF81000CB844 /* Main.storyboard */ = { 219 | isa = PBXVariantGroup; 220 | children = ( 221 | BF6A36CB263BFF81000CB844 /* Base */, 222 | ); 223 | name = Main.storyboard; 224 | sourceTree = ""; 225 | }; 226 | BF6A36CF263BFF84000CB844 /* LaunchScreen.storyboard */ = { 227 | isa = PBXVariantGroup; 228 | children = ( 229 | BF6A36D0263BFF84000CB844 /* Base */, 230 | ); 231 | name = LaunchScreen.storyboard; 232 | sourceTree = ""; 233 | }; 234 | /* End PBXVariantGroup section */ 235 | 236 | /* Begin XCBuildConfiguration section */ 237 | BF6A36D3263BFF84000CB844 /* Debug */ = { 238 | isa = XCBuildConfiguration; 239 | buildSettings = { 240 | ALWAYS_SEARCH_USER_PATHS = NO; 241 | CLANG_ANALYZER_NONNULL = YES; 242 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 243 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 244 | CLANG_CXX_LIBRARY = "libc++"; 245 | CLANG_ENABLE_MODULES = YES; 246 | CLANG_ENABLE_OBJC_ARC = YES; 247 | CLANG_ENABLE_OBJC_WEAK = YES; 248 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 249 | CLANG_WARN_BOOL_CONVERSION = YES; 250 | CLANG_WARN_COMMA = YES; 251 | CLANG_WARN_CONSTANT_CONVERSION = YES; 252 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 253 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 254 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 255 | CLANG_WARN_EMPTY_BODY = YES; 256 | CLANG_WARN_ENUM_CONVERSION = YES; 257 | CLANG_WARN_INFINITE_RECURSION = YES; 258 | CLANG_WARN_INT_CONVERSION = YES; 259 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 260 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 261 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 262 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 263 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 264 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 265 | CLANG_WARN_STRICT_PROTOTYPES = YES; 266 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 267 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 268 | CLANG_WARN_UNREACHABLE_CODE = YES; 269 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 270 | COPY_PHASE_STRIP = NO; 271 | DEBUG_INFORMATION_FORMAT = dwarf; 272 | ENABLE_STRICT_OBJC_MSGSEND = YES; 273 | ENABLE_TESTABILITY = YES; 274 | GCC_C_LANGUAGE_STANDARD = gnu11; 275 | GCC_DYNAMIC_NO_PIC = NO; 276 | GCC_NO_COMMON_BLOCKS = YES; 277 | GCC_OPTIMIZATION_LEVEL = 0; 278 | GCC_PREPROCESSOR_DEFINITIONS = ( 279 | "DEBUG=1", 280 | "$(inherited)", 281 | ); 282 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 283 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 284 | GCC_WARN_UNDECLARED_SELECTOR = YES; 285 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 286 | GCC_WARN_UNUSED_FUNCTION = YES; 287 | GCC_WARN_UNUSED_VARIABLE = YES; 288 | IPHONEOS_DEPLOYMENT_TARGET = 14.5; 289 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 290 | MTL_FAST_MATH = YES; 291 | ONLY_ACTIVE_ARCH = YES; 292 | SDKROOT = iphoneos; 293 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 294 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 295 | }; 296 | name = Debug; 297 | }; 298 | BF6A36D4263BFF84000CB844 /* Release */ = { 299 | isa = XCBuildConfiguration; 300 | buildSettings = { 301 | ALWAYS_SEARCH_USER_PATHS = NO; 302 | CLANG_ANALYZER_NONNULL = YES; 303 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 304 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 305 | CLANG_CXX_LIBRARY = "libc++"; 306 | CLANG_ENABLE_MODULES = YES; 307 | CLANG_ENABLE_OBJC_ARC = YES; 308 | CLANG_ENABLE_OBJC_WEAK = YES; 309 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 310 | CLANG_WARN_BOOL_CONVERSION = YES; 311 | CLANG_WARN_COMMA = YES; 312 | CLANG_WARN_CONSTANT_CONVERSION = YES; 313 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 314 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 315 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 316 | CLANG_WARN_EMPTY_BODY = YES; 317 | CLANG_WARN_ENUM_CONVERSION = YES; 318 | CLANG_WARN_INFINITE_RECURSION = YES; 319 | CLANG_WARN_INT_CONVERSION = YES; 320 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 321 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 322 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 323 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 324 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 325 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 326 | CLANG_WARN_STRICT_PROTOTYPES = YES; 327 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 328 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 329 | CLANG_WARN_UNREACHABLE_CODE = YES; 330 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 331 | COPY_PHASE_STRIP = NO; 332 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 333 | ENABLE_NS_ASSERTIONS = NO; 334 | ENABLE_STRICT_OBJC_MSGSEND = YES; 335 | GCC_C_LANGUAGE_STANDARD = gnu11; 336 | GCC_NO_COMMON_BLOCKS = YES; 337 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 338 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 339 | GCC_WARN_UNDECLARED_SELECTOR = YES; 340 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 341 | GCC_WARN_UNUSED_FUNCTION = YES; 342 | GCC_WARN_UNUSED_VARIABLE = YES; 343 | IPHONEOS_DEPLOYMENT_TARGET = 14.5; 344 | MTL_ENABLE_DEBUG_INFO = NO; 345 | MTL_FAST_MATH = YES; 346 | SDKROOT = iphoneos; 347 | SWIFT_COMPILATION_MODE = wholemodule; 348 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 349 | VALIDATE_PRODUCT = YES; 350 | }; 351 | name = Release; 352 | }; 353 | BF6A36D6263BFF84000CB844 /* Debug */ = { 354 | isa = XCBuildConfiguration; 355 | baseConfigurationReference = 18FE1A9005B5D818D3FB6510 /* Pods-TTGTagSwiftExample.debug.xcconfig */; 356 | buildSettings = { 357 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 358 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 359 | CODE_SIGN_STYLE = Automatic; 360 | INFOPLIST_FILE = TTGTagSwiftExample/Info.plist; 361 | LD_RUNPATH_SEARCH_PATHS = ( 362 | "$(inherited)", 363 | "@executable_path/Frameworks", 364 | ); 365 | PRODUCT_BUNDLE_IDENTIFIER = me.tutuge.swift.TTGTagSwiftExample; 366 | PRODUCT_NAME = "$(TARGET_NAME)"; 367 | SWIFT_VERSION = 5.0; 368 | TARGETED_DEVICE_FAMILY = "1,2"; 369 | }; 370 | name = Debug; 371 | }; 372 | BF6A36D7263BFF84000CB844 /* Release */ = { 373 | isa = XCBuildConfiguration; 374 | baseConfigurationReference = BB73C78F0F625132A98AF918 /* Pods-TTGTagSwiftExample.release.xcconfig */; 375 | buildSettings = { 376 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 377 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 378 | CODE_SIGN_STYLE = Automatic; 379 | INFOPLIST_FILE = TTGTagSwiftExample/Info.plist; 380 | LD_RUNPATH_SEARCH_PATHS = ( 381 | "$(inherited)", 382 | "@executable_path/Frameworks", 383 | ); 384 | PRODUCT_BUNDLE_IDENTIFIER = me.tutuge.swift.TTGTagSwiftExample; 385 | PRODUCT_NAME = "$(TARGET_NAME)"; 386 | SWIFT_VERSION = 5.0; 387 | TARGETED_DEVICE_FAMILY = "1,2"; 388 | }; 389 | name = Release; 390 | }; 391 | /* End XCBuildConfiguration section */ 392 | 393 | /* Begin XCConfigurationList section */ 394 | BF6A36BC263BFF80000CB844 /* Build configuration list for PBXProject "TTGTagSwiftExample" */ = { 395 | isa = XCConfigurationList; 396 | buildConfigurations = ( 397 | BF6A36D3263BFF84000CB844 /* Debug */, 398 | BF6A36D4263BFF84000CB844 /* Release */, 399 | ); 400 | defaultConfigurationIsVisible = 0; 401 | defaultConfigurationName = Release; 402 | }; 403 | BF6A36D5263BFF84000CB844 /* Build configuration list for PBXNativeTarget "TTGTagSwiftExample" */ = { 404 | isa = XCConfigurationList; 405 | buildConfigurations = ( 406 | BF6A36D6263BFF84000CB844 /* Debug */, 407 | BF6A36D7263BFF84000CB844 /* Release */, 408 | ); 409 | defaultConfigurationIsVisible = 0; 410 | defaultConfigurationName = Release; 411 | }; 412 | /* End XCConfigurationList section */ 413 | }; 414 | rootObject = BF6A36B9263BFF80000CB844 /* Project object */; 415 | } 416 | -------------------------------------------------------------------------------- /ExampleSwift/TTGTagSwiftExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ExampleSwift/TTGTagSwiftExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ExampleSwift/TTGTagSwiftExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ExampleSwift/TTGTagSwiftExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ExampleSwift/TTGTagSwiftExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // TTGTagSwiftExample 4 | // 5 | // Created by zekunyan on 2021/4/30. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | // MARK: UISceneSession Lifecycle 21 | 22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 23 | // Called when a new scene session is being created. 24 | // Use this method to select a configuration to create the new scene with. 25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 26 | } 27 | 28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 29 | // Called when the user discards a scene session. 30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /ExampleSwift/TTGTagSwiftExample/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ExampleSwift/TTGTagSwiftExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /ExampleSwift/TTGTagSwiftExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ExampleSwift/TTGTagSwiftExample/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 | -------------------------------------------------------------------------------- /ExampleSwift/TTGTagSwiftExample/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 | -------------------------------------------------------------------------------- /ExampleSwift/TTGTagSwiftExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UIApplicationSupportsIndirectInputEvents 43 | 44 | UILaunchStoryboardName 45 | LaunchScreen 46 | UIMainStoryboardFile 47 | Main 48 | UIRequiredDeviceCapabilities 49 | 50 | armv7 51 | 52 | UISupportedInterfaceOrientations 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationLandscapeLeft 56 | UIInterfaceOrientationLandscapeRight 57 | 58 | UISupportedInterfaceOrientations~ipad 59 | 60 | UIInterfaceOrientationPortrait 61 | UIInterfaceOrientationPortraitUpsideDown 62 | UIInterfaceOrientationLandscapeLeft 63 | UIInterfaceOrientationLandscapeRight 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /ExampleSwift/TTGTagSwiftExample/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // TTGTagSwiftExample 4 | // 5 | // Created by zekunyan on 2021/4/30. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let _ = (scene as? UIWindowScene) else { return } 20 | } 21 | 22 | func sceneDidDisconnect(_ scene: UIScene) { 23 | // Called as the scene is being released by the system. 24 | // This occurs shortly after the scene enters the background, or when its session is discarded. 25 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 26 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 27 | } 28 | 29 | func sceneDidBecomeActive(_ scene: UIScene) { 30 | // Called when the scene has moved from an inactive state to an active state. 31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 32 | } 33 | 34 | func sceneWillResignActive(_ scene: UIScene) { 35 | // Called when the scene will move from an active state to an inactive state. 36 | // This may occur due to temporary interruptions (ex. an incoming phone call). 37 | } 38 | 39 | func sceneWillEnterForeground(_ scene: UIScene) { 40 | // Called as the scene transitions from the background to the foreground. 41 | // Use this method to undo the changes made on entering the background. 42 | } 43 | 44 | func sceneDidEnterBackground(_ scene: UIScene) { 45 | // Called as the scene transitions from the foreground to the background. 46 | // Use this method to save data, release shared resources, and store enough scene-specific state information 47 | // to restore the scene back to its current state. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /ExampleSwift/TTGTagSwiftExample/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // TTGTagSwiftExample 4 | // 5 | // Created by zekunyan on 2021/4/30. 6 | // 7 | 8 | import UIKit 9 | import TTGTags 10 | 11 | class ViewController: UIViewController { 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | 15 | let tagView = TTGTextTagCollectionView.init(frame: CGRect.init(x: 16, y: 100, width: UIScreen.main.bounds.width - 32, height: 300)) 16 | tagView.backgroundColor = UIColor.lightGray 17 | self.view.addSubview(tagView) 18 | 19 | let strings = ["AutoLayout", "dynamically", "calculates", "the", "size", "and", "position", 20 | "of", "all", "the", "views", "in", "your", "view", "hierarchy", "based", 21 | "on", "constraints", "placed", "on", "those", "views"] 22 | 23 | for text in strings { 24 | let content = TTGTextTagStringContent.init(text: text) 25 | content.textColor = UIColor.black 26 | content.textFont = UIFont.boldSystemFont(ofSize: 20) 27 | 28 | let normalStyle = TTGTextTagStyle.init() 29 | normalStyle.backgroundColor = UIColor.white 30 | normalStyle.extraSpace = CGSize.init(width: 8, height: 8) 31 | 32 | let selectedStyle = TTGTextTagStyle.init() 33 | selectedStyle.backgroundColor = UIColor.green 34 | selectedStyle.extraSpace = CGSize.init(width: 8, height: 8) 35 | 36 | let tag = TTGTextTag.init() 37 | tag.content = content 38 | tag.style = normalStyle 39 | tag.selectedStyle = selectedStyle 40 | 41 | tagView.addTag(tag) 42 | } 43 | 44 | tagView.reload() 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 zekunyan 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "TTGTags", 7 | platforms: [.iOS(.v11)], 8 | products: [ 9 | .library( 10 | name: "TTGTags", 11 | targets: ["TTGTags"]), 12 | ], 13 | dependencies: [], 14 | targets: [ 15 | .target( 16 | name: "TTGTags", 17 | path: "Sources", 18 | publicHeadersPath: "" 19 | ), 20 | ] 21 | ) 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TTGTagCollectionView 2 | ✨Learn it by DeepWiki✨: [https://deepwiki.com/zekunyan/TTGTagCollectionView/1-overview](https://deepwiki.com/zekunyan/TTGTagCollectionView/1-overview) 3 | 4 | [![CI Status](http://img.shields.io/travis/zekunyan/TTGTagCollectionView.svg?style=flat)](https://travis-ci.org/zekunyan/TTGTagCollectionView) 5 | [![Version](https://img.shields.io/cocoapods/v/TTGTagCollectionView.svg?style=flat)](http://cocoapods.org/pods/TTGTagCollectionView) 6 | [![License](https://img.shields.io/cocoapods/l/TTGTagCollectionView.svg?style=flat)](http://cocoapods.org/pods/TTGTagCollectionView) 7 | [![Platform](https://img.shields.io/cocoapods/p/TTGTagCollectionView.svg?style=flat)](http://cocoapods.org/pods/TTGTagCollectionView) 8 | [![Apps Using](https://img.shields.io/badge/Apps%20Using-%3E%201,096-blue.svg)](https://github.com/zekunyan/TTGTagCollectionView) 9 | [![Total Download](https://img.shields.io/badge/Total%20Download-%3E%2068,461-blue.svg)](https://github.com/zekunyan/TTGTagCollectionView) 10 | 11 | ![Screenshot](https://github.com/zekunyan/TTGTagCollectionView/raw/master/Resources/screen_shot.png) 12 | 13 | ![Alignment Type](https://github.com/zekunyan/TTGTagCollectionView/raw/master/Resources/alignment_type.png) 14 | 15 | ## What 16 | 17 | `TTGTagCollectionView` is useful for showing different size tag views in a vertical or horizontal scrollable view. And if you only want to show text tags, you can use `TTGTextTagCollectionView` instead, which has more simple api. At the same time, It is highly customizable that many features of the text tag can be configured, like the tag font size and the background color. 18 | 19 | ## Features 20 | 21 | * Both rich style text tag and custom view tag supported. 22 | * Highly customizable, each text tag can be configured. 23 | * Vertical and horizontal scrollable. 24 | * Support `NSAttributedString` rich text tag. 25 | * Support different kinds of alignment types. 26 | * Support specifying number of lines. 27 | * Support Autolayout `intrinsicContentSize` to auto determine height based on content size. 28 | * Support pull to refresh, like `SVPullToRefresh`. 29 | * Use `preferredMaxLayoutWidth` to set available width like UIlabel. 30 | * Support `CocoaPods` and `Swift Package Manager` 31 | 32 | ## Demo 33 | 34 | You can find demos in the `Example->TTGTagCollectionView.xcworkspace` or `ExampleSwift->TTGTagSwiftExample.xcworkspace` project. 35 | Run `pod update` before try it. 36 | 37 | ![Example project](https://github.com/zekunyan/TTGTagCollectionView/raw/master/Resources/demo_example.jpeg) 38 | 39 | ## Code Structure 40 | 41 | ![Example project](https://github.com/zekunyan/TTGTagCollectionView/raw/master/Resources/code_structure.png) 42 | 43 | ## Requirements 44 | 45 | iOS 9 and later. 46 | 47 | ## Installation 48 | 49 | ### CocoaPods for Objective-C 50 | ```ruby 51 | pod "TTGTagCollectionView" 52 | ``` 53 | 54 | ### CocoaPods for Swift 55 | ```ruby 56 | use_frameworks! 57 | pod "TTGTagCollectionView" 58 | ``` 59 | 60 | ### Swift Package Manager 61 | Add by `git@github.com:zekunyan/TTGTagCollectionView.git` 62 | 63 | ## Usage 64 | 65 | ### TTGTextTagCollectionView 66 | 67 | Use `TTGTextTagCollectionView` to show text tags. 68 | 69 | #### Basic usage 70 | 71 | ##### Swift 72 | 73 | ```swift 74 | // import 75 | import TTGTags 76 | // Create TTGTextTagCollectionView view 77 | let tagView = TTGTextTagCollectionView.init(frame: CGRect(x: 20, y: 100, width: 200, height: 200)) 78 | self.view .addSubview(tagView) 79 | // Create TTGTextTag object 80 | let textTag = TTGTextTag(content: TTGTextTagStringContent(text: "tutuge"), style: TTGTextTagStyle()) 81 | // Add tag 82 | tagView.addTag(textTag) 83 | // !!! Never forget this !!! 84 | tagView.reload() 85 | ``` 86 | 87 | ##### Objective-C 88 | 89 | ```Objective-C 90 | // import 91 | #import 92 | // Create TTGTextTagCollectionView view 93 | TTGTextTagCollectionView *tagCollectionView = [[TTGTextTagCollectionView alloc] initWithFrame:CGRectMake(20, 20, 200, 200)]; 94 | [self.view addSubview:tagCollectionView]; 95 | // Create TTGTextTag object 96 | TTGTextTag *textTag = [TTGTextTag tagWithContent:[TTGTextTagStringContent contentWithText:@"Some text"] style:[TTGTextTagStyle new]]; 97 | // Add tag 98 | [tagCollectionView addTag:textTag]; 99 | // !!! Never forget this !!! 100 | [tagCollectionView reload]; 101 | ``` 102 | 103 | ##### Accessibility 104 | 105 | ```Objective-C 106 | // Auto set accessibilityLabel value 107 | tag.enableAutoDetectAccessibility = YES; 108 | 109 | // Manual 110 | tag.isAccessibilityElement = YES; 111 | tag.accessibilityLabel = text; 112 | tag.accessibilityIdentifier = [NSString stringWithFormat:@"identifier: %@", text]; 113 | tag.accessibilityHint = [NSString stringWithFormat:@"hint: %@", text]; 114 | tag.accessibilityValue = [NSString stringWithFormat:@"value: %@", text]; 115 | ``` 116 | 117 | #### Delegate 118 | 119 | Conform the `TTGTextTagCollectionViewDelegate` protocol to get callback when you select the tag or content height changes. 120 | 121 | ```Objective-C 122 | @protocol TTGTextTagCollectionViewDelegate 123 | @optional 124 | - (BOOL)textTagCollectionView:(TTGTextTagCollectionView *)textTagCollectionView 125 | canTapTag:(TTGTextTag *)tag 126 | atIndex:(NSUInteger)index; 127 | 128 | - (void)textTagCollectionView:(TTGTextTagCollectionView *)textTagCollectionView 129 | didTapTag:(TTGTextTag *)tag 130 | atIndex:(NSUInteger)index; 131 | 132 | - (void)textTagCollectionView:(TTGTextTagCollectionView *)textTagCollectionView 133 | updateContentSize:(CGSize)contentSize; 134 | @end 135 | ``` 136 | 137 | #### Customization 138 | 139 | Each tag can be configured. 140 | 141 | ```Objective-C 142 | @interface TTGTextTag : NSObject 143 | 144 | /// ID 145 | @property (nonatomic, assign, readonly) NSUInteger tagId; // Auto increase. The only identifier and main key for a tag 146 | 147 | /// Attachment object. You can use this to bind any object you want to each tag. 148 | @property (nonatomic, strong) id _Nullable attachment; 149 | 150 | /// Normal state content and style 151 | @property (nonatomic, copy) TTGTextTagContent * _Nonnull content; 152 | @property (nonatomic, copy) TTGTextTagStyle * _Nonnull style; 153 | 154 | /// Selected state content and style 155 | @property (nonatomic, copy) TTGTextTagContent * _Nullable selectedContent; 156 | @property (nonatomic, copy) TTGTextTagStyle * _Nullable selectedStyle; 157 | 158 | /// Selection state 159 | @property (nonatomic, assign) BOOL selected; 160 | @property (nonatomic, copy) OnSelectStateChanged _Nullable onSelectStateChanged; // State changed callback 161 | 162 | /// Accessibility 163 | @property (nonatomic, assign) BOOL isAccessibilityElement; // Default = NO 164 | @property (nonatomic, copy) NSString * _Nullable accessibilityIdentifier; // Default = nil 165 | @property (nonatomic, copy) NSString * _Nullable accessibilityLabel; // Default = nil 166 | @property (nonatomic, copy) NSString * _Nullable accessibilityHint; // Default = nil 167 | @property (nonatomic, copy) NSString * _Nullable accessibilityValue; // Default = nil 168 | @property (nonatomic, assign) UIAccessibilityTraits accessibilityTraits; // Default = UIAccessibilityTraitNone 169 | 170 | /// Auto detect accessibility 171 | /// When enableAutoDetectAccessibility = YES, the property below will be set automatically 172 | /// ---------------------------- 173 | /// isAccessibilityElement = YES 174 | /// accessibilityLabel = (selected ? selectedContent : content).getContentAttributedString.string 175 | /// accessibilityTraits = selected ? UIAccessibilityTraitSelected : UIAccessibilityTraitButton 176 | /// ---------------------------- 177 | /// But: accessibilityHint and accessibilityValue still keep your custom value; 178 | @property (nonatomic, assign) BOOL enableAutoDetectAccessibility; // Default = NO 179 | 180 | @end 181 | ``` 182 | 183 | `TTGTextTagContent` has two sub classes. 184 | 185 | ```Objective-C 186 | // Normal Text 187 | @interface TTGTextTagStringContent : TTGTextTagContent 188 | /// Text 189 | @property (nonatomic, copy) NSString * _Nonnull text; 190 | /// Text font 191 | @property (nonatomic, copy) UIFont * _Nonnull textFont; 192 | /// Text color 193 | @property (nonatomic, copy) UIColor * _Nonnull textColor; 194 | @end 195 | 196 | // NSAttributedString Text 197 | @interface TTGTextTagAttributedStringContent : TTGTextTagContent 198 | /// Attributed text 199 | @property (nonatomic, copy) NSAttributedString * _Nonnull attributedText; 200 | @end 201 | ``` 202 | 203 | Config `TTGTextTagStyle` if you want to change tag styles. 204 | 205 | ```Objective-C 206 | @interface TTGTextTagStyle : NSObject 207 | 208 | /// Background color 209 | @property (nonatomic, copy) UIColor * _Nonnull backgroundColor; // Default is [UIColor lightGrayColor] 210 | 211 | /// Text alignment 212 | @property (nonatomic, assign) NSTextAlignment textAlignment; // Default is NSTextAlignmentCenter 213 | 214 | /// Gradient background color 215 | @property (nonatomic, assign) BOOL enableGradientBackground; // Default is NO 216 | @property (nonatomic, copy) UIColor * _Nonnull gradientBackgroundStartColor; 217 | @property (nonatomic, copy) UIColor * _Nonnull gradientBackgroundEndColor; 218 | @property (nonatomic, assign) CGPoint gradientBackgroundStartPoint; 219 | @property (nonatomic, assign) CGPoint gradientBackgroundEndPoint; 220 | 221 | /// Corner radius 222 | @property (nonatomic, assign) CGFloat cornerRadius; // Default is 4 223 | @property (nonatomic, assign) Boolean cornerTopRight; 224 | @property (nonatomic, assign) Boolean cornerTopLeft; 225 | @property (nonatomic, assign) Boolean cornerBottomRight; 226 | @property (nonatomic, assign) Boolean cornerBottomLeft; 227 | 228 | /// Border 229 | @property (nonatomic, assign) CGFloat borderWidth; // Default is [UIColor whiteColor] 230 | @property (nonatomic, copy) UIColor * _Nonnull borderColor; // Default is 1 231 | 232 | /// Shadow. 233 | @property (nonatomic, copy) UIColor * _Nonnull shadowColor; // Default is [UIColor blackColor] 234 | @property (nonatomic, assign) CGSize shadowOffset; // Default is (2, 2) 235 | @property (nonatomic, assign) CGFloat shadowRadius; // Default is 2f 236 | @property (nonatomic, assign) CGFloat shadowOpacity; // Default is 0.3f 237 | 238 | /// Extra space in width and height, will expand each tag's size 239 | @property (nonatomic, assign) CGSize extraSpace; 240 | 241 | /// Max width for a text tag. 0 and below means no max width. 242 | @property (nonatomic, assign) CGFloat maxWidth; 243 | /// Min width for a text tag. 0 and below means no min width. 244 | @property (nonatomic, assign) CGFloat minWidth; 245 | 246 | /// Max height for a text tag. 0 and below means no max height. 247 | @property (nonatomic, assign) CGFloat maxHeight; 248 | /// Min height for a text tag. 0 and below means no min height. 249 | @property (nonatomic, assign) CGFloat minHeight; 250 | 251 | /// Exact width. 0 and below means no work 252 | @property (nonatomic, assign) CGFloat exactWidth; 253 | /// Exact height. 0 and below means no work 254 | @property (nonatomic, assign) CGFloat exactHeight; 255 | 256 | @end 257 | ``` 258 | 259 | You can also configure scroll direction, alignment, lines limit, spacing and inset. 260 | 261 | ```Objective-C 262 | // TTGTextTagCollectionView.h 263 | // Define if the tag can be selected. 264 | @property (assign, nonatomic) BOOL enableTagSelection; 265 | 266 | // Tags scroll direction, default is vertical. 267 | @property (nonatomic, assign) TTGTagCollectionScrollDirection scrollDirection; 268 | 269 | // Tags layout alignment, default is left. 270 | @property (nonatomic, assign) TTGTagCollectionAlignment alignment; 271 | 272 | // Number of lines. 0 means no limit, default is 0 for vertical and 1 for horizontal. 273 | @property (nonatomic, assign) NSUInteger numberOfLines; 274 | 275 | // Tag selection limit, default is 0, means no limit 276 | @property (nonatomic, assign) NSUInteger selectionLimit; 277 | 278 | // Horizontal and vertical space between tags, default is 4. 279 | @property (assign, nonatomic) CGFloat horizontalSpacing; 280 | @property (assign, nonatomic) CGFloat verticalSpacing; 281 | 282 | // Content inset, default is UIEdgeInsetsMake(2, 2, 2, 2). 283 | @property (nonatomic, assign) UIEdgeInsets contentInset; 284 | 285 | // The true tags content size, readonly 286 | @property (nonatomic, assign, readonly) CGSize contentSize; 287 | 288 | // Manual content height 289 | // Default = NO, set will update content 290 | @property (nonatomic, assign) BOOL manualCalculateHeight; 291 | // Default = 0, set will update content 292 | @property (nonatomic, assign) CGFloat preferredMaxLayoutWidth; 293 | 294 | // Scroll indicator 295 | @property (nonatomic, assign) BOOL showsHorizontalScrollIndicator; 296 | @property (nonatomic, assign) BOOL showsVerticalScrollIndicator; 297 | ``` 298 | 299 | Alignment types: 300 | 301 | ```Objective-C 302 | typedef NS_ENUM(NSInteger, TTGTagCollectionAlignment) { 303 | TTGTagCollectionAlignmentLeft = 0, // Default 304 | TTGTagCollectionAlignmentCenter, // Center 305 | TTGTagCollectionAlignmentRight, // Right 306 | TTGTagCollectionAlignmentFillByExpandingSpace, // Expand horizontal spacing and fill 307 | TTGTagCollectionAlignmentFillByExpandingWidth, // Expand width and fill 308 | TTGTagCollectionAlignmentFillByExpandingWidthExceptLastLine, // Expand width and fill, except last line 309 | }; 310 | ``` 311 | 312 | #### Modify tags 313 | 314 | Add tag. 315 | 316 | ```Objective-C 317 | // TTGTextTagCollectionView.h 318 | /// Add 319 | - (void)addTag:(TTGTextTag *)tag; 320 | - (void)addTags:(NSArray *)tags; 321 | ``` 322 | 323 | Insert tag. 324 | 325 | ```Objective-C 326 | // TTGTextTagCollectionView.h 327 | /// Insert 328 | - (void)insertTag:(TTGTextTag *)tag atIndex:(NSUInteger)index; 329 | - (void)insertTags:(NSArray *)tags atIndex:(NSUInteger)index; 330 | ``` 331 | 332 | Update tag. 333 | 334 | ```Objective-C 335 | // TTGTextTagCollectionView.h 336 | /// Update 337 | - (void)updateTagAtIndex:(NSUInteger)index selected:(BOOL)selected; 338 | - (void)updateTagAtIndex:(NSUInteger)index withNewTag:(TTGTextTag *)tag; 339 | ``` 340 | 341 | Remove tag. 342 | 343 | ```Objective-C 344 | // TTGTextTagCollectionView.h 345 | // Remove tag 346 | - (void)removeTag:(TTGTextTag *)tag; 347 | - (void)removeTagById:(NSUInteger)tagId; 348 | - (void)removeTagAtIndex:(NSUInteger)index; 349 | - (void)removeAllTags; 350 | ``` 351 | 352 | Get tags. 353 | 354 | ```Objective-C 355 | // TTGTextTagCollectionView.h 356 | /// Get tag 357 | - (TTGTextTag *)getTagAtIndex:(NSUInteger)index; 358 | - (NSArray *)getTagsInRange:(NSRange)range; 359 | 360 | /// Get all 361 | - (NSArray *)allTags; 362 | - (NSArray *)allSelectedTags; 363 | - (NSArray *)allNotSelectedTags; 364 | ``` 365 | 366 | #### Reload 367 | 368 | You can reload tags programmatically. 369 | 370 | ```Objective-C 371 | // TTGTextTagCollectionView.h 372 | - (void)reload; 373 | ``` 374 | 375 | #### Index at point 376 | 377 | Returns the index of the tag located at the specified point. 378 | 379 | ```Objective-C 380 | // TTGTextTagCollectionView.h 381 | - (NSInteger)indexOfTagAt:(CGPoint)point; 382 | ``` 383 | 384 | ### TTGTagCollectionView 385 | 386 | Use `TTGTagCollectionView` to show custom tag views. 387 | 388 | #### DataSource and Delegate 389 | 390 | Just like the UITableView, you must conform and implement the required methods of `TTGTagCollectionViewDelegate` and `TTGTagCollectionViewDataSource` to get `TTGTagCollectionView` work. 391 | 392 | **DataSource** 393 | 394 | ```Objective-C 395 | @protocol TTGTagCollectionViewDataSource 396 | @required 397 | - (NSUInteger)numberOfTagsInTagCollectionView:(TTGTagCollectionView *)tagCollectionView; 398 | 399 | - (UIView *)tagCollectionView:(TTGTagCollectionView *)tagCollectionView tagViewForIndex:(NSUInteger)index; 400 | @end 401 | ``` 402 | 403 | **Delegate** 404 | 405 | ```Objective-C 406 | @protocol TTGTagCollectionViewDelegate 407 | @required 408 | - (CGSize)tagCollectionView:(TTGTagCollectionView *)tagCollectionView sizeForTagAtIndex:(NSUInteger)index; 409 | 410 | @optional 411 | - (BOOL)tagCollectionView:(TTGTagCollectionView *)tagCollectionView shouldSelectTag:(UIView *)tagView atIndex:(NSUInteger)index; 412 | 413 | - (void)tagCollectionView:(TTGTagCollectionView *)tagCollectionView didSelectTag:(UIView *)tagView atIndex:(NSUInteger)index; 414 | 415 | - (void)tagCollectionView:(TTGTagCollectionView *)tagCollectionView updateContentSize:(CGSize)contentSize; 416 | @end 417 | ``` 418 | 419 | #### Customization 420 | 421 | ```Objective-C 422 | // TTGTagCollectionView.h 423 | // Tags scroll direction, default is vertical. 424 | @property (nonatomic, assign) TTGTagCollectionScrollDirection scrollDirection; 425 | 426 | // Tags layout alignment, default is left. 427 | @property (nonatomic, assign) TTGTagCollectionAlignment alignment; 428 | 429 | // Number of lines. 0 means no limit, default is 0 for vertical and 1 for horizontal. 430 | @property (nonatomic, assign) NSUInteger numberOfLines; 431 | 432 | // Horizontal and vertical space between tags, default is 4. 433 | @property (nonatomic, assign) CGFloat horizontalSpacing; 434 | @property (nonatomic, assign) CGFloat verticalSpacing; 435 | 436 | // Content inset, default is UIEdgeInsetsMake(2, 2, 2, 2). 437 | @property (nonatomic, assign) UIEdgeInsets contentInset; 438 | 439 | // The true tags content size, readonly 440 | @property (nonatomic, assign, readonly) CGSize contentSize; 441 | 442 | // Manual content height 443 | // Default = NO, set will update content 444 | @property (nonatomic, assign) BOOL manualCalculateHeight; 445 | // Default = 0, set will update content 446 | @property (nonatomic, assign) CGFloat preferredMaxLayoutWidth; 447 | 448 | // Scroll indicator 449 | @property (nonatomic, assign) BOOL showsHorizontalScrollIndicator; 450 | @property (nonatomic, assign) BOOL showsVerticalScrollIndicator; 451 | ``` 452 | 453 | #### Reload 454 | 455 | You can reload tags programmatically. 456 | 457 | ```Objective-C 458 | // TTGTagCollectionView.h 459 | - (void)reload; 460 | ``` 461 | 462 | #### Index at point 463 | 464 | Returns the index of the tag located at the specified point. 465 | 466 | ```Objective-C 467 | // TTGTagCollectionView.h 468 | - (NSInteger)indexOfTagAt:(CGPoint)point; 469 | ``` 470 | 471 | ## Fix 472 | 473 | `UITableViewAutomaticDimension` may not work when using tagView in tableViewCell. You should reload your tableView in the `viewDidAppear`. 474 | 475 | ## Author 476 | 477 | zekunyan, zekunyan@163.com 478 | 479 | ## License 480 | 481 | TTGTagCollectionView is available under the MIT license. See the LICENSE file for more info. 482 | -------------------------------------------------------------------------------- /Resources/alignment_type.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Resources/alignment_type.png -------------------------------------------------------------------------------- /Resources/code_structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Resources/code_structure.png -------------------------------------------------------------------------------- /Resources/demo_example.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Resources/demo_example.jpeg -------------------------------------------------------------------------------- /Resources/screen_shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zekunyan/TTGTagCollectionView/fd3c41c81a23481c3bde2dbdc81844ff1f750fc9/Resources/screen_shot.png -------------------------------------------------------------------------------- /Sources/TTGTagCollectionView-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // TTGTagCollectionView-Bridging-Header.h 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2021/4/21. 6 | // 7 | 8 | #ifndef TTGTagCollectionView_Bridging_Header_h 9 | #define TTGTagCollectionView_Bridging_Header_h 10 | 11 | #import "TTGTagCollectionView.h" 12 | #import "TTGTextTagCollectionView.h" 13 | #import "TTGTextTag.h" 14 | #import "TTGTextTagContent.h" 15 | #import "TTGTextTagStringContent.h" 16 | #import "TTGTextTagAttributedStringContent.h" 17 | #import "TTGTextTagStyle.h" 18 | 19 | #endif /* TTGTagCollectionView_Bridging_Header_h */ 20 | -------------------------------------------------------------------------------- /Sources/TTGTagCollectionView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TTGTagCollectionView.h 3 | // Pods 4 | // 5 | // Created by zekunyan on 15/12/26. 6 | // Copyright (c) 2021 zekunyan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class TTGTagCollectionView; 12 | 13 | /** 14 | * Tags scroll direction 15 | */ 16 | typedef NS_ENUM(NSInteger, TTGTagCollectionScrollDirection) { 17 | TTGTagCollectionScrollDirectionVertical = 0, // Default 18 | TTGTagCollectionScrollDirectionHorizontal = 1 19 | }; 20 | 21 | /** 22 | * Tags alignment 23 | */ 24 | typedef NS_ENUM(NSInteger, TTGTagCollectionAlignment) { 25 | TTGTagCollectionAlignmentLeft = 0, // Default 26 | TTGTagCollectionAlignmentCenter, // Center 27 | TTGTagCollectionAlignmentRight, // Right 28 | TTGTagCollectionAlignmentFillByExpandingSpace, // Expand horizontal spacing and fill 29 | TTGTagCollectionAlignmentFillByExpandingWidth, // Expand width and fill 30 | TTGTagCollectionAlignmentFillByExpandingWidthExceptLastLine // Expand width and fill, except last line 31 | }; 32 | 33 | /** 34 | * Tags delegate 35 | */ 36 | @protocol TTGTagCollectionViewDelegate 37 | @required 38 | - (CGSize)tagCollectionView:(TTGTagCollectionView *)tagCollectionView sizeForTagAtIndex:(NSUInteger)index; 39 | 40 | @optional 41 | - (BOOL)tagCollectionView:(TTGTagCollectionView *)tagCollectionView shouldSelectTag:(UIView *)tagView atIndex:(NSUInteger)index; 42 | 43 | - (void)tagCollectionView:(TTGTagCollectionView *)tagCollectionView didSelectTag:(UIView *)tagView atIndex:(NSUInteger)index; 44 | 45 | - (void)tagCollectionView:(TTGTagCollectionView *)tagCollectionView updateContentSize:(CGSize)contentSize; 46 | @end 47 | 48 | /** 49 | * Tags dataSource 50 | */ 51 | @protocol TTGTagCollectionViewDataSource 52 | @required 53 | - (NSUInteger)numberOfTagsInTagCollectionView:(TTGTagCollectionView *)tagCollectionView; 54 | 55 | - (UIView *)tagCollectionView:(TTGTagCollectionView *)tagCollectionView tagViewForIndex:(NSUInteger)index; 56 | @end 57 | 58 | @interface TTGTagCollectionView : UIView 59 | @property (nonatomic, weak) id dataSource; 60 | @property (nonatomic, weak) id delegate; 61 | 62 | // Inside scrollView 63 | @property (nonatomic, strong, readonly) UIScrollView *scrollView; 64 | 65 | // Tags scroll direction, default is vertical. 66 | @property (nonatomic, assign) TTGTagCollectionScrollDirection scrollDirection; 67 | 68 | // Tags layout alignment, default is left. 69 | @property (nonatomic, assign) TTGTagCollectionAlignment alignment; 70 | 71 | // Number of lines. 0 means no limit, default is 0 for vertical and 1 for horizontal. 72 | @property (nonatomic, assign) NSUInteger numberOfLines; 73 | // The real number of lines ignoring the numberOfLines value 74 | @property (nonatomic, assign, readonly) NSUInteger actualNumberOfLines; 75 | 76 | // Horizontal and vertical space between tags, default is 4. 77 | @property (nonatomic, assign) CGFloat horizontalSpacing; 78 | @property (nonatomic, assign) CGFloat verticalSpacing; 79 | 80 | // Content inset, default is UIEdgeInsetsMake(2, 2, 2, 2). 81 | @property (nonatomic, assign) UIEdgeInsets contentInset; 82 | 83 | // The true tags content size, readonly 84 | @property (nonatomic, assign, readonly) CGSize contentSize; 85 | 86 | // Manual content height 87 | // Default = NO, set will update content 88 | @property (nonatomic, assign) BOOL manualCalculateHeight; 89 | // Default = 0, set will update content 90 | @property (nonatomic, assign) CGFloat preferredMaxLayoutWidth; 91 | 92 | // Scroll indicator 93 | @property (nonatomic, assign) BOOL showsHorizontalScrollIndicator; 94 | @property (nonatomic, assign) BOOL showsVerticalScrollIndicator; 95 | 96 | // Tap blank area callback 97 | @property (nonatomic, copy) void (^onTapBlankArea)(CGPoint location); 98 | // Tap all area callback 99 | @property (nonatomic, copy) void (^onTapAllArea)(CGPoint location); 100 | 101 | /** 102 | * Reload all tag cells 103 | */ 104 | - (void)reload; 105 | 106 | /** 107 | * Returns the index of the tag located at the specified point. 108 | * If item at point is not found, returns NSNotFound. 109 | */ 110 | - (NSInteger)indexOfTagAt:(CGPoint)point; 111 | 112 | @end 113 | -------------------------------------------------------------------------------- /Sources/TTGTagCollectionView.m: -------------------------------------------------------------------------------- 1 | // 2 | // TTGTagCollectionView.m 3 | // Pods 4 | // 5 | // Created by zekunyan on 15/12/26. 6 | // Copyright (c) 2021 zekunyan. All rights reserved. 7 | // 8 | 9 | #import "TTGTagCollectionView.h" 10 | 11 | @interface TTGTagCollectionView () 12 | @property (nonatomic, strong) UIScrollView *scrollView; 13 | @property (nonatomic, strong) UIView *containerView; 14 | @property (nonatomic, assign) BOOL needsLayoutTagViews; 15 | @property (nonatomic, assign) NSUInteger actualNumberOfLines; 16 | @end 17 | 18 | @implementation TTGTagCollectionView 19 | 20 | #pragma mark - Init 21 | 22 | - (instancetype)initWithFrame:(CGRect)frame { 23 | self = [super initWithFrame:frame]; 24 | if (self) { 25 | [self commonInit]; 26 | } 27 | 28 | return self; 29 | } 30 | 31 | - (instancetype)initWithCoder:(NSCoder *)coder { 32 | self = [super initWithCoder:coder]; 33 | if (self) { 34 | [self commonInit]; 35 | } 36 | 37 | return self; 38 | } 39 | 40 | - (void)commonInit { 41 | if (_scrollView) { 42 | return; 43 | } 44 | 45 | _horizontalSpacing = 4; 46 | _verticalSpacing = 4; 47 | _contentInset = UIEdgeInsetsMake(2, 2, 2, 2); 48 | 49 | _scrollView = [[UIScrollView alloc] initWithFrame:self.bounds]; 50 | _scrollView.backgroundColor = [UIColor clearColor]; 51 | _scrollView.userInteractionEnabled = YES; 52 | [self addSubview:_scrollView]; 53 | 54 | _containerView = [[UIView alloc] initWithFrame:_scrollView.bounds]; 55 | _containerView.backgroundColor = [UIColor clearColor]; 56 | _containerView.userInteractionEnabled = YES; 57 | [_scrollView addSubview:_containerView]; 58 | 59 | UITapGestureRecognizer *tapGesture = [UITapGestureRecognizer new]; 60 | [tapGesture addTarget:self action:@selector(onTapGesture:)]; 61 | [_containerView addGestureRecognizer:tapGesture]; 62 | 63 | [self setNeedsLayoutTagViews]; 64 | } 65 | 66 | #pragma mark - Public methods 67 | 68 | - (void)reload { 69 | if (![self isDelegateAndDataSourceValid]) { 70 | return; 71 | } 72 | 73 | // Remove all tag views 74 | [_containerView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; 75 | 76 | // Add tag view 77 | for (NSUInteger i = 0; i < [_dataSource numberOfTagsInTagCollectionView:self]; i++) { 78 | [_containerView addSubview:[_dataSource tagCollectionView:self tagViewForIndex:i]]; 79 | } 80 | 81 | // Update tag view frame 82 | [self setNeedsLayoutTagViews]; 83 | [self layoutTagViews]; 84 | } 85 | 86 | - (NSInteger)indexOfTagAt:(CGPoint)point { 87 | // We expect the point to be a point wrt to the TTGTagCollectionView. 88 | // so convert this point first to a point wrt to the container view. 89 | CGPoint convertedPoint = [self convertPoint:point toView:_containerView]; 90 | for (NSUInteger i = 0; i < [self.dataSource numberOfTagsInTagCollectionView:self]; i++) { 91 | UIView *tagView = [self.dataSource tagCollectionView:self tagViewForIndex:i]; 92 | if (CGRectContainsPoint(tagView.frame, convertedPoint) && !tagView.isHidden) { 93 | return i; 94 | } 95 | } 96 | return NSNotFound; 97 | } 98 | 99 | #pragma mark - Gesture 100 | 101 | - (void)onTapGesture:(UITapGestureRecognizer *)tapGesture { 102 | CGPoint tapPointInCollectionView = [tapGesture locationInView:self]; 103 | 104 | if (![self.dataSource respondsToSelector:@selector(numberOfTagsInTagCollectionView:)] || 105 | ![self.dataSource respondsToSelector:@selector(tagCollectionView:tagViewForIndex:)] || 106 | ![self.delegate respondsToSelector:@selector(tagCollectionView:didSelectTag:atIndex:)]) { 107 | if (_onTapBlankArea) { 108 | _onTapBlankArea(tapPointInCollectionView); 109 | } 110 | if (_onTapAllArea) { 111 | _onTapAllArea(tapPointInCollectionView); 112 | } 113 | return; 114 | } 115 | 116 | CGPoint tapPointInScrollView = [tapGesture locationInView:_containerView]; 117 | BOOL hasLocatedToTag = NO; 118 | 119 | for (NSUInteger i = 0; i < [self.dataSource numberOfTagsInTagCollectionView:self]; i++) { 120 | UIView *tagView = [self.dataSource tagCollectionView:self tagViewForIndex:i]; 121 | if (CGRectContainsPoint(tagView.frame, tapPointInScrollView) && !tagView.isHidden) { 122 | hasLocatedToTag = YES; 123 | if ([self.delegate respondsToSelector:@selector(tagCollectionView:shouldSelectTag:atIndex:)]) { 124 | if ([self.delegate tagCollectionView:self shouldSelectTag:tagView atIndex:i]) { 125 | [self.delegate tagCollectionView:self didSelectTag:tagView atIndex:i]; 126 | } 127 | } else { 128 | [self.delegate tagCollectionView:self didSelectTag:tagView atIndex:i]; 129 | } 130 | } 131 | } 132 | 133 | if (!hasLocatedToTag) { 134 | if (_onTapBlankArea) { 135 | _onTapBlankArea(tapPointInCollectionView); 136 | } 137 | } 138 | if (_onTapAllArea) { 139 | _onTapAllArea(tapPointInCollectionView); 140 | } 141 | } 142 | 143 | #pragma mark - Override 144 | 145 | - (void)layoutSubviews { 146 | [super layoutSubviews]; 147 | 148 | if (!CGRectEqualToRect(_scrollView.frame, self.bounds)) { 149 | _scrollView.frame = self.bounds; 150 | [self setNeedsLayoutTagViews]; 151 | [self layoutTagViews]; 152 | _containerView.frame = (CGRect){CGPointZero, _scrollView.contentSize}; 153 | } 154 | [self layoutTagViews]; 155 | } 156 | 157 | - (CGSize)intrinsicContentSize { 158 | return _scrollView.contentSize; 159 | } 160 | 161 | - (CGSize)sizeThatFits:(CGSize)size { 162 | return self.contentSize; 163 | } 164 | 165 | #pragma mark - Layout 166 | 167 | - (void)layoutTagViews { 168 | if (!_needsLayoutTagViews || ![self isDelegateAndDataSourceValid]) { 169 | return; 170 | } 171 | 172 | if (_scrollDirection == TTGTagCollectionScrollDirectionVertical) { 173 | [self layoutTagViewsForVerticalDirection]; 174 | } else { 175 | [self layoutTagViewsForHorizontalDirection]; 176 | } 177 | 178 | _needsLayoutTagViews = NO; 179 | [self invalidateIntrinsicContentSize]; 180 | } 181 | 182 | - (void)layoutTagViewsForVerticalDirection { 183 | NSUInteger count = [_dataSource numberOfTagsInTagCollectionView:self]; 184 | NSUInteger currentLineTagsCount = 0; 185 | CGFloat totalWidth = (_manualCalculateHeight && _preferredMaxLayoutWidth > 0) ? _preferredMaxLayoutWidth : CGRectGetWidth(self.bounds); 186 | CGFloat maxLineWidth = totalWidth - _contentInset.left - _contentInset.right; 187 | CGFloat currentLineX = 0; 188 | CGFloat currentLineMaxHeight = 0; 189 | 190 | NSMutableArray *eachLineMaxHeightNumbers = [NSMutableArray new]; 191 | NSMutableArray *eachLineWidthNumbers = [NSMutableArray new]; 192 | NSMutableArray *eachLineTagCountNumbers = [NSMutableArray new]; 193 | 194 | NSMutableArray *> *eachLineTagIndexs = [NSMutableArray new]; 195 | NSMutableArray *tmpTagIndexNumbers = [NSMutableArray new]; 196 | 197 | // Get each line max height ,width and tag count 198 | for (NSUInteger i = 0; i < count; i++) { 199 | CGSize tagSize = [_delegate tagCollectionView:self sizeForTagAtIndex:i]; 200 | 201 | if (currentLineX + tagSize.width > maxLineWidth && tmpTagIndexNumbers.count > 0) { 202 | // New Line 203 | [eachLineMaxHeightNumbers addObject:@(currentLineMaxHeight)]; 204 | [eachLineWidthNumbers addObject:@(currentLineX - _horizontalSpacing)]; 205 | [eachLineTagCountNumbers addObject:@(currentLineTagsCount)]; 206 | [eachLineTagIndexs addObject:tmpTagIndexNumbers]; 207 | tmpTagIndexNumbers = [NSMutableArray new]; 208 | currentLineTagsCount = 0; 209 | currentLineMaxHeight = 0; 210 | currentLineX = 0; 211 | } 212 | 213 | // Line limit 214 | if (_numberOfLines != 0) { 215 | UIView *tagView = [_dataSource tagCollectionView:self tagViewForIndex:i]; 216 | tagView.hidden = eachLineWidthNumbers.count >= _numberOfLines; 217 | } 218 | 219 | currentLineX += tagSize.width + _horizontalSpacing; 220 | currentLineTagsCount += 1; 221 | currentLineMaxHeight = MAX(tagSize.height, currentLineMaxHeight); 222 | [tmpTagIndexNumbers addObject:@(i)]; 223 | } 224 | 225 | // Add last 226 | [eachLineMaxHeightNumbers addObject:@(currentLineMaxHeight)]; 227 | [eachLineWidthNumbers addObject:@(currentLineX - _horizontalSpacing)]; 228 | [eachLineTagCountNumbers addObject:@(currentLineTagsCount)]; 229 | [eachLineTagIndexs addObject:tmpTagIndexNumbers]; 230 | 231 | // Actual number of lines 232 | _actualNumberOfLines = eachLineTagCountNumbers.count; 233 | 234 | // Line limit 235 | if (_numberOfLines != 0) { 236 | eachLineMaxHeightNumbers = [[eachLineMaxHeightNumbers subarrayWithRange:NSMakeRange(0, MIN(eachLineMaxHeightNumbers.count, _numberOfLines))] mutableCopy]; 237 | eachLineWidthNumbers = [[eachLineWidthNumbers subarrayWithRange:NSMakeRange(0, MIN(eachLineWidthNumbers.count, _numberOfLines))] mutableCopy]; 238 | eachLineTagCountNumbers = [[eachLineTagCountNumbers subarrayWithRange:NSMakeRange(0, MIN(eachLineTagCountNumbers.count, _numberOfLines))] mutableCopy]; 239 | eachLineTagIndexs = [[eachLineTagIndexs subarrayWithRange:NSMakeRange(0, MIN(eachLineTagIndexs.count, _numberOfLines))] mutableCopy]; 240 | } 241 | 242 | // Prepare 243 | [self layoutEachLineTagsWithMaxLineWidth:maxLineWidth 244 | numberOfLines:eachLineTagCountNumbers.count 245 | eachLineTagIndexs:eachLineTagIndexs 246 | eachLineTagCount:eachLineTagCountNumbers 247 | eachLineWidth:eachLineWidthNumbers 248 | eachLineMaxHeight:eachLineMaxHeightNumbers]; 249 | } 250 | 251 | - (void)layoutTagViewsForHorizontalDirection { 252 | NSInteger count = [_dataSource numberOfTagsInTagCollectionView:self]; 253 | _numberOfLines = _numberOfLines == 0 ? 1 : _numberOfLines; 254 | 255 | CGFloat maxLineWidth = 0; 256 | 257 | NSMutableArray *eachLineMaxHeightNumbers = [NSMutableArray new]; 258 | NSMutableArray *eachLineWidthNumbers = [NSMutableArray new]; 259 | NSMutableArray *eachLineTagCountNumbers = [NSMutableArray new]; 260 | 261 | NSMutableArray *> *eachLineTagIndexs = [NSMutableArray new]; 262 | 263 | // Init each line 264 | for (NSInteger currentLine = 0; currentLine < _numberOfLines; currentLine++) { 265 | [eachLineMaxHeightNumbers addObject:@0]; 266 | [eachLineWidthNumbers addObject:@0]; 267 | [eachLineTagCountNumbers addObject:@0]; 268 | [eachLineTagIndexs addObject:[NSMutableArray new]]; 269 | } 270 | 271 | // Add tags 272 | for (NSUInteger tagIndex = 0; tagIndex < count; tagIndex++) { 273 | NSUInteger currentLine = tagIndex % _numberOfLines; 274 | 275 | NSUInteger currentLineTagsCount = eachLineTagCountNumbers[currentLine].unsignedIntegerValue; 276 | CGFloat currentLineMaxHeight = eachLineMaxHeightNumbers[currentLine].floatValue; 277 | CGFloat currentLineX = eachLineWidthNumbers[currentLine].floatValue; 278 | NSMutableArray *currentLineTagIndexNumbers = eachLineTagIndexs[currentLine]; 279 | 280 | CGSize tagSize = [_delegate tagCollectionView:self sizeForTagAtIndex:tagIndex]; 281 | currentLineX += tagSize.width + _horizontalSpacing; 282 | currentLineMaxHeight = MAX(tagSize.height, currentLineMaxHeight); 283 | currentLineTagsCount += 1; 284 | [currentLineTagIndexNumbers addObject:@(tagIndex)]; 285 | 286 | eachLineTagCountNumbers[currentLine] = @(currentLineTagsCount); 287 | eachLineMaxHeightNumbers[currentLine] = @(currentLineMaxHeight); 288 | eachLineWidthNumbers[currentLine] = @(currentLineX); 289 | eachLineTagIndexs[currentLine] = currentLineTagIndexNumbers; 290 | } 291 | 292 | // Remove extra space 293 | for (NSInteger currentLine = 0; currentLine < _numberOfLines; currentLine++) { 294 | CGFloat currentLineWidth = eachLineWidthNumbers[currentLine].floatValue; 295 | currentLineWidth -= _horizontalSpacing; 296 | eachLineWidthNumbers[currentLine] = @(currentLineWidth); 297 | 298 | maxLineWidth = MAX(currentLineWidth, maxLineWidth); 299 | } 300 | 301 | // Prepare 302 | [self layoutEachLineTagsWithMaxLineWidth:maxLineWidth 303 | numberOfLines:eachLineTagCountNumbers.count 304 | eachLineTagIndexs:eachLineTagIndexs 305 | eachLineTagCount:eachLineTagCountNumbers 306 | eachLineWidth:eachLineWidthNumbers 307 | eachLineMaxHeight:eachLineMaxHeightNumbers]; 308 | } 309 | 310 | - (void)layoutEachLineTagsWithMaxLineWidth:(CGFloat)maxLineWidth 311 | numberOfLines:(NSUInteger)numberOfLines 312 | eachLineTagIndexs:(NSArray *> *)eachLineTagIndexs 313 | eachLineTagCount:(NSArray *)eachLineTagCount 314 | eachLineWidth:(NSArray *)eachLineWidth 315 | eachLineMaxHeight:(NSArray *)eachLineMaxHeight { 316 | 317 | CGFloat currentYBase = _contentInset.top; 318 | 319 | for (NSUInteger currentLine = 0; currentLine < numberOfLines; currentLine++) { 320 | CGFloat currentLineMaxHeight = eachLineMaxHeight[currentLine].floatValue; 321 | CGFloat currentLineWidth = eachLineWidth[currentLine].floatValue; 322 | CGFloat currentLineTagsCount = eachLineTagCount[currentLine].unsignedIntegerValue; 323 | 324 | // Alignment x offset 325 | CGFloat currentLineXOffset = 0; 326 | CGFloat currentLineAdditionWidth = 0; 327 | CGFloat acturalHorizontalSpacing = _horizontalSpacing; 328 | __block CGFloat currentLineX = 0; 329 | 330 | switch (_alignment) { 331 | case TTGTagCollectionAlignmentLeft: 332 | currentLineXOffset = _contentInset.left; 333 | break; 334 | case TTGTagCollectionAlignmentCenter: 335 | currentLineXOffset = (maxLineWidth - currentLineWidth) / 2 + _contentInset.left; 336 | break; 337 | case TTGTagCollectionAlignmentRight: 338 | currentLineXOffset = maxLineWidth - currentLineWidth + _contentInset.left; 339 | break; 340 | case TTGTagCollectionAlignmentFillByExpandingSpace: 341 | currentLineXOffset = _contentInset.left; 342 | acturalHorizontalSpacing = _horizontalSpacing + 343 | (maxLineWidth - currentLineWidth) / (CGFloat)(currentLineTagsCount - 1); 344 | currentLineWidth = maxLineWidth; 345 | break; 346 | case TTGTagCollectionAlignmentFillByExpandingWidth: 347 | case TTGTagCollectionAlignmentFillByExpandingWidthExceptLastLine: 348 | currentLineXOffset = _contentInset.left; 349 | currentLineAdditionWidth = (maxLineWidth - currentLineWidth) / (CGFloat)currentLineTagsCount; 350 | currentLineWidth = maxLineWidth; 351 | 352 | if (_alignment == TTGTagCollectionAlignmentFillByExpandingWidthExceptLastLine && 353 | currentLine == numberOfLines - 1 && 354 | numberOfLines != 1) { 355 | // Reset last line width for TTGTagCollectionAlignmentFillByExpandingWidthExceptLastLine 356 | currentLineAdditionWidth = 0; 357 | } 358 | 359 | break; 360 | } 361 | 362 | // Current line 363 | [eachLineTagIndexs[currentLine] enumerateObjectsUsingBlock:^(NSNumber * _Nonnull tagIndexNumber, NSUInteger idx, BOOL * _Nonnull stop) { 364 | NSUInteger tagIndex = tagIndexNumber.unsignedIntegerValue; 365 | 366 | UIView *tagView = [self.dataSource tagCollectionView:self tagViewForIndex:tagIndex]; 367 | CGSize tagSize = [self.delegate tagCollectionView:self sizeForTagAtIndex:tagIndex]; 368 | 369 | CGPoint origin; 370 | origin.x = currentLineXOffset + currentLineX; 371 | origin.y = currentYBase + (currentLineMaxHeight - tagSize.height) / 2; 372 | 373 | tagSize.width += currentLineAdditionWidth; 374 | if (self.scrollDirection == TTGTagCollectionScrollDirectionVertical && tagSize.width > maxLineWidth) { 375 | tagSize.width = maxLineWidth; 376 | } 377 | 378 | tagView.hidden = NO; 379 | tagView.frame = (CGRect){origin, tagSize}; 380 | 381 | currentLineX += tagSize.width + acturalHorizontalSpacing; 382 | }]; 383 | 384 | // Next line 385 | currentYBase += currentLineMaxHeight + _verticalSpacing; 386 | } 387 | 388 | // Content size 389 | maxLineWidth += _contentInset.right + _contentInset.left; 390 | CGSize contentSize = CGSizeMake(maxLineWidth, currentYBase - _verticalSpacing + _contentInset.bottom); 391 | if (!CGSizeEqualToSize(contentSize, _scrollView.contentSize)) { 392 | _scrollView.contentSize = contentSize; 393 | _containerView.frame = (CGRect){CGPointZero, contentSize}; 394 | 395 | if ([self.delegate respondsToSelector:@selector(tagCollectionView:updateContentSize:)]) { 396 | [self.delegate tagCollectionView:self updateContentSize:contentSize]; 397 | } 398 | } 399 | } 400 | 401 | - (void)setNeedsLayoutTagViews { 402 | _needsLayoutTagViews = YES; 403 | } 404 | 405 | #pragma mark - Check delegate and dataSource 406 | 407 | - (BOOL)isDelegateAndDataSourceValid { 408 | BOOL isValid = _delegate != nil && _dataSource != nil; 409 | isValid = isValid && [_delegate respondsToSelector:@selector(tagCollectionView:sizeForTagAtIndex:)]; 410 | isValid = isValid && [_dataSource respondsToSelector:@selector(tagCollectionView:tagViewForIndex:)]; 411 | isValid = isValid && [_dataSource respondsToSelector:@selector(numberOfTagsInTagCollectionView:)]; 412 | return isValid; 413 | } 414 | 415 | #pragma mark - Setter Getter 416 | 417 | - (UIScrollView *)scrollView { 418 | return _scrollView; 419 | } 420 | 421 | - (void)setScrollDirection:(TTGTagCollectionScrollDirection)scrollDirection { 422 | _scrollDirection = scrollDirection; 423 | [self setNeedsLayoutTagViews]; 424 | } 425 | 426 | - (void)setAlignment:(TTGTagCollectionAlignment)alignment { 427 | _alignment = alignment; 428 | [self setNeedsLayoutTagViews]; 429 | } 430 | 431 | - (void)setNumberOfLines:(NSUInteger)numberOfLines { 432 | _numberOfLines = numberOfLines; 433 | [self setNeedsLayoutTagViews]; 434 | } 435 | 436 | - (NSUInteger)actualNumberOfLines { 437 | if (_scrollDirection == TTGTagCollectionScrollDirectionHorizontal) { 438 | return _numberOfLines; 439 | } else { 440 | return _actualNumberOfLines; 441 | } 442 | } 443 | 444 | - (void)setHorizontalSpacing:(CGFloat)horizontalSpacing { 445 | _horizontalSpacing = horizontalSpacing; 446 | [self setNeedsLayoutTagViews]; 447 | } 448 | 449 | - (void)setVerticalSpacing:(CGFloat)verticalSpacing { 450 | _verticalSpacing = verticalSpacing; 451 | [self setNeedsLayoutTagViews]; 452 | } 453 | 454 | - (void)setContentInset:(UIEdgeInsets)contentInset { 455 | _contentInset = contentInset; 456 | [self setNeedsLayoutTagViews]; 457 | } 458 | 459 | - (CGSize)contentSize { 460 | [self layoutTagViews]; 461 | return _scrollView.contentSize; 462 | } 463 | 464 | - (void)setManualCalculateHeight:(BOOL)manualCalculateHeight { 465 | _manualCalculateHeight = manualCalculateHeight; 466 | [self setNeedsLayoutTagViews]; 467 | } 468 | 469 | - (void)setPreferredMaxLayoutWidth:(CGFloat)preferredMaxLayoutWidth { 470 | _preferredMaxLayoutWidth = preferredMaxLayoutWidth; 471 | [self setNeedsLayoutTagViews]; 472 | } 473 | 474 | - (void)setShowsHorizontalScrollIndicator:(BOOL)showsHorizontalScrollIndicator { 475 | _scrollView.showsHorizontalScrollIndicator = showsHorizontalScrollIndicator; 476 | } 477 | 478 | - (BOOL)showsHorizontalScrollIndicator { 479 | return _scrollView.showsHorizontalScrollIndicator; 480 | } 481 | 482 | - (void)setShowsVerticalScrollIndicator:(BOOL)showsVerticalScrollIndicator { 483 | _scrollView.showsVerticalScrollIndicator = showsVerticalScrollIndicator; 484 | } 485 | 486 | - (BOOL)showsVerticalScrollIndicator { 487 | return _scrollView.showsVerticalScrollIndicator; 488 | } 489 | 490 | @end 491 | -------------------------------------------------------------------------------- /Sources/TTGTextTag.h: -------------------------------------------------------------------------------- 1 | // 2 | // TTGTextTag.h 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2019/5/24. 6 | // Copyright (c) 2021 zekunyan. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | #import "TTGTextTagStyle.h" 13 | #import "TTGTextTagContent.h" 14 | 15 | /// Tag selection state change callback 16 | typedef void (^OnSelectStateChanged)(bool selected); 17 | 18 | @interface TTGTextTag : NSObject 19 | 20 | /// ID 21 | @property (nonatomic, assign, readonly) NSUInteger tagId; // Auto increase. The only identifier and main key for a tag 22 | 23 | /// Attachment object. You can use this to bind any object you want to each tag. 24 | @property (nonatomic, strong) id _Nullable attachment; 25 | 26 | /// Normal state content and style 27 | @property (nonatomic, copy) TTGTextTagContent * _Nonnull content; 28 | @property (nonatomic, copy) TTGTextTagStyle * _Nonnull style; 29 | 30 | /// Selected state content and style 31 | @property (nonatomic, copy) TTGTextTagContent * _Nullable selectedContent; 32 | @property (nonatomic, copy) TTGTextTagStyle * _Nullable selectedStyle; 33 | 34 | /// Selection state 35 | @property (nonatomic, assign) BOOL selected; 36 | @property (nonatomic, copy) OnSelectStateChanged _Nullable onSelectStateChanged; // State changed callback 37 | 38 | /// Accessibility 39 | @property (nonatomic, assign) BOOL isAccessibilityElement; // Default = NO 40 | @property (nonatomic, copy) NSString * _Nullable accessibilityIdentifier; // Default = nil 41 | @property (nonatomic, copy) NSString * _Nullable accessibilityLabel; // Default = nil 42 | @property (nonatomic, copy) NSString * _Nullable accessibilityHint; // Default = nil 43 | @property (nonatomic, copy) NSString * _Nullable accessibilityValue; // Default = nil 44 | @property (nonatomic, assign) UIAccessibilityTraits accessibilityTraits; // Default = UIAccessibilityTraitNone 45 | 46 | /// Auto detect accessibility 47 | /// When enableAutoDetectAccessibility = YES, the property below will be set automatically 48 | /// ---------------------------- 49 | /// isAccessibilityElement = YES 50 | /// accessibilityLabel = (selected ? selectedContent : content).getContentAttributedString.string 51 | /// accessibilityTraits = selected ? UIAccessibilityTraitSelected : UIAccessibilityTraitButton 52 | /// ---------------------------- 53 | /// But: accessibilityHint and accessibilityValue still keep your custom value; 54 | @property (nonatomic, assign) BOOL enableAutoDetectAccessibility; // Default = NO 55 | 56 | /// Init 57 | 58 | /** 59 | Init with single content and style 60 | 61 | @param content content for both normal and selection state. 62 | @param style style for both normal and selection state. 63 | @return instance 64 | */ 65 | - (instancetype _Nonnull)initWithContent:(TTGTextTagContent *_Nonnull)content 66 | style:(TTGTextTagStyle *_Nonnull)style; 67 | 68 | /** 69 | Init with different content and style 70 | 71 | @param content content for normal state 72 | @param style style for normal state 73 | @param selectedContent content for selection state 74 | @param selectedStyle style for selection state 75 | @return instance 76 | */ 77 | - (instancetype _Nonnull)initWithContent:(TTGTextTagContent *_Nonnull)content 78 | style:(TTGTextTagStyle *_Nonnull)style 79 | selectedContent:(TTGTextTagContent *_Nullable)selectedContent 80 | selectedStyle:(TTGTextTagStyle *_Nullable)selectedStyle; 81 | 82 | /** 83 | Tag with single content and style 84 | 85 | @param content content for both normal and selection state. 86 | @param style style for both normal and selection state. 87 | @return instance 88 | */ 89 | + (instancetype _Nonnull)tagWithContent:(TTGTextTagContent *_Nonnull)content 90 | style:(TTGTextTagStyle *_Nonnull)style; 91 | 92 | /** 93 | Tag with different content and style 94 | 95 | @param content content for normal state 96 | @param style style for normal state 97 | @param selectedContent content for selection state 98 | @param selectedStyle style for selection state 99 | @return instance 100 | */ 101 | + (instancetype _Nonnull)tagWithContent:(TTGTextTagContent *_Nonnull)content 102 | style:(TTGTextTagStyle *_Nonnull)style 103 | selectedContent:(TTGTextTagContent *_Nullable)selectedContent 104 | selectedStyle:(TTGTextTagStyle *_Nullable)selectedStyle; 105 | 106 | /** 107 | Get current state rightful content 108 | 109 | @return content 110 | */ 111 | - (TTGTextTagContent *_Nonnull)getRightfulContent; 112 | 113 | /** 114 | Get current state rightful style 115 | 116 | @return style 117 | */ 118 | - (TTGTextTagStyle *_Nonnull)getRightfulStyle; 119 | 120 | /// Base system methods 121 | - (BOOL)isEqual:(id _Nullable)other; 122 | - (BOOL)isEqualToTag:(TTGTextTag *_Nullable)tag; 123 | - (NSUInteger)hash; 124 | 125 | /// Copy 126 | - (id _Nonnull)copyWithZone:(NSZone *_Nullable)zone; 127 | 128 | @end 129 | -------------------------------------------------------------------------------- /Sources/TTGTextTag.m: -------------------------------------------------------------------------------- 1 | // 2 | // TTGTextTag.m 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2019/5/24. 6 | // Copyright (c) 2021 zekunyan. All rights reserved. 7 | // 8 | 9 | #import "TTGTextTag.h" 10 | #import "TTGTextTagStringContent.h" 11 | 12 | static NSUInteger TTGTextTagAutoIncreasedId = 0; 13 | 14 | @implementation TTGTextTag 15 | 16 | - (instancetype)initWithContent:(TTGTextTagContent *)content 17 | style:(TTGTextTagStyle *)style { 18 | self = [self init]; 19 | if (self) { 20 | self.content = content; 21 | self.style = style; 22 | } 23 | return self; 24 | } 25 | 26 | - (instancetype)initWithContent:(TTGTextTagContent *)content 27 | style:(TTGTextTagStyle *)style 28 | selectedContent:(TTGTextTagContent *)selectedContent 29 | selectedStyle:(TTGTextTagStyle *)selectedStyle { 30 | self = [self init]; 31 | if (self) { 32 | self.content = content; 33 | self.style = style; 34 | self.selectedContent = selectedContent; 35 | self.selectedStyle = selectedStyle; 36 | } 37 | return self; 38 | } 39 | 40 | + (instancetype)tagWithContent:(TTGTextTagContent *)content 41 | style:(TTGTextTagStyle *)style { 42 | return [[self alloc] initWithContent:content style:style]; 43 | } 44 | 45 | + (instancetype)tagWithContent:(TTGTextTagContent *)content 46 | style:(TTGTextTagStyle *)style 47 | selectedContent:(TTGTextTagContent *)selectedContent 48 | selectedStyle:(TTGTextTagStyle *)selectedStyle { 49 | return [[self alloc] initWithContent:content 50 | style:style 51 | selectedContent:selectedContent 52 | selectedStyle:selectedStyle]; 53 | } 54 | 55 | - (instancetype)init { 56 | self = [super init]; 57 | if (self) { 58 | _tagId = TTGTextTagAutoIncreasedId++; 59 | _attachment = nil; 60 | } 61 | return self; 62 | } 63 | 64 | - (void)setSelected:(BOOL)selected { 65 | _selected = selected; 66 | // Callback 67 | if (_onSelectStateChanged) { 68 | _onSelectStateChanged(selected); 69 | } 70 | } 71 | 72 | - (TTGTextTagContent *)selectedContent { 73 | if (_selectedContent == nil) { 74 | _selectedContent = [_content copy]; 75 | } 76 | return _selectedContent; 77 | } 78 | 79 | - (TTGTextTagStyle *)selectedStyle { 80 | if (_selectedStyle == nil) { 81 | _selectedStyle = [_style copy]; 82 | } 83 | return _selectedStyle; 84 | } 85 | 86 | - (TTGTextTagContent *)getRightfulContent { 87 | return _selected ? self.selectedContent : self.content; 88 | } 89 | 90 | - (TTGTextTagStyle *)getRightfulStyle { 91 | return _selected ? self.selectedStyle : self.style; 92 | } 93 | 94 | - (BOOL)isAccessibilityElement { 95 | return _enableAutoDetectAccessibility || _isAccessibilityElement; 96 | } 97 | 98 | - (NSString *)accessibilityLabel { 99 | if (_enableAutoDetectAccessibility) { 100 | return [self getRightfulContent].getContentAttributedString.string; 101 | } 102 | return _accessibilityLabel; 103 | } 104 | 105 | - (UIAccessibilityTraits)accessibilityTraits { 106 | if (_enableAutoDetectAccessibility) { 107 | return _selected ? UIAccessibilityTraitSelected : UIAccessibilityTraitButton; 108 | } 109 | return _accessibilityTraits; 110 | } 111 | 112 | - (BOOL)isEqual:(id)other { 113 | if (other == self) 114 | return YES; 115 | if (!other || ![[other class] isEqual:[self class]]) 116 | return NO; 117 | 118 | return [self isEqualToTag:other]; 119 | } 120 | 121 | - (BOOL)isEqualToTag:(TTGTextTag *)tag { 122 | if (self == tag) 123 | return YES; 124 | if (tag == nil) 125 | return NO; 126 | if (self.tagId != tag.tagId) 127 | return NO; 128 | return YES; 129 | } 130 | 131 | - (NSUInteger)hash { 132 | return (NSUInteger)self.tagId; 133 | } 134 | 135 | - (id)copyWithZone:(nullable NSZone *)zone { 136 | TTGTextTag *copy = (TTGTextTag *)[[[self class] allocWithZone:zone] init]; 137 | if (copy != nil) { 138 | copy->_tagId = TTGTextTagAutoIncreasedId++; 139 | copy.attachment = self.attachment; 140 | copy.content = self.content; 141 | copy.style = self.style; 142 | copy.selected = self.selected; 143 | copy.selectedContent = self.selectedContent; 144 | copy.selectedStyle = self.selectedStyle; 145 | copy.isAccessibilityElement = self.isAccessibilityElement; 146 | copy.accessibilityLabel = self.accessibilityLabel; 147 | copy.accessibilityHint = self.accessibilityHint; 148 | copy.accessibilityValue = self.accessibilityValue; 149 | copy.accessibilityTraits = self.accessibilityTraits; 150 | copy.enableAutoDetectAccessibility = self.enableAutoDetectAccessibility; 151 | } 152 | return copy; 153 | } 154 | 155 | @end 156 | -------------------------------------------------------------------------------- /Sources/TTGTextTagAttributedStringContent.h: -------------------------------------------------------------------------------- 1 | // 2 | // TTGTextTagAttributedStringContent.h 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2019/5/24. 6 | // Copyright (c) 2021 zekunyan. All rights reserved. 7 | // 8 | 9 | #import "TTGTextTagContent.h" 10 | 11 | /** 12 | Rich text content for tag 13 | */ 14 | @interface TTGTextTagAttributedStringContent : TTGTextTagContent 15 | 16 | /// Attributed text 17 | @property (nonatomic, copy) NSAttributedString * _Nonnull attributedText; 18 | 19 | /// Init with rich text 20 | - (instancetype _Nonnull)initWithAttributedText:(NSAttributedString *_Nonnull)attributedText; 21 | 22 | /// Content with rich text 23 | + (instancetype _Nonnull)contentWithAttributedText:(NSAttributedString *_Nonnull)attributedText; 24 | 25 | /// Base system methods 26 | - (id _Nonnull)copyWithZone:(NSZone *_Nullable)zone; 27 | - (NSString *_Nonnull)description; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /Sources/TTGTextTagAttributedStringContent.m: -------------------------------------------------------------------------------- 1 | // 2 | // TTGTextTagAttributedStringContent.m 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2019/5/24. 6 | // Copyright (c) 2021 zekunyan. All rights reserved. 7 | // 8 | 9 | #import "TTGTextTagAttributedStringContent.h" 10 | 11 | @implementation TTGTextTagAttributedStringContent 12 | 13 | - (instancetype)initWithAttributedText:(NSAttributedString *)attributedText { 14 | self = [super init]; 15 | if (self) { 16 | self.attributedText = attributedText; 17 | } 18 | return self; 19 | } 20 | 21 | + (instancetype)contentWithAttributedText:(NSAttributedString *)attributedText { 22 | return [[self alloc] initWithAttributedText:attributedText]; 23 | } 24 | 25 | - (NSAttributedString *)getContentAttributedString { 26 | return self.attributedText; 27 | } 28 | 29 | - (NSAttributedString *)attributedText { 30 | return _attributedText ?: [NSAttributedString new]; 31 | } 32 | 33 | - (id)copyWithZone:(NSZone *)zone { 34 | TTGTextTagAttributedStringContent *copy = (TTGTextTagAttributedStringContent *)[super copyWithZone:zone]; 35 | if (copy != nil) { 36 | copy.attributedText = self.attributedText; 37 | } 38 | return copy; 39 | } 40 | 41 | - (NSString *)description { 42 | NSMutableString *description = [NSMutableString stringWithFormat:@"<%@: ", NSStringFromClass([self class])]; 43 | [description appendFormat:@"self.attributedText=%@", self.attributedText]; 44 | [description appendString:@">"]; 45 | return description; 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /Sources/TTGTextTagCollectionView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TTGTextTagCollectionView.h 3 | // Pods 4 | // 5 | // Created by zekunyan on 15/12/26. 6 | // Copyright (c) 2021 zekunyan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "TTGTagCollectionView.h" 12 | #import "TTGTextTag.h" 13 | #import "TTGTextTagStringContent.h" 14 | #import "TTGTextTagAttributedStringContent.h" 15 | 16 | /** 17 | Highly useful for text tag display. 18 | */ 19 | 20 | @class TTGTextTagCollectionView; 21 | 22 | /// Delegate 23 | @protocol TTGTextTagCollectionViewDelegate 24 | @optional 25 | 26 | - (BOOL)textTagCollectionView:(TTGTextTagCollectionView *)textTagCollectionView 27 | canTapTag:(TTGTextTag *)tag 28 | atIndex:(NSUInteger)index; 29 | 30 | - (void)textTagCollectionView:(TTGTextTagCollectionView *)textTagCollectionView 31 | didTapTag:(TTGTextTag *)tag 32 | atIndex:(NSUInteger)index; 33 | 34 | - (void)textTagCollectionView:(TTGTextTagCollectionView *)textTagCollectionView 35 | updateContentSize:(CGSize)contentSize; 36 | @end 37 | 38 | /// Main Class 39 | @interface TTGTextTagCollectionView : UIView 40 | /// Delegate 41 | @property (weak, nonatomic) id delegate; 42 | 43 | /// Inside scrollView 44 | @property (nonatomic, strong, readonly) UIScrollView *scrollView; 45 | 46 | /// Define if the tag can be selected. 47 | @property (assign, nonatomic) BOOL enableTagSelection; 48 | 49 | /// Tags scroll direction, default is vertical. 50 | @property (nonatomic, assign) TTGTagCollectionScrollDirection scrollDirection; 51 | 52 | /// Tags layout alignment, default is left. 53 | @property (nonatomic, assign) TTGTagCollectionAlignment alignment; 54 | 55 | /// Number of lines. 0 means no limit, default is 0 for vertical and 1 for horizontal. 56 | @property (nonatomic, assign) NSUInteger numberOfLines; 57 | /// The real number of lines ignoring the numberOfLines value 58 | @property (nonatomic, assign, readonly) NSUInteger actualNumberOfLines; 59 | 60 | /// Tag selection limit, default is 0, means no limit 61 | @property (nonatomic, assign) NSUInteger selectionLimit; 62 | 63 | /// Horizontal and vertical space between tags, default is 4. 64 | @property (assign, nonatomic) CGFloat horizontalSpacing; 65 | @property (assign, nonatomic) CGFloat verticalSpacing; 66 | 67 | /// Content inset, like padding, default is UIEdgeInsetsMake(2, 2, 2, 2). 68 | @property (nonatomic, assign) UIEdgeInsets contentInset; 69 | 70 | /// The true tags content size, readonly 71 | @property (nonatomic, assign, readonly) CGSize contentSize; 72 | 73 | /// Manual content height 74 | /// Default = NO, set will update content 75 | @property (nonatomic, assign) BOOL manualCalculateHeight; 76 | /// Default = 0, set will update content 77 | @property (nonatomic, assign) CGFloat preferredMaxLayoutWidth; 78 | 79 | /// Scroll indicator 80 | @property (nonatomic, assign) BOOL showsHorizontalScrollIndicator; 81 | @property (nonatomic, assign) BOOL showsVerticalScrollIndicator; 82 | 83 | /// Tap blank area callback 84 | @property (nonatomic, copy) void (^onTapBlankArea)(CGPoint location); 85 | /// Tap all area callback 86 | @property (nonatomic, copy) void (^onTapAllArea)(CGPoint location); 87 | 88 | /// Reload 89 | - (void)reload; 90 | 91 | /// Add 92 | - (void)addTag:(TTGTextTag *)tag; 93 | - (void)addTags:(NSArray *)tags; 94 | 95 | /// Insert 96 | - (void)insertTag:(TTGTextTag *)tag atIndex:(NSUInteger)index; 97 | - (void)insertTags:(NSArray *)tags atIndex:(NSUInteger)index; 98 | 99 | /// Update 100 | - (void)updateTagAtIndex:(NSUInteger)index selected:(BOOL)selected; 101 | - (void)updateTagAtIndex:(NSUInteger)index withNewTag:(TTGTextTag *)tag; 102 | 103 | /// Remove 104 | - (void)removeTag:(TTGTextTag *)tag; 105 | - (void)removeTagById:(NSUInteger)tagId; 106 | - (void)removeTagAtIndex:(NSUInteger)index; 107 | - (void)removeAllTags; 108 | 109 | /// Get tag 110 | - (TTGTextTag *)getTagAtIndex:(NSUInteger)index; 111 | - (NSArray *)getTagsInRange:(NSRange)range; 112 | 113 | /// Get all 114 | - (NSArray *)allTags; 115 | - (NSArray *)allSelectedTags; 116 | - (NSArray *)allNotSelectedTags; 117 | 118 | /** 119 | * Returns the index of the tag located at the specified point. 120 | * If item at point is not found, returns NSNotFound. 121 | */ 122 | - (NSInteger)indexOfTagAtPoint:(CGPoint)point; 123 | 124 | @end 125 | -------------------------------------------------------------------------------- /Sources/TTGTextTagContent.h: -------------------------------------------------------------------------------- 1 | // 2 | // TTGTextTagContent.h 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2019/5/24. 6 | // Copyright (c) 2021 zekunyan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | Base content class. 13 | Do not use this class directly! 14 | */ 15 | @interface TTGTextTagContent : NSObject 16 | 17 | /// Must be override by subClass 18 | - (NSAttributedString *_Nonnull)getContentAttributedString; 19 | 20 | /// Must be override by subClass 21 | - (id _Nonnull)copyWithZone:(NSZone *_Nullable)zone; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Sources/TTGTextTagContent.m: -------------------------------------------------------------------------------- 1 | // 2 | // TTGTextTagContent.m 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2019/5/24. 6 | // Copyright (c) 2021 zekunyan. All rights reserved. 7 | // 8 | 9 | #import "TTGTextTagContent.h" 10 | 11 | @implementation TTGTextTagContent 12 | 13 | - (NSAttributedString *)getContentAttributedString { 14 | NSAssert(NO, @"Do not use TTGTextTagContent directly."); 15 | return [NSAttributedString new]; 16 | } 17 | 18 | - (id)copyWithZone:(NSZone *)zone { 19 | return [[[self class] allocWithZone:zone] init]; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Sources/TTGTextTagStringContent.h: -------------------------------------------------------------------------------- 1 | // 2 | // TTGTextTagStringContent.h 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2019/5/24. 6 | // Copyright (c) 2021 zekunyan. All rights reserved. 7 | // 8 | 9 | #import "TTGTextTagContent.h" 10 | 11 | /** 12 | Normal text content with custom font and color. 13 | */ 14 | @interface TTGTextTagStringContent : TTGTextTagContent 15 | 16 | /// Text 17 | @property (nonatomic, copy) NSString * _Nonnull text; 18 | /// Text font 19 | @property (nonatomic, copy) UIFont * _Nonnull textFont; 20 | /// Text color 21 | @property (nonatomic, copy) UIColor * _Nonnull textColor; 22 | 23 | /// Init 24 | - (instancetype _Nonnull)initWithText:(NSString *_Nonnull)text; 25 | - (instancetype _Nonnull)initWithText:(NSString *_Nonnull)text 26 | textFont:(UIFont *_Nullable)textFont 27 | textColor:(UIColor *_Nullable)textColor; 28 | 29 | /// Content 30 | + (instancetype _Nonnull)contentWithText:(NSString *_Nonnull)text; 31 | + (instancetype _Nonnull)contentWithText:(NSString *_Nonnull)text 32 | textFont:(UIFont *_Nullable)textFont 33 | textColor:(UIColor *_Nullable)textColor; 34 | 35 | /// Base system methods 36 | - (id _Nonnull)copyWithZone:(NSZone *_Nullable)zone; 37 | - (NSString *_Nonnull)description; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Sources/TTGTextTagStringContent.m: -------------------------------------------------------------------------------- 1 | // 2 | // TTGTextTagStringContent.m 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2019/5/24. 6 | // Copyright (c) 2021 zekunyan. All rights reserved. 7 | // 8 | 9 | #import "TTGTextTagStringContent.h" 10 | 11 | @implementation TTGTextTagStringContent 12 | 13 | - (instancetype)initWithText:(NSString *)text { 14 | self = [super init]; 15 | if (self) { 16 | self.text = text; 17 | } 18 | return self; 19 | } 20 | 21 | + (instancetype)contentWithText:(NSString *)text { 22 | return [[self alloc] initWithText:text]; 23 | } 24 | 25 | - (instancetype)initWithText:(NSString *)text 26 | textFont:(UIFont *)textFont 27 | textColor:(UIColor *)textColor { 28 | self = [super init]; 29 | if (self) { 30 | self.text = text; 31 | self.textFont = textFont; 32 | self.textColor = textColor; 33 | } 34 | return self; 35 | } 36 | 37 | + (instancetype)contentWithText:(NSString *)text 38 | textFont:(UIFont *)textFont 39 | textColor:(UIColor *)textColor { 40 | return [[self alloc] initWithText:text 41 | textFont:textFont 42 | textColor:textColor]; 43 | } 44 | 45 | - (NSAttributedString *)getContentAttributedString { 46 | return [[NSAttributedString alloc] initWithString:self.text 47 | attributes:@{NSForegroundColorAttributeName: self.textColor, 48 | NSFontAttributeName: self.textFont}]; 49 | } 50 | 51 | - (NSString *)text { 52 | return _text ?: @""; 53 | } 54 | 55 | - (UIFont *)textFont { 56 | return _textFont ?: [UIFont systemFontOfSize:14]; 57 | } 58 | 59 | - (UIColor *)textColor { 60 | return _textColor ?: [UIColor blackColor]; 61 | } 62 | 63 | - (id)copyWithZone:(NSZone *)zone { 64 | TTGTextTagStringContent *copy = (TTGTextTagStringContent *)[super copyWithZone:zone]; 65 | if (copy != nil) { 66 | copy.text = self.text; 67 | copy.textFont = self.textFont; 68 | copy.textColor = self.textColor; 69 | } 70 | return copy; 71 | } 72 | 73 | - (NSString *)description { 74 | NSMutableString *description = [NSMutableString stringWithFormat:@"<%@: ", NSStringFromClass([self class])]; 75 | [description appendFormat:@"self.text=%@", self.text]; 76 | [description appendString:@">"]; 77 | return description; 78 | } 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /Sources/TTGTextTagStyle.h: -------------------------------------------------------------------------------- 1 | // 2 | // TTGTextTagStyle.h 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2019/5/24. 6 | // Copyright (c) 2021 zekunyan. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface TTGTextTagStyle : NSObject 13 | 14 | /// Background color 15 | @property (nonatomic, copy) UIColor * _Nonnull backgroundColor; // Default is [UIColor lightGrayColor] 16 | 17 | /// Text alignment 18 | @property (nonatomic, assign) NSTextAlignment textAlignment; // Default is NSTextAlignmentCenter 19 | 20 | /// Gradient background color 21 | @property (nonatomic, assign) BOOL enableGradientBackground; // Default is NO 22 | @property (nonatomic, copy) UIColor * _Nonnull gradientBackgroundStartColor; 23 | @property (nonatomic, copy) UIColor * _Nonnull gradientBackgroundEndColor; 24 | @property (nonatomic, assign) CGPoint gradientBackgroundStartPoint; 25 | @property (nonatomic, assign) CGPoint gradientBackgroundEndPoint; 26 | 27 | /// Corner radius 28 | @property (nonatomic, assign) CGFloat cornerRadius; // Default is 4 29 | @property (nonatomic, assign) Boolean cornerTopRight; 30 | @property (nonatomic, assign) Boolean cornerTopLeft; 31 | @property (nonatomic, assign) Boolean cornerBottomRight; 32 | @property (nonatomic, assign) Boolean cornerBottomLeft; 33 | 34 | /// Border 35 | @property (nonatomic, assign) CGFloat borderWidth; // Default is [UIColor whiteColor] 36 | @property (nonatomic, copy) UIColor * _Nonnull borderColor; // Default is 1 37 | 38 | /// Shadow. 39 | @property (nonatomic, copy) UIColor * _Nonnull shadowColor; // Default is [UIColor blackColor] 40 | @property (nonatomic, assign) CGSize shadowOffset; // Default is (2, 2) 41 | @property (nonatomic, assign) CGFloat shadowRadius; // Default is 2f 42 | @property (nonatomic, assign) CGFloat shadowOpacity; // Default is 0.3f 43 | 44 | /// Extra space in width and height, will expand each tag's size 45 | @property (nonatomic, assign) CGSize extraSpace; 46 | 47 | /// Max width for a text tag. 0 and below means no max width. 48 | @property (nonatomic, assign) CGFloat maxWidth; 49 | /// Min width for a text tag. 0 and below means no min width. 50 | @property (nonatomic, assign) CGFloat minWidth; 51 | 52 | /// Max height for a text tag. 0 and below means no max height. 53 | @property (nonatomic, assign) CGFloat maxHeight; 54 | /// Min height for a text tag. 0 and below means no min height. 55 | @property (nonatomic, assign) CGFloat minHeight; 56 | 57 | /// Exact width. 0 and below means no work 58 | @property (nonatomic, assign) CGFloat exactWidth; 59 | /// Exact height. 0 and below means no work 60 | @property (nonatomic, assign) CGFloat exactHeight; 61 | 62 | /// Copy 63 | - (id _Nonnull)copyWithZone:(NSZone *_Nullable)zone; 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /Sources/TTGTextTagStyle.m: -------------------------------------------------------------------------------- 1 | // 2 | // TTGTextTagStyle.m 3 | // TTGTagCollectionView 4 | // 5 | // Created by zekunyan on 2019/5/24. 6 | // Copyright (c) 2021 zekunyan. All rights reserved. 7 | // 8 | 9 | #import "TTGTextTagStyle.h" 10 | 11 | @implementation TTGTextTagStyle 12 | 13 | - (instancetype)init { 14 | self = [super init]; 15 | if (self) { 16 | _backgroundColor = [UIColor lightGrayColor]; 17 | _textAlignment = NSTextAlignmentCenter; 18 | _enableGradientBackground = NO; 19 | _cornerRadius = 4; 20 | _borderColor = [UIColor whiteColor]; 21 | _borderWidth = 1; 22 | _shadowColor = [UIColor blackColor]; 23 | _shadowOffset = CGSizeMake(2, 2); 24 | _shadowRadius = 2; 25 | _shadowOpacity = 0.3; 26 | } 27 | return self; 28 | } 29 | 30 | - (id)copyWithZone:(NSZone *)zone { 31 | TTGTextTagStyle *copy = (TTGTextTagStyle *)[[[self class] allocWithZone:zone] init]; 32 | 33 | if (copy != nil) { 34 | copy.backgroundColor = self.backgroundColor; 35 | copy.textAlignment = self.textAlignment; 36 | copy.enableGradientBackground = self.enableGradientBackground; 37 | copy.gradientBackgroundStartColor = self.gradientBackgroundStartColor; 38 | copy.gradientBackgroundEndColor = self.gradientBackgroundEndColor; 39 | copy.gradientBackgroundStartPoint = self.gradientBackgroundStartPoint; 40 | copy.gradientBackgroundEndPoint = self.gradientBackgroundEndPoint; 41 | copy.cornerRadius = self.cornerRadius; 42 | copy.cornerTopRight = self.cornerTopRight; 43 | copy.cornerTopLeft = self.cornerTopLeft; 44 | copy.cornerBottomRight = self.cornerBottomRight; 45 | copy.cornerBottomLeft = self.cornerBottomLeft; 46 | copy.borderWidth = self.borderWidth; 47 | copy.borderColor = self.borderColor; 48 | copy.shadowColor = self.shadowColor; 49 | copy.shadowOffset = self.shadowOffset; 50 | copy.shadowRadius = self.shadowRadius; 51 | copy.shadowOpacity = self.shadowOpacity; 52 | copy.extraSpace = self.extraSpace; 53 | copy.maxWidth = self.maxWidth; 54 | copy.minWidth = self.minWidth; 55 | copy.maxHeight = self.maxHeight; 56 | copy.minHeight = self.minHeight; 57 | copy.exactWidth = self.exactWidth; 58 | copy.exactHeight = self.exactHeight; 59 | } 60 | 61 | return copy; 62 | } 63 | 64 | - (UIColor *)backgroundColor { 65 | return _backgroundColor ?: [UIColor clearColor]; 66 | } 67 | 68 | - (UIColor *)gradientBackgroundStartColor { 69 | return _gradientBackgroundStartColor ?: [UIColor clearColor]; 70 | } 71 | 72 | - (UIColor *)gradientBackgroundEndColor { 73 | return _gradientBackgroundEndColor ?: [UIColor clearColor]; 74 | } 75 | 76 | - (UIColor *)borderColor { 77 | return _borderColor ?: [UIColor clearColor]; 78 | } 79 | 80 | - (UIColor *)shadowColor { 81 | return _shadowColor ?: [UIColor clearColor]; 82 | } 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /TTGTagCollectionView.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "TTGTagCollectionView" 3 | s.module_name = "TTGTags" 4 | s.version = "2.4.2" 5 | s.summary = "Show rich style text tags or custom tag views in a vertical or horizontal scrollable view." 6 | 7 | s.description = <<-DESC 8 | TTGTagCollectionView is useful for showing different size tag views in a vertical or horizontal scrollable view and support Autolayout intrinsicContentSize at the same time. And if you only want to show text tags, you can use TTGTextTagCollectionView instead, which has more simple api. At the same time, It is highly customizable that many features of the text tag can be configured, like the tag font size and the background color. 9 | DESC 10 | 11 | s.homepage = "https://github.com/zekunyan/TTGTagCollectionView" 12 | s.license = 'MIT' 13 | s.author = { "zekunyan" => "zekunyan@163.com" } 14 | s.source = { :git => "https://github.com/zekunyan/TTGTagCollectionView.git", :tag => s.version.to_s } 15 | s.social_media_url = 'http://tutuge.me' 16 | 17 | s.swift_version = "5.0" 18 | s.platform = :ios, '11.0' 19 | s.requires_arc = true 20 | 21 | s.source_files = 'Sources/**/*.{h,m}' 22 | s.public_header_files = 'Sources/**/*.h' 23 | end 24 | -------------------------------------------------------------------------------- /TTGTagCollectionView/TTGTagCollectionView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | BF8CF4C928D344240074EB9D /* TTGTextTagStringContent.m in Sources */ = {isa = PBXBuildFile; fileRef = BF8CF4BA28D344240074EB9D /* TTGTextTagStringContent.m */; }; 11 | BF8CF4CA28D344240074EB9D /* TTGTextTag.m in Sources */ = {isa = PBXBuildFile; fileRef = BF8CF4BB28D344240074EB9D /* TTGTextTag.m */; }; 12 | BF8CF4CB28D344240074EB9D /* TTGTextTagAttributedStringContent.h in Headers */ = {isa = PBXBuildFile; fileRef = BF8CF4BC28D344240074EB9D /* TTGTextTagAttributedStringContent.h */; }; 13 | BF8CF4CC28D344240074EB9D /* TTGTagCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = BF8CF4BD28D344240074EB9D /* TTGTagCollectionView.h */; }; 14 | BF8CF4CD28D344240074EB9D /* TTGTextTagStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = BF8CF4BE28D344240074EB9D /* TTGTextTagStyle.h */; }; 15 | BF8CF4CE28D344240074EB9D /* TTGTagCollectionView-Bridging-Header.h in Headers */ = {isa = PBXBuildFile; fileRef = BF8CF4BF28D344240074EB9D /* TTGTagCollectionView-Bridging-Header.h */; }; 16 | BF8CF4CF28D344240074EB9D /* TTGTextTagCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = BF8CF4C028D344240074EB9D /* TTGTextTagCollectionView.h */; }; 17 | BF8CF4D028D344240074EB9D /* TTGTextTagContent.h in Headers */ = {isa = PBXBuildFile; fileRef = BF8CF4C128D344240074EB9D /* TTGTextTagContent.h */; }; 18 | BF8CF4D128D344240074EB9D /* TTGTagCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = BF8CF4C228D344240074EB9D /* TTGTagCollectionView.m */; }; 19 | BF8CF4D228D344240074EB9D /* TTGTextTagAttributedStringContent.m in Sources */ = {isa = PBXBuildFile; fileRef = BF8CF4C328D344240074EB9D /* TTGTextTagAttributedStringContent.m */; }; 20 | BF8CF4D328D344240074EB9D /* TTGTextTag.h in Headers */ = {isa = PBXBuildFile; fileRef = BF8CF4C428D344240074EB9D /* TTGTextTag.h */; }; 21 | BF8CF4D428D344240074EB9D /* TTGTextTagStringContent.h in Headers */ = {isa = PBXBuildFile; fileRef = BF8CF4C528D344240074EB9D /* TTGTextTagStringContent.h */; }; 22 | BF8CF4D528D344240074EB9D /* TTGTextTagStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = BF8CF4C628D344240074EB9D /* TTGTextTagStyle.m */; }; 23 | BF8CF4D628D344240074EB9D /* TTGTextTagContent.m in Sources */ = {isa = PBXBuildFile; fileRef = BF8CF4C728D344240074EB9D /* TTGTextTagContent.m */; }; 24 | BF8CF4D728D344240074EB9D /* TTGTextTagCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = BF8CF4C828D344240074EB9D /* TTGTextTagCollectionView.m */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | BF695C4C262044EB00B5B6AC /* TTGTagCollectionView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TTGTagCollectionView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | BF695C50262044EB00B5B6AC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 30 | BF8CF4BA28D344240074EB9D /* TTGTextTagStringContent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTGTextTagStringContent.m; sourceTree = ""; }; 31 | BF8CF4BB28D344240074EB9D /* TTGTextTag.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTGTextTag.m; sourceTree = ""; }; 32 | BF8CF4BC28D344240074EB9D /* TTGTextTagAttributedStringContent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTGTextTagAttributedStringContent.h; sourceTree = ""; }; 33 | BF8CF4BD28D344240074EB9D /* TTGTagCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTGTagCollectionView.h; sourceTree = ""; }; 34 | BF8CF4BE28D344240074EB9D /* TTGTextTagStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTGTextTagStyle.h; sourceTree = ""; }; 35 | BF8CF4BF28D344240074EB9D /* TTGTagCollectionView-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TTGTagCollectionView-Bridging-Header.h"; sourceTree = ""; }; 36 | BF8CF4C028D344240074EB9D /* TTGTextTagCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTGTextTagCollectionView.h; sourceTree = ""; }; 37 | BF8CF4C128D344240074EB9D /* TTGTextTagContent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTGTextTagContent.h; sourceTree = ""; }; 38 | BF8CF4C228D344240074EB9D /* TTGTagCollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTGTagCollectionView.m; sourceTree = ""; }; 39 | BF8CF4C328D344240074EB9D /* TTGTextTagAttributedStringContent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTGTextTagAttributedStringContent.m; sourceTree = ""; }; 40 | BF8CF4C428D344240074EB9D /* TTGTextTag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTGTextTag.h; sourceTree = ""; }; 41 | BF8CF4C528D344240074EB9D /* TTGTextTagStringContent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTGTextTagStringContent.h; sourceTree = ""; }; 42 | BF8CF4C628D344240074EB9D /* TTGTextTagStyle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTGTextTagStyle.m; sourceTree = ""; }; 43 | BF8CF4C728D344240074EB9D /* TTGTextTagContent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTGTextTagContent.m; sourceTree = ""; }; 44 | BF8CF4C828D344240074EB9D /* TTGTextTagCollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTGTextTagCollectionView.m; sourceTree = ""; }; 45 | BFE7206F262EDA69008C29D0 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 46 | /* End PBXFileReference section */ 47 | 48 | /* Begin PBXFrameworksBuildPhase section */ 49 | BF695C49262044EB00B5B6AC /* Frameworks */ = { 50 | isa = PBXFrameworksBuildPhase; 51 | buildActionMask = 2147483647; 52 | files = ( 53 | ); 54 | runOnlyForDeploymentPostprocessing = 0; 55 | }; 56 | /* End PBXFrameworksBuildPhase section */ 57 | 58 | /* Begin PBXGroup section */ 59 | BF695C42262044EB00B5B6AC = { 60 | isa = PBXGroup; 61 | children = ( 62 | BF695C4E262044EB00B5B6AC /* TTGTagCollectionView */, 63 | BF695C4D262044EB00B5B6AC /* Products */, 64 | BFE7206E262EDA69008C29D0 /* Frameworks */, 65 | ); 66 | sourceTree = ""; 67 | }; 68 | BF695C4D262044EB00B5B6AC /* Products */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | BF695C4C262044EB00B5B6AC /* TTGTagCollectionView.framework */, 72 | ); 73 | name = Products; 74 | sourceTree = ""; 75 | }; 76 | BF695C4E262044EB00B5B6AC /* TTGTagCollectionView */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | BF8CF4B928D344240074EB9D /* Sources */, 80 | BF695C50262044EB00B5B6AC /* Info.plist */, 81 | ); 82 | path = TTGTagCollectionView; 83 | sourceTree = ""; 84 | }; 85 | BF8CF4B928D344240074EB9D /* Sources */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | BF8CF4BA28D344240074EB9D /* TTGTextTagStringContent.m */, 89 | BF8CF4BB28D344240074EB9D /* TTGTextTag.m */, 90 | BF8CF4BC28D344240074EB9D /* TTGTextTagAttributedStringContent.h */, 91 | BF8CF4BD28D344240074EB9D /* TTGTagCollectionView.h */, 92 | BF8CF4BE28D344240074EB9D /* TTGTextTagStyle.h */, 93 | BF8CF4BF28D344240074EB9D /* TTGTagCollectionView-Bridging-Header.h */, 94 | BF8CF4C028D344240074EB9D /* TTGTextTagCollectionView.h */, 95 | BF8CF4C128D344240074EB9D /* TTGTextTagContent.h */, 96 | BF8CF4C228D344240074EB9D /* TTGTagCollectionView.m */, 97 | BF8CF4C328D344240074EB9D /* TTGTextTagAttributedStringContent.m */, 98 | BF8CF4C428D344240074EB9D /* TTGTextTag.h */, 99 | BF8CF4C528D344240074EB9D /* TTGTextTagStringContent.h */, 100 | BF8CF4C628D344240074EB9D /* TTGTextTagStyle.m */, 101 | BF8CF4C728D344240074EB9D /* TTGTextTagContent.m */, 102 | BF8CF4C828D344240074EB9D /* TTGTextTagCollectionView.m */, 103 | ); 104 | name = Sources; 105 | path = ../../Sources; 106 | sourceTree = ""; 107 | }; 108 | BFE7206E262EDA69008C29D0 /* Frameworks */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | BFE7206F262EDA69008C29D0 /* UIKit.framework */, 112 | ); 113 | name = Frameworks; 114 | sourceTree = ""; 115 | }; 116 | /* End PBXGroup section */ 117 | 118 | /* Begin PBXHeadersBuildPhase section */ 119 | BF695C47262044EB00B5B6AC /* Headers */ = { 120 | isa = PBXHeadersBuildPhase; 121 | buildActionMask = 2147483647; 122 | files = ( 123 | BF8CF4CF28D344240074EB9D /* TTGTextTagCollectionView.h in Headers */, 124 | BF8CF4D428D344240074EB9D /* TTGTextTagStringContent.h in Headers */, 125 | BF8CF4CC28D344240074EB9D /* TTGTagCollectionView.h in Headers */, 126 | BF8CF4CB28D344240074EB9D /* TTGTextTagAttributedStringContent.h in Headers */, 127 | BF8CF4CD28D344240074EB9D /* TTGTextTagStyle.h in Headers */, 128 | BF8CF4D028D344240074EB9D /* TTGTextTagContent.h in Headers */, 129 | BF8CF4D328D344240074EB9D /* TTGTextTag.h in Headers */, 130 | BF8CF4CE28D344240074EB9D /* TTGTagCollectionView-Bridging-Header.h in Headers */, 131 | ); 132 | runOnlyForDeploymentPostprocessing = 0; 133 | }; 134 | /* End PBXHeadersBuildPhase section */ 135 | 136 | /* Begin PBXNativeTarget section */ 137 | BF695C4B262044EB00B5B6AC /* TTGTagCollectionView */ = { 138 | isa = PBXNativeTarget; 139 | buildConfigurationList = BF695C54262044EB00B5B6AC /* Build configuration list for PBXNativeTarget "TTGTagCollectionView" */; 140 | buildPhases = ( 141 | BF695C47262044EB00B5B6AC /* Headers */, 142 | BF695C48262044EB00B5B6AC /* Sources */, 143 | BF695C49262044EB00B5B6AC /* Frameworks */, 144 | BF695C4A262044EB00B5B6AC /* Resources */, 145 | ); 146 | buildRules = ( 147 | ); 148 | dependencies = ( 149 | ); 150 | name = TTGTagCollectionView; 151 | productName = TTGTagCollectionView; 152 | productReference = BF695C4C262044EB00B5B6AC /* TTGTagCollectionView.framework */; 153 | productType = "com.apple.product-type.framework"; 154 | }; 155 | /* End PBXNativeTarget section */ 156 | 157 | /* Begin PBXProject section */ 158 | BF695C43262044EB00B5B6AC /* Project object */ = { 159 | isa = PBXProject; 160 | attributes = { 161 | LastUpgradeCheck = 1240; 162 | TargetAttributes = { 163 | BF695C4B262044EB00B5B6AC = { 164 | CreatedOnToolsVersion = 12.4; 165 | }; 166 | }; 167 | }; 168 | buildConfigurationList = BF695C46262044EB00B5B6AC /* Build configuration list for PBXProject "TTGTagCollectionView" */; 169 | compatibilityVersion = "Xcode 9.3"; 170 | developmentRegion = en; 171 | hasScannedForEncodings = 0; 172 | knownRegions = ( 173 | en, 174 | Base, 175 | ); 176 | mainGroup = BF695C42262044EB00B5B6AC; 177 | productRefGroup = BF695C4D262044EB00B5B6AC /* Products */; 178 | projectDirPath = ""; 179 | projectRoot = ""; 180 | targets = ( 181 | BF695C4B262044EB00B5B6AC /* TTGTagCollectionView */, 182 | ); 183 | }; 184 | /* End PBXProject section */ 185 | 186 | /* Begin PBXResourcesBuildPhase section */ 187 | BF695C4A262044EB00B5B6AC /* Resources */ = { 188 | isa = PBXResourcesBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | ); 192 | runOnlyForDeploymentPostprocessing = 0; 193 | }; 194 | /* End PBXResourcesBuildPhase section */ 195 | 196 | /* Begin PBXSourcesBuildPhase section */ 197 | BF695C48262044EB00B5B6AC /* Sources */ = { 198 | isa = PBXSourcesBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | BF8CF4D228D344240074EB9D /* TTGTextTagAttributedStringContent.m in Sources */, 202 | BF8CF4D628D344240074EB9D /* TTGTextTagContent.m in Sources */, 203 | BF8CF4D728D344240074EB9D /* TTGTextTagCollectionView.m in Sources */, 204 | BF8CF4C928D344240074EB9D /* TTGTextTagStringContent.m in Sources */, 205 | BF8CF4D128D344240074EB9D /* TTGTagCollectionView.m in Sources */, 206 | BF8CF4D528D344240074EB9D /* TTGTextTagStyle.m in Sources */, 207 | BF8CF4CA28D344240074EB9D /* TTGTextTag.m in Sources */, 208 | ); 209 | runOnlyForDeploymentPostprocessing = 0; 210 | }; 211 | /* End PBXSourcesBuildPhase section */ 212 | 213 | /* Begin XCBuildConfiguration section */ 214 | BF695C52262044EB00B5B6AC /* Debug */ = { 215 | isa = XCBuildConfiguration; 216 | buildSettings = { 217 | ALWAYS_SEARCH_USER_PATHS = NO; 218 | CLANG_ANALYZER_NONNULL = YES; 219 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 220 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 221 | CLANG_CXX_LIBRARY = "libc++"; 222 | CLANG_ENABLE_MODULES = YES; 223 | CLANG_ENABLE_OBJC_ARC = YES; 224 | CLANG_ENABLE_OBJC_WEAK = YES; 225 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 226 | CLANG_WARN_BOOL_CONVERSION = YES; 227 | CLANG_WARN_COMMA = YES; 228 | CLANG_WARN_CONSTANT_CONVERSION = YES; 229 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 230 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 231 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 232 | CLANG_WARN_EMPTY_BODY = YES; 233 | CLANG_WARN_ENUM_CONVERSION = YES; 234 | CLANG_WARN_INFINITE_RECURSION = YES; 235 | CLANG_WARN_INT_CONVERSION = YES; 236 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 237 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 238 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 239 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 240 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 241 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 242 | CLANG_WARN_STRICT_PROTOTYPES = YES; 243 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 244 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 245 | CLANG_WARN_UNREACHABLE_CODE = YES; 246 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 247 | COPY_PHASE_STRIP = NO; 248 | CURRENT_PROJECT_VERSION = 1; 249 | DEBUG_INFORMATION_FORMAT = dwarf; 250 | ENABLE_STRICT_OBJC_MSGSEND = YES; 251 | ENABLE_TESTABILITY = YES; 252 | GCC_C_LANGUAGE_STANDARD = gnu11; 253 | GCC_DYNAMIC_NO_PIC = NO; 254 | GCC_NO_COMMON_BLOCKS = YES; 255 | GCC_OPTIMIZATION_LEVEL = 0; 256 | GCC_PREPROCESSOR_DEFINITIONS = ( 257 | "DEBUG=1", 258 | "$(inherited)", 259 | ); 260 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 261 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 262 | GCC_WARN_UNDECLARED_SELECTOR = YES; 263 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 264 | GCC_WARN_UNUSED_FUNCTION = YES; 265 | GCC_WARN_UNUSED_VARIABLE = YES; 266 | IPHONEOS_DEPLOYMENT_TARGET = 14.4; 267 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 268 | MTL_FAST_MATH = YES; 269 | ONLY_ACTIVE_ARCH = YES; 270 | SDKROOT = iphoneos; 271 | VERSIONING_SYSTEM = "apple-generic"; 272 | VERSION_INFO_PREFIX = ""; 273 | }; 274 | name = Debug; 275 | }; 276 | BF695C53262044EB00B5B6AC /* Release */ = { 277 | isa = XCBuildConfiguration; 278 | buildSettings = { 279 | ALWAYS_SEARCH_USER_PATHS = NO; 280 | CLANG_ANALYZER_NONNULL = YES; 281 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 282 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 283 | CLANG_CXX_LIBRARY = "libc++"; 284 | CLANG_ENABLE_MODULES = YES; 285 | CLANG_ENABLE_OBJC_ARC = YES; 286 | CLANG_ENABLE_OBJC_WEAK = YES; 287 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 288 | CLANG_WARN_BOOL_CONVERSION = YES; 289 | CLANG_WARN_COMMA = YES; 290 | CLANG_WARN_CONSTANT_CONVERSION = YES; 291 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 292 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 293 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 294 | CLANG_WARN_EMPTY_BODY = YES; 295 | CLANG_WARN_ENUM_CONVERSION = YES; 296 | CLANG_WARN_INFINITE_RECURSION = YES; 297 | CLANG_WARN_INT_CONVERSION = YES; 298 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 299 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 300 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 301 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 302 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 303 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 304 | CLANG_WARN_STRICT_PROTOTYPES = YES; 305 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 306 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 307 | CLANG_WARN_UNREACHABLE_CODE = YES; 308 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 309 | COPY_PHASE_STRIP = NO; 310 | CURRENT_PROJECT_VERSION = 1; 311 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 312 | ENABLE_NS_ASSERTIONS = NO; 313 | ENABLE_STRICT_OBJC_MSGSEND = YES; 314 | GCC_C_LANGUAGE_STANDARD = gnu11; 315 | GCC_NO_COMMON_BLOCKS = YES; 316 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 317 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 318 | GCC_WARN_UNDECLARED_SELECTOR = YES; 319 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 320 | GCC_WARN_UNUSED_FUNCTION = YES; 321 | GCC_WARN_UNUSED_VARIABLE = YES; 322 | IPHONEOS_DEPLOYMENT_TARGET = 14.4; 323 | MTL_ENABLE_DEBUG_INFO = NO; 324 | MTL_FAST_MATH = YES; 325 | SDKROOT = iphoneos; 326 | VALIDATE_PRODUCT = YES; 327 | VERSIONING_SYSTEM = "apple-generic"; 328 | VERSION_INFO_PREFIX = ""; 329 | }; 330 | name = Release; 331 | }; 332 | BF695C55262044EB00B5B6AC /* Debug */ = { 333 | isa = XCBuildConfiguration; 334 | buildSettings = { 335 | CODE_SIGN_IDENTITY = "Apple Development"; 336 | CODE_SIGN_STYLE = Automatic; 337 | DEFINES_MODULE = YES; 338 | DEVELOPMENT_TEAM = HPQ8F9926T; 339 | DYLIB_COMPATIBILITY_VERSION = 1; 340 | DYLIB_CURRENT_VERSION = 1; 341 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 342 | INFOPLIST_FILE = TTGTagCollectionView/Info.plist; 343 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 344 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 345 | LD_RUNPATH_SEARCH_PATHS = ( 346 | "$(inherited)", 347 | "@executable_path/Frameworks", 348 | "@loader_path/Frameworks", 349 | ); 350 | PRODUCT_BUNDLE_IDENTIFIER = me.tutuge.ios.opensource.TTGTagCollectionView; 351 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 352 | SKIP_INSTALL = YES; 353 | SUPPORTS_MACCATALYST = NO; 354 | TARGETED_DEVICE_FAMILY = "1,2"; 355 | }; 356 | name = Debug; 357 | }; 358 | BF695C56262044EB00B5B6AC /* Release */ = { 359 | isa = XCBuildConfiguration; 360 | buildSettings = { 361 | CODE_SIGN_IDENTITY = "Apple Development"; 362 | CODE_SIGN_STYLE = Automatic; 363 | DEFINES_MODULE = YES; 364 | DEVELOPMENT_TEAM = HPQ8F9926T; 365 | DYLIB_COMPATIBILITY_VERSION = 1; 366 | DYLIB_CURRENT_VERSION = 1; 367 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 368 | INFOPLIST_FILE = TTGTagCollectionView/Info.plist; 369 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 370 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 371 | LD_RUNPATH_SEARCH_PATHS = ( 372 | "$(inherited)", 373 | "@executable_path/Frameworks", 374 | "@loader_path/Frameworks", 375 | ); 376 | PRODUCT_BUNDLE_IDENTIFIER = me.tutuge.ios.opensource.TTGTagCollectionView; 377 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 378 | SKIP_INSTALL = YES; 379 | SUPPORTS_MACCATALYST = NO; 380 | TARGETED_DEVICE_FAMILY = "1,2"; 381 | }; 382 | name = Release; 383 | }; 384 | /* End XCBuildConfiguration section */ 385 | 386 | /* Begin XCConfigurationList section */ 387 | BF695C46262044EB00B5B6AC /* Build configuration list for PBXProject "TTGTagCollectionView" */ = { 388 | isa = XCConfigurationList; 389 | buildConfigurations = ( 390 | BF695C52262044EB00B5B6AC /* Debug */, 391 | BF695C53262044EB00B5B6AC /* Release */, 392 | ); 393 | defaultConfigurationIsVisible = 0; 394 | defaultConfigurationName = Release; 395 | }; 396 | BF695C54262044EB00B5B6AC /* Build configuration list for PBXNativeTarget "TTGTagCollectionView" */ = { 397 | isa = XCConfigurationList; 398 | buildConfigurations = ( 399 | BF695C55262044EB00B5B6AC /* Debug */, 400 | BF695C56262044EB00B5B6AC /* Release */, 401 | ); 402 | defaultConfigurationIsVisible = 0; 403 | defaultConfigurationName = Release; 404 | }; 405 | /* End XCConfigurationList section */ 406 | }; 407 | rootObject = BF695C43262044EB00B5B6AC /* Project object */; 408 | } 409 | -------------------------------------------------------------------------------- /TTGTagCollectionView/TTGTagCollectionView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TTGTagCollectionView/TTGTagCollectionView.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /TTGTagCollectionView/TTGTagCollectionView/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | --------------------------------------------------------------------------------