├── .gitignore ├── .swift-version ├── ACTagView.gif ├── ACTagView.podspec ├── ACTagViewDemo ├── ACTagView │ ├── ACTagAttribute.swift │ ├── ACTagButton.swift │ ├── ACTagConfig.swift │ ├── ACTagView.h │ ├── ACTagView.swift │ ├── ACTagViewAutoLineFeedLayout.swift │ ├── ACTagViewCell.swift │ ├── ACTagViewFlowLayout.swift │ ├── ACTagViewOneLineLayout.swift │ ├── ACUtil.swift │ └── Info.plist ├── ACTagViewDemo.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── ACTagView.xcscheme └── ACTagViewDemo │ ├── AppDelegate.swift │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── TagViewController.swift │ └── ViewController.swift ├── ACTagView_ver2.0.0.gif ├── LICENSE ├── README(ver1.2.2).md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 5.0 -------------------------------------------------------------------------------- /ACTagView.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChaselAn/ACTagView/b29f697a693b6511f016bbbaf1f7e7d8610fe90c/ACTagView.gif -------------------------------------------------------------------------------- /ACTagView.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'ACTagView' 3 | s.version = '2.3.0' 4 | s.summary = 'Swift4版本的标签页' 5 | s.homepage = 'https://github.com/ChaselAn/ACTagView' 6 | s.license = 'MIT' 7 | s.authors = {'ChaselAn' => '865770853@qq.com'} 8 | s.platform = :ios, '8.0' 9 | s.source = {:git => 'https://github.com/ChaselAn/ACTagView.git', :tag => s.version} 10 | s.source_files = 'ACTagViewDemo/ACTagView/*.swift' 11 | s.requires_arc = true 12 | s.swift_version = '5.0' 13 | end 14 | -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagView/ACTagAttribute.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BetaACTag.swift 3 | // ACTagViewDemo 4 | // 5 | // Created by ac on 2017/7/30. 6 | // Copyright © 2017年 ac. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ACTagAttribute: NSObject { 12 | 13 | open var text = "" 14 | open var selectedBackgroundColor = ACTagConfig.default.selectedTagBackgroundColor 15 | open var backgroundColor = ACTagConfig.default.tagBackgroundColor 16 | open var selectedBorderColor = ACTagConfig.default.selectedTagBorderColor 17 | open var borderColor = ACTagConfig.default.tagBorderColor 18 | open var textColor = ACTagConfig.default.tagTextColor 19 | open var selectedTextColor = ACTagConfig.default.selectedTagTextColor 20 | open var borderType = ACTagConfig.default.tagBorderType 21 | // tag的内边距 22 | open var tagHorizontalPadding = ACTagConfig.default.tagHorizontalPadding 23 | open var font: UIFont = ACTagConfig.default.tagFont 24 | open var borderWidth = ACTagConfig.default.tagBorderWidth 25 | 26 | open func getWidth(height: CGFloat) -> CGFloat { 27 | return text.ac_getWidth(font.pointSize) + 2 * tagHorizontalPadding 28 | } 29 | 30 | convenience public init(text: String) { 31 | self.init() 32 | self.text = text 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagView/ACTagButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ACTagButton.swift 3 | // ACTagViewDemo 4 | // 5 | // Created by ac on 2017/7/30. 6 | // Copyright © 2017年 ac. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ACTagButton: UIButton { 12 | 13 | open var tagAttribute = ACTagAttribute() { 14 | didSet { 15 | setUpUI() 16 | } 17 | } 18 | 19 | override public init(frame: CGRect) { 20 | super.init(frame: frame) 21 | isUserInteractionEnabled = false 22 | isSelected = false 23 | } 24 | 25 | required public init?(coder aDecoder: NSCoder) { 26 | fatalError("init(coder:) has not been implemented") 27 | } 28 | 29 | override open var isSelected: Bool { 30 | didSet { 31 | if isSelected { 32 | backgroundColor = tagAttribute.selectedBackgroundColor 33 | layer.borderColor = tagAttribute.selectedBorderColor.cgColor 34 | } else { 35 | backgroundColor = tagAttribute.backgroundColor 36 | layer.borderColor = tagAttribute.borderColor.cgColor 37 | } 38 | } 39 | } 40 | 41 | private func setUpUI() { 42 | 43 | setTitle(tagAttribute.text, for: .normal) 44 | setTitleColor(tagAttribute.textColor, for: .normal) 45 | setTitleColor(tagAttribute.selectedTextColor, for: .selected) 46 | backgroundColor = tagAttribute.backgroundColor 47 | layer.borderWidth = tagAttribute.borderWidth 48 | titleLabel?.font = tagAttribute.font 49 | 50 | setBorderTyper(tagAttribute.borderType) 51 | } 52 | 53 | private func setBorderTyper(_ type: ACTagBorderType) { 54 | 55 | superview?.layoutIfNeeded() 56 | switch type { 57 | case .halfOfCircle: 58 | layer.cornerRadius = frame.height / 2 59 | layer.masksToBounds = true 60 | case .custom(radius: let radius): 61 | layer.cornerRadius = radius 62 | layer.masksToBounds = true 63 | case .none: 64 | layer.cornerRadius = 0 65 | layer.masksToBounds = false 66 | } 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagView/ACTagConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ACTagConfig.swift 3 | // ACTagViewDemo 4 | // 5 | // Created by ac on 2017/7/5. 6 | // Copyright © 2017年 ac. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public enum ACTagBorderType { 12 | case none 13 | case halfOfCircle 14 | case custom(radius: CGFloat) 15 | } 16 | 17 | public struct ACTagDistance { 18 | 19 | public var horizontal: CGFloat 20 | public var vertical: CGFloat 21 | 22 | public static var zero: ACTagDistance { 23 | return ACTagDistance(horizontal: 0, vertical: 0) 24 | } 25 | 26 | public init(horizontal: CGFloat, vertical: CGFloat) { 27 | self.horizontal = horizontal 28 | self.vertical = vertical 29 | } 30 | } 31 | 32 | //public enum ACInputTagBorderState { 33 | // case none 34 | // case fullLine(cornerRadius: CGFloat) 35 | // case dashLine(cornerRadius: CGFloat, lineDashPattern: [NSNumber]) 36 | // case circleWithFullLine 37 | // case circleWithDashLine(lineDashPattern: [NSNumber]) 38 | //} 39 | 40 | public class ACTagConfig { 41 | 42 | public static let `default` = ACTagConfig() 43 | 44 | public var tagBorderWidth: CGFloat = 1 45 | public var tagBorderType = ACTagBorderType.halfOfCircle 46 | public var tagViewMargin = ACTagDistance(horizontal: 10, vertical: 10) 47 | public var tagMargin = ACTagDistance(horizontal: 10, vertical: 10) 48 | public var tagHorizontalPadding: CGFloat = 15 49 | public var tagFont = UIFont.systemFont(ofSize: 14) 50 | 51 | public var selectedTagBackgroundColor = UIColor.white 52 | public var selectedTagBorderColor = UIColor.red 53 | public var selectedTagTextColor = UIColor.red 54 | public var tagBackgroundColor = UIColor.white 55 | public var tagBorderColor = UIColor.black 56 | public var tagTextColor = UIColor.black 57 | 58 | public var tagDefaultHeight: CGFloat = 30 59 | 60 | // public var inputTagBorderType = ACInputTagBorderState.circleWithDashLine(lineDashPattern: [3, 3]) 61 | // public var inputTagPaddingSize = CGSize.zero 62 | // public var inputTagFontSize: CGFloat = 14 63 | // public var inputTagBorderColor = UIColor.black 64 | 65 | } 66 | -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagView/ACTagView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ACTagView.h 3 | // ACTagView 4 | // 5 | // Created by ancheng on 2018/9/27. 6 | // Copyright © 2018年 ac. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for ACTagView. 12 | FOUNDATION_EXPORT double ACTagViewVersionNumber; 13 | 14 | //! Project version string for ACTagView. 15 | FOUNDATION_EXPORT const unsigned char ACTagViewVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagView/ACTagView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ACTagView.swift 3 | // ACTagViewDemo 4 | // 5 | // Created by ancheng on 2017/7/28. 6 | // Copyright © 2017年 ac. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public enum ACTagViewLayoutType { 12 | case autoLineFeed 13 | case oneLine 14 | case custom(ACTagViewFlowLayout) 15 | } 16 | 17 | public protocol ACTagViewDataSource: NSObjectProtocol { 18 | 19 | func numberOfTags(in tagView: ACTagView) -> Int 20 | 21 | func tagView(_ tagView: ACTagView, tagAttributeForIndexAt index: Int) -> ACTagAttribute 22 | 23 | } 24 | 25 | @objc public protocol ACTagViewDelegate: NSObjectProtocol { 26 | 27 | @objc optional func tagView(_ tagView: ACTagView, didSelectTagAt index: Int) 28 | 29 | @objc optional func tagView(_ tagView: ACTagView, didDeselectTagAt index: Int) 30 | } 31 | 32 | open class ACTagView: UIView { 33 | 34 | open weak var tagDataSource: ACTagViewDataSource? 35 | open weak var tagDelegate: ACTagViewDelegate? 36 | open var tagHeight: CGFloat = ACTagConfig.default.tagDefaultHeight { 37 | didSet { 38 | layout.tagHeight = tagHeight 39 | } 40 | } 41 | // 第一个tag距离整个view的上边距和左边距 42 | open var margin = ACTagConfig.default.tagViewMargin { 43 | didSet { 44 | layout.tagViewMargin = margin 45 | } 46 | } 47 | // 两个tag之间的距离 48 | open var tagMargin = ACTagConfig.default.tagMargin { 49 | didSet { 50 | layout.tagMargin = tagMargin 51 | } 52 | } 53 | open var allowsMultipleSelection: Bool = false { 54 | didSet { 55 | collectionView?.allowsMultipleSelection = allowsMultipleSelection 56 | } 57 | } 58 | open var isScrollEnabled: Bool = true { 59 | didSet { 60 | collectionView?.isScrollEnabled = isScrollEnabled 61 | } 62 | } 63 | open var indexsForSelectedTags: [Int] { 64 | return collectionView?.indexPathsForSelectedItems?.map({ $0.item }) ?? [] 65 | } 66 | open var estimatedHeight: CGFloat { 67 | return calculateSize().height 68 | } 69 | open var estimatedSize: CGSize { 70 | return calculateSize() 71 | } 72 | 73 | private var collectionView: UICollectionView? 74 | private var layout: ACTagViewFlowLayout! 75 | 76 | public init(frame: CGRect, layoutType: ACTagViewLayoutType) { 77 | 78 | super.init(frame: frame) 79 | 80 | initTagView(layoutType: layoutType) 81 | } 82 | 83 | required public init?(coder aDecoder: NSCoder) { 84 | super.init(coder: aDecoder) 85 | } 86 | 87 | open override func layoutSubviews() { 88 | super.layoutSubviews() 89 | collectionView?.frame = bounds 90 | } 91 | 92 | // 使用xib创建此控件时,必须调用此方法 93 | open func initTagView(layoutType: ACTagViewLayoutType) { 94 | switch layoutType { 95 | case .autoLineFeed: 96 | layout = ACTagViewAutoLineFeedLayout() 97 | case .oneLine: 98 | layout = ACTagViewOneLineLayout() 99 | case .custom(let customLayout): 100 | layout = customLayout 101 | } 102 | 103 | initCollectionView(layout: layout) 104 | } 105 | 106 | open func selectTag(at index: Int) { 107 | let indexPath = IndexPath(item: index, section: 0) 108 | let cell = collectionView?.cellForItem(at: indexPath) as? ACTagViewCell 109 | cell?.selected() 110 | collectionView?.selectItem(at: indexPath, animated: true, scrollPosition: .top) 111 | } 112 | 113 | open func deselectTag(at index: Int) { 114 | let indexPath = IndexPath(item: index, section: 0) 115 | let cell = collectionView?.cellForItem(at: indexPath) as? ACTagViewCell 116 | cell?.deselected() 117 | collectionView?.deselectItem(at: indexPath, animated: true) 118 | } 119 | 120 | open func tagForIndex(at index: Int) -> ACTagButton? { 121 | let indexPath = IndexPath(item: index, section: 0) 122 | return (collectionView?.cellForItem(at: indexPath) as? ACTagViewCell)?.tagButton 123 | } 124 | 125 | open func reloadData() { 126 | collectionView?.reloadData() 127 | } 128 | 129 | private func initCollectionView(layout: ACTagViewFlowLayout) { 130 | 131 | self.layout = layout 132 | collectionView = UICollectionView(frame: bounds, collectionViewLayout: layout) 133 | 134 | collectionView!.dataSource = self 135 | collectionView!.delegate = self 136 | collectionView!.backgroundColor = UIColor.clear 137 | 138 | collectionView!.register(ACTagViewCell.self, forCellWithReuseIdentifier: "ACTagViewCell") 139 | addSubview(collectionView!) 140 | } 141 | 142 | private func calculateSize() -> CGSize { 143 | return layout.getEstimatedSize(in: self) 144 | } 145 | 146 | } 147 | 148 | extension ACTagView: UICollectionViewDataSource { 149 | 150 | public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 151 | return tagDataSource?.numberOfTags(in: self) ?? 0 152 | } 153 | 154 | public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 155 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ACTagViewCell", for: indexPath) as! ACTagViewCell 156 | guard let tagDataSource = tagDataSource else { return cell } 157 | cell.tagAttribute = tagDataSource.tagView(self, tagAttributeForIndexAt: indexPath.item) 158 | return cell 159 | } 160 | 161 | } 162 | 163 | extension ACTagView: UICollectionViewDelegateFlowLayout { 164 | 165 | public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 166 | guard let tagDataSource = tagDataSource else { return CGSize.zero } 167 | let tagAttribute = tagDataSource.tagView(self, tagAttributeForIndexAt: indexPath.item) 168 | var width = tagAttribute.getWidth(height: tagHeight) 169 | if width < tagHeight { 170 | width = tagHeight 171 | } 172 | return CGSize(width: width, height: tagHeight) 173 | } 174 | 175 | public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 176 | 177 | let cell = collectionView.cellForItem(at: indexPath) as? ACTagViewCell 178 | cell?.selected() 179 | tagDelegate?.tagView?(self, didSelectTagAt: indexPath.item) 180 | } 181 | 182 | public func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { 183 | 184 | let cell = collectionView.cellForItem(at: indexPath) as? ACTagViewCell 185 | cell?.deselected() 186 | tagDelegate?.tagView?(self, didDeselectTagAt: indexPath.item) 187 | } 188 | 189 | } 190 | -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagView/ACTagViewAutoLineFeedLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ACTagViewAutoLineFeedLayout.swift 3 | // ACTagViewDemo 4 | // 5 | // Created by ac on 2017/7/29. 6 | // Copyright © 2017年 ac. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ACTagViewAutoLineFeedLayout: ACTagViewFlowLayout { 12 | 13 | private var offsetY: CGFloat = 0 14 | 15 | override var collectionViewContentSize: CGSize { 16 | guard let collectionView = collectionView else { 17 | return CGSize.zero 18 | } 19 | 20 | collectionView.layoutIfNeeded() 21 | collectionView.superview?.layoutIfNeeded() 22 | 23 | return CGSize(width: collectionView.bounds.width, height: offsetY + tagHeight + tagViewMargin.vertical) 24 | } 25 | 26 | override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 27 | guard let array = super.layoutAttributesForElements(in: rect), let collectionView = collectionView else { return nil } 28 | 29 | collectionView.layoutIfNeeded() 30 | collectionView.superview?.layoutIfNeeded() 31 | 32 | var offsetX = tagViewMargin.horizontal 33 | var offsetY = tagViewMargin.vertical 34 | 35 | var finalAttrs: [UICollectionViewLayoutAttributes] = [] 36 | 37 | for (index,attribute) in array.enumerated() { 38 | 39 | let attrCopy = attribute.copy() as! UICollectionViewLayoutAttributes 40 | 41 | var tempFrame = attrCopy.frame 42 | 43 | if (offsetX + tempFrame.width + tagViewMargin.horizontal) > collectionView.bounds.width { 44 | if index != 0 { 45 | offsetX = tagViewMargin.horizontal 46 | offsetY += tagHeight + tagMargin.vertical 47 | if !collectionView.isScrollEnabled && offsetY + tagHeight + tagViewMargin.vertical > collectionView.bounds.height { 48 | self.offsetY = offsetY 49 | return finalAttrs 50 | } 51 | } else { 52 | offsetX = tagViewMargin.horizontal 53 | } 54 | } 55 | 56 | tempFrame.origin.x = offsetX 57 | tempFrame.origin.y = offsetY 58 | offsetX += tempFrame.width + tagMargin.horizontal 59 | tempFrame.size.height = tagHeight 60 | attrCopy.frame = tempFrame 61 | self.offsetY = offsetY 62 | 63 | finalAttrs += [attrCopy] 64 | } 65 | 66 | return finalAttrs 67 | } 68 | 69 | override func getEstimatedSize(in tagView: ACTagView) -> CGSize { 70 | 71 | guard let collectionView = collectionView, let dataSource = tagView.tagDataSource else { return CGSize.zero } 72 | 73 | tagView.layoutIfNeeded() 74 | tagView.superview?.layoutIfNeeded() 75 | 76 | var offsetX = tagViewMargin.horizontal 77 | var offsetY = tagViewMargin.vertical 78 | 79 | for i in 0 ..< dataSource.numberOfTags(in: tagView) { 80 | 81 | let attribute = dataSource.tagView(tagView, tagAttributeForIndexAt: i) 82 | let width = attribute.getWidth(height: tagHeight) 83 | 84 | if (offsetX + width + tagViewMargin.horizontal) > collectionView.bounds.width { 85 | if i != 0 { 86 | offsetX = tagViewMargin.horizontal 87 | offsetY += tagHeight + tagMargin.vertical 88 | } else { 89 | offsetX = tagViewMargin.horizontal 90 | } 91 | } 92 | 93 | offsetX += width + tagMargin.horizontal 94 | } 95 | 96 | return CGSize(width: collectionView.bounds.width, height: offsetY + tagHeight + tagViewMargin.vertical) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagView/ACTagViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ACTagViewCell.swift 3 | // ACTagViewDemo 4 | // 5 | // Created by ac on 2017/7/30. 6 | // Copyright © 2017年 ac. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ACTagViewCell: UICollectionViewCell { 12 | 13 | var tagButton = ACTagButton() 14 | var tagAttribute: ACTagAttribute? { 15 | didSet { 16 | tagButton.frame = bounds 17 | tagButton.tagAttribute = tagAttribute! 18 | tagButton.isSelected = isSelected 19 | } 20 | } 21 | 22 | override init(frame: CGRect) { 23 | super.init(frame: frame) 24 | 25 | contentView.addSubview(tagButton) 26 | 27 | } 28 | 29 | required init?(coder aDecoder: NSCoder) { 30 | fatalError("init(coder:) has not been implemented") 31 | } 32 | 33 | func selected() { 34 | tagButton.isSelected = true 35 | } 36 | 37 | func deselected() { 38 | tagButton.isSelected = false 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagView/ACTagViewFlowLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ACTagViewFlowLayout.swift 3 | // ACTagViewDemo 4 | // 5 | // Created by ac on 2017/7/30. 6 | // Copyright © 2017年 ac. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ACTagViewFlowLayout: UICollectionViewFlowLayout { 12 | 13 | open var tagMargin = ACTagConfig.default.tagMargin 14 | open var tagHeight: CGFloat = ACTagConfig.default.tagDefaultHeight 15 | open var tagViewMargin = ACTagConfig.default.tagViewMargin 16 | 17 | open func getEstimatedSize(in tagView: ACTagView) -> CGSize { 18 | return CGSize.zero 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagView/ACTagViewOneLineLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ACTagViewOneLineLayout.swift 3 | // ACTagViewDemo 4 | // 5 | // Created by ac on 2017/7/30. 6 | // Copyright © 2017年 ac. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ACTagViewOneLineLayout: ACTagViewFlowLayout { 12 | 13 | private var offsetX: CGFloat = 0 14 | 15 | override var collectionViewContentSize: CGSize { 16 | guard let collectionView = collectionView else { 17 | return CGSize.zero 18 | } 19 | 20 | collectionView.layoutIfNeeded() 21 | collectionView.superview?.layoutIfNeeded() 22 | 23 | return CGSize(width: max(collectionView.bounds.width, offsetX), height: collectionView.bounds.height) 24 | } 25 | 26 | override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 27 | guard let array = super.layoutAttributesForElements(in: rect), let collectionView = collectionView else { return nil } 28 | 29 | collectionView.layoutIfNeeded() 30 | collectionView.superview?.layoutIfNeeded() 31 | 32 | var finalAttrs: [UICollectionViewLayoutAttributes] = [] 33 | 34 | var offsetX = tagViewMargin.horizontal 35 | let offsetY = tagViewMargin.vertical 36 | 37 | for attribute in array { 38 | 39 | let attrCopy = attribute.copy() as! UICollectionViewLayoutAttributes 40 | 41 | attrCopy.frame.origin.x = offsetX 42 | attrCopy.frame.origin.y = offsetY 43 | 44 | if !collectionView.isScrollEnabled && offsetX + attribute.frame.width + tagViewMargin.horizontal > collectionView.bounds.width { 45 | self.offsetX = offsetX 46 | return finalAttrs 47 | } 48 | 49 | offsetX += tagMargin.horizontal + attribute.frame.width 50 | 51 | finalAttrs += [attrCopy] 52 | } 53 | 54 | if offsetX > tagViewMargin.horizontal { 55 | offsetX = offsetX - tagMargin.horizontal + tagViewMargin.horizontal 56 | } 57 | self.offsetX = offsetX 58 | 59 | return finalAttrs 60 | } 61 | 62 | override func getEstimatedSize(in tagView: ACTagView) -> CGSize { 63 | 64 | guard let dataSource = tagView.tagDataSource else { return CGSize.zero } 65 | 66 | tagView.layoutIfNeeded() 67 | tagView.superview?.layoutIfNeeded() 68 | 69 | var offsetX = tagViewMargin.horizontal 70 | 71 | for i in 0 ..< dataSource.numberOfTags(in: tagView) { 72 | 73 | let attribute = dataSource.tagView(tagView, tagAttributeForIndexAt: i) 74 | let width = attribute.getWidth(height: tagHeight) 75 | 76 | offsetX += tagMargin.horizontal + width 77 | 78 | } 79 | 80 | if offsetX > tagViewMargin.horizontal { 81 | offsetX = offsetX - tagMargin.horizontal + tagViewMargin.horizontal 82 | } 83 | 84 | return CGSize(width: offsetX, height: tagHeight + 2 * tagViewMargin.vertical) 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagView/ACUtil.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ACUtil.swift 3 | // ACTagViewDemo 4 | // 5 | // Created by ancheng on 2017/3/3. 6 | // Copyright © 2017年 ac. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | let ACScreenWidth = UIScreen.main.bounds.width 12 | let ACScreenHeight = UIScreen.main.bounds.height 13 | 14 | extension String{ 15 | 16 | /** 17 | 得到文字宽度 18 | 19 | - parameter fontSize: 字号 20 | - parameter height: 字符串最大高度 21 | - returns: 宽度值 22 | */ 23 | func ac_getWidth(_ fontSize : CGFloat, height: CGFloat = 0) -> CGFloat { 24 | 25 | let size = CGSize(width: CGFloat.greatestFiniteMagnitude, height: height) 26 | return getSizeOfString(fontSize, textSize: size).width + 1 27 | } 28 | /** 29 | 得到文字高度 30 | 31 | - parameter fontSize: 字号 32 | - parameter width: 字符串最大宽度 33 | - returns: 高度值 34 | */ 35 | func ac_getHeight(_ fontSize: CGFloat, width: CGFloat) -> CGFloat { 36 | 37 | let size = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude) 38 | return getSizeOfString(fontSize, textSize: size).height 39 | } 40 | 41 | // 计算文字宽高的辅助方法 42 | private func getSizeOfString(_ fontSize: CGFloat, textSize: CGSize) -> CGSize { 43 | let att = NSMutableAttributedString(string: self) 44 | let font = UIFont.systemFont(ofSize: fontSize) 45 | att.addAttributes([NSAttributedString.Key.font: font], range: NSRange(location: 0, length: count)) 46 | return att.boundingRect(with: textSize, options: [.usesLineFragmentOrigin,.usesFontLeading], context: nil).size 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagView/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagViewDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4333C36C1E6902BA00FFEC85 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4333C36B1E6902BA00FFEC85 /* AppDelegate.swift */; }; 11 | 4333C36E1E6902BA00FFEC85 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4333C36D1E6902BA00FFEC85 /* ViewController.swift */; }; 12 | 4333C3711E6902BB00FFEC85 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4333C36F1E6902BB00FFEC85 /* Main.storyboard */; }; 13 | 4333C3731E6902BB00FFEC85 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4333C3721E6902BB00FFEC85 /* Assets.xcassets */; }; 14 | 4333C3761E6902BB00FFEC85 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4333C3741E6902BB00FFEC85 /* LaunchScreen.storyboard */; }; 15 | 4384F1371F0F662B00E5A333 /* TagViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4384F1361F0F662B00E5A333 /* TagViewController.swift */; }; 16 | F19198BE215CC30000798295 /* ACTagView.h in Headers */ = {isa = PBXBuildFile; fileRef = F19198BC215CC30000798295 /* ACTagView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17 | F19198C1215CC30000798295 /* ACTagView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F19198BA215CC30000798295 /* ACTagView.framework */; }; 18 | F19198C2215CC30000798295 /* ACTagView.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F19198BA215CC30000798295 /* ACTagView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 19 | F19198C7215CC31200798295 /* ACTagView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431015811F2B2DCB00E6AB2F /* ACTagView.swift */; }; 20 | F19198C8215CC31200798295 /* ACTagViewFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E561051F2ECC1700B824F1 /* ACTagViewFlowLayout.swift */; }; 21 | F19198C9215CC31200798295 /* ACTagViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40518A7B1F2D863F00947FE2 /* ACTagViewCell.swift */; }; 22 | F19198CA215CC31200798295 /* ACTagViewOneLineLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E561061F2ECC1700B824F1 /* ACTagViewOneLineLayout.swift */; }; 23 | F19198CB215CC31200798295 /* ACTagAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40518AA31F2DF23400947FE2 /* ACTagAttribute.swift */; }; 24 | F19198CC215CC31200798295 /* ACTagButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40518AA51F2DF24600947FE2 /* ACTagButton.swift */; }; 25 | F19198CD215CC31200798295 /* ACUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43306A391E6E5984009E4A9D /* ACUtil.swift */; }; 26 | F19198CE215CC31200798295 /* ACTagViewAutoLineFeedLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E561041F2ECC1700B824F1 /* ACTagViewAutoLineFeedLayout.swift */; }; 27 | F19198CF215CC31200798295 /* ACTagConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40DE557B1F0D19350053E988 /* ACTagConfig.swift */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXContainerItemProxy section */ 31 | F19198BF215CC30000798295 /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = 4333C3601E6902BA00FFEC85 /* Project object */; 34 | proxyType = 1; 35 | remoteGlobalIDString = F19198B9215CC30000798295; 36 | remoteInfo = ACTagView; 37 | }; 38 | /* End PBXContainerItemProxy section */ 39 | 40 | /* Begin PBXCopyFilesBuildPhase section */ 41 | F19198C6215CC30000798295 /* Embed Frameworks */ = { 42 | isa = PBXCopyFilesBuildPhase; 43 | buildActionMask = 2147483647; 44 | dstPath = ""; 45 | dstSubfolderSpec = 10; 46 | files = ( 47 | F19198C2215CC30000798295 /* ACTagView.framework in Embed Frameworks */, 48 | ); 49 | name = "Embed Frameworks"; 50 | runOnlyForDeploymentPostprocessing = 0; 51 | }; 52 | /* End PBXCopyFilesBuildPhase section */ 53 | 54 | /* Begin PBXFileReference section */ 55 | 40518A7B1F2D863F00947FE2 /* ACTagViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ACTagViewCell.swift; sourceTree = ""; }; 56 | 40518AA31F2DF23400947FE2 /* ACTagAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ACTagAttribute.swift; sourceTree = ""; }; 57 | 40518AA51F2DF24600947FE2 /* ACTagButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ACTagButton.swift; sourceTree = ""; }; 58 | 40DE557B1F0D19350053E988 /* ACTagConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ACTagConfig.swift; sourceTree = ""; }; 59 | 431015811F2B2DCB00E6AB2F /* ACTagView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ACTagView.swift; sourceTree = ""; }; 60 | 43306A391E6E5984009E4A9D /* ACUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ACUtil.swift; sourceTree = ""; }; 61 | 4333C3681E6902BA00FFEC85 /* ACTagViewDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ACTagViewDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 62 | 4333C36B1E6902BA00FFEC85 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 63 | 4333C36D1E6902BA00FFEC85 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 64 | 4333C3701E6902BB00FFEC85 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 65 | 4333C3721E6902BB00FFEC85 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 66 | 4333C3751E6902BB00FFEC85 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 67 | 4333C3771E6902BB00FFEC85 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 68 | 4384F1361F0F662B00E5A333 /* TagViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TagViewController.swift; sourceTree = ""; }; 69 | 43E561041F2ECC1700B824F1 /* ACTagViewAutoLineFeedLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ACTagViewAutoLineFeedLayout.swift; sourceTree = ""; }; 70 | 43E561051F2ECC1700B824F1 /* ACTagViewFlowLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ACTagViewFlowLayout.swift; sourceTree = ""; }; 71 | 43E561061F2ECC1700B824F1 /* ACTagViewOneLineLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ACTagViewOneLineLayout.swift; sourceTree = ""; }; 72 | F19198BA215CC30000798295 /* ACTagView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ACTagView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 73 | F19198BC215CC30000798295 /* ACTagView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ACTagView.h; sourceTree = ""; }; 74 | F19198BD215CC30000798295 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 75 | /* End PBXFileReference section */ 76 | 77 | /* Begin PBXFrameworksBuildPhase section */ 78 | 4333C3651E6902BA00FFEC85 /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | F19198C1215CC30000798295 /* ACTagView.framework in Frameworks */, 83 | ); 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | F19198B7215CC30000798295 /* Frameworks */ = { 87 | isa = PBXFrameworksBuildPhase; 88 | buildActionMask = 2147483647; 89 | files = ( 90 | ); 91 | runOnlyForDeploymentPostprocessing = 0; 92 | }; 93 | /* End PBXFrameworksBuildPhase section */ 94 | 95 | /* Begin PBXGroup section */ 96 | 4333C35F1E6902BA00FFEC85 = { 97 | isa = PBXGroup; 98 | children = ( 99 | 4333C36A1E6902BA00FFEC85 /* ACTagViewDemo */, 100 | F19198BB215CC30000798295 /* ACTagView */, 101 | 4333C3691E6902BA00FFEC85 /* Products */, 102 | ); 103 | sourceTree = ""; 104 | }; 105 | 4333C3691E6902BA00FFEC85 /* Products */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 4333C3681E6902BA00FFEC85 /* ACTagViewDemo.app */, 109 | F19198BA215CC30000798295 /* ACTagView.framework */, 110 | ); 111 | name = Products; 112 | sourceTree = ""; 113 | }; 114 | 4333C36A1E6902BA00FFEC85 /* ACTagViewDemo */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 4333C36B1E6902BA00FFEC85 /* AppDelegate.swift */, 118 | 4333C36D1E6902BA00FFEC85 /* ViewController.swift */, 119 | 4384F1361F0F662B00E5A333 /* TagViewController.swift */, 120 | 4333C36F1E6902BB00FFEC85 /* Main.storyboard */, 121 | 4333C3721E6902BB00FFEC85 /* Assets.xcassets */, 122 | 4333C3741E6902BB00FFEC85 /* LaunchScreen.storyboard */, 123 | 4333C3771E6902BB00FFEC85 /* Info.plist */, 124 | ); 125 | path = ACTagViewDemo; 126 | sourceTree = ""; 127 | }; 128 | F19198BB215CC30000798295 /* ACTagView */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | F19198BC215CC30000798295 /* ACTagView.h */, 132 | 431015811F2B2DCB00E6AB2F /* ACTagView.swift */, 133 | 43E561051F2ECC1700B824F1 /* ACTagViewFlowLayout.swift */, 134 | 40DE557B1F0D19350053E988 /* ACTagConfig.swift */, 135 | 40518AA31F2DF23400947FE2 /* ACTagAttribute.swift */, 136 | 40518AA51F2DF24600947FE2 /* ACTagButton.swift */, 137 | 40518A7B1F2D863F00947FE2 /* ACTagViewCell.swift */, 138 | 43306A391E6E5984009E4A9D /* ACUtil.swift */, 139 | 43E561041F2ECC1700B824F1 /* ACTagViewAutoLineFeedLayout.swift */, 140 | 43E561061F2ECC1700B824F1 /* ACTagViewOneLineLayout.swift */, 141 | F19198BD215CC30000798295 /* Info.plist */, 142 | ); 143 | path = ACTagView; 144 | sourceTree = ""; 145 | }; 146 | /* End PBXGroup section */ 147 | 148 | /* Begin PBXHeadersBuildPhase section */ 149 | F19198B5215CC30000798295 /* Headers */ = { 150 | isa = PBXHeadersBuildPhase; 151 | buildActionMask = 2147483647; 152 | files = ( 153 | F19198BE215CC30000798295 /* ACTagView.h in Headers */, 154 | ); 155 | runOnlyForDeploymentPostprocessing = 0; 156 | }; 157 | /* End PBXHeadersBuildPhase section */ 158 | 159 | /* Begin PBXNativeTarget section */ 160 | 4333C3671E6902BA00FFEC85 /* ACTagViewDemo */ = { 161 | isa = PBXNativeTarget; 162 | buildConfigurationList = 4333C37A1E6902BB00FFEC85 /* Build configuration list for PBXNativeTarget "ACTagViewDemo" */; 163 | buildPhases = ( 164 | 4333C3641E6902BA00FFEC85 /* Sources */, 165 | 4333C3651E6902BA00FFEC85 /* Frameworks */, 166 | 4333C3661E6902BA00FFEC85 /* Resources */, 167 | F19198C6215CC30000798295 /* Embed Frameworks */, 168 | ); 169 | buildRules = ( 170 | ); 171 | dependencies = ( 172 | F19198C0215CC30000798295 /* PBXTargetDependency */, 173 | ); 174 | name = ACTagViewDemo; 175 | productName = ACTagViewDemo; 176 | productReference = 4333C3681E6902BA00FFEC85 /* ACTagViewDemo.app */; 177 | productType = "com.apple.product-type.application"; 178 | }; 179 | F19198B9215CC30000798295 /* ACTagView */ = { 180 | isa = PBXNativeTarget; 181 | buildConfigurationList = F19198C3215CC30000798295 /* Build configuration list for PBXNativeTarget "ACTagView" */; 182 | buildPhases = ( 183 | F19198B5215CC30000798295 /* Headers */, 184 | F19198B6215CC30000798295 /* Sources */, 185 | F19198B7215CC30000798295 /* Frameworks */, 186 | F19198B8215CC30000798295 /* Resources */, 187 | ); 188 | buildRules = ( 189 | ); 190 | dependencies = ( 191 | ); 192 | name = ACTagView; 193 | productName = ACTagView; 194 | productReference = F19198BA215CC30000798295 /* ACTagView.framework */; 195 | productType = "com.apple.product-type.framework"; 196 | }; 197 | /* End PBXNativeTarget section */ 198 | 199 | /* Begin PBXProject section */ 200 | 4333C3601E6902BA00FFEC85 /* Project object */ = { 201 | isa = PBXProject; 202 | attributes = { 203 | LastSwiftUpdateCheck = 0820; 204 | LastUpgradeCheck = 1020; 205 | ORGANIZATIONNAME = ac; 206 | TargetAttributes = { 207 | 4333C3671E6902BA00FFEC85 = { 208 | CreatedOnToolsVersion = 8.2.1; 209 | DevelopmentTeam = GY6E74AYN5; 210 | LastSwiftMigration = 1020; 211 | ProvisioningStyle = Automatic; 212 | }; 213 | F19198B9215CC30000798295 = { 214 | CreatedOnToolsVersion = 10.0; 215 | DevelopmentTeam = GY6E74AYN5; 216 | LastSwiftMigration = 1020; 217 | ProvisioningStyle = Automatic; 218 | }; 219 | }; 220 | }; 221 | buildConfigurationList = 4333C3631E6902BA00FFEC85 /* Build configuration list for PBXProject "ACTagViewDemo" */; 222 | compatibilityVersion = "Xcode 3.2"; 223 | developmentRegion = en; 224 | hasScannedForEncodings = 0; 225 | knownRegions = ( 226 | en, 227 | Base, 228 | ); 229 | mainGroup = 4333C35F1E6902BA00FFEC85; 230 | productRefGroup = 4333C3691E6902BA00FFEC85 /* Products */; 231 | projectDirPath = ""; 232 | projectRoot = ""; 233 | targets = ( 234 | 4333C3671E6902BA00FFEC85 /* ACTagViewDemo */, 235 | F19198B9215CC30000798295 /* ACTagView */, 236 | ); 237 | }; 238 | /* End PBXProject section */ 239 | 240 | /* Begin PBXResourcesBuildPhase section */ 241 | 4333C3661E6902BA00FFEC85 /* Resources */ = { 242 | isa = PBXResourcesBuildPhase; 243 | buildActionMask = 2147483647; 244 | files = ( 245 | 4333C3761E6902BB00FFEC85 /* LaunchScreen.storyboard in Resources */, 246 | 4333C3731E6902BB00FFEC85 /* Assets.xcassets in Resources */, 247 | 4333C3711E6902BB00FFEC85 /* Main.storyboard in Resources */, 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | }; 251 | F19198B8215CC30000798295 /* Resources */ = { 252 | isa = PBXResourcesBuildPhase; 253 | buildActionMask = 2147483647; 254 | files = ( 255 | ); 256 | runOnlyForDeploymentPostprocessing = 0; 257 | }; 258 | /* End PBXResourcesBuildPhase section */ 259 | 260 | /* Begin PBXSourcesBuildPhase section */ 261 | 4333C3641E6902BA00FFEC85 /* Sources */ = { 262 | isa = PBXSourcesBuildPhase; 263 | buildActionMask = 2147483647; 264 | files = ( 265 | 4333C36E1E6902BA00FFEC85 /* ViewController.swift in Sources */, 266 | 4333C36C1E6902BA00FFEC85 /* AppDelegate.swift in Sources */, 267 | 4384F1371F0F662B00E5A333 /* TagViewController.swift in Sources */, 268 | ); 269 | runOnlyForDeploymentPostprocessing = 0; 270 | }; 271 | F19198B6215CC30000798295 /* Sources */ = { 272 | isa = PBXSourcesBuildPhase; 273 | buildActionMask = 2147483647; 274 | files = ( 275 | F19198CC215CC31200798295 /* ACTagButton.swift in Sources */, 276 | F19198CF215CC31200798295 /* ACTagConfig.swift in Sources */, 277 | F19198C9215CC31200798295 /* ACTagViewCell.swift in Sources */, 278 | F19198C7215CC31200798295 /* ACTagView.swift in Sources */, 279 | F19198CD215CC31200798295 /* ACUtil.swift in Sources */, 280 | F19198CB215CC31200798295 /* ACTagAttribute.swift in Sources */, 281 | F19198CA215CC31200798295 /* ACTagViewOneLineLayout.swift in Sources */, 282 | F19198CE215CC31200798295 /* ACTagViewAutoLineFeedLayout.swift in Sources */, 283 | F19198C8215CC31200798295 /* ACTagViewFlowLayout.swift in Sources */, 284 | ); 285 | runOnlyForDeploymentPostprocessing = 0; 286 | }; 287 | /* End PBXSourcesBuildPhase section */ 288 | 289 | /* Begin PBXTargetDependency section */ 290 | F19198C0215CC30000798295 /* PBXTargetDependency */ = { 291 | isa = PBXTargetDependency; 292 | target = F19198B9215CC30000798295 /* ACTagView */; 293 | targetProxy = F19198BF215CC30000798295 /* PBXContainerItemProxy */; 294 | }; 295 | /* End PBXTargetDependency section */ 296 | 297 | /* Begin PBXVariantGroup section */ 298 | 4333C36F1E6902BB00FFEC85 /* Main.storyboard */ = { 299 | isa = PBXVariantGroup; 300 | children = ( 301 | 4333C3701E6902BB00FFEC85 /* Base */, 302 | ); 303 | name = Main.storyboard; 304 | sourceTree = ""; 305 | }; 306 | 4333C3741E6902BB00FFEC85 /* LaunchScreen.storyboard */ = { 307 | isa = PBXVariantGroup; 308 | children = ( 309 | 4333C3751E6902BB00FFEC85 /* Base */, 310 | ); 311 | name = LaunchScreen.storyboard; 312 | sourceTree = ""; 313 | }; 314 | /* End PBXVariantGroup section */ 315 | 316 | /* Begin XCBuildConfiguration section */ 317 | 4333C3781E6902BB00FFEC85 /* Debug */ = { 318 | isa = XCBuildConfiguration; 319 | buildSettings = { 320 | ALWAYS_SEARCH_USER_PATHS = NO; 321 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 322 | CLANG_ANALYZER_NONNULL = YES; 323 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 324 | CLANG_CXX_LIBRARY = "libc++"; 325 | CLANG_ENABLE_MODULES = YES; 326 | CLANG_ENABLE_OBJC_ARC = YES; 327 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 328 | CLANG_WARN_BOOL_CONVERSION = YES; 329 | CLANG_WARN_COMMA = YES; 330 | CLANG_WARN_CONSTANT_CONVERSION = YES; 331 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 332 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 333 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 334 | CLANG_WARN_EMPTY_BODY = YES; 335 | CLANG_WARN_ENUM_CONVERSION = YES; 336 | CLANG_WARN_INFINITE_RECURSION = YES; 337 | CLANG_WARN_INT_CONVERSION = YES; 338 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 339 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 340 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 341 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 342 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 343 | CLANG_WARN_STRICT_PROTOTYPES = YES; 344 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 345 | CLANG_WARN_UNREACHABLE_CODE = YES; 346 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 347 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 348 | COPY_PHASE_STRIP = NO; 349 | DEBUG_INFORMATION_FORMAT = dwarf; 350 | ENABLE_STRICT_OBJC_MSGSEND = YES; 351 | ENABLE_TESTABILITY = YES; 352 | GCC_C_LANGUAGE_STANDARD = gnu99; 353 | GCC_DYNAMIC_NO_PIC = NO; 354 | GCC_NO_COMMON_BLOCKS = YES; 355 | GCC_OPTIMIZATION_LEVEL = 0; 356 | GCC_PREPROCESSOR_DEFINITIONS = ( 357 | "DEBUG=1", 358 | "$(inherited)", 359 | ); 360 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 361 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 362 | GCC_WARN_UNDECLARED_SELECTOR = YES; 363 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 364 | GCC_WARN_UNUSED_FUNCTION = YES; 365 | GCC_WARN_UNUSED_VARIABLE = YES; 366 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 367 | MTL_ENABLE_DEBUG_INFO = YES; 368 | ONLY_ACTIVE_ARCH = YES; 369 | SDKROOT = iphoneos; 370 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 371 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 372 | SWIFT_SWIFT3_OBJC_INFERENCE = Off; 373 | SWIFT_VERSION = 5.0; 374 | }; 375 | name = Debug; 376 | }; 377 | 4333C3791E6902BB00FFEC85 /* Release */ = { 378 | isa = XCBuildConfiguration; 379 | buildSettings = { 380 | ALWAYS_SEARCH_USER_PATHS = NO; 381 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 382 | CLANG_ANALYZER_NONNULL = YES; 383 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 384 | CLANG_CXX_LIBRARY = "libc++"; 385 | CLANG_ENABLE_MODULES = YES; 386 | CLANG_ENABLE_OBJC_ARC = YES; 387 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 388 | CLANG_WARN_BOOL_CONVERSION = YES; 389 | CLANG_WARN_COMMA = YES; 390 | CLANG_WARN_CONSTANT_CONVERSION = YES; 391 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 392 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 393 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 394 | CLANG_WARN_EMPTY_BODY = YES; 395 | CLANG_WARN_ENUM_CONVERSION = YES; 396 | CLANG_WARN_INFINITE_RECURSION = YES; 397 | CLANG_WARN_INT_CONVERSION = YES; 398 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 399 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 400 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 401 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 402 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 403 | CLANG_WARN_STRICT_PROTOTYPES = YES; 404 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 405 | CLANG_WARN_UNREACHABLE_CODE = YES; 406 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 407 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 408 | COPY_PHASE_STRIP = NO; 409 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 410 | ENABLE_NS_ASSERTIONS = NO; 411 | ENABLE_STRICT_OBJC_MSGSEND = YES; 412 | GCC_C_LANGUAGE_STANDARD = gnu99; 413 | GCC_NO_COMMON_BLOCKS = YES; 414 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 415 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 416 | GCC_WARN_UNDECLARED_SELECTOR = YES; 417 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 418 | GCC_WARN_UNUSED_FUNCTION = YES; 419 | GCC_WARN_UNUSED_VARIABLE = YES; 420 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 421 | MTL_ENABLE_DEBUG_INFO = NO; 422 | SDKROOT = iphoneos; 423 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 424 | SWIFT_SWIFT3_OBJC_INFERENCE = Off; 425 | SWIFT_VERSION = 5.0; 426 | VALIDATE_PRODUCT = YES; 427 | }; 428 | name = Release; 429 | }; 430 | 4333C37B1E6902BB00FFEC85 /* Debug */ = { 431 | isa = XCBuildConfiguration; 432 | buildSettings = { 433 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 434 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 435 | CODE_SIGN_IDENTITY = "iPhone Developer"; 436 | DEVELOPMENT_TEAM = GY6E74AYN5; 437 | INFOPLIST_FILE = ACTagViewDemo/Info.plist; 438 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 439 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 440 | PRODUCT_BUNDLE_IDENTIFIER = com.ac.ACTagViewDemo; 441 | PRODUCT_NAME = "$(TARGET_NAME)"; 442 | SWIFT_VERSION = 5.0; 443 | }; 444 | name = Debug; 445 | }; 446 | 4333C37C1E6902BB00FFEC85 /* Release */ = { 447 | isa = XCBuildConfiguration; 448 | buildSettings = { 449 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 450 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 451 | CODE_SIGN_IDENTITY = "iPhone Developer"; 452 | DEVELOPMENT_TEAM = GY6E74AYN5; 453 | INFOPLIST_FILE = ACTagViewDemo/Info.plist; 454 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 455 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 456 | PRODUCT_BUNDLE_IDENTIFIER = com.ac.ACTagViewDemo; 457 | PRODUCT_NAME = "$(TARGET_NAME)"; 458 | SWIFT_VERSION = 5.0; 459 | }; 460 | name = Release; 461 | }; 462 | F19198C4215CC30000798295 /* Debug */ = { 463 | isa = XCBuildConfiguration; 464 | buildSettings = { 465 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 466 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 467 | CLANG_ENABLE_OBJC_WEAK = YES; 468 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 469 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 470 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 471 | CODE_SIGN_IDENTITY = ""; 472 | CODE_SIGN_STYLE = Automatic; 473 | CURRENT_PROJECT_VERSION = 1; 474 | DEFINES_MODULE = YES; 475 | DEVELOPMENT_TEAM = GY6E74AYN5; 476 | DYLIB_COMPATIBILITY_VERSION = 1; 477 | DYLIB_CURRENT_VERSION = 1; 478 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 479 | GCC_C_LANGUAGE_STANDARD = gnu11; 480 | INFOPLIST_FILE = ACTagView/Info.plist; 481 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 482 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 483 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 484 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 485 | MTL_FAST_MATH = YES; 486 | PRODUCT_BUNDLE_IDENTIFIER = com.ac.ACTagView; 487 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 488 | SKIP_INSTALL = YES; 489 | SWIFT_VERSION = 5.0; 490 | TARGETED_DEVICE_FAMILY = "1,2"; 491 | VERSIONING_SYSTEM = "apple-generic"; 492 | VERSION_INFO_PREFIX = ""; 493 | }; 494 | name = Debug; 495 | }; 496 | F19198C5215CC30000798295 /* Release */ = { 497 | isa = XCBuildConfiguration; 498 | buildSettings = { 499 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 500 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 501 | CLANG_ENABLE_OBJC_WEAK = YES; 502 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 503 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 504 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 505 | CODE_SIGN_IDENTITY = ""; 506 | CODE_SIGN_STYLE = Automatic; 507 | CURRENT_PROJECT_VERSION = 1; 508 | DEFINES_MODULE = YES; 509 | DEVELOPMENT_TEAM = GY6E74AYN5; 510 | DYLIB_COMPATIBILITY_VERSION = 1; 511 | DYLIB_CURRENT_VERSION = 1; 512 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 513 | GCC_C_LANGUAGE_STANDARD = gnu11; 514 | INFOPLIST_FILE = ACTagView/Info.plist; 515 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 516 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 517 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 518 | MTL_FAST_MATH = YES; 519 | PRODUCT_BUNDLE_IDENTIFIER = com.ac.ACTagView; 520 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 521 | SKIP_INSTALL = YES; 522 | SWIFT_VERSION = 5.0; 523 | TARGETED_DEVICE_FAMILY = "1,2"; 524 | VERSIONING_SYSTEM = "apple-generic"; 525 | VERSION_INFO_PREFIX = ""; 526 | }; 527 | name = Release; 528 | }; 529 | /* End XCBuildConfiguration section */ 530 | 531 | /* Begin XCConfigurationList section */ 532 | 4333C3631E6902BA00FFEC85 /* Build configuration list for PBXProject "ACTagViewDemo" */ = { 533 | isa = XCConfigurationList; 534 | buildConfigurations = ( 535 | 4333C3781E6902BB00FFEC85 /* Debug */, 536 | 4333C3791E6902BB00FFEC85 /* Release */, 537 | ); 538 | defaultConfigurationIsVisible = 0; 539 | defaultConfigurationName = Release; 540 | }; 541 | 4333C37A1E6902BB00FFEC85 /* Build configuration list for PBXNativeTarget "ACTagViewDemo" */ = { 542 | isa = XCConfigurationList; 543 | buildConfigurations = ( 544 | 4333C37B1E6902BB00FFEC85 /* Debug */, 545 | 4333C37C1E6902BB00FFEC85 /* Release */, 546 | ); 547 | defaultConfigurationIsVisible = 0; 548 | defaultConfigurationName = Release; 549 | }; 550 | F19198C3215CC30000798295 /* Build configuration list for PBXNativeTarget "ACTagView" */ = { 551 | isa = XCConfigurationList; 552 | buildConfigurations = ( 553 | F19198C4215CC30000798295 /* Debug */, 554 | F19198C5215CC30000798295 /* Release */, 555 | ); 556 | defaultConfigurationIsVisible = 0; 557 | defaultConfigurationName = Release; 558 | }; 559 | /* End XCConfigurationList section */ 560 | }; 561 | rootObject = 4333C3601E6902BA00FFEC85 /* Project object */; 562 | } 563 | -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagViewDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagViewDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagViewDemo.xcodeproj/xcshareddata/xcschemes/ACTagView.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagViewDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ACTagViewDemo 4 | // 5 | // Created by ancheng on 2017/3/3. 6 | // Copyright © 2017年 ac. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagViewDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagViewDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagViewDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagViewDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagViewDemo/TagViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TagViewController.swift 3 | // ACTagViewDemo 4 | // 5 | // Created by ancheng on 2017/7/7. 6 | // Copyright © 2017年 ac. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ACTagView 11 | 12 | class TagViewController: UIViewController { 13 | 14 | fileprivate let tagsStrList = ["我喜欢", "胸大", "腿长", "瓜子脸", "黑长直", "身材高挑", "会卖萌", "会发嗲", "会做饭", "会洗衣", "的", "男生"] 15 | 16 | private var autoLineFeedTagView = ACTagView(frame: CGRect(x: 0, y: 100, width: UIScreen.main.bounds.width, height: 100), layoutType: .autoLineFeed) 17 | private var oneLineTagView = ACTagView(frame: CGRect(x: 0, y: 250, width: UIScreen.main.bounds.width, height: 50), layoutType: .oneLine) 18 | private var oneLineTagViewNoScroll = ACTagView(frame: CGRect(x: 0, y: 350, width: UIScreen.main.bounds.width, height: 50), layoutType: .oneLine) 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | 23 | automaticallyAdjustsScrollViewInsets = false 24 | view.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1) 25 | 26 | setupNormalTagView() 27 | 28 | } 29 | 30 | private func setupNormalTagView() { 31 | 32 | autoLineFeedTagView.tagDataSource = self 33 | autoLineFeedTagView.tagDelegate = self 34 | autoLineFeedTagView.allowsMultipleSelection = true 35 | autoLineFeedTagView.backgroundColor = UIColor.white 36 | print(autoLineFeedTagView.estimatedSize) 37 | view.addSubview(autoLineFeedTagView) 38 | 39 | oneLineTagView.tagDataSource = self 40 | oneLineTagView.tagDelegate = self 41 | oneLineTagView.backgroundColor = UIColor.white 42 | print(oneLineTagView.estimatedSize) 43 | view.addSubview(oneLineTagView) 44 | 45 | oneLineTagViewNoScroll.tagDataSource = self 46 | oneLineTagViewNoScroll.tagDelegate = self 47 | oneLineTagViewNoScroll.backgroundColor = UIColor.white 48 | oneLineTagViewNoScroll.isScrollEnabled = false 49 | view.addSubview(oneLineTagViewNoScroll) 50 | } 51 | 52 | } 53 | 54 | extension TagViewController: ACTagViewDataSource { 55 | func numberOfTags(in tagView: ACTagView) -> Int { 56 | return tagsStrList.count 57 | } 58 | 59 | func tagView(_ tagView: ACTagView, tagAttributeForIndexAt index: Int) -> ACTagAttribute { 60 | let tag = ACTagAttribute(text: tagsStrList[index]) 61 | return tag 62 | } 63 | } 64 | 65 | extension TagViewController: ACTagViewDelegate { 66 | 67 | func tagView(_ tagView: ACTagView, didSelectTagAt index: Int) { 68 | print(index) 69 | print("selectedTagsList-----------", tagView.indexsForSelectedTags) 70 | } 71 | 72 | func tagView(_ tagView: ACTagView, didDeselectTagAt index: Int) { 73 | print("deselected------------",index) 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /ACTagViewDemo/ACTagViewDemo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ACTagViewDemo 4 | // 5 | // Created by ancheng on 2017/3/3. 6 | // Copyright © 2017年 ac. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UITableViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | tableView.delegate = self 16 | } 17 | 18 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 19 | tableView.deselectRow(at: indexPath, animated: false) 20 | switch indexPath.row { 21 | case 0: 22 | navigationController?.pushViewController(TagViewController(), animated: true) 23 | // case 1: 24 | // navigationController?.pushViewController(EditTagViewController(), animated: true) 25 | // case 2: 26 | // navigationController?.pushViewController(EditTagAutoFeedViewController(), animated: true) 27 | default: 28 | break 29 | } 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ACTagView_ver2.0.0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChaselAn/ACTagView/b29f697a693b6511f016bbbaf1f7e7d8610fe90c/ACTagView_ver2.0.0.gif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README(ver1.2.2).md: -------------------------------------------------------------------------------- 1 | # ACTagView(1.2.2) 2 | 3 | * Swift3.0 4 | 5 | 6 | 7 | ## 安装 8 | 9 | ### CocoaPods 10 | 11 | ```ruby 12 | pod 'ACTagView', '~> 1.2.2' 13 | ``` 14 | 15 | Then, run the following command: 16 | 17 | ```bash 18 | $ pod install 19 | ``` 20 | 21 | ## 使用 22 | ### 设置全局属性 23 | ```swift 24 | ACTagManager.shared.selectedTagBackgroundColor = UIColor.white // tag选中背景色 25 | ACTagManager.shared.selectedTagBorderColor = UIColor.red // tag选中边框颜色 26 | ACTagManager.shared.selectedTagTextColor = UIColor.red // tag选中文字颜色 27 | ACTagManager.shared.tagBackgroundColor = UIColor.white // tag背景色 28 | ACTagManager.shared.tagBorderColor = UIColor.black // tag边框颜色 29 | ACTagManager.shared.tagTextColor = UIColor.black // tag文字颜色 30 | ACTagManager.shared.autoLineFeed = true // tag是否自动换行 31 | ... 32 | ``` 33 | 34 | ### 普通展示标签 35 | 36 | ```swift 37 | let tagStrList: [String] = ["标签1", "标签2", "标签3"] 38 | let tagView = ACTagView(frame: CGRect(x: 0, y: 100, width: 300, height: 50)) 39 | tagView.dataSource = self 40 | tagView.tagDelegate = self 41 | tagView.autoLineFeed = true // 是否自动换行,false表示只有一行,横向滑动 42 | tagView.backgroundColor = UIColor.white 43 | view.addSubview(tagView) 44 | ``` 45 | 46 | ```swift 47 | extension TestTagViewController: ACTagViewDataSource { 48 | func numberOfTags(in tagView: ACTagView) -> Int { 49 | return tagStrList.count 50 | } 51 | 52 | func tagView(_ tagView: ACTagView, tagForIndexAt index: Int) -> ACTag { 53 | let tag = ACTag() 54 | tag.setTitle(tagStrList[index], for: .normal) 55 | return tag 56 | } 57 | } 58 | 59 | extension TestTagViewController: ACTagViewDelegate { 60 | func tagView(_ tagView: ACTagView, didClickTagAt index: Int, clickedTag tag: ACTag) { 61 | tag.isSelected = !tag.isSelected 62 | } 63 | } 64 | ``` 65 | 66 | ### 可编辑标签 67 | ```swift 68 | let tagStrList: [String] = ["标签1", "标签2", "标签3"] 69 | let tagView = ACTagView(frame: CGRect(x: 0, y: 100, width: 300, height: 50)) 70 | tagView.dataSource = self 71 | tagView.tagDelegate = self 72 | let inputTag = ACInputTag() 73 | inputTag.position = .head 74 | tagView.inputTag = inputTag 75 | tagView.autoLineFeed = true // 是否自动换行,false表示只有一行,横向滑动 76 | tagView.backgroundColor = UIColor.white 77 | view.addSubview(tagView) 78 | ``` 79 | 80 | ```swift 81 | extension TestTagViewController: ACTagViewDataSource { 82 | func numberOfTags(in tagView: ACTagView) -> Int { 83 | return tagStrList.count 84 | } 85 | 86 | func tagView(_ tagView: ACTagView, tagForIndexAt index: Int) -> ACTag { 87 | let tag = ACTag() 88 | tag.setTitle(tagStrList[index], for: .normal) 89 | return tag 90 | } 91 | } 92 | 93 | extension TestTagViewController: ACTagViewDelegate { 94 | func tagView(_ tagView: ACTagView, didClickTagAt index: Int, clickedTag tag: ACTag) { 95 | tag.isSelected = !tag.isSelected 96 | } 97 | func tagView(_ tagView: ACTagView, inputTagShouldReturnWith inputTag: ACInputTag) -> Bool { 98 | inputTag.resignFirstResponder() 99 | guard let text = inputTag.text, !text.isEmpty else { return true } 100 | 101 | tagStrList.append(text) 102 | inputTag.text = "" 103 | tagView.reloadData() 104 | return true 105 | } 106 | } 107 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ACTagView 2 | 3 | * Swift 4 | * 此版本暂不支持可编辑标签,如有编辑标签需求,请先使用1.2.2版本。 5 | * 1.2.2版本使用说明请点击此处[1.2.2使用说明](https://github.com/ChaselAn/ACTagView/blob/master/README(ver1.2.2).md) 6 | 7 | 8 | 9 | ## 安装 10 | 11 | ### CocoaPods 12 | 13 | * Swift 3.0: 14 | 15 | ```ruby 16 | pod 'ACTagView', '~> 2.0.1' 17 | ``` 18 | 19 | * Swift 4.0: 20 | 21 | ```ruby 22 | pod 'ACTagView', '~> 2.2.4' 23 | ``` 24 | 25 | - Swift 4.2: 26 | 27 | ```ruby 28 | pod 'ACTagView', '~> 2.2.6' 29 | ``` 30 | 31 | - Swift 5.0: 32 | 33 | ```ruby 34 | pod 'ACTagView', '~> 2.3.0' 35 | ``` 36 | 37 | Then, run the following command: 38 | 39 | ```bash 40 | $ pod install 41 | ``` 42 | 43 | ## 使用 44 | ### 设置全局属性 45 | ```swift 46 | ACTagConfig.default.selectedTagBackgroundColor = UIColor.white // tag选中背景色 47 | ACTagConfig.default.selectedTagBorderColor = UIColor.red // tag选中边框颜色 48 | ACTagConfig.default.selectedTagTextColor = UIColor.red // tag选中文字颜色 49 | ACTagConfig.default.tagBackgroundColor = UIColor.white // tag背景色 50 | ACTagConfig.default.tagBorderColor = UIColor.black // tag边框颜色 51 | ACTagConfig.default.tagTextColor = UIColor.black // tag文字颜色 52 | ... 53 | ``` 54 | 55 | ### 自动换行的标签View 56 | 57 | ```swift 58 | let tagsStrList = ["我喜欢", "胸大", "腿长"] 59 | var autoLineFeedTagView = ACTagView(frame: CGRect(x: 0, y: 100, width: UIScreen.main.bounds.width, height: 100), layoutType: .autoLineFeed) 60 | autoLineFeedTagView.tagDataSource = self 61 | autoLineFeedTagView.tagDelegate = self 62 | autoLineFeedTagView.allowsMultipleSelection = true // 是否支持多选 63 | autoLineFeedTagView.backgroundColor = UIColor.white 64 | print(autoLineFeedTagView.estimatedHeight) // 打印预估高度 65 | view.addSubview(autoLineFeedTagView) 66 | ``` 67 | 68 | ### 一行标签的View,可横向滚动 69 | 70 | ```swift 71 | let tagsStrList = ["我喜欢", "胸大", "腿长"] 72 | var oneLineTagView = ACTagView(frame: CGRect(x: 0, y: 300, width: UIScreen.main.bounds.width, height: 50), layoutType: .oneLine) 73 | oneLineTagView.tagDataSource = self 74 | oneLineTagView.tagDelegate = self 75 | oneLineTagView.backgroundColor = UIColor.white 76 | view.addSubview(oneLineTagView) 77 | ``` 78 | 79 | ### 数据源及代理 80 | ```swift 81 | extension TagViewController: ACTagViewDataSource { 82 | func numberOfTags(in tagView: ACTagView) -> Int { 83 | return tagsStrList.count 84 | } 85 | 86 | func tagView(_ tagView: ACTagView, tagAttributeForIndexAt index: Int) -> ACTagAttribute { 87 | let tag = ACTagAttribute(text: tagsStrList[index]) 88 | return tag 89 | } 90 | } 91 | 92 | extension TagViewController: ACTagViewDelegate { 93 | 94 | func tagView(_ tagView: ACTagView, didSelectTagAt index: Int) { 95 | print(index) 96 | print("selectedTagsList-----------", tagView.indexsForSelectedTags) // 打印所有已选中标签的下标 97 | } 98 | 99 | func tagView(_ tagView: ACTagView, didDeselectTagAt index: Int) { 100 | print("deselected------------",index) 101 | } 102 | 103 | } 104 | ``` 105 | 106 | ### 使用注意 107 | * 使用xib关联时,必须在`awakeFromNib`方法中调用标签View的`initTagView(layoutType: ACTagViewLayoutType)`方法 --------------------------------------------------------------------------------