├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .swiftpm └── xcode │ └── package.xcworkspace │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── CODE_OF_CONDUCT.md ├── Center.jpg ├── LICENSE ├── Left.jpg ├── Package.swift ├── README.md ├── Replay_Insert_Remove.gif ├── Right.jpg ├── Sources └── TagsFlowLayout │ └── TagsFlowLayout.swift ├── TagsFlowLayout.podspec ├── TagsFlowLayout ├── .gitignore ├── .travis.yml └── 1.0 │ └── TagsFlowLayout.podspec └── Tests └── TagsFlowLayoutTests └── TagsFlowLayoutTests.swift /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData/ 7 | .swiftpm/config/registries.json 8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 9 | .netrc 10 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | rastaman.alex007@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /Center.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rastaman111/TagsFlowLayout/d6e407d166c981e546659177555745e118a59796/Center.jpg -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | MIT License 3 | 4 | Copyright © 2022 Alexandr Sibirtsev 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /Left.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rastaman111/TagsFlowLayout/d6e407d166c981e546659177555745e118a59796/Left.jpg -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.6 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "TagsFlowLayout", 7 | platforms: [ 8 | .iOS(.v11) 9 | ], 10 | products: [ 11 | .library( 12 | name: "TagsFlowLayout", targets: ["TagsFlowLayout"]), 13 | ], 14 | dependencies: [], 15 | targets: [ 16 | .target( 17 | name: "TagsFlowLayout", 18 | dependencies: [], 19 | path: "Sources") 20 | ] 21 | ) 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TagsFlowLayout 2 | 3 | This implementation is built using a `UICollectionView` and a custom flowLayout. 4 | 5 | 6 | cocoapods version 7 | 8 | 9 | Carthage compatible 10 | 11 | 12 | 13 | GitHub 14 | 15 | 16 | 17 | platform 18 | 19 | 20 | 21 | swift5.0 22 | 23 | 24 | ### Horizontal Alignment: 25 | 26 | * `horizontalAlignment = .left` 27 | 28 |
29 | 30 | * `horizontalAlignment = .right` 31 | 32 |
33 | 34 | * `horizontalAlignment = .center` 35 | 36 |
37 | 38 | ### Inserting and Deleting tags 39 | 40 |
Replay_Insert_Remove
41 | 42 | # Table of contents 43 | 44 | * [Requirements](#requirements) 45 | * [Installation](#installation) 46 | - [CocoaPods](#cocoapods) 47 | - [Swift Package Manager](#swift-package-manager) 48 | - [Carthage](#carthage) 49 | - [Manually](#manually) 50 | * [Usage](#usage) 51 | * [License](#license) 52 | * [Donation](#donation) 53 | 54 | ## Requirements 55 | * iOS 11.0+ 56 | * Swift 5 57 | 58 | ## Installation 59 | 60 | ### CocoaPods 61 | Add Instructions to your Podfile: 62 | 63 | ```ruby 64 | pod 'TagsFlowLayout' 65 | ``` 66 | 67 | Then, run the following command: 68 | 69 | ```bash 70 | $ pod install 71 | ``` 72 | 73 | ### Swift Package Manager 74 | In Xcode, use File > Swift Packages > Add Package Dependency and use `https://github.com/rastaman111/TagsFlowLayout`. 75 | 76 | ### Carthage 77 | To install with [Carthage](https://github.com/Carthage/Carthage), simply add the following line to your Podfile: 78 | ```ruby 79 | github "rastaman111/TagsFlowLayout" 80 | ``` 81 | 82 | ### Manually 83 | If you prefer not to use any of dependency managers, you can integrate manually. Put `Sources/TagsFlowLayout` folder in your Xcode project. Make sure to enable `Copy items if needed` and `Create groups`. 84 | 85 | ## Usage 86 | To use `TagsFlowLayout` inside your `UIViewController`: 87 | 88 | ```swift 89 | import TagsFlowLayout 90 | 91 | class ViewController: UICollectionViewController { 92 | 93 | override func viewDidLoad() { 94 | super.viewDidLoad() 95 | 96 | let tagsFlowLayout = TagsFlowLayout(alignment: .left ,minimumInteritemSpacing: 10, minimumLineSpacing: 10, sectionInset: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)) 97 | collectionView.collectionViewLayout = tagsFlowLayout 98 | 99 | // register cell 100 | collectionView.register(nib: UINib(nibName: "ExampleCell", bundle: nil), forCellWithReuseIdentifier: "ExampleCell") 101 | } 102 | 103 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 104 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ExampleCell", for: indexPath) as! ExampleCell 105 | 106 | cell.maxWidth = collectionView.bounds.width - 30 107 | 108 | return cell 109 | } 110 | } 111 | ``` 112 | ```swift 113 | class TagCollectionViewCell: UICollectionViewCell { 114 | 115 | @IBOutlet weak var tagLabel: UILabel! 116 | 117 | @IBOutlet private var maxWidthConstraint: NSLayoutConstraint! { 118 | didSet { 119 | maxWidthConstraint.isActive = false 120 | } 121 | } 122 | 123 | var maxWidth: CGFloat? = nil { 124 | didSet { 125 | guard let maxWidth = maxWidth else { 126 | return 127 | } 128 | maxWidthConstraint.isActive = true 129 | maxWidthConstraint.constant = maxWidth 130 | } 131 | } 132 | } 133 | ``` 134 | ## License 135 | TagsFlowLayout is available under the MIT license. See the LICENSE file for more info. 136 | 137 | ## Donation 138 | Buy Me A Coffee 139 | -------------------------------------------------------------------------------- /Replay_Insert_Remove.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rastaman111/TagsFlowLayout/d6e407d166c981e546659177555745e118a59796/Replay_Insert_Remove.gif -------------------------------------------------------------------------------- /Right.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rastaman111/TagsFlowLayout/d6e407d166c981e546659177555745e118a59796/Right.jpg -------------------------------------------------------------------------------- /Sources/TagsFlowLayout/TagsFlowLayout.swift: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2022 Alexandr Sibirtsev 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 | 23 | import UIKit 24 | 25 | public extension TagsFlowLayout { 26 | 27 | enum LayoutAlignment: Int { 28 | case left 29 | case center 30 | case right 31 | } 32 | } 33 | 34 | public class TagsFlowLayout: UICollectionViewFlowLayout { 35 | 36 | let alignment: LayoutAlignment 37 | 38 | //MARK: - Init Methods 39 | 40 | required init(alignment: LayoutAlignment = .left, minimumInteritemSpacing: CGFloat = 0, minimumLineSpacing: CGFloat = 0, sectionInset: UIEdgeInsets = .zero) { 41 | self.alignment = alignment 42 | super.init() 43 | 44 | self.minimumInteritemSpacing = minimumInteritemSpacing 45 | self.minimumLineSpacing = minimumLineSpacing 46 | self.sectionInset = sectionInset 47 | 48 | self.estimatedItemSize = UICollectionViewFlowLayout.automaticSize 49 | self.sectionInsetReference = SectionInsetReference.fromLayoutMargins 50 | } 51 | 52 | required init?(coder aDecoder: NSCoder) { 53 | fatalError("init(coder:) has not been implemented") 54 | } 55 | 56 | public override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 57 | guard let superArray = super.layoutAttributesForElements(in: rect) else { return nil } 58 | guard let attributes = NSArray(array: superArray, copyItems: true) as? [UICollectionViewLayoutAttributes] else { return nil } 59 | // Constants 60 | let leftPadding: CGFloat = 8 61 | let interItemSpacing = minimumInteritemSpacing 62 | // Tracking values 63 | var leftMargin: CGFloat = leftPadding // Modified to determine origin.x for each item 64 | var maxY: CGFloat = -1.0 // Modified to determine origin.y for each item 65 | var rowSizes: [[CGFloat]] = [] // Tracks the starting and ending x-values for the first and last item in the row 66 | var currentRow: Int = 0 // Tracks the current row 67 | attributes.forEach { layoutAttribute in 68 | guard layoutAttribute.representedElementCategory == .cell else { 69 | return 70 | } 71 | // Each layoutAttribute represents its own item 72 | if layoutAttribute.frame.origin.y >= maxY { 73 | // This layoutAttribute represents the left-most item in the row 74 | leftMargin = leftPadding 75 | // Register its origin.x in rowSizes for use later 76 | if rowSizes.count == 0 { 77 | // Add to first row 78 | rowSizes = [[leftMargin, 0]] 79 | } else { 80 | // Append a new row 81 | rowSizes.append([leftMargin, 0]) 82 | currentRow += 1 83 | } 84 | } 85 | layoutAttribute.frame.origin.x = leftMargin 86 | leftMargin += layoutAttribute.frame.width + interItemSpacing 87 | maxY = max(layoutAttribute.frame.maxY, maxY) 88 | // Add right-most x value for last item in the row 89 | rowSizes[currentRow][1] = leftMargin - interItemSpacing 90 | } 91 | 92 | guard alignment != .left && alignment != .right else { 93 | return attributes 94 | } 95 | 96 | // At this point, all cells are left aligned 97 | // Reset tracking values and add extra left padding to center align entire row 98 | leftMargin = leftPadding 99 | maxY = -1.0 100 | currentRow = 0 101 | attributes.forEach { layoutAttribute in 102 | // Each layoutAttribute is its own item 103 | if layoutAttribute.frame.origin.y >= maxY { 104 | // This layoutAttribute represents the left-most item in the row 105 | leftMargin = leftPadding 106 | // Need to bump it up by an appended margin 107 | let rowWidth = rowSizes[currentRow][1] - rowSizes[currentRow][0] // last.x - first.x 108 | let appendedMargin = (collectionView!.frame.width - leftPadding - rowWidth - leftPadding) / 2 109 | leftMargin += appendedMargin 110 | currentRow += 1 111 | } 112 | layoutAttribute.frame.origin.x = leftMargin 113 | leftMargin += layoutAttribute.frame.width + interItemSpacing 114 | maxY = max(layoutAttribute.frame.maxY, maxY) 115 | } 116 | 117 | return attributes 118 | } 119 | 120 | public override var flipsHorizontallyInOppositeLayoutDirection: Bool { 121 | return true 122 | } 123 | 124 | public override var developmentLayoutDirection: UIUserInterfaceLayoutDirection { 125 | if alignment == .right { 126 | return UIUserInterfaceLayoutDirection.rightToLeft 127 | } else { 128 | return UIUserInterfaceLayoutDirection.leftToRight 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /TagsFlowLayout.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "TagsFlowLayout" 4 | s.version = "1.0" 5 | s.summary = "This implementation is built using a UICollectionView and a custom flowLayout in Swift." 6 | s.homepage = "https://github.com/rastaman111/TagsFlowLayout" 7 | s.source = { :git => "https://github.com/rastaman111/TagsFlowLayout.git", :tag => s.version } 8 | s.license = { :type => "MIT", :file => "LICENSE" } 9 | s.author = { 'Alexander' => "rastaman.alex007@gmail.com" } 10 | 11 | s.swift_version = '5.0' 12 | s.ios.deployment_target = '11.0' 13 | 14 | s.source_files = 'Sources/TagsFlowLayout/**/*.swift' 15 | 16 | end 17 | -------------------------------------------------------------------------------- /TagsFlowLayout/.gitignore: -------------------------------------------------------------------------------- 1 | # macOS 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 26 | # Carthage/Checkouts 27 | 28 | Carthage/Build 29 | 30 | # We recommend against adding the Pods directory to your .gitignore. However 31 | # you should judge for yourself, the pros and cons are mentioned at: 32 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 33 | # 34 | # Note: if you ignore the Pods directory, make sure to uncomment 35 | # `pod install` in .travis.yml 36 | # 37 | # Pods/ 38 | -------------------------------------------------------------------------------- /TagsFlowLayout/.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * https://www.objc.io/issues/6-build-tools/travis-ci/ 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode7.3 6 | language: objective-c 7 | # cache: cocoapods 8 | # podfile: Example/Podfile 9 | # before_install: 10 | # - gem install cocoapods # Since Travis is not always on latest version 11 | # - pod install --project-directory=Example 12 | script: 13 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/TagsFlowLayout.xcworkspace -scheme TagsFlowLayout-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty 14 | - pod lib lint 15 | -------------------------------------------------------------------------------- /TagsFlowLayout/1.0/TagsFlowLayout.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "TagsFlowLayout" 4 | s.version = "1.0" 5 | s.summary = "This implementation is built using a UICollectionView and a custom flowLayout in Swift." 6 | s.homepage = "https://github.com/rastaman111/TagsFlowLayout" 7 | s.source = { :git => "https://github.com/rastaman111/TagsFlowLayout.git", :tag => s.version } 8 | s.license = { :type => "MIT", :file => "LICENSE" } 9 | s.author = { 'Alexander' => "rastaman.alex007@gmail.com" } 10 | 11 | s.swift_version = '5.0' 12 | s.ios.deployment_target = '11.0' 13 | 14 | s.source_files = 'Sources/TagsFlowLayout/**/*.swift' 15 | 16 | end 17 | -------------------------------------------------------------------------------- /Tests/TagsFlowLayoutTests/TagsFlowLayoutTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import TagsFlowLayout 3 | 4 | final class TagsFlowLayoutTests: XCTestCase { 5 | func testExample() throws { 6 | // This is an example of a functional test case. 7 | // Use XCTAssert and related functions to verify your tests produce the correct 8 | // results. 9 | } 10 | } 11 | --------------------------------------------------------------------------------