├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── .travis.yml
├── CONTRIBUTING.md
├── Example
├── Podfile
├── Podfile.lock
├── Pods
│ ├── Local Podspecs
│ │ └── StoriesLayout.podspec.json
│ ├── Manifest.lock
│ ├── Pods.xcodeproj
│ │ └── project.pbxproj
│ └── Target Support Files
│ │ ├── Pods-StoriesLayout_Example
│ │ ├── Pods-StoriesLayout_Example-Info.plist
│ │ ├── Pods-StoriesLayout_Example-acknowledgements.markdown
│ │ ├── Pods-StoriesLayout_Example-acknowledgements.plist
│ │ ├── Pods-StoriesLayout_Example-dummy.m
│ │ ├── Pods-StoriesLayout_Example-frameworks.sh
│ │ ├── Pods-StoriesLayout_Example-umbrella.h
│ │ ├── Pods-StoriesLayout_Example.debug.xcconfig
│ │ ├── Pods-StoriesLayout_Example.modulemap
│ │ └── Pods-StoriesLayout_Example.release.xcconfig
│ │ ├── Pods-StoriesLayout_Tests
│ │ ├── Pods-StoriesLayout_Tests-Info.plist
│ │ ├── Pods-StoriesLayout_Tests-acknowledgements.markdown
│ │ ├── Pods-StoriesLayout_Tests-acknowledgements.plist
│ │ ├── Pods-StoriesLayout_Tests-dummy.m
│ │ ├── Pods-StoriesLayout_Tests-umbrella.h
│ │ ├── Pods-StoriesLayout_Tests.debug.xcconfig
│ │ ├── Pods-StoriesLayout_Tests.modulemap
│ │ └── Pods-StoriesLayout_Tests.release.xcconfig
│ │ └── StoriesLayout
│ │ ├── StoriesLayout-Info.plist
│ │ ├── StoriesLayout-dummy.m
│ │ ├── StoriesLayout-prefix.pch
│ │ ├── StoriesLayout-umbrella.h
│ │ ├── StoriesLayout.modulemap
│ │ └── StoriesLayout.xcconfig
├── StoriesLayout.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── StoriesLayout-Example.xcscheme
├── StoriesLayout.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── StoriesLayout
│ ├── AppDelegate.swift
│ ├── Base.lproj
│ │ └── LaunchScreen.xib
│ ├── Images.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ └── storiesLayout
│ │ │ ├── 001.imageset
│ │ │ ├── Contents.json
│ │ │ └── architecture-blue-sky-cliff-248771.jpg
│ │ │ ├── 002.imageset
│ │ │ ├── Contents.json
│ │ │ └── bird-s-eye-view-curve-drone-photography-1658967.jpg
│ │ │ ├── 003.imageset
│ │ │ ├── Contents.json
│ │ │ └── bay-beach-boats-919237.jpg
│ │ │ ├── 004.imageset
│ │ │ ├── Contents.json
│ │ │ └── aerial-architecture-blue-731229.jpg
│ │ │ ├── 005.imageset
│ │ │ ├── Contents.json
│ │ │ └── architecture-building-city-311066.jpg
│ │ │ ├── 006.imageset
│ │ │ ├── Contents.json
│ │ │ └── aerial-architecture-buildings-434194.jpg
│ │ │ └── Contents.json
│ ├── Info.plist
│ ├── LayoutCollectionViewCell.swift
│ ├── LayoutCollectionViewCell.xib
│ ├── MySafariCollectionViewCell.swift
│ ├── MySafariCollectionViewCell.xib
│ ├── MyStoriesCollectionViewCell.swift
│ ├── MyStoriesCollectionViewCell.xib
│ ├── ViewControllers
│ │ ├── DemoController
│ │ │ ├── DemoController.swift
│ │ │ └── DemoRoutable.swift
│ │ └── ListViewController
│ │ │ └── ListViewController.swift
│ └── ViewModels
│ │ ├── ImageViewModel.swift
│ │ ├── LayoutItemViewModel.swift
│ │ └── Section.swift
├── Tests
│ ├── Helpers
│ │ └── CATransform3D+Helper.swift
│ ├── Info.plist
│ ├── Mocks
│ │ └── UICollectionDataSourceMock.swift
│ └── StoriesLayout
│ │ ├── StoriesCollectionViewLayoutSpec.swift
│ │ └── StoriesLayoutAttributesSpec.swift
└── fastlane
│ ├── Appfile
│ ├── Fastfile
│ ├── README.md
│ └── report.xml
├── LICENSE
├── README.md
├── SafariLayout.podspec
├── SafariLayout
└── Classes
│ ├── SafariCollectionViewCell.swift
│ ├── SafariCollectionViewLayout.swift
│ └── SafariCollectionViewLayoutAttributes.swift
├── StoriesLayout.podspec
├── StoriesLayout
├── Assets
│ └── .gitkeep
└── Classes
│ ├── .gitkeep
│ ├── StoriesCollectionViewCell.swift
│ ├── StoriesCollectionViewLayout.swift
│ └── StoriesLayoutAttributes.swift
└── _Pods.xcodeproj
/.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 | # OS X
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 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: objective-c
2 | osx_image: xcode10.2
3 | before_install:
4 | - gem update fastlane --no-document
5 | - gem update cocoapods --no-document
6 | script:
7 | - cd Example/
8 | - fastlane ci
9 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | When contributing to this repository, please first discuss the change you wish to make via issue,
4 | email, or any other method with the owners of this repository before making a change.
5 |
6 | Please note we have a code of conduct, please follow it in all your interactions with the project.
7 |
8 | ## Pull Request Process
9 |
10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a
11 | build.
12 | 2. Ensure your work is thoroughly tested, to the best of your abilities
13 | 2. You may merge the Pull Request in once you have the sign-off from a maintainer
14 |
15 | ## Code of Conduct
16 |
17 | ### Our Pledge
18 |
19 | In the interest of fostering an open and welcoming environment, we as
20 | contributors and maintainers pledge to making participation in our project and
21 | our community a harassment-free experience for everyone, regardless of age, body
22 | size, disability, ethnicity, gender identity and expression, level of experience,
23 | nationality, personal appearance, race, religion, or sexual identity and
24 | orientation.
25 |
26 | ### Our Standards
27 |
28 | Examples of behavior that contributes to creating a positive environment
29 | include:
30 |
31 | - Using welcoming and inclusive language
32 | - Being respectful of differing viewpoints and experiences
33 | - Gracefully accepting constructive criticism
34 | - Focusing on what is best for the community
35 | - Showing empathy towards other community members
36 |
37 | Examples of unacceptable behavior by participants include:
38 |
39 | - The use of sexualized language or imagery and unwelcome sexual attention or
40 | advances
41 | - Trolling, insulting/derogatory comments, and personal or political attacks
42 | - Public or private harassment
43 | - Publishing others' private information, such as a physical or electronic
44 | address, without explicit permission
45 | - Other conduct which could reasonably be considered inappropriate in a
46 | professional setting
47 |
48 | ### Our Responsibilities
49 |
50 | Project maintainers are responsible for clarifying the standards of acceptable
51 | behavior and are expected to take appropriate and fair corrective action in
52 | response to any instances of unacceptable behavior.
53 |
54 | Project maintainers have the right and responsibility to remove, edit, or
55 | reject comments, commits, code, wiki edits, issues, and other contributions
56 | that are not aligned to this Code of Conduct, or to ban temporarily or
57 | permanently any contributor for other behaviors that they deem inappropriate,
58 | threatening, offensive, or harmful.
59 |
60 | ### Scope
61 |
62 | This Code of Conduct applies both within project spaces and in public spaces
63 | when an individual is representing the project or its community. Examples of
64 | representing a project or community include using an official project e-mail
65 | address, posting via an official social media account, or acting as an appointed
66 | representative at an online or offline event. Representation of a project may be
67 | further defined and clarified by project maintainers.
68 |
69 | ### Enforcement
70 |
71 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
72 | reported by contacting the project team at andy@hankchizljaw.io. All
73 | complaints will be reviewed and investigated and will result in a response that
74 | is deemed necessary and appropriate to the circumstances. The project team is
75 | obligated to maintain confidentiality with regard to the reporter of an incident.
76 | Further details of specific enforcement policies may be posted separately.
77 |
78 | Project maintainers who do not follow or enforce the Code of Conduct in good
79 | faith may face temporary or permanent repercussions as determined by other
80 | members of the project's leadership.
81 |
82 | ### Attribution
83 |
84 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
85 | available at [http://contributor-covenant.org/version/1/4][version]
86 |
87 | [homepage]: http://contributor-covenant.org
88 | [version]: http://contributor-covenant.org/version/1/4/
89 |
--------------------------------------------------------------------------------
/Example/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '10.0'
2 | use_frameworks!
3 |
4 | target 'StoriesLayout_Example' do
5 | pod 'PowerTools', '~> 0.3'
6 | pod 'StoriesLayout', :path => '../'
7 | pod 'SafariLayout', :path => '../'
8 |
9 | target 'StoriesLayout_Tests' do
10 | inherit! :search_paths
11 | pod 'Quick'
12 | pod 'Nimble'
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/Example/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Nimble (8.0.2)
3 | - PowerTools (0.3.0):
4 | - PowerTools/CollectionVM (= 0.3.0)
5 | - PowerTools/Core (= 0.3.0)
6 | - PowerTools/CollectionVM (0.3.0):
7 | - PowerTools/Core
8 | - PowerTools/Core (0.3.0)
9 | - Quick (2.1.0)
10 | - SafariLayout (0.1.1)
11 | - StoriesLayout (0.1.1)
12 |
13 | DEPENDENCIES:
14 | - Nimble
15 | - PowerTools (~> 0.3)
16 | - Quick
17 | - SafariLayout (from `../`)
18 | - StoriesLayout (from `../`)
19 |
20 | SPEC REPOS:
21 | https://github.com/cocoapods/specs.git:
22 | - Nimble
23 | - PowerTools
24 | - Quick
25 |
26 | EXTERNAL SOURCES:
27 | SafariLayout:
28 | :path: "../"
29 | StoriesLayout:
30 | :path: "../"
31 |
32 | SPEC CHECKSUMS:
33 | Nimble: 622629381bda1dd5678162f21f1368cec7cbba60
34 | PowerTools: c8c15dae195d431668aef9feb79b684ea764def9
35 | Quick: 4be43f6634acfa727dd106bdf3929ce125ffa79d
36 | SafariLayout: ead4b2e58882f84b0ebbd32cd3255795277ac744
37 | StoriesLayout: 694a3c67a705dbb8bdd6b4ce7c512fd7389d487c
38 |
39 | PODFILE CHECKSUM: 309fea3f2752413a2a2904e6e028b24f0d958e2a
40 |
41 | COCOAPODS: 1.7.2
42 |
--------------------------------------------------------------------------------
/Example/Pods/Local Podspecs/StoriesLayout.podspec.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "StoriesLayout",
3 | "version": "0.1.1",
4 | "summary": "An Instagram Stories like UICollectionViewLayout",
5 | "swift_versions": "5.0",
6 | "description": "This is a UICollectionViewLayout that reproduce the Instagram Stories experience,\njust use StoriesCollectionViewLayout in your UICollectionView and subclass your cells from StoriesCollectionViewCell!",
7 | "homepage": "https://github.com/Oni-zerone/CollectionLayouts",
8 | "license": {
9 | "type": "MIT",
10 | "file": "LICENSE"
11 | },
12 | "authors": {
13 | "Andrea Altea": "oni.zerone@gmail.com"
14 | },
15 | "source": {
16 | "git": "https://github.com/Oni-zerone/CollectionLayouts.git",
17 | "tag": "StoriesLayout-0.1.1"
18 | },
19 | "social_media_url": "https://twitter.com/Oni_zerone",
20 | "platforms": {
21 | "ios": "10.0"
22 | },
23 | "source_files": "StoriesLayout/Classes/**/*",
24 | "frameworks": [
25 | "UIKit",
26 | "CoreGraphics"
27 | ],
28 | "swift_version": "5.0"
29 | }
30 |
--------------------------------------------------------------------------------
/Example/Pods/Manifest.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Nimble (8.0.2)
3 | - PowerTools (0.3.0):
4 | - PowerTools/CollectionVM (= 0.3.0)
5 | - PowerTools/Core (= 0.3.0)
6 | - PowerTools/CollectionVM (0.3.0):
7 | - PowerTools/Core
8 | - PowerTools/Core (0.3.0)
9 | - Quick (2.1.0)
10 | - SafariLayout (0.1.1)
11 | - StoriesLayout (0.1.1)
12 |
13 | DEPENDENCIES:
14 | - Nimble
15 | - PowerTools (~> 0.3)
16 | - Quick
17 | - SafariLayout (from `../`)
18 | - StoriesLayout (from `../`)
19 |
20 | SPEC REPOS:
21 | https://github.com/cocoapods/specs.git:
22 | - Nimble
23 | - PowerTools
24 | - Quick
25 |
26 | EXTERNAL SOURCES:
27 | SafariLayout:
28 | :path: "../"
29 | StoriesLayout:
30 | :path: "../"
31 |
32 | SPEC CHECKSUMS:
33 | Nimble: 622629381bda1dd5678162f21f1368cec7cbba60
34 | PowerTools: c8c15dae195d431668aef9feb79b684ea764def9
35 | Quick: 4be43f6634acfa727dd106bdf3929ce125ffa79d
36 | SafariLayout: ead4b2e58882f84b0ebbd32cd3255795277ac744
37 | StoriesLayout: 694a3c67a705dbb8bdd6b4ce7c512fd7389d487c
38 |
39 | PODFILE CHECKSUM: 309fea3f2752413a2a2904e6e028b24f0d958e2a
40 |
41 | COCOAPODS: 1.7.2
42 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-StoriesLayout_Example/Pods-StoriesLayout_Example-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 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-StoriesLayout_Example/Pods-StoriesLayout_Example-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 |
4 | ## PowerTools
5 |
6 | Copyright (c) 2018 acct=
7 |
8 | Permission is hereby granted, free of charge, to any person obtaining a copy
9 | of this software and associated documentation files (the "Software"), to deal
10 | in the Software without restriction, including without limitation the rights
11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | copies of the Software, and to permit persons to whom the Software is
13 | furnished to do so, subject to the following conditions:
14 |
15 | The above copyright notice and this permission notice shall be included in
16 | all copies or substantial portions of the Software.
17 |
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | THE SOFTWARE.
25 |
26 |
27 | ## SafariLayout
28 |
29 | Copyright (c) 2019 oni.zerone@gmail.com
30 |
31 | Permission is hereby granted, free of charge, to any person obtaining a copy
32 | of this software and associated documentation files (the "Software"), to deal
33 | in the Software without restriction, including without limitation the rights
34 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
35 | copies of the Software, and to permit persons to whom the Software is
36 | furnished to do so, subject to the following conditions:
37 |
38 | The above copyright notice and this permission notice shall be included in
39 | all copies or substantial portions of the Software.
40 |
41 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
44 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
45 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
46 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
47 | THE SOFTWARE.
48 |
49 |
50 | ## StoriesLayout
51 |
52 | Copyright (c) 2019 oni.zerone@gmail.com
53 |
54 | Permission is hereby granted, free of charge, to any person obtaining a copy
55 | of this software and associated documentation files (the "Software"), to deal
56 | in the Software without restriction, including without limitation the rights
57 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
58 | copies of the Software, and to permit persons to whom the Software is
59 | furnished to do so, subject to the following conditions:
60 |
61 | The above copyright notice and this permission notice shall be included in
62 | all copies or substantial portions of the Software.
63 |
64 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
65 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
66 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
67 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
68 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
69 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
70 | THE SOFTWARE.
71 |
72 | Generated by CocoaPods - https://cocoapods.org
73 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-StoriesLayout_Example/Pods-StoriesLayout_Example-acknowledgements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreferenceSpecifiers
6 |
7 |
8 | FooterText
9 | This application makes use of the following third party libraries:
10 | Title
11 | Acknowledgements
12 | Type
13 | PSGroupSpecifier
14 |
15 |
16 | FooterText
17 | Copyright (c) 2018 acct<blob>=<NULL> <oni.zerone@gmail.com>
18 |
19 | Permission is hereby granted, free of charge, to any person obtaining a copy
20 | of this software and associated documentation files (the "Software"), to deal
21 | in the Software without restriction, including without limitation the rights
22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | copies of the Software, and to permit persons to whom the Software is
24 | furnished to do so, subject to the following conditions:
25 |
26 | The above copyright notice and this permission notice shall be included in
27 | all copies or substantial portions of the Software.
28 |
29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
35 | THE SOFTWARE.
36 |
37 | License
38 | MIT
39 | Title
40 | PowerTools
41 | Type
42 | PSGroupSpecifier
43 |
44 |
45 | FooterText
46 | Copyright (c) 2019 oni.zerone@gmail.com <oni.zerone@gmail.com>
47 |
48 | Permission is hereby granted, free of charge, to any person obtaining a copy
49 | of this software and associated documentation files (the "Software"), to deal
50 | in the Software without restriction, including without limitation the rights
51 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
52 | copies of the Software, and to permit persons to whom the Software is
53 | furnished to do so, subject to the following conditions:
54 |
55 | The above copyright notice and this permission notice shall be included in
56 | all copies or substantial portions of the Software.
57 |
58 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
59 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
60 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
61 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
62 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
63 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
64 | THE SOFTWARE.
65 |
66 | License
67 | MIT
68 | Title
69 | SafariLayout
70 | Type
71 | PSGroupSpecifier
72 |
73 |
74 | FooterText
75 | Copyright (c) 2019 oni.zerone@gmail.com <oni.zerone@gmail.com>
76 |
77 | Permission is hereby granted, free of charge, to any person obtaining a copy
78 | of this software and associated documentation files (the "Software"), to deal
79 | in the Software without restriction, including without limitation the rights
80 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
81 | copies of the Software, and to permit persons to whom the Software is
82 | furnished to do so, subject to the following conditions:
83 |
84 | The above copyright notice and this permission notice shall be included in
85 | all copies or substantial portions of the Software.
86 |
87 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
88 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
89 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
90 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
91 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
92 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
93 | THE SOFTWARE.
94 |
95 | License
96 | MIT
97 | Title
98 | StoriesLayout
99 | Type
100 | PSGroupSpecifier
101 |
102 |
103 | FooterText
104 | Generated by CocoaPods - https://cocoapods.org
105 | Title
106 |
107 | Type
108 | PSGroupSpecifier
109 |
110 |
111 | StringsTable
112 | Acknowledgements
113 | Title
114 | Acknowledgements
115 |
116 |
117 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-StoriesLayout_Example/Pods-StoriesLayout_Example-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_StoriesLayout_Example : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_StoriesLayout_Example
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-StoriesLayout_Example/Pods-StoriesLayout_Example-frameworks.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 | set -u
4 | set -o pipefail
5 |
6 | function on_error {
7 | echo "$(realpath -mq "${0}"):$1: error: Unexpected failure"
8 | }
9 | trap 'on_error $LINENO' ERR
10 |
11 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then
12 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy
13 | # frameworks to, so exit 0 (signalling the script phase was successful).
14 | exit 0
15 | fi
16 |
17 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
18 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
19 |
20 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}"
21 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
22 |
23 | # Used as a return value for each invocation of `strip_invalid_archs` function.
24 | STRIP_BINARY_RETVAL=0
25 |
26 | # This protects against multiple targets copying the same framework dependency at the same time. The solution
27 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
28 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
29 |
30 | # Copies and strips a vendored framework
31 | install_framework()
32 | {
33 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
34 | local source="${BUILT_PRODUCTS_DIR}/$1"
35 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
36 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
37 | elif [ -r "$1" ]; then
38 | local source="$1"
39 | fi
40 |
41 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
42 |
43 | if [ -L "${source}" ]; then
44 | echo "Symlinked..."
45 | source="$(readlink "${source}")"
46 | fi
47 |
48 | # Use filter instead of exclude so missing patterns don't throw errors.
49 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
50 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
51 |
52 | local basename
53 | basename="$(basename -s .framework "$1")"
54 | binary="${destination}/${basename}.framework/${basename}"
55 |
56 | if ! [ -r "$binary" ]; then
57 | binary="${destination}/${basename}"
58 | elif [ -L "${binary}" ]; then
59 | echo "Destination binary is symlinked..."
60 | dirname="$(dirname "${binary}")"
61 | binary="${dirname}/$(readlink "${binary}")"
62 | fi
63 |
64 | # Strip invalid architectures so "fat" simulator / device frameworks work on device
65 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
66 | strip_invalid_archs "$binary"
67 | fi
68 |
69 | # Resign the code if required by the build settings to avoid unstable apps
70 | code_sign_if_enabled "${destination}/$(basename "$1")"
71 |
72 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
73 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
74 | local swift_runtime_libs
75 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u)
76 | for lib in $swift_runtime_libs; do
77 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
78 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
79 | code_sign_if_enabled "${destination}/${lib}"
80 | done
81 | fi
82 | }
83 |
84 | # Copies and strips a vendored dSYM
85 | install_dsym() {
86 | local source="$1"
87 | if [ -r "$source" ]; then
88 | # Copy the dSYM into a the targets temp dir.
89 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\""
90 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}"
91 |
92 | local basename
93 | basename="$(basename -s .framework.dSYM "$source")"
94 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}"
95 |
96 | # Strip invalid architectures so "fat" simulator / device frameworks work on device
97 | if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then
98 | strip_invalid_archs "$binary"
99 | fi
100 |
101 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then
102 | # Move the stripped file into its final destination.
103 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\""
104 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}"
105 | else
106 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing.
107 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM"
108 | fi
109 | fi
110 | }
111 |
112 | # Copies the bcsymbolmap files of a vendored framework
113 | install_bcsymbolmap() {
114 | local bcsymbolmap_path="$1"
115 | local destination="${BUILT_PRODUCTS_DIR}"
116 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}""
117 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"
118 | }
119 |
120 | # Signs a framework with the provided identity
121 | code_sign_if_enabled() {
122 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
123 | # Use the current code_sign_identity
124 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
125 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'"
126 |
127 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
128 | code_sign_cmd="$code_sign_cmd &"
129 | fi
130 | echo "$code_sign_cmd"
131 | eval "$code_sign_cmd"
132 | fi
133 | }
134 |
135 | # Strip invalid architectures
136 | strip_invalid_archs() {
137 | binary="$1"
138 | # Get architectures for current target binary
139 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)"
140 | # Intersect them with the architectures we are building for
141 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)"
142 | # If there are no archs supported by this binary then warn the user
143 | if [[ -z "$intersected_archs" ]]; then
144 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)."
145 | STRIP_BINARY_RETVAL=0
146 | return
147 | fi
148 | stripped=""
149 | for arch in $binary_archs; do
150 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then
151 | # Strip non-valid architectures in-place
152 | lipo -remove "$arch" -output "$binary" "$binary"
153 | stripped="$stripped $arch"
154 | fi
155 | done
156 | if [[ "$stripped" ]]; then
157 | echo "Stripped $binary of architectures:$stripped"
158 | fi
159 | STRIP_BINARY_RETVAL=1
160 | }
161 |
162 |
163 | if [[ "$CONFIGURATION" == "Debug" ]]; then
164 | install_framework "${BUILT_PRODUCTS_DIR}/PowerTools/PowerTools.framework"
165 | install_framework "${BUILT_PRODUCTS_DIR}/SafariLayout/SafariLayout.framework"
166 | install_framework "${BUILT_PRODUCTS_DIR}/StoriesLayout/StoriesLayout.framework"
167 | fi
168 | if [[ "$CONFIGURATION" == "Release" ]]; then
169 | install_framework "${BUILT_PRODUCTS_DIR}/PowerTools/PowerTools.framework"
170 | install_framework "${BUILT_PRODUCTS_DIR}/SafariLayout/SafariLayout.framework"
171 | install_framework "${BUILT_PRODUCTS_DIR}/StoriesLayout/StoriesLayout.framework"
172 | fi
173 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
174 | wait
175 | fi
176 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-StoriesLayout_Example/Pods-StoriesLayout_Example-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double Pods_StoriesLayout_ExampleVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char Pods_StoriesLayout_ExampleVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-StoriesLayout_Example/Pods-StoriesLayout_Example.debug.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PowerTools" "${PODS_CONFIGURATION_BUILD_DIR}/SafariLayout" "${PODS_CONFIGURATION_BUILD_DIR}/StoriesLayout"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PowerTools/PowerTools.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SafariLayout/SafariLayout.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/StoriesLayout/StoriesLayout.framework/Headers"
5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
6 | OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "PowerTools" -framework "SafariLayout" -framework "StoriesLayout" -framework "UIKit"
7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
8 | PODS_BUILD_DIR = ${BUILD_DIR}
9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
11 | PODS_ROOT = ${SRCROOT}/Pods
12 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-StoriesLayout_Example/Pods-StoriesLayout_Example.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_StoriesLayout_Example {
2 | umbrella header "Pods-StoriesLayout_Example-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-StoriesLayout_Example/Pods-StoriesLayout_Example.release.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PowerTools" "${PODS_CONFIGURATION_BUILD_DIR}/SafariLayout" "${PODS_CONFIGURATION_BUILD_DIR}/StoriesLayout"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PowerTools/PowerTools.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SafariLayout/SafariLayout.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/StoriesLayout/StoriesLayout.framework/Headers"
5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
6 | OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "PowerTools" -framework "SafariLayout" -framework "StoriesLayout" -framework "UIKit"
7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
8 | PODS_BUILD_DIR = ${BUILD_DIR}
9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
11 | PODS_ROOT = ${SRCROOT}/Pods
12 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-StoriesLayout_Tests/Pods-StoriesLayout_Tests-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-StoriesLayout_Tests/Pods-StoriesLayout_Tests-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 |
4 | ## Nimble
5 |
6 | Apache License
7 | Version 2.0, January 2004
8 | http://www.apache.org/licenses/
9 |
10 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
11 |
12 | 1. Definitions.
13 |
14 | "License" shall mean the terms and conditions for use, reproduction,
15 | and distribution as defined by Sections 1 through 9 of this document.
16 |
17 | "Licensor" shall mean the copyright owner or entity authorized by
18 | the copyright owner that is granting the License.
19 |
20 | "Legal Entity" shall mean the union of the acting entity and all
21 | other entities that control, are controlled by, or are under common
22 | control with that entity. For the purposes of this definition,
23 | "control" means (i) the power, direct or indirect, to cause the
24 | direction or management of such entity, whether by contract or
25 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
26 | outstanding shares, or (iii) beneficial ownership of such entity.
27 |
28 | "You" (or "Your") shall mean an individual or Legal Entity
29 | exercising permissions granted by this License.
30 |
31 | "Source" form shall mean the preferred form for making modifications,
32 | including but not limited to software source code, documentation
33 | source, and configuration files.
34 |
35 | "Object" form shall mean any form resulting from mechanical
36 | transformation or translation of a Source form, including but
37 | not limited to compiled object code, generated documentation,
38 | and conversions to other media types.
39 |
40 | "Work" shall mean the work of authorship, whether in Source or
41 | Object form, made available under the License, as indicated by a
42 | copyright notice that is included in or attached to the work
43 | (an example is provided in the Appendix below).
44 |
45 | "Derivative Works" shall mean any work, whether in Source or Object
46 | form, that is based on (or derived from) the Work and for which the
47 | editorial revisions, annotations, elaborations, or other modifications
48 | represent, as a whole, an original work of authorship. For the purposes
49 | of this License, Derivative Works shall not include works that remain
50 | separable from, or merely link (or bind by name) to the interfaces of,
51 | the Work and Derivative Works thereof.
52 |
53 | "Contribution" shall mean any work of authorship, including
54 | the original version of the Work and any modifications or additions
55 | to that Work or Derivative Works thereof, that is intentionally
56 | submitted to Licensor for inclusion in the Work by the copyright owner
57 | or by an individual or Legal Entity authorized to submit on behalf of
58 | the copyright owner. For the purposes of this definition, "submitted"
59 | means any form of electronic, verbal, or written communication sent
60 | to the Licensor or its representatives, including but not limited to
61 | communication on electronic mailing lists, source code control systems,
62 | and issue tracking systems that are managed by, or on behalf of, the
63 | Licensor for the purpose of discussing and improving the Work, but
64 | excluding communication that is conspicuously marked or otherwise
65 | designated in writing by the copyright owner as "Not a Contribution."
66 |
67 | "Contributor" shall mean Licensor and any individual or Legal Entity
68 | on behalf of whom a Contribution has been received by Licensor and
69 | subsequently incorporated within the Work.
70 |
71 | 2. Grant of Copyright License. Subject to the terms and conditions of
72 | this License, each Contributor hereby grants to You a perpetual,
73 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
74 | copyright license to reproduce, prepare Derivative Works of,
75 | publicly display, publicly perform, sublicense, and distribute the
76 | Work and such Derivative Works in Source or Object form.
77 |
78 | 3. Grant of Patent License. Subject to the terms and conditions of
79 | this License, each Contributor hereby grants to You a perpetual,
80 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
81 | (except as stated in this section) patent license to make, have made,
82 | use, offer to sell, sell, import, and otherwise transfer the Work,
83 | where such license applies only to those patent claims licensable
84 | by such Contributor that are necessarily infringed by their
85 | Contribution(s) alone or by combination of their Contribution(s)
86 | with the Work to which such Contribution(s) was submitted. If You
87 | institute patent litigation against any entity (including a
88 | cross-claim or counterclaim in a lawsuit) alleging that the Work
89 | or a Contribution incorporated within the Work constitutes direct
90 | or contributory patent infringement, then any patent licenses
91 | granted to You under this License for that Work shall terminate
92 | as of the date such litigation is filed.
93 |
94 | 4. Redistribution. You may reproduce and distribute copies of the
95 | Work or Derivative Works thereof in any medium, with or without
96 | modifications, and in Source or Object form, provided that You
97 | meet the following conditions:
98 |
99 | (a) You must give any other recipients of the Work or
100 | Derivative Works a copy of this License; and
101 |
102 | (b) You must cause any modified files to carry prominent notices
103 | stating that You changed the files; and
104 |
105 | (c) You must retain, in the Source form of any Derivative Works
106 | that You distribute, all copyright, patent, trademark, and
107 | attribution notices from the Source form of the Work,
108 | excluding those notices that do not pertain to any part of
109 | the Derivative Works; and
110 |
111 | (d) If the Work includes a "NOTICE" text file as part of its
112 | distribution, then any Derivative Works that You distribute must
113 | include a readable copy of the attribution notices contained
114 | within such NOTICE file, excluding those notices that do not
115 | pertain to any part of the Derivative Works, in at least one
116 | of the following places: within a NOTICE text file distributed
117 | as part of the Derivative Works; within the Source form or
118 | documentation, if provided along with the Derivative Works; or,
119 | within a display generated by the Derivative Works, if and
120 | wherever such third-party notices normally appear. The contents
121 | of the NOTICE file are for informational purposes only and
122 | do not modify the License. You may add Your own attribution
123 | notices within Derivative Works that You distribute, alongside
124 | or as an addendum to the NOTICE text from the Work, provided
125 | that such additional attribution notices cannot be construed
126 | as modifying the License.
127 |
128 | You may add Your own copyright statement to Your modifications and
129 | may provide additional or different license terms and conditions
130 | for use, reproduction, or distribution of Your modifications, or
131 | for any such Derivative Works as a whole, provided Your use,
132 | reproduction, and distribution of the Work otherwise complies with
133 | the conditions stated in this License.
134 |
135 | 5. Submission of Contributions. Unless You explicitly state otherwise,
136 | any Contribution intentionally submitted for inclusion in the Work
137 | by You to the Licensor shall be under the terms and conditions of
138 | this License, without any additional terms or conditions.
139 | Notwithstanding the above, nothing herein shall supersede or modify
140 | the terms of any separate license agreement you may have executed
141 | with Licensor regarding such Contributions.
142 |
143 | 6. Trademarks. This License does not grant permission to use the trade
144 | names, trademarks, service marks, or product names of the Licensor,
145 | except as required for reasonable and customary use in describing the
146 | origin of the Work and reproducing the content of the NOTICE file.
147 |
148 | 7. Disclaimer of Warranty. Unless required by applicable law or
149 | agreed to in writing, Licensor provides the Work (and each
150 | Contributor provides its Contributions) on an "AS IS" BASIS,
151 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
152 | implied, including, without limitation, any warranties or conditions
153 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
154 | PARTICULAR PURPOSE. You are solely responsible for determining the
155 | appropriateness of using or redistributing the Work and assume any
156 | risks associated with Your exercise of permissions under this License.
157 |
158 | 8. Limitation of Liability. In no event and under no legal theory,
159 | whether in tort (including negligence), contract, or otherwise,
160 | unless required by applicable law (such as deliberate and grossly
161 | negligent acts) or agreed to in writing, shall any Contributor be
162 | liable to You for damages, including any direct, indirect, special,
163 | incidental, or consequential damages of any character arising as a
164 | result of this License or out of the use or inability to use the
165 | Work (including but not limited to damages for loss of goodwill,
166 | work stoppage, computer failure or malfunction, or any and all
167 | other commercial damages or losses), even if such Contributor
168 | has been advised of the possibility of such damages.
169 |
170 | 9. Accepting Warranty or Additional Liability. While redistributing
171 | the Work or Derivative Works thereof, You may choose to offer,
172 | and charge a fee for, acceptance of support, warranty, indemnity,
173 | or other liability obligations and/or rights consistent with this
174 | License. However, in accepting such obligations, You may act only
175 | on Your own behalf and on Your sole responsibility, not on behalf
176 | of any other Contributor, and only if You agree to indemnify,
177 | defend, and hold each Contributor harmless for any liability
178 | incurred by, or claims asserted against, such Contributor by reason
179 | of your accepting any such warranty or additional liability.
180 |
181 | END OF TERMS AND CONDITIONS
182 |
183 | APPENDIX: How to apply the Apache License to your work.
184 |
185 | To apply the Apache License to your work, attach the following
186 | boilerplate notice, with the fields enclosed by brackets "{}"
187 | replaced with your own identifying information. (Don't include
188 | the brackets!) The text should be enclosed in the appropriate
189 | comment syntax for the file format. We also recommend that a
190 | file or class name and description of purpose be included on the
191 | same "printed page" as the copyright notice for easier
192 | identification within third-party archives.
193 |
194 | Copyright 2016 Quick Team
195 |
196 | Licensed under the Apache License, Version 2.0 (the "License");
197 | you may not use this file except in compliance with the License.
198 | You may obtain a copy of the License at
199 |
200 | http://www.apache.org/licenses/LICENSE-2.0
201 |
202 | Unless required by applicable law or agreed to in writing, software
203 | distributed under the License is distributed on an "AS IS" BASIS,
204 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
205 | See the License for the specific language governing permissions and
206 | limitations under the License.
207 |
208 |
209 | ## Quick
210 |
211 | Apache License
212 | Version 2.0, January 2004
213 | http://www.apache.org/licenses/
214 |
215 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
216 |
217 | 1. Definitions.
218 |
219 | "License" shall mean the terms and conditions for use, reproduction,
220 | and distribution as defined by Sections 1 through 9 of this document.
221 |
222 | "Licensor" shall mean the copyright owner or entity authorized by
223 | the copyright owner that is granting the License.
224 |
225 | "Legal Entity" shall mean the union of the acting entity and all
226 | other entities that control, are controlled by, or are under common
227 | control with that entity. For the purposes of this definition,
228 | "control" means (i) the power, direct or indirect, to cause the
229 | direction or management of such entity, whether by contract or
230 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
231 | outstanding shares, or (iii) beneficial ownership of such entity.
232 |
233 | "You" (or "Your") shall mean an individual or Legal Entity
234 | exercising permissions granted by this License.
235 |
236 | "Source" form shall mean the preferred form for making modifications,
237 | including but not limited to software source code, documentation
238 | source, and configuration files.
239 |
240 | "Object" form shall mean any form resulting from mechanical
241 | transformation or translation of a Source form, including but
242 | not limited to compiled object code, generated documentation,
243 | and conversions to other media types.
244 |
245 | "Work" shall mean the work of authorship, whether in Source or
246 | Object form, made available under the License, as indicated by a
247 | copyright notice that is included in or attached to the work
248 | (an example is provided in the Appendix below).
249 |
250 | "Derivative Works" shall mean any work, whether in Source or Object
251 | form, that is based on (or derived from) the Work and for which the
252 | editorial revisions, annotations, elaborations, or other modifications
253 | represent, as a whole, an original work of authorship. For the purposes
254 | of this License, Derivative Works shall not include works that remain
255 | separable from, or merely link (or bind by name) to the interfaces of,
256 | the Work and Derivative Works thereof.
257 |
258 | "Contribution" shall mean any work of authorship, including
259 | the original version of the Work and any modifications or additions
260 | to that Work or Derivative Works thereof, that is intentionally
261 | submitted to Licensor for inclusion in the Work by the copyright owner
262 | or by an individual or Legal Entity authorized to submit on behalf of
263 | the copyright owner. For the purposes of this definition, "submitted"
264 | means any form of electronic, verbal, or written communication sent
265 | to the Licensor or its representatives, including but not limited to
266 | communication on electronic mailing lists, source code control systems,
267 | and issue tracking systems that are managed by, or on behalf of, the
268 | Licensor for the purpose of discussing and improving the Work, but
269 | excluding communication that is conspicuously marked or otherwise
270 | designated in writing by the copyright owner as "Not a Contribution."
271 |
272 | "Contributor" shall mean Licensor and any individual or Legal Entity
273 | on behalf of whom a Contribution has been received by Licensor and
274 | subsequently incorporated within the Work.
275 |
276 | 2. Grant of Copyright License. Subject to the terms and conditions of
277 | this License, each Contributor hereby grants to You a perpetual,
278 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
279 | copyright license to reproduce, prepare Derivative Works of,
280 | publicly display, publicly perform, sublicense, and distribute the
281 | Work and such Derivative Works in Source or Object form.
282 |
283 | 3. Grant of Patent License. Subject to the terms and conditions of
284 | this License, each Contributor hereby grants to You a perpetual,
285 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
286 | (except as stated in this section) patent license to make, have made,
287 | use, offer to sell, sell, import, and otherwise transfer the Work,
288 | where such license applies only to those patent claims licensable
289 | by such Contributor that are necessarily infringed by their
290 | Contribution(s) alone or by combination of their Contribution(s)
291 | with the Work to which such Contribution(s) was submitted. If You
292 | institute patent litigation against any entity (including a
293 | cross-claim or counterclaim in a lawsuit) alleging that the Work
294 | or a Contribution incorporated within the Work constitutes direct
295 | or contributory patent infringement, then any patent licenses
296 | granted to You under this License for that Work shall terminate
297 | as of the date such litigation is filed.
298 |
299 | 4. Redistribution. You may reproduce and distribute copies of the
300 | Work or Derivative Works thereof in any medium, with or without
301 | modifications, and in Source or Object form, provided that You
302 | meet the following conditions:
303 |
304 | (a) You must give any other recipients of the Work or
305 | Derivative Works a copy of this License; and
306 |
307 | (b) You must cause any modified files to carry prominent notices
308 | stating that You changed the files; and
309 |
310 | (c) You must retain, in the Source form of any Derivative Works
311 | that You distribute, all copyright, patent, trademark, and
312 | attribution notices from the Source form of the Work,
313 | excluding those notices that do not pertain to any part of
314 | the Derivative Works; and
315 |
316 | (d) If the Work includes a "NOTICE" text file as part of its
317 | distribution, then any Derivative Works that You distribute must
318 | include a readable copy of the attribution notices contained
319 | within such NOTICE file, excluding those notices that do not
320 | pertain to any part of the Derivative Works, in at least one
321 | of the following places: within a NOTICE text file distributed
322 | as part of the Derivative Works; within the Source form or
323 | documentation, if provided along with the Derivative Works; or,
324 | within a display generated by the Derivative Works, if and
325 | wherever such third-party notices normally appear. The contents
326 | of the NOTICE file are for informational purposes only and
327 | do not modify the License. You may add Your own attribution
328 | notices within Derivative Works that You distribute, alongside
329 | or as an addendum to the NOTICE text from the Work, provided
330 | that such additional attribution notices cannot be construed
331 | as modifying the License.
332 |
333 | You may add Your own copyright statement to Your modifications and
334 | may provide additional or different license terms and conditions
335 | for use, reproduction, or distribution of Your modifications, or
336 | for any such Derivative Works as a whole, provided Your use,
337 | reproduction, and distribution of the Work otherwise complies with
338 | the conditions stated in this License.
339 |
340 | 5. Submission of Contributions. Unless You explicitly state otherwise,
341 | any Contribution intentionally submitted for inclusion in the Work
342 | by You to the Licensor shall be under the terms and conditions of
343 | this License, without any additional terms or conditions.
344 | Notwithstanding the above, nothing herein shall supersede or modify
345 | the terms of any separate license agreement you may have executed
346 | with Licensor regarding such Contributions.
347 |
348 | 6. Trademarks. This License does not grant permission to use the trade
349 | names, trademarks, service marks, or product names of the Licensor,
350 | except as required for reasonable and customary use in describing the
351 | origin of the Work and reproducing the content of the NOTICE file.
352 |
353 | 7. Disclaimer of Warranty. Unless required by applicable law or
354 | agreed to in writing, Licensor provides the Work (and each
355 | Contributor provides its Contributions) on an "AS IS" BASIS,
356 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
357 | implied, including, without limitation, any warranties or conditions
358 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
359 | PARTICULAR PURPOSE. You are solely responsible for determining the
360 | appropriateness of using or redistributing the Work and assume any
361 | risks associated with Your exercise of permissions under this License.
362 |
363 | 8. Limitation of Liability. In no event and under no legal theory,
364 | whether in tort (including negligence), contract, or otherwise,
365 | unless required by applicable law (such as deliberate and grossly
366 | negligent acts) or agreed to in writing, shall any Contributor be
367 | liable to You for damages, including any direct, indirect, special,
368 | incidental, or consequential damages of any character arising as a
369 | result of this License or out of the use or inability to use the
370 | Work (including but not limited to damages for loss of goodwill,
371 | work stoppage, computer failure or malfunction, or any and all
372 | other commercial damages or losses), even if such Contributor
373 | has been advised of the possibility of such damages.
374 |
375 | 9. Accepting Warranty or Additional Liability. While redistributing
376 | the Work or Derivative Works thereof, You may choose to offer,
377 | and charge a fee for, acceptance of support, warranty, indemnity,
378 | or other liability obligations and/or rights consistent with this
379 | License. However, in accepting such obligations, You may act only
380 | on Your own behalf and on Your sole responsibility, not on behalf
381 | of any other Contributor, and only if You agree to indemnify,
382 | defend, and hold each Contributor harmless for any liability
383 | incurred by, or claims asserted against, such Contributor by reason
384 | of your accepting any such warranty or additional liability.
385 |
386 | END OF TERMS AND CONDITIONS
387 |
388 | APPENDIX: How to apply the Apache License to your work.
389 |
390 | To apply the Apache License to your work, attach the following
391 | boilerplate notice, with the fields enclosed by brackets "{}"
392 | replaced with your own identifying information. (Don't include
393 | the brackets!) The text should be enclosed in the appropriate
394 | comment syntax for the file format. We also recommend that a
395 | file or class name and description of purpose be included on the
396 | same "printed page" as the copyright notice for easier
397 | identification within third-party archives.
398 |
399 | Copyright 2014, Quick Team
400 |
401 | Licensed under the Apache License, Version 2.0 (the "License");
402 | you may not use this file except in compliance with the License.
403 | You may obtain a copy of the License at
404 |
405 | http://www.apache.org/licenses/LICENSE-2.0
406 |
407 | Unless required by applicable law or agreed to in writing, software
408 | distributed under the License is distributed on an "AS IS" BASIS,
409 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
410 | See the License for the specific language governing permissions and
411 | limitations under the License.
412 |
413 | Generated by CocoaPods - https://cocoapods.org
414 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-StoriesLayout_Tests/Pods-StoriesLayout_Tests-acknowledgements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreferenceSpecifiers
6 |
7 |
8 | FooterText
9 | This application makes use of the following third party libraries:
10 | Title
11 | Acknowledgements
12 | Type
13 | PSGroupSpecifier
14 |
15 |
16 | FooterText
17 | Apache License
18 | Version 2.0, January 2004
19 | http://www.apache.org/licenses/
20 |
21 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
22 |
23 | 1. Definitions.
24 |
25 | "License" shall mean the terms and conditions for use, reproduction,
26 | and distribution as defined by Sections 1 through 9 of this document.
27 |
28 | "Licensor" shall mean the copyright owner or entity authorized by
29 | the copyright owner that is granting the License.
30 |
31 | "Legal Entity" shall mean the union of the acting entity and all
32 | other entities that control, are controlled by, or are under common
33 | control with that entity. For the purposes of this definition,
34 | "control" means (i) the power, direct or indirect, to cause the
35 | direction or management of such entity, whether by contract or
36 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
37 | outstanding shares, or (iii) beneficial ownership of such entity.
38 |
39 | "You" (or "Your") shall mean an individual or Legal Entity
40 | exercising permissions granted by this License.
41 |
42 | "Source" form shall mean the preferred form for making modifications,
43 | including but not limited to software source code, documentation
44 | source, and configuration files.
45 |
46 | "Object" form shall mean any form resulting from mechanical
47 | transformation or translation of a Source form, including but
48 | not limited to compiled object code, generated documentation,
49 | and conversions to other media types.
50 |
51 | "Work" shall mean the work of authorship, whether in Source or
52 | Object form, made available under the License, as indicated by a
53 | copyright notice that is included in or attached to the work
54 | (an example is provided in the Appendix below).
55 |
56 | "Derivative Works" shall mean any work, whether in Source or Object
57 | form, that is based on (or derived from) the Work and for which the
58 | editorial revisions, annotations, elaborations, or other modifications
59 | represent, as a whole, an original work of authorship. For the purposes
60 | of this License, Derivative Works shall not include works that remain
61 | separable from, or merely link (or bind by name) to the interfaces of,
62 | the Work and Derivative Works thereof.
63 |
64 | "Contribution" shall mean any work of authorship, including
65 | the original version of the Work and any modifications or additions
66 | to that Work or Derivative Works thereof, that is intentionally
67 | submitted to Licensor for inclusion in the Work by the copyright owner
68 | or by an individual or Legal Entity authorized to submit on behalf of
69 | the copyright owner. For the purposes of this definition, "submitted"
70 | means any form of electronic, verbal, or written communication sent
71 | to the Licensor or its representatives, including but not limited to
72 | communication on electronic mailing lists, source code control systems,
73 | and issue tracking systems that are managed by, or on behalf of, the
74 | Licensor for the purpose of discussing and improving the Work, but
75 | excluding communication that is conspicuously marked or otherwise
76 | designated in writing by the copyright owner as "Not a Contribution."
77 |
78 | "Contributor" shall mean Licensor and any individual or Legal Entity
79 | on behalf of whom a Contribution has been received by Licensor and
80 | subsequently incorporated within the Work.
81 |
82 | 2. Grant of Copyright License. Subject to the terms and conditions of
83 | this License, each Contributor hereby grants to You a perpetual,
84 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
85 | copyright license to reproduce, prepare Derivative Works of,
86 | publicly display, publicly perform, sublicense, and distribute the
87 | Work and such Derivative Works in Source or Object form.
88 |
89 | 3. Grant of Patent License. Subject to the terms and conditions of
90 | this License, each Contributor hereby grants to You a perpetual,
91 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
92 | (except as stated in this section) patent license to make, have made,
93 | use, offer to sell, sell, import, and otherwise transfer the Work,
94 | where such license applies only to those patent claims licensable
95 | by such Contributor that are necessarily infringed by their
96 | Contribution(s) alone or by combination of their Contribution(s)
97 | with the Work to which such Contribution(s) was submitted. If You
98 | institute patent litigation against any entity (including a
99 | cross-claim or counterclaim in a lawsuit) alleging that the Work
100 | or a Contribution incorporated within the Work constitutes direct
101 | or contributory patent infringement, then any patent licenses
102 | granted to You under this License for that Work shall terminate
103 | as of the date such litigation is filed.
104 |
105 | 4. Redistribution. You may reproduce and distribute copies of the
106 | Work or Derivative Works thereof in any medium, with or without
107 | modifications, and in Source or Object form, provided that You
108 | meet the following conditions:
109 |
110 | (a) You must give any other recipients of the Work or
111 | Derivative Works a copy of this License; and
112 |
113 | (b) You must cause any modified files to carry prominent notices
114 | stating that You changed the files; and
115 |
116 | (c) You must retain, in the Source form of any Derivative Works
117 | that You distribute, all copyright, patent, trademark, and
118 | attribution notices from the Source form of the Work,
119 | excluding those notices that do not pertain to any part of
120 | the Derivative Works; and
121 |
122 | (d) If the Work includes a "NOTICE" text file as part of its
123 | distribution, then any Derivative Works that You distribute must
124 | include a readable copy of the attribution notices contained
125 | within such NOTICE file, excluding those notices that do not
126 | pertain to any part of the Derivative Works, in at least one
127 | of the following places: within a NOTICE text file distributed
128 | as part of the Derivative Works; within the Source form or
129 | documentation, if provided along with the Derivative Works; or,
130 | within a display generated by the Derivative Works, if and
131 | wherever such third-party notices normally appear. The contents
132 | of the NOTICE file are for informational purposes only and
133 | do not modify the License. You may add Your own attribution
134 | notices within Derivative Works that You distribute, alongside
135 | or as an addendum to the NOTICE text from the Work, provided
136 | that such additional attribution notices cannot be construed
137 | as modifying the License.
138 |
139 | You may add Your own copyright statement to Your modifications and
140 | may provide additional or different license terms and conditions
141 | for use, reproduction, or distribution of Your modifications, or
142 | for any such Derivative Works as a whole, provided Your use,
143 | reproduction, and distribution of the Work otherwise complies with
144 | the conditions stated in this License.
145 |
146 | 5. Submission of Contributions. Unless You explicitly state otherwise,
147 | any Contribution intentionally submitted for inclusion in the Work
148 | by You to the Licensor shall be under the terms and conditions of
149 | this License, without any additional terms or conditions.
150 | Notwithstanding the above, nothing herein shall supersede or modify
151 | the terms of any separate license agreement you may have executed
152 | with Licensor regarding such Contributions.
153 |
154 | 6. Trademarks. This License does not grant permission to use the trade
155 | names, trademarks, service marks, or product names of the Licensor,
156 | except as required for reasonable and customary use in describing the
157 | origin of the Work and reproducing the content of the NOTICE file.
158 |
159 | 7. Disclaimer of Warranty. Unless required by applicable law or
160 | agreed to in writing, Licensor provides the Work (and each
161 | Contributor provides its Contributions) on an "AS IS" BASIS,
162 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
163 | implied, including, without limitation, any warranties or conditions
164 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
165 | PARTICULAR PURPOSE. You are solely responsible for determining the
166 | appropriateness of using or redistributing the Work and assume any
167 | risks associated with Your exercise of permissions under this License.
168 |
169 | 8. Limitation of Liability. In no event and under no legal theory,
170 | whether in tort (including negligence), contract, or otherwise,
171 | unless required by applicable law (such as deliberate and grossly
172 | negligent acts) or agreed to in writing, shall any Contributor be
173 | liable to You for damages, including any direct, indirect, special,
174 | incidental, or consequential damages of any character arising as a
175 | result of this License or out of the use or inability to use the
176 | Work (including but not limited to damages for loss of goodwill,
177 | work stoppage, computer failure or malfunction, or any and all
178 | other commercial damages or losses), even if such Contributor
179 | has been advised of the possibility of such damages.
180 |
181 | 9. Accepting Warranty or Additional Liability. While redistributing
182 | the Work or Derivative Works thereof, You may choose to offer,
183 | and charge a fee for, acceptance of support, warranty, indemnity,
184 | or other liability obligations and/or rights consistent with this
185 | License. However, in accepting such obligations, You may act only
186 | on Your own behalf and on Your sole responsibility, not on behalf
187 | of any other Contributor, and only if You agree to indemnify,
188 | defend, and hold each Contributor harmless for any liability
189 | incurred by, or claims asserted against, such Contributor by reason
190 | of your accepting any such warranty or additional liability.
191 |
192 | END OF TERMS AND CONDITIONS
193 |
194 | APPENDIX: How to apply the Apache License to your work.
195 |
196 | To apply the Apache License to your work, attach the following
197 | boilerplate notice, with the fields enclosed by brackets "{}"
198 | replaced with your own identifying information. (Don't include
199 | the brackets!) The text should be enclosed in the appropriate
200 | comment syntax for the file format. We also recommend that a
201 | file or class name and description of purpose be included on the
202 | same "printed page" as the copyright notice for easier
203 | identification within third-party archives.
204 |
205 | Copyright 2016 Quick Team
206 |
207 | Licensed under the Apache License, Version 2.0 (the "License");
208 | you may not use this file except in compliance with the License.
209 | You may obtain a copy of the License at
210 |
211 | http://www.apache.org/licenses/LICENSE-2.0
212 |
213 | Unless required by applicable law or agreed to in writing, software
214 | distributed under the License is distributed on an "AS IS" BASIS,
215 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
216 | See the License for the specific language governing permissions and
217 | limitations under the License.
218 |
219 | License
220 | Apache 2.0
221 | Title
222 | Nimble
223 | Type
224 | PSGroupSpecifier
225 |
226 |
227 | FooterText
228 | Apache License
229 | Version 2.0, January 2004
230 | http://www.apache.org/licenses/
231 |
232 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
233 |
234 | 1. Definitions.
235 |
236 | "License" shall mean the terms and conditions for use, reproduction,
237 | and distribution as defined by Sections 1 through 9 of this document.
238 |
239 | "Licensor" shall mean the copyright owner or entity authorized by
240 | the copyright owner that is granting the License.
241 |
242 | "Legal Entity" shall mean the union of the acting entity and all
243 | other entities that control, are controlled by, or are under common
244 | control with that entity. For the purposes of this definition,
245 | "control" means (i) the power, direct or indirect, to cause the
246 | direction or management of such entity, whether by contract or
247 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
248 | outstanding shares, or (iii) beneficial ownership of such entity.
249 |
250 | "You" (or "Your") shall mean an individual or Legal Entity
251 | exercising permissions granted by this License.
252 |
253 | "Source" form shall mean the preferred form for making modifications,
254 | including but not limited to software source code, documentation
255 | source, and configuration files.
256 |
257 | "Object" form shall mean any form resulting from mechanical
258 | transformation or translation of a Source form, including but
259 | not limited to compiled object code, generated documentation,
260 | and conversions to other media types.
261 |
262 | "Work" shall mean the work of authorship, whether in Source or
263 | Object form, made available under the License, as indicated by a
264 | copyright notice that is included in or attached to the work
265 | (an example is provided in the Appendix below).
266 |
267 | "Derivative Works" shall mean any work, whether in Source or Object
268 | form, that is based on (or derived from) the Work and for which the
269 | editorial revisions, annotations, elaborations, or other modifications
270 | represent, as a whole, an original work of authorship. For the purposes
271 | of this License, Derivative Works shall not include works that remain
272 | separable from, or merely link (or bind by name) to the interfaces of,
273 | the Work and Derivative Works thereof.
274 |
275 | "Contribution" shall mean any work of authorship, including
276 | the original version of the Work and any modifications or additions
277 | to that Work or Derivative Works thereof, that is intentionally
278 | submitted to Licensor for inclusion in the Work by the copyright owner
279 | or by an individual or Legal Entity authorized to submit on behalf of
280 | the copyright owner. For the purposes of this definition, "submitted"
281 | means any form of electronic, verbal, or written communication sent
282 | to the Licensor or its representatives, including but not limited to
283 | communication on electronic mailing lists, source code control systems,
284 | and issue tracking systems that are managed by, or on behalf of, the
285 | Licensor for the purpose of discussing and improving the Work, but
286 | excluding communication that is conspicuously marked or otherwise
287 | designated in writing by the copyright owner as "Not a Contribution."
288 |
289 | "Contributor" shall mean Licensor and any individual or Legal Entity
290 | on behalf of whom a Contribution has been received by Licensor and
291 | subsequently incorporated within the Work.
292 |
293 | 2. Grant of Copyright License. Subject to the terms and conditions of
294 | this License, each Contributor hereby grants to You a perpetual,
295 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
296 | copyright license to reproduce, prepare Derivative Works of,
297 | publicly display, publicly perform, sublicense, and distribute the
298 | Work and such Derivative Works in Source or Object form.
299 |
300 | 3. Grant of Patent License. Subject to the terms and conditions of
301 | this License, each Contributor hereby grants to You a perpetual,
302 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
303 | (except as stated in this section) patent license to make, have made,
304 | use, offer to sell, sell, import, and otherwise transfer the Work,
305 | where such license applies only to those patent claims licensable
306 | by such Contributor that are necessarily infringed by their
307 | Contribution(s) alone or by combination of their Contribution(s)
308 | with the Work to which such Contribution(s) was submitted. If You
309 | institute patent litigation against any entity (including a
310 | cross-claim or counterclaim in a lawsuit) alleging that the Work
311 | or a Contribution incorporated within the Work constitutes direct
312 | or contributory patent infringement, then any patent licenses
313 | granted to You under this License for that Work shall terminate
314 | as of the date such litigation is filed.
315 |
316 | 4. Redistribution. You may reproduce and distribute copies of the
317 | Work or Derivative Works thereof in any medium, with or without
318 | modifications, and in Source or Object form, provided that You
319 | meet the following conditions:
320 |
321 | (a) You must give any other recipients of the Work or
322 | Derivative Works a copy of this License; and
323 |
324 | (b) You must cause any modified files to carry prominent notices
325 | stating that You changed the files; and
326 |
327 | (c) You must retain, in the Source form of any Derivative Works
328 | that You distribute, all copyright, patent, trademark, and
329 | attribution notices from the Source form of the Work,
330 | excluding those notices that do not pertain to any part of
331 | the Derivative Works; and
332 |
333 | (d) If the Work includes a "NOTICE" text file as part of its
334 | distribution, then any Derivative Works that You distribute must
335 | include a readable copy of the attribution notices contained
336 | within such NOTICE file, excluding those notices that do not
337 | pertain to any part of the Derivative Works, in at least one
338 | of the following places: within a NOTICE text file distributed
339 | as part of the Derivative Works; within the Source form or
340 | documentation, if provided along with the Derivative Works; or,
341 | within a display generated by the Derivative Works, if and
342 | wherever such third-party notices normally appear. The contents
343 | of the NOTICE file are for informational purposes only and
344 | do not modify the License. You may add Your own attribution
345 | notices within Derivative Works that You distribute, alongside
346 | or as an addendum to the NOTICE text from the Work, provided
347 | that such additional attribution notices cannot be construed
348 | as modifying the License.
349 |
350 | You may add Your own copyright statement to Your modifications and
351 | may provide additional or different license terms and conditions
352 | for use, reproduction, or distribution of Your modifications, or
353 | for any such Derivative Works as a whole, provided Your use,
354 | reproduction, and distribution of the Work otherwise complies with
355 | the conditions stated in this License.
356 |
357 | 5. Submission of Contributions. Unless You explicitly state otherwise,
358 | any Contribution intentionally submitted for inclusion in the Work
359 | by You to the Licensor shall be under the terms and conditions of
360 | this License, without any additional terms or conditions.
361 | Notwithstanding the above, nothing herein shall supersede or modify
362 | the terms of any separate license agreement you may have executed
363 | with Licensor regarding such Contributions.
364 |
365 | 6. Trademarks. This License does not grant permission to use the trade
366 | names, trademarks, service marks, or product names of the Licensor,
367 | except as required for reasonable and customary use in describing the
368 | origin of the Work and reproducing the content of the NOTICE file.
369 |
370 | 7. Disclaimer of Warranty. Unless required by applicable law or
371 | agreed to in writing, Licensor provides the Work (and each
372 | Contributor provides its Contributions) on an "AS IS" BASIS,
373 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
374 | implied, including, without limitation, any warranties or conditions
375 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
376 | PARTICULAR PURPOSE. You are solely responsible for determining the
377 | appropriateness of using or redistributing the Work and assume any
378 | risks associated with Your exercise of permissions under this License.
379 |
380 | 8. Limitation of Liability. In no event and under no legal theory,
381 | whether in tort (including negligence), contract, or otherwise,
382 | unless required by applicable law (such as deliberate and grossly
383 | negligent acts) or agreed to in writing, shall any Contributor be
384 | liable to You for damages, including any direct, indirect, special,
385 | incidental, or consequential damages of any character arising as a
386 | result of this License or out of the use or inability to use the
387 | Work (including but not limited to damages for loss of goodwill,
388 | work stoppage, computer failure or malfunction, or any and all
389 | other commercial damages or losses), even if such Contributor
390 | has been advised of the possibility of such damages.
391 |
392 | 9. Accepting Warranty or Additional Liability. While redistributing
393 | the Work or Derivative Works thereof, You may choose to offer,
394 | and charge a fee for, acceptance of support, warranty, indemnity,
395 | or other liability obligations and/or rights consistent with this
396 | License. However, in accepting such obligations, You may act only
397 | on Your own behalf and on Your sole responsibility, not on behalf
398 | of any other Contributor, and only if You agree to indemnify,
399 | defend, and hold each Contributor harmless for any liability
400 | incurred by, or claims asserted against, such Contributor by reason
401 | of your accepting any such warranty or additional liability.
402 |
403 | END OF TERMS AND CONDITIONS
404 |
405 | APPENDIX: How to apply the Apache License to your work.
406 |
407 | To apply the Apache License to your work, attach the following
408 | boilerplate notice, with the fields enclosed by brackets "{}"
409 | replaced with your own identifying information. (Don't include
410 | the brackets!) The text should be enclosed in the appropriate
411 | comment syntax for the file format. We also recommend that a
412 | file or class name and description of purpose be included on the
413 | same "printed page" as the copyright notice for easier
414 | identification within third-party archives.
415 |
416 | Copyright 2014, Quick Team
417 |
418 | Licensed under the Apache License, Version 2.0 (the "License");
419 | you may not use this file except in compliance with the License.
420 | You may obtain a copy of the License at
421 |
422 | http://www.apache.org/licenses/LICENSE-2.0
423 |
424 | Unless required by applicable law or agreed to in writing, software
425 | distributed under the License is distributed on an "AS IS" BASIS,
426 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
427 | See the License for the specific language governing permissions and
428 | limitations under the License.
429 |
430 | License
431 | Apache 2.0
432 | Title
433 | Quick
434 | Type
435 | PSGroupSpecifier
436 |
437 |
438 | FooterText
439 | Generated by CocoaPods - https://cocoapods.org
440 | Title
441 |
442 | Type
443 | PSGroupSpecifier
444 |
445 |
446 | StringsTable
447 | Acknowledgements
448 | Title
449 | Acknowledgements
450 |
451 |
452 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-StoriesLayout_Tests/Pods-StoriesLayout_Tests-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_StoriesLayout_Tests : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_StoriesLayout_Tests
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-StoriesLayout_Tests/Pods-StoriesLayout_Tests-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double Pods_StoriesLayout_TestsVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char Pods_StoriesLayout_TestsVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-StoriesLayout_Tests/Pods-StoriesLayout_Tests.debug.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" "${PODS_CONFIGURATION_BUILD_DIR}/Nimble" "${PODS_CONFIGURATION_BUILD_DIR}/PowerTools" "${PODS_CONFIGURATION_BUILD_DIR}/Quick" "${PODS_CONFIGURATION_BUILD_DIR}/SafariLayout" "${PODS_CONFIGURATION_BUILD_DIR}/StoriesLayout"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Nimble/Nimble.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PowerTools/PowerTools.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Quick/Quick.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SafariLayout/SafariLayout.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/StoriesLayout/StoriesLayout.framework/Headers"
5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
6 | OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "Nimble" -framework "PowerTools" -framework "Quick" -framework "SafariLayout" -framework "StoriesLayout" -framework "UIKit" -framework "XCTest" -weak_framework "XCTest"
7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
8 | PODS_BUILD_DIR = ${BUILD_DIR}
9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
11 | PODS_ROOT = ${SRCROOT}/Pods
12 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-StoriesLayout_Tests/Pods-StoriesLayout_Tests.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_StoriesLayout_Tests {
2 | umbrella header "Pods-StoriesLayout_Tests-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-StoriesLayout_Tests/Pods-StoriesLayout_Tests.release.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" "${PODS_CONFIGURATION_BUILD_DIR}/Nimble" "${PODS_CONFIGURATION_BUILD_DIR}/PowerTools" "${PODS_CONFIGURATION_BUILD_DIR}/Quick" "${PODS_CONFIGURATION_BUILD_DIR}/SafariLayout" "${PODS_CONFIGURATION_BUILD_DIR}/StoriesLayout"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Nimble/Nimble.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PowerTools/PowerTools.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Quick/Quick.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SafariLayout/SafariLayout.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/StoriesLayout/StoriesLayout.framework/Headers"
5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
6 | OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "Nimble" -framework "PowerTools" -framework "Quick" -framework "SafariLayout" -framework "StoriesLayout" -framework "UIKit" -framework "XCTest" -weak_framework "XCTest"
7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
8 | PODS_BUILD_DIR = ${BUILD_DIR}
9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
11 | PODS_ROOT = ${SRCROOT}/Pods
12 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/StoriesLayout/StoriesLayout-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 | FMWK
17 | CFBundleShortVersionString
18 | 0.1.1
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/StoriesLayout/StoriesLayout-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_StoriesLayout : NSObject
3 | @end
4 | @implementation PodsDummy_StoriesLayout
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/StoriesLayout/StoriesLayout-prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/StoriesLayout/StoriesLayout-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double StoriesLayoutVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char StoriesLayoutVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/StoriesLayout/StoriesLayout.modulemap:
--------------------------------------------------------------------------------
1 | framework module StoriesLayout {
2 | umbrella header "StoriesLayout-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/StoriesLayout/StoriesLayout.xcconfig:
--------------------------------------------------------------------------------
1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/StoriesLayout
2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
3 | OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "UIKit"
4 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
5 | PODS_BUILD_DIR = ${BUILD_DIR}
6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
7 | PODS_ROOT = ${SRCROOT}
8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../..
9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
10 | SKIP_INSTALL = YES
11 |
--------------------------------------------------------------------------------
/Example/StoriesLayout.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/StoriesLayout.xcodeproj/xcshareddata/xcschemes/StoriesLayout-Example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
46 |
47 |
53 |
54 |
60 |
61 |
62 |
63 |
65 |
71 |
72 |
73 |
74 |
75 |
81 |
82 |
83 |
84 |
85 |
86 |
96 |
98 |
104 |
105 |
106 |
107 |
108 |
109 |
115 |
117 |
123 |
124 |
125 |
126 |
128 |
129 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/Example/StoriesLayout.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Example/StoriesLayout.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Example/StoriesLayout/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // StoriesLayout
4 | //
5 | // Created by oni.zerone@gmail.com on 05/25/2019.
6 | // Copyright (c) 2019 oni.zerone@gmail.com. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
18 |
19 | let window = UIWindow(frame: UIScreen.main.bounds)
20 | window.rootViewController = UINavigationController(rootViewController: ListViewController())
21 | self.window = window
22 | window.makeKeyAndVisible()
23 | return true
24 | }
25 |
26 | func applicationWillResignActive(_ application: UIApplication) {
27 | // 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.
28 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
29 | }
30 |
31 | func applicationDidEnterBackground(_ application: UIApplication) {
32 | // 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.
33 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
34 | }
35 |
36 | func applicationWillEnterForeground(_ application: UIApplication) {
37 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
38 | }
39 |
40 | func applicationDidBecomeActive(_ application: UIApplication) {
41 | // 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.
42 | }
43 |
44 | func applicationWillTerminate(_ application: UIApplication) {
45 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
46 | }
47 |
48 |
49 | }
50 |
51 |
--------------------------------------------------------------------------------
/Example/StoriesLayout/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
25 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/Example/StoriesLayout/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ios-marketing",
45 | "size" : "1024x1024",
46 | "scale" : "1x"
47 | }
48 | ],
49 | "info" : {
50 | "version" : 1,
51 | "author" : "xcode"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Example/StoriesLayout/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Example/StoriesLayout/Images.xcassets/storiesLayout/001.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "architecture-blue-sky-cliff-248771.jpg"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/Example/StoriesLayout/Images.xcassets/storiesLayout/001.imageset/architecture-blue-sky-cliff-248771.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Oni-zerone/CollectionLayouts/faef2a79540f1790916c79bb74edb0eca3689302/Example/StoriesLayout/Images.xcassets/storiesLayout/001.imageset/architecture-blue-sky-cliff-248771.jpg
--------------------------------------------------------------------------------
/Example/StoriesLayout/Images.xcassets/storiesLayout/002.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "bird-s-eye-view-curve-drone-photography-1658967.jpg"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/Example/StoriesLayout/Images.xcassets/storiesLayout/002.imageset/bird-s-eye-view-curve-drone-photography-1658967.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Oni-zerone/CollectionLayouts/faef2a79540f1790916c79bb74edb0eca3689302/Example/StoriesLayout/Images.xcassets/storiesLayout/002.imageset/bird-s-eye-view-curve-drone-photography-1658967.jpg
--------------------------------------------------------------------------------
/Example/StoriesLayout/Images.xcassets/storiesLayout/003.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "bay-beach-boats-919237.jpg"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/Example/StoriesLayout/Images.xcassets/storiesLayout/003.imageset/bay-beach-boats-919237.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Oni-zerone/CollectionLayouts/faef2a79540f1790916c79bb74edb0eca3689302/Example/StoriesLayout/Images.xcassets/storiesLayout/003.imageset/bay-beach-boats-919237.jpg
--------------------------------------------------------------------------------
/Example/StoriesLayout/Images.xcassets/storiesLayout/004.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aerial-architecture-blue-731229.jpg"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/Example/StoriesLayout/Images.xcassets/storiesLayout/004.imageset/aerial-architecture-blue-731229.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Oni-zerone/CollectionLayouts/faef2a79540f1790916c79bb74edb0eca3689302/Example/StoriesLayout/Images.xcassets/storiesLayout/004.imageset/aerial-architecture-blue-731229.jpg
--------------------------------------------------------------------------------
/Example/StoriesLayout/Images.xcassets/storiesLayout/005.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "architecture-building-city-311066.jpg"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/Example/StoriesLayout/Images.xcassets/storiesLayout/005.imageset/architecture-building-city-311066.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Oni-zerone/CollectionLayouts/faef2a79540f1790916c79bb74edb0eca3689302/Example/StoriesLayout/Images.xcassets/storiesLayout/005.imageset/architecture-building-city-311066.jpg
--------------------------------------------------------------------------------
/Example/StoriesLayout/Images.xcassets/storiesLayout/006.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aerial-architecture-buildings-434194.jpg"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/Example/StoriesLayout/Images.xcassets/storiesLayout/006.imageset/aerial-architecture-buildings-434194.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Oni-zerone/CollectionLayouts/faef2a79540f1790916c79bb74edb0eca3689302/Example/StoriesLayout/Images.xcassets/storiesLayout/006.imageset/aerial-architecture-buildings-434194.jpg
--------------------------------------------------------------------------------
/Example/StoriesLayout/Images.xcassets/storiesLayout/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "properties" : {
7 | "provides-namespace" : true
8 | }
9 | }
--------------------------------------------------------------------------------
/Example/StoriesLayout/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 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/Example/StoriesLayout/LayoutCollectionViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LayoutCollectionViewCell.swift
3 | // StoriesLayout_Example
4 | //
5 | // Created by Andrea Altea on 02/06/2019.
6 | // Copyright © 2019 CocoaPods. All rights reserved.
7 | //
8 |
9 | import PowerTools
10 |
11 | class LayoutCollectionViewCell: UICollectionViewCell {
12 |
13 | @IBOutlet weak var title: UILabel!
14 |
15 | override func awakeFromNib() {
16 | super.awakeFromNib()
17 | self.title.text = nil
18 | }
19 | }
20 |
21 | extension LayoutCollectionViewCell: LayoutCell {
22 | func setupLayoutCell(title: String) {
23 | self.title.text = title
24 | }
25 | }
26 |
27 | extension LayoutCollectionViewCell {
28 |
29 | static let descriptor = String(describing: LayoutCollectionViewCell.self)
30 |
31 | struct Descriptor: ItemViewDescriptor, GridDescriptor {
32 | let reuseIdentifier = LayoutCollectionViewCell.descriptor
33 |
34 | var ratio: ViewRatio {
35 | return ViewRatio(multiplier: 0.0, constant: 60)
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Example/StoriesLayout/LayoutCollectionViewCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/Example/StoriesLayout/MySafariCollectionViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MySafariCollectionViewCell.swift
3 | // StoriesLayout_Example
4 | //
5 | // Created by Andrea Altea on 27/05/2019.
6 | // Copyright © 2019 CocoaPods. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SafariLayout
11 | import PowerTools
12 |
13 | class MySafariCollectionViewCell: SafariCollectionViewCell {
14 |
15 | @IBOutlet weak var imageView: UIImageView!
16 | @IBOutlet weak var label: UILabel!
17 |
18 | override func awakeFromNib() {
19 | super.awakeFromNib()
20 | self.clipsToBounds = true
21 | self.layer.cornerRadius = 8.0
22 | }
23 |
24 | }
25 |
26 | extension MySafariCollectionViewCell {
27 |
28 | static let identifier = String(describing: MySafariCollectionViewCell.self)
29 |
30 | struct Descriptor: ItemViewDescriptor {
31 | var reuseIdentifier: String = MySafariCollectionViewCell.identifier
32 | }
33 | }
34 |
35 | extension MySafariCollectionViewCell: ImageCell {
36 |
37 | func set(image: UIImage) {
38 | self.imageView.image = image
39 | }
40 |
41 | func set(index: Int) {
42 | self.label.text = "\(index)"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Example/StoriesLayout/MySafariCollectionViewCell.xib:
--------------------------------------------------------------------------------
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 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/Example/StoriesLayout/MyStoriesCollectionViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StoriesCollectionViewCell+Descriptor.swift
3 | // StoriesLayout_Example
4 | //
5 | // Created by Andrea Altea on 26/05/2019.
6 | // Copyright © 2019 CocoaPods. All rights reserved.
7 | //
8 |
9 | import PowerTools
10 | import StoriesLayout
11 |
12 | class MyStoriesCollectionViewCell: StoriesCollectionViewCell {
13 |
14 | static let sIdentifier = String(describing: MyStoriesCollectionViewCell.self)
15 |
16 | struct Descriptor: ItemViewDescriptor {
17 | var reuseIdentifier: String = MyStoriesCollectionViewCell.sIdentifier
18 | }
19 |
20 | @IBOutlet weak var imageView: UIImageView!
21 | @IBOutlet weak var label: UILabel!
22 |
23 | override func prepareForReuse() {
24 | self.imageView.image = nil
25 | }
26 | }
27 |
28 | extension MyStoriesCollectionViewCell: ImageCell {
29 |
30 | func set(image: UIImage) {
31 | self.imageView.image = image
32 | }
33 |
34 | func set(index: Int) {
35 | self.label.text = "\(index)"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Example/StoriesLayout/MyStoriesCollectionViewCell.xib:
--------------------------------------------------------------------------------
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 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/Example/StoriesLayout/ViewControllers/DemoController/DemoController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // StoriesLayout
4 | //
5 | // Created by oni.zerone@gmail.com on 05/25/2019.
6 | // Copyright (c) 2019 oni.zerone@gmail.com. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import PowerTools
11 | import StoriesLayout
12 |
13 | class DemoController: UIViewController {
14 |
15 | var defaultImages = ["storiesLayout/001",
16 | "storiesLayout/002",
17 | "storiesLayout/003",
18 | "storiesLayout/004",
19 | "storiesLayout/005",
20 | "storiesLayout/006",
21 | "storiesLayout/001",
22 | "storiesLayout/002",
23 | "storiesLayout/003",
24 | "storiesLayout/004",
25 | "storiesLayout/005",
26 | "storiesLayout/006"]
27 |
28 | var layout: UICollectionViewLayout
29 | var cellDescriptor: ItemViewDescriptor
30 |
31 | weak var collectionView: UICollectionView!
32 | var dataSource: CollectionBinderDataSource!
33 |
34 | init(layout: UICollectionViewLayout, cellDescriptor: ItemViewDescriptor) {
35 | self.layout = layout
36 | self.cellDescriptor = cellDescriptor
37 | super.init(nibName: nil, bundle: nil)
38 |
39 | self.title = String(describing: type(of: layout.self))
40 | }
41 |
42 | required init?(coder aDecoder: NSCoder) {
43 | fatalError("you should not create from xib")
44 | }
45 |
46 | override func viewDidLoad() {
47 | super.viewDidLoad()
48 | setupCollection()
49 | setupCells()
50 | setupDataSource()
51 | }
52 |
53 | func setupCollection() {
54 | let collection = UICollectionView(frame: .zero, collectionViewLayout: layout)
55 | collection.translatesAutoresizingMaskIntoConstraints = false
56 | view.addSubview(collection)
57 |
58 | view.safeAreaLayoutGuide.topAnchor.constraint(equalTo: collection.topAnchor).isActive = true
59 | view.safeAreaLayoutGuide.leftAnchor.constraint(equalTo: collection.leftAnchor).isActive = true
60 | view.safeAreaLayoutGuide.rightAnchor.constraint(equalTo: collection.rightAnchor).isActive = true
61 | view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: collection.bottomAnchor).isActive = true
62 | collectionView = collection
63 | }
64 |
65 | func setupCells() {
66 | collectionView.register(UINib(nibName: cellDescriptor.reuseIdentifier,
67 | bundle: .main), forCellWithReuseIdentifier: cellDescriptor.reuseIdentifier)
68 | }
69 |
70 | func setupDataSource() {
71 | let section = ConcreteSection(items:
72 | defaultImages.map { ImageViewModel(imageNamed: $0, cellDescriptor: self.cellDescriptor) })
73 |
74 | dataSource = CollectionBinderDataSource(view: collectionView, model: [section])
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Example/StoriesLayout/ViewControllers/DemoController/DemoRoutable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DemoRoutable.swift
3 | // StoriesLayout_Example
4 | //
5 | // Created by Andrea Altea on 02/06/2019.
6 | // Copyright © 2019 CocoaPods. All rights reserved.
7 | //
8 |
9 | import PowerTools
10 |
11 | class DemoRoutable: Builder {
12 |
13 | var layout: UICollectionViewLayout
14 | var descriptor: ItemViewDescriptor
15 |
16 | init(layout: UICollectionViewLayout, descriptor: ItemViewDescriptor) {
17 | self.layout = layout
18 | self.descriptor = descriptor
19 | }
20 |
21 | override func build(_ context: UIViewController) -> UIViewController? {
22 | return DemoController(layout: layout,
23 | cellDescriptor: descriptor)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Example/StoriesLayout/ViewControllers/ListViewController/ListViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListViewController.swift
3 | // StoriesLayout_Example
4 | //
5 | // Created by Andrea Altea on 02/06/2019.
6 | // Copyright © 2019 CocoaPods. All rights reserved.
7 | //
8 |
9 | import PowerTools
10 | import StoriesLayout
11 | import SafariLayout
12 |
13 | class ListViewController: UIViewController {
14 |
15 | weak var collectionView: UICollectionView!
16 | var dataSource: GridCollectionDataSource!
17 |
18 | var layouts: [ItemViewModel] = [
19 | LayoutItemViewModel(descriptor:MyStoriesCollectionViewCell.Descriptor()) { StoriesCollectionViewLayout() },
20 | LayoutItemViewModel(descriptor:MySafariCollectionViewCell.Descriptor()) { SafariCollectionViewLayout() }
21 | ]
22 |
23 | override func viewDidLoad() {
24 | super.viewDidLoad()
25 |
26 | setupCollection()
27 | setupCells()
28 | setupDataSource()
29 | }
30 |
31 | func setupCollection() {
32 | let collection = UICollectionView(frame: .zero,
33 | collectionViewLayout: UICollectionViewFlowLayout())
34 | collection.translatesAutoresizingMaskIntoConstraints = false
35 | collection.backgroundColor = .white
36 | view.addSubview(collection)
37 |
38 | view.topAnchor.constraint(equalTo: collection.topAnchor).isActive = true
39 | view.bottomAnchor.constraint(equalTo: collection.bottomAnchor).isActive = true
40 | view.leftAnchor.constraint(equalTo: collection.leftAnchor).isActive = true
41 | view.rightAnchor.constraint(equalTo: collection.rightAnchor).isActive = true
42 | self.collectionView = collection
43 | }
44 |
45 | func setupCells() {
46 | layouts.forEach { item in
47 | self.collectionView.register(UINib(nibName: item.descriptor.reuseIdentifier,
48 | bundle: Bundle.main),
49 | forCellWithReuseIdentifier: item.descriptor.reuseIdentifier)
50 | }
51 | }
52 |
53 | func setupDataSource() {
54 | self.dataSource = GridCollectionDataSource(view: collectionView,
55 | model: [ConcreteGridSection(items: layouts)])
56 | self.dataSource.interactionDelegate = self
57 | }
58 | }
59 |
60 | extension ListViewController: InteractionFactory {
61 | var context: UIViewController {
62 | return self
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Example/StoriesLayout/ViewModels/ImageViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageViewModel.swift
3 | // StoriesLayout_Example
4 | //
5 | // Created by Andrea Altea on 26/05/2019.
6 | // Copyright © 2019 CocoaPods. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import PowerTools
11 | import StoriesLayout
12 |
13 | protocol ImageCell {
14 |
15 | func set(image: UIImage)
16 | func set(index: Int)
17 | }
18 |
19 | struct ImageViewModel: ItemViewModel {
20 |
21 | var descriptor: ItemViewDescriptor
22 | var image: UIImage?
23 |
24 | var hashValue: Int {
25 | return image.hashValue
26 | }
27 |
28 | init(imageNamed name: String, cellDescriptor: ItemViewDescriptor) {
29 | self.descriptor = cellDescriptor
30 | self.image = UIImage(named: name)
31 | }
32 |
33 | func setup(_ view: UIView, in containerView: UIView, at indexPath: IndexPath) {
34 | guard let image = image else { return }
35 | (view as? ImageCell)?.set(image: image)
36 | (view as? ImageCell)?.set(index: indexPath.item)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Example/StoriesLayout/ViewModels/LayoutItemViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LayouutItemViewModel.swift
3 | // StoriesLayout_Example
4 | //
5 | // Created by Andrea Altea on 02/06/2019.
6 | // Copyright © 2019 CocoaPods. All rights reserved.
7 | //
8 |
9 | import PowerTools
10 |
11 | protocol LayoutCell {
12 | func setupLayoutCell(title: String)
13 | }
14 |
15 | class LayoutItemViewModel: GridItemViewModel {
16 |
17 | typealias LayoutInitializer = () -> Layout
18 |
19 | var descriptor: ItemViewDescriptor = LayoutCollectionViewCell.Descriptor()
20 |
21 | var layoutInitializer: LayoutInitializer
22 | var layoutCellDescriptor: ItemViewDescriptor
23 |
24 | private(set) var layoutName: String = {
25 | String(describing: Layout.self)
26 | }()
27 |
28 | var hashValue: Int {
29 | return layoutName.hashValue
30 | }
31 |
32 | init(descriptor: ItemViewDescriptor, layoutInitializer: @escaping LayoutInitializer) {
33 | self.layoutInitializer = layoutInitializer
34 | self.layoutCellDescriptor = descriptor
35 | }
36 |
37 | func setup(_ view: UIView, in containerView: UIView, at indexPath: IndexPath) {
38 | (view as? LayoutCell)?.setupLayoutCell(title: layoutName)
39 | }
40 | }
41 |
42 | extension LayoutItemViewModel: BuilderContainer {
43 |
44 | typealias Context = Any
45 |
46 | func getBuilder(_ contextType: Context.Type) -> Builder? {
47 | return DemoRoutable(layout: layoutInitializer(), descriptor: layoutCellDescriptor) as? Builder
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Example/StoriesLayout/ViewModels/Section.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Section.swift
3 | // StoriesLayout_Example
4 | //
5 | // Created by Andrea Altea on 02/06/2019.
6 | // Copyright © 2019 CocoaPods. All rights reserved.
7 | //
8 |
9 | import PowerTools
10 |
11 | struct ConcreteSection: SectionViewModel {
12 | var header: ItemViewModel?
13 |
14 | var items: [ItemViewModel]
15 |
16 | var footer: ItemViewModel?
17 |
18 | init(items: [ItemViewModel]) {
19 | self.header = nil
20 | self.items = items
21 | self.footer = nil
22 | }
23 | }
24 |
25 |
26 | struct ConcreteGridSection: SectionViewModel, GridSection {
27 |
28 | var header: ItemViewModel?
29 |
30 | var items: [ItemViewModel]
31 |
32 | var footer: ItemViewModel?
33 |
34 | init(items: [ItemViewModel]) {
35 | self.header = nil
36 | self.items = items
37 | self.footer = nil
38 | }
39 |
40 | var lineItems: Int? = 1
41 | var sectionInsets: UIEdgeInsets = .zero
42 | var sectionVerticalItemSpacing: CGFloat = 0.0
43 | var sectionHorizontalItemSpacing: CGFloat = 0.0
44 | }
45 |
--------------------------------------------------------------------------------
/Example/Tests/Helpers/CATransform3D+Helper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CATransform3D+Helper.swift
3 | // StoriesLayout_Example
4 | //
5 | // Created by Andrea Altea on 06/07/2019.
6 | // Copyright © 2019 CocoaPods. All rights reserved.
7 | //
8 |
9 | import QuartzCore
10 |
11 | extension CATransform3D: Equatable {
12 |
13 | public static func == (lhs: CATransform3D, rhs: CATransform3D) -> Bool {
14 | return lhs.m11 == rhs.m11
15 | && lhs.m12 == rhs.m12
16 | && lhs.m13 == rhs.m13
17 | && lhs.m14 == rhs.m14
18 |
19 | && lhs.m21 == rhs.m21
20 | && lhs.m22 == rhs.m22
21 | && lhs.m23 == rhs.m23
22 | && lhs.m24 == rhs.m24
23 |
24 | && lhs.m31 == rhs.m31
25 | && lhs.m32 == rhs.m32
26 | && lhs.m33 == rhs.m33
27 | && lhs.m34 == rhs.m34
28 |
29 | && lhs.m41 == rhs.m41
30 | && lhs.m42 == rhs.m42
31 | && lhs.m43 == rhs.m43
32 | && lhs.m44 == rhs.m44
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Example/Tests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Example/Tests/Mocks/UICollectionDataSourceMock.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UICollectionDataSourceMock.swift
3 | // StoriesLayout_Tests
4 | //
5 | // Created by Andrea Altea on 29/06/2019.
6 | // Copyright © 2019 CocoaPods. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class UICollectionViewDataSourceMock: NSObject, UICollectionViewDataSource {
12 |
13 | //MARK: - collectionView
14 |
15 | var collectionViewNumberOfItemsInSectionCallsCount = 0
16 | var collectionViewNumberOfItemsInSectionCalled: Bool {
17 | return collectionViewNumberOfItemsInSectionCallsCount > 0
18 | }
19 | var collectionViewNumberOfItemsInSectionReceivedArguments: (collectionView: UICollectionView, section: Int)?
20 | var collectionViewNumberOfItemsInSectionReceivedInvocations: [(collectionView: UICollectionView, section: Int)] = []
21 | var collectionViewNumberOfItemsInSectionReturnValue: Int!
22 | var collectionViewNumberOfItemsInSectionClosure: ((UICollectionView, Int) -> Int)?
23 |
24 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
25 | collectionViewNumberOfItemsInSectionCallsCount += 1
26 | collectionViewNumberOfItemsInSectionReceivedArguments = (collectionView: collectionView, section: section)
27 | collectionViewNumberOfItemsInSectionReceivedInvocations.append((collectionView: collectionView, section: section))
28 | return collectionViewNumberOfItemsInSectionClosure.map({ $0(collectionView, section) }) ?? collectionViewNumberOfItemsInSectionReturnValue
29 | }
30 |
31 | //MARK: - collectionView
32 |
33 | var collectionViewCellForItemAtCallsCount = 0
34 | var collectionViewCellForItemAtCalled: Bool {
35 | return collectionViewCellForItemAtCallsCount > 0
36 | }
37 | var collectionViewCellForItemAtReceivedArguments: (collectionView: UICollectionView, indexPath: IndexPath)?
38 | var collectionViewCellForItemAtReceivedInvocations: [(collectionView: UICollectionView, indexPath: IndexPath)] = []
39 | var collectionViewCellForItemAtReturnValue: UICollectionViewCell!
40 | var collectionViewCellForItemAtClosure: ((UICollectionView, IndexPath) -> UICollectionViewCell)?
41 |
42 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
43 | collectionViewCellForItemAtCallsCount += 1
44 | collectionViewCellForItemAtReceivedArguments = (collectionView: collectionView, indexPath: indexPath)
45 | collectionViewCellForItemAtReceivedInvocations.append((collectionView: collectionView, indexPath: indexPath))
46 | return collectionViewCellForItemAtClosure.map({ $0(collectionView, indexPath) }) ?? collectionViewCellForItemAtReturnValue
47 | }
48 |
49 | //MARK: - numberOfSections
50 |
51 | var numberOfSectionsInCallsCount = 0
52 | var numberOfSectionsInCalled: Bool {
53 | return numberOfSectionsInCallsCount > 0
54 | }
55 | var numberOfSectionsInReceivedCollectionView: UICollectionView?
56 | var numberOfSectionsInReceivedInvocations: [UICollectionView] = []
57 | var numberOfSectionsInReturnValue: Int!
58 | var numberOfSectionsInClosure: ((UICollectionView) -> Int)?
59 |
60 | func numberOfSections(in collectionView: UICollectionView) -> Int {
61 | numberOfSectionsInCallsCount += 1
62 | numberOfSectionsInReceivedCollectionView = collectionView
63 | numberOfSectionsInReceivedInvocations.append(collectionView)
64 | return numberOfSectionsInClosure.map({ $0(collectionView) }) ?? numberOfSectionsInReturnValue
65 | }
66 |
67 | //MARK: - collectionView
68 |
69 | var collectionViewViewForSupplementaryElementOfKindAtCallsCount = 0
70 | var collectionViewViewForSupplementaryElementOfKindAtCalled: Bool {
71 | return collectionViewViewForSupplementaryElementOfKindAtCallsCount > 0
72 | }
73 | var collectionViewViewForSupplementaryElementOfKindAtReceivedArguments: (collectionView: UICollectionView, kind: String, indexPath: IndexPath)?
74 | var collectionViewViewForSupplementaryElementOfKindAtReceivedInvocations: [(collectionView: UICollectionView, kind: String, indexPath: IndexPath)] = []
75 | var collectionViewViewForSupplementaryElementOfKindAtReturnValue: UICollectionReusableView!
76 | var collectionViewViewForSupplementaryElementOfKindAtClosure: ((UICollectionView, String, IndexPath) -> UICollectionReusableView)?
77 |
78 | func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
79 | collectionViewViewForSupplementaryElementOfKindAtCallsCount += 1
80 | collectionViewViewForSupplementaryElementOfKindAtReceivedArguments = (collectionView: collectionView, kind: kind, indexPath: indexPath)
81 | collectionViewViewForSupplementaryElementOfKindAtReceivedInvocations.append((collectionView: collectionView, kind: kind, indexPath: indexPath))
82 | return collectionViewViewForSupplementaryElementOfKindAtClosure.map({ $0(collectionView, kind, indexPath) }) ?? collectionViewViewForSupplementaryElementOfKindAtReturnValue
83 | }
84 |
85 | //MARK: - collectionView
86 |
87 | var collectionViewCanMoveItemAtCallsCount = 0
88 | var collectionViewCanMoveItemAtCalled: Bool {
89 | return collectionViewCanMoveItemAtCallsCount > 0
90 | }
91 | var collectionViewCanMoveItemAtReceivedArguments: (collectionView: UICollectionView, indexPath: IndexPath)?
92 | var collectionViewCanMoveItemAtReceivedInvocations: [(collectionView: UICollectionView, indexPath: IndexPath)] = []
93 | var collectionViewCanMoveItemAtReturnValue: Bool!
94 | var collectionViewCanMoveItemAtClosure: ((UICollectionView, IndexPath) -> Bool)?
95 |
96 | func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool {
97 | collectionViewCanMoveItemAtCallsCount += 1
98 | collectionViewCanMoveItemAtReceivedArguments = (collectionView: collectionView, indexPath: indexPath)
99 | collectionViewCanMoveItemAtReceivedInvocations.append((collectionView: collectionView, indexPath: indexPath))
100 | return collectionViewCanMoveItemAtClosure.map({ $0(collectionView, indexPath) }) ?? collectionViewCanMoveItemAtReturnValue
101 | }
102 |
103 | //MARK: - collectionView
104 |
105 | var collectionViewMoveItemAtToCallsCount = 0
106 | var collectionViewMoveItemAtToCalled: Bool {
107 | return collectionViewMoveItemAtToCallsCount > 0
108 | }
109 | var collectionViewMoveItemAtToReceivedArguments: (collectionView: UICollectionView, sourceIndexPath: IndexPath, destinationIndexPath: IndexPath)?
110 | var collectionViewMoveItemAtToReceivedInvocations: [(collectionView: UICollectionView, sourceIndexPath: IndexPath, destinationIndexPath: IndexPath)] = []
111 | var collectionViewMoveItemAtToClosure: ((UICollectionView, IndexPath, IndexPath) -> Void)?
112 |
113 | func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
114 | collectionViewMoveItemAtToCallsCount += 1
115 | collectionViewMoveItemAtToReceivedArguments = (collectionView: collectionView, sourceIndexPath: sourceIndexPath, destinationIndexPath: destinationIndexPath)
116 | collectionViewMoveItemAtToReceivedInvocations.append((collectionView: collectionView, sourceIndexPath: sourceIndexPath, destinationIndexPath: destinationIndexPath))
117 | collectionViewMoveItemAtToClosure?(collectionView, sourceIndexPath, destinationIndexPath)
118 | }
119 |
120 | //MARK: - indexTitles
121 |
122 | var indexTitlesForCallsCount = 0
123 | var indexTitlesForCalled: Bool {
124 | return indexTitlesForCallsCount > 0
125 | }
126 | var indexTitlesForReceivedCollectionView: UICollectionView?
127 | var indexTitlesForReceivedInvocations: [UICollectionView] = []
128 | var indexTitlesForReturnValue: [String]?
129 | var indexTitlesForClosure: ((UICollectionView) -> [String]?)?
130 |
131 | func indexTitles(for collectionView: UICollectionView) -> [String]? {
132 | indexTitlesForCallsCount += 1
133 | indexTitlesForReceivedCollectionView = collectionView
134 | indexTitlesForReceivedInvocations.append(collectionView)
135 | return indexTitlesForClosure.map({ $0(collectionView) }) ?? indexTitlesForReturnValue
136 | }
137 |
138 | //MARK: - collectionView
139 |
140 | var collectionViewIndexPathForIndexTitleAtCallsCount = 0
141 | var collectionViewIndexPathForIndexTitleAtCalled: Bool {
142 | return collectionViewIndexPathForIndexTitleAtCallsCount > 0
143 | }
144 | var collectionViewIndexPathForIndexTitleAtReceivedArguments: (collectionView: UICollectionView, title: String, index: Int)?
145 | var collectionViewIndexPathForIndexTitleAtReceivedInvocations: [(collectionView: UICollectionView, title: String, index: Int)] = []
146 | var collectionViewIndexPathForIndexTitleAtReturnValue: IndexPath!
147 | var collectionViewIndexPathForIndexTitleAtClosure: ((UICollectionView, String, Int) -> IndexPath)?
148 |
149 | func collectionView(_ collectionView: UICollectionView, indexPathForIndexTitle title: String, at index: Int) -> IndexPath {
150 | collectionViewIndexPathForIndexTitleAtCallsCount += 1
151 | collectionViewIndexPathForIndexTitleAtReceivedArguments = (collectionView: collectionView, title: title, index: index)
152 | collectionViewIndexPathForIndexTitleAtReceivedInvocations.append((collectionView: collectionView, title: title, index: index))
153 | return collectionViewIndexPathForIndexTitleAtClosure.map({ $0(collectionView, title, index) }) ?? collectionViewIndexPathForIndexTitleAtReturnValue
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/Example/Tests/StoriesLayout/StoriesCollectionViewLayoutSpec.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StoriesCollectionViewLayoutSpec.swift
3 | // StoriesLayout_Tests
4 | //
5 | // Created by Andrea Altea on 29/06/2019.
6 | // Copyright © 2019 CocoaPods. All rights reserved.
7 | //
8 |
9 | import Quick
10 | import Nimble
11 | import StoriesLayout
12 |
13 | class StoriesCollectionViewLayoutSpec: QuickSpec {
14 |
15 | override func spec() {
16 | let cellIdentifier = "testCell"
17 |
18 | var sut: StoriesCollectionViewLayout!
19 | var collectionViewSize: CGRect!
20 | var collectionView: UICollectionView!
21 |
22 | var dataSource: UICollectionViewDataSourceMock!
23 |
24 | beforeEach {
25 | sut = StoriesCollectionViewLayout()
26 | }
27 |
28 | context("check default values") {
29 | xit("must have base transformation") {
30 | expect(sut.baseTransform.m34).to(equal(1.0 / 1000))
31 | }
32 | }
33 |
34 | context("collection interaction") {
35 | beforeEach {
36 | collectionViewSize = CGRect(origin: .zero, size: CGSize(width: 320, height: 650))
37 | collectionView = UICollectionView(frame: collectionViewSize,
38 | collectionViewLayout: sut)
39 | collectionView.register(StoriesCollectionViewCell.self,
40 | forCellWithReuseIdentifier: cellIdentifier)
41 | dataSource = .create(sections: 1,
42 | items: 10,
43 | cellIdentifier: cellIdentifier)
44 | collectionView.dataSource = dataSource
45 | }
46 |
47 | it("it reloads") {
48 | collectionView.reloadData()
49 | }
50 |
51 | it("calculates contentSize") {
52 | let numberOfItems = CGFloat(collectionView.numberOfItems(inSection: 0))
53 | let contentSize = sut.collectionViewContentSize
54 | expect(contentSize.height).to(equal(collectionView.bounds.height))
55 | expect(contentSize.width).to(equal(collectionView.bounds.width * numberOfItems))
56 | }
57 |
58 | it("should always invalidate layout") {
59 | let shouldInvalidate = sut.shouldInvalidateLayout(forBoundsChange: collectionView.bounds)
60 | expect(shouldInvalidate).to(beTrue())
61 | }
62 |
63 | it("must return first cell in default position") {
64 | collectionView.contentOffset = .zero
65 | let indexPath = IndexPath(item: 0, section: 0)
66 | guard let attributes = sut.layoutAttributesForItem(at: indexPath) as? StoriesLayoutAttributes else {
67 | XCTFail("Should be StoruesLayoutAttributes")
68 | return
69 | }
70 | expect(attributes.indexPath).to(equal(indexPath))
71 | expect(attributes.frame).to(equal(collectionView.bounds))
72 | expect(attributes.anchorPoint).to(equal(CGPoint(x: 0.5, y: 0.5)))
73 | expect(attributes.transform3D).to(equal(CATransform3DIdentity))
74 | }
75 |
76 | it("must return first cell half folded") {
77 | collectionView.contentOffset = CGPoint(x: collectionView.bounds.width / 2, y: 0)
78 | let indexPath = IndexPath(item: 0, section: 0)
79 | guard let attributes = sut.layoutAttributesForItem(at: indexPath) as? StoriesLayoutAttributes else {
80 | XCTFail("Should be StoruesLayoutAttributes")
81 | return
82 | }
83 | expect(attributes.indexPath).to(equal(indexPath))
84 | expect(attributes.bounds).to(equal(CGRect(origin: .zero, size: collectionView.bounds.size)))
85 | expect(attributes.anchorPoint).to(equal(CGPoint(x: 1, y: 0.5)))
86 | expect(attributes.transform3D).toNot(equal(CATransform3DIdentity))
87 | }
88 |
89 | it("must return first cell half folded") {
90 | collectionView.contentOffset = CGPoint(x: -collectionView.bounds.width / 2, y: 0)
91 | let indexPath = IndexPath(item: 0, section: 0)
92 | guard let attributes = sut.layoutAttributesForItem(at: indexPath) as? StoriesLayoutAttributes else {
93 | fail("Should be StoruesLayoutAttributes")
94 | return
95 | }
96 | expect(attributes.indexPath).to(equal(indexPath))
97 | expect(attributes.bounds).to(equal(CGRect(origin: .zero, size: collectionView.bounds.size)))
98 | expect(attributes.anchorPoint).to(equal(CGPoint(x: 0.0, y: 0.5)))
99 | expect(attributes.transform3D).toNot(equal(CATransform3DIdentity))
100 | }
101 |
102 | it("must return only first item") {
103 | collectionView.contentOffset = .zero
104 | guard let attributes = sut.layoutAttributesForElements(in: collectionView.bounds) as? [StoriesLayoutAttributes] else {
105 | fail("Should be StoriesLayoutAttributes")
106 | return
107 | }
108 | expect(attributes.count).to(equal(1))
109 | expect(attributes.filter { $0.indexPath == IndexPath(item: 0, section: 0) }.count).to(equal(1))
110 | }
111 |
112 | it("must return first and second item") {
113 | collectionView.contentOffset = CGPoint(x: 10, y: 0)
114 | guard let attributes = sut.layoutAttributesForElements(in: collectionView.bounds) as? [StoriesLayoutAttributes] else {
115 | fail("Should be StoriesLayoutAttributes")
116 | return
117 | }
118 | expect(attributes.count).to(equal(2))
119 | expect(attributes.filter { $0.indexPath == IndexPath(item: 0, section: 0) }.count).to(equal(1))
120 | expect(attributes.filter { $0.indexPath == IndexPath(item: 1, section: 0) }.count).to(equal(1))
121 | }
122 |
123 | it("must return only first item") {
124 | collectionView.contentOffset = CGPoint(x: -10, y: 0)
125 | guard let attributes = sut.layoutAttributesForElements(in: collectionView.bounds) as? [StoriesLayoutAttributes] else {
126 | fail("Should be StoriesLayoutAttributes")
127 | return
128 | }
129 | expect(attributes.count).to(equal(1))
130 | expect(attributes.filter { $0.indexPath == IndexPath(item: 0, section: 0) }.count).to(equal(1))
131 | }
132 |
133 | it("must return only first item") {
134 | collectionView.contentOffset = CGPoint(x: collectionView.bounds.width * 1.5, y: 0)
135 | guard let attributes = sut.layoutAttributesForElements(in: collectionView.bounds) as? [StoriesLayoutAttributes] else {
136 | fail("Should be StoriesLayoutAttributes")
137 | return
138 | }
139 | expect(attributes.count).to(equal(2))
140 | expect(attributes.filter { $0.indexPath == IndexPath(item: 1, section: 0) }.count).to(equal(1))
141 | expect(attributes.filter { $0.indexPath == IndexPath(item: 2, section: 0) }.count).to(equal(1))
142 | }
143 | }
144 | }
145 | }
146 |
147 | extension UICollectionViewDataSourceMock {
148 |
149 | static func create(sections: Int, items: Int, cellIdentifier identifier: String) -> UICollectionViewDataSourceMock {
150 |
151 | let dataSource = UICollectionViewDataSourceMock()
152 | dataSource.numberOfSectionsInClosure = { _ in return sections }
153 | dataSource.collectionViewNumberOfItemsInSectionClosure = { _, _ in return items }
154 | dataSource.collectionViewCellForItemAtClosure = { collection, indexPath in
155 | return collection.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath)
156 | }
157 | return dataSource
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/Example/Tests/StoriesLayout/StoriesLayoutAttributesSpec.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StoriesLayoutAttributesSpec.swift
3 | // StoriesLayout_Tests
4 | //
5 | // Created by Andrea Altea on 29/06/2019.
6 | // Copyright © 2019 CocoaPods. All rights reserved.
7 | //
8 |
9 | import Quick
10 | import Nimble
11 | import StoriesLayout
12 |
13 | class StoriesLayoutAttributesSpec: QuickSpec {
14 |
15 | override func spec() {
16 | var sut: StoriesLayoutAttributes!
17 |
18 | beforeEach {
19 | sut = StoriesLayoutAttributes(forCellWith: IndexPath(item: 0, section: 0))
20 | }
21 |
22 | context("test default values") {
23 | it("should have alpha value") {
24 | expect(sut.anchorPoint).to(equal(CGPoint(x: 0.5, y: 0.5)))
25 | }
26 |
27 | it("should not have gradient value") {
28 | expect(sut.gradient).to(beNil())
29 | }
30 | }
31 |
32 | context("test parameters NSCopying") {
33 | it("must copy IndexPath") {
34 | let indexPath = IndexPath(item: 1, section: 2)
35 | sut.indexPath = indexPath
36 |
37 | guard let sutCopy = sut.copy() as? StoriesLayoutAttributes else {
38 | return fail("should be copied")
39 | }
40 | expect(sutCopy.indexPath).to(equal(indexPath))
41 | }
42 |
43 | it("must copy anchor value") {
44 | let anchor = CGPoint(x: 0.7, y: 0.3)
45 | sut.anchorPoint = anchor
46 |
47 | guard let sutCopy = sut.copy() as? StoriesLayoutAttributes else {
48 | return fail("should be copied")
49 | }
50 | expect(sutCopy.anchorPoint).to(equal(anchor))
51 | }
52 |
53 | it("must copy anchor value") {
54 | let gradient = StoriesLayoutAttributes.Gradient.left(percent: 0.6)
55 | sut.gradient = gradient
56 |
57 | guard let sutCopy = sut.copy() as? StoriesLayoutAttributes else {
58 | return fail("should be copied")
59 | }
60 | expect(sutCopy.gradient).to(equal(gradient))
61 | }
62 | }
63 |
64 | context("test equality between attributes") {
65 | it("copied attributes without gradient should be equal") {
66 | guard let sutCopy = sut.copy() as? StoriesLayoutAttributes else {
67 | return fail("should be copied")
68 | }
69 | expect(sutCopy).to(equal(sut))
70 | }
71 |
72 | it("copied attributes with gradient should be equal") {
73 | sut.gradient = .left(percent: 0.4)
74 | guard let sutCopy = sut.copy() as? StoriesLayoutAttributes else {
75 | return fail("should be copied")
76 | }
77 | expect(sutCopy).to(equal(sut))
78 | }
79 |
80 | it("copied attributes with different gradient should not be equal") {
81 | guard let sutCopy = sut.copy() as? StoriesLayoutAttributes else {
82 | return fail("should be copied")
83 | }
84 | sut.gradient = .left(percent: 0.4)
85 | expect(sutCopy).toNot(equal(sut))
86 | }
87 |
88 | it("copied attributes with different anchorPoint should not be equal") {
89 | guard let sutCopy = sut.copy() as? StoriesLayoutAttributes else {
90 | return fail("should be copied")
91 | }
92 | sut.anchorPoint = CGPoint(x: 0.3, y: 0.1)
93 | expect(sutCopy).toNot(equal(sut))
94 | }
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/Example/fastlane/Appfile:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Oni-zerone/CollectionLayouts/faef2a79540f1790916c79bb74edb0eca3689302/Example/fastlane/Appfile
--------------------------------------------------------------------------------
/Example/fastlane/Fastfile:
--------------------------------------------------------------------------------
1 | # This file contains the fastlane.tools configuration
2 | # You can find the documentation at https://docs.fastlane.tools
3 | #
4 | # For a list of all available actions, check out
5 | #
6 | # https://docs.fastlane.tools/actions
7 | #
8 | # For a list of all available plugins, check out
9 | #
10 | # https://docs.fastlane.tools/plugins/available-plugins
11 | #
12 |
13 | update_fastlane
14 |
15 | default_platform(:ios)
16 |
17 | platform :ios do
18 |
19 | desc "Default CI lane"
20 | lane :ci do
21 | update_dependencies
22 | pod_lint
23 | test
24 | coverage
25 | end
26 |
27 | desc "Update dependencies"
28 | lane :update_dependencies do
29 | cocoapods(
30 | repo_update: true
31 | )
32 | end
33 |
34 | desc "SwiftLint linting"
35 | lane :lint do
36 | swiftlint(
37 | mode: :lint,
38 | ignore_exit_status: false
39 | )
40 | end
41 |
42 | desc "Pod lib lint"
43 | lane :pod_lint do
44 | Dir.chdir("../..") do
45 | sh("pod lib lint")
46 | end
47 | end
48 |
49 | desc "Runs all the tests"
50 | lane :test do
51 | scan(
52 | scheme: "StoriesLayout-Example",
53 | device: "iPhone XS",
54 | code_coverage: true
55 | )
56 | end
57 |
58 | desc "Slather sends coverage to Coveralls"
59 | lane :coverage do
60 | slather(
61 | travis: true,
62 | workspace: "StoriesLayout.xcworkspace",
63 | proj: "StoriesLayout.xcodeproj",
64 | scheme: "StoriesLayout-Example",
65 | binary_basename: "StoriesLayout",
66 | source_directory: "../StoriesLayout/*",
67 | simple_output: true,
68 | coveralls: true
69 | )
70 | end
71 |
72 | desc "Clean environment after all operations"
73 | lane :clean do
74 | clean_build_artifacts
75 | end
76 |
77 | after_all do |lane|
78 | clean
79 | end
80 | end
81 |
--------------------------------------------------------------------------------
/Example/fastlane/README.md:
--------------------------------------------------------------------------------
1 | fastlane documentation
2 | ================
3 | # Installation
4 |
5 | Make sure you have the latest version of the Xcode command line tools installed:
6 |
7 | ```
8 | xcode-select --install
9 | ```
10 |
11 | Install _fastlane_ using
12 | ```
13 | [sudo] gem install fastlane -NV
14 | ```
15 | or alternatively using `brew cask install fastlane`
16 |
17 | # Available Actions
18 | ## iOS
19 | ### ios ci
20 | ```
21 | fastlane ios ci
22 | ```
23 | Default CI lane
24 | ### ios lint
25 | ```
26 | fastlane ios lint
27 | ```
28 | SwiftLint linting
29 | ### ios pod_lint
30 | ```
31 | fastlane ios pod_lint
32 | ```
33 | Pod lib lint
34 | ### ios test
35 | ```
36 | fastlane ios test
37 | ```
38 | Runs all the tests
39 | ### ios coverage
40 | ```
41 | fastlane ios coverage
42 | ```
43 | Slather sends coverage to Coveralls
44 | ### ios clean
45 | ```
46 | fastlane ios clean
47 | ```
48 | Clean environment after all operations
49 |
50 | ----
51 |
52 | This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run.
53 | More information about fastlane can be found on [fastlane.tools](https://fastlane.tools).
54 | The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
55 |
--------------------------------------------------------------------------------
/Example/fastlane/report.xml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2019 oni.zerone@gmail.com
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CollectionLayouts
2 | [](https://travis-ci.com/Oni-zerone/CollectionLayouts)
3 | [](https://codebeat.co/projects/github-com-oni-zerone-collectionlayouts-develop)
4 | [](https://coveralls.io/github/Oni-zerone/CollectionLayouts?branch=develop)
5 |
6 | 
7 |
8 | [](https://cocoapods.org/pods/SafariLayout)
9 | [](https://cocoapods.org/pods/SafariLayout)
10 | [](https://cocoapods.org/pods/SafariLayout)
11 |
12 | This is a UICollectionViewLayout that reproduce the iOS Safari tabs experience,
13 | just use SafariCollectionViewLayout in your UICollectionView and subclass your cells from SafariCollectionViewCell!
14 |
15 | ### Installation
16 | * Via cocoapods:
17 | ```ruby
18 | pod 'SafariLayout'
19 | ```
20 |
21 | 
22 |
23 | [](https://cocoapods.org/pods/StoriesLayout)
24 | [](https://cocoapods.org/pods/StoriesLayout)
25 | [](https://cocoapods.org/pods/StoriesLayout)
26 |
27 | This is a UICollectionViewLayout that reproduce the Instagram Stories experience,
28 | just use StoriesCollectionViewLayout in your UICollectionView and subclass your cells from StoriesCollectionViewCell!
29 |
30 | ### Installation
31 | * Via cocoapods:
32 | ```ruby
33 | pod 'StoriesLayout'
34 | ```
35 |
36 | ### Usage
37 | You can find more info about usage in the [wiki](https://github.com/Oni-zerone/CollectionLayouts/wiki/StoriesLayout)
38 |
--------------------------------------------------------------------------------
/SafariLayout.podspec:
--------------------------------------------------------------------------------
1 |
2 | Pod::Spec.new do |s|
3 | s.name = 'SafariLayout'
4 | s.version = '0.1.1'
5 | s.summary = 'A Safari tabs like UICollectionViewLayout'
6 | s.swift_version = '5.0'
7 | s.description = <<-DESC
8 | This is a UICollectionViewLayout that reproduce the iOS Safari tabs experience,
9 | just use SafariCollectionViewLayout in your UICollectionView and subclass your cells from SafariCollectionViewCell!
10 | DESC
11 |
12 | s.homepage = 'https://github.com/Oni-zerone/CollectionLayouts'
13 | s.license = { :type => 'MIT', :file => 'LICENSE' }
14 | s.author = { 'Andrea Altea' => 'oni.zerone@gmail.com' }
15 | s.source = { :git => 'https://github.com/Oni-zerone/CollectionLayouts.git', :tag => 'SafariLayout-' + s.version.to_s }
16 | s.social_media_url = 'https://twitter.com/Oni_zerone'
17 |
18 | s.ios.deployment_target = '10.0'
19 |
20 | s.source_files = 'SafariLayout/Classes/**/*'
21 | s.frameworks = 'UIKit', 'CoreGraphics'
22 | end
23 |
--------------------------------------------------------------------------------
/SafariLayout/Classes/SafariCollectionViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SafariCollectionViewCell.swift
3 | // SafariLayout
4 | //
5 | // Created by Andrea Altea on 27/05/2019.
6 | //
7 |
8 | import UIKit
9 |
10 | /**
11 | SafariCollectionViewCell is a base class that you could use to create your custom cells with the tilted tab experience.
12 | */
13 | open class SafariCollectionViewCell: UICollectionViewCell {
14 |
15 | override open func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
16 | super.apply(layoutAttributes)
17 | guard let storiesAttributes = layoutAttributes as? SafariCollectionViewLayoutAttributes else { return }
18 | self.layer.anchorPoint = storiesAttributes.anchorPoint
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/SafariLayout/Classes/SafariCollectionViewLayout.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SafariCollectionViewLayout.swift
3 | // Pods
4 | //
5 | // Created by Andrea Altea on 27/05/2019.
6 | //
7 |
8 | import UIKit
9 |
10 | /**
11 | A concrete layout object that reproduce the look and feel of the Safari tab bars.
12 |
13 | The items will be shown in a single column aligned as cards with the same dimension of the screen tilted in relation to the position in the collection.
14 |
15 | Actually there isn't a specific delegate to customize layout behviors of the cells.
16 | */
17 | @IBDesignable
18 | public class SafariCollectionViewLayout: UICollectionViewLayout {
19 |
20 | private var _tabsCount: Int = 4
21 |
22 | /**
23 | The maximum number of tabs shown on the screen.
24 |
25 | Line spacing between the cells will be calculated based on this parameter.
26 |
27 | Default value of this property is 4
28 | */
29 | @IBInspectable public var tabsCount: Int {
30 | set {
31 | _tabsCount = max(newValue, 1)
32 | collectionView?.reloadData()
33 | }
34 | get {
35 | return _tabsCount
36 | }
37 | }
38 |
39 | /**
40 | The angle that the cell assumes at the center of the collection.
41 |
42 | This is the main value used to tune the cell position in the collection.
43 | Define the overall orientation of the cell and the appearance in the collection.
44 |
45 | Default value of this property is 114 degrees.
46 | */
47 | var defaultAngle: CGFloat = CGFloat.pi * 0.2
48 |
49 | /**
50 | The angle variation between the top and the center of the collection.
51 |
52 | This is the value used to realize the tilting effect when the cell moves in the collection.
53 | Define the overall orientation of the cell and the appearance in the collection.
54 |
55 | Default value of this property is 180 degrees.
56 | */
57 | var variationAngle: CGFloat = CGFloat.pi * 0.2
58 |
59 | /**
60 | Defines the contentSize of the collectionView
61 |
62 | Actually SafariCollectionViewLayout can handle a single section and the cell size is determined as the same of the collection, but tilted.
63 |
64 | Line spacing between the cells is defined by the tabs count.
65 | */
66 | override public var collectionViewContentSize: CGSize {
67 | guard let collection = collectionView,
68 | let dataSource = collection.dataSource else {
69 | return .zero
70 | }
71 |
72 | let cellsCount = dataSource.collectionView(collection, numberOfItemsInSection: 0)
73 | if cellsCount < 1 {
74 | return .zero
75 | }
76 |
77 | if cellsCount == 1 {
78 | return collection.bounds.size
79 | }
80 |
81 | return CGSize(width: collection.bounds.width,
82 | height: CGFloat(cellsCount) * (collection.bounds.height / CGFloat(_tabsCount)))
83 | }
84 |
85 | override public func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
86 |
87 | let firstVisibleItem = self.firstVisibleItem
88 | let lastVisibleItem = min(firstVisibleItem + tabsCount, itemsCount - 1)
89 | if lastVisibleItem < firstVisibleItem {
90 | let indexPath = IndexPath(item: lastVisibleItem, section: 0)
91 | guard let cellAttributes = layoutAttributesForItem(at: indexPath) else { return nil }
92 | return [cellAttributes]
93 | }
94 |
95 | var attributes = [UICollectionViewLayoutAttributes]()
96 | (firstVisibleItem ... lastVisibleItem).forEach { index in
97 | let indexPath = IndexPath(item: index, section: 0)
98 | guard let cellAttributes = layoutAttributesForItem(at: indexPath) else { return }
99 | attributes.append(cellAttributes)
100 | }
101 | return attributes
102 | }
103 |
104 | override public func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
105 | let collectionBounds = self.collectionBounds
106 | let collectionOffset = self.collectionOffset
107 |
108 | let attributes = SafariCollectionViewLayoutAttributes(forCellWith: indexPath)
109 | attributes.frame = frameForItem(at: indexPath)
110 |
111 | let tilt = ((collectionOffset.y - attributes.frame.minY) / collectionBounds.height)
112 | var perspective = CATransform3DIdentity
113 | perspective.m34 = -1/1000
114 |
115 | let rotation = CATransform3DRotate(perspective, (tilt * variationAngle) - defaultAngle, 1.0, 0.0, 0.0)
116 | let translation = CATransform3DMakeTranslation(0.0, collectionBounds.height / -2, 0.0)
117 | attributes.transform3D = CATransform3DConcat(rotation, translation)
118 | attributes.anchorPoint = CGPoint(x: 0.5, y: 0.0)
119 | return attributes
120 | }
121 |
122 | public override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
123 | return true
124 | }
125 | }
126 |
127 | extension SafariCollectionViewLayout {
128 |
129 | var itemsCount: Int {
130 | guard let collection = collectionView,
131 | let dataSource = collection.dataSource else {
132 | return 0
133 | }
134 | return dataSource.collectionView(collection, numberOfItemsInSection: 0)
135 | }
136 |
137 | var firstVisibleItem: Int {
138 | guard let collection = collectionView else {
139 | return 0
140 | }
141 | return max(Int(collection.contentOffset.y / (collection.bounds.height / CGFloat(tabsCount))), 0)
142 | }
143 |
144 | var collectionBounds: CGRect {
145 | return self.collectionView?.bounds ?? .zero
146 | }
147 |
148 | var collectionOffset: CGPoint {
149 | return self.collectionView?.contentOffset ?? .zero
150 | }
151 |
152 | func sectionInset(for section: Int) -> UIEdgeInsets {
153 | guard let collection = collectionView,
154 | let insets = (collection.delegate as? UICollectionViewDelegateFlowLayout)?
155 | .collectionView?(collection, layout: self, insetForSectionAt: section) else {
156 | return collectionView?.contentInset ?? .zero
157 | }
158 | return insets
159 | }
160 |
161 | func frameForItem(at indexPath: IndexPath) -> CGRect {
162 | let insets = sectionInset(for: indexPath.section)
163 | return CGRect(x: insets.left,
164 | y: CGFloat(indexPath.item) * (collectionBounds.height / CGFloat(tabsCount)) + insets.top,
165 | width: collectionBounds.width - (insets.left + insets.right),
166 | height: collectionBounds.height - (insets.top + insets.bottom))
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/SafariLayout/Classes/SafariCollectionViewLayoutAttributes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SafariCollectionViewLayoutAttributes.swift
3 | // SafariLayout
4 | //
5 | // Created by Andrea Altea on 27/05/2019.
6 | //
7 |
8 | import UIKit
9 | /**
10 | The custom `UICollectionViewLayoutAttributes` subclass used by `SafariCollectionViewLayout`
11 | */
12 | class SafariCollectionViewLayoutAttributes: UICollectionViewLayoutAttributes {
13 |
14 | /**
15 | Anchor point is the realtive position used to calculate the tilt transformation of the cell.
16 |
17 | If you won't use the `SafariCollectionViewCell` as your base cell class remember to override
18 |
19 | `func apply(_ layoutAttributes: UICollectionViewLayoutAttributes)` method to pass the anchorPoint property
20 | to the cell layer.
21 | */
22 | var anchorPoint = CGPoint(x: 0.5, y: 0.5)
23 |
24 | /// Overridden for NSCopying compatibility
25 | override func copy(with zone: NSZone? = nil) -> Any {
26 | let attribute = super.copy(with: zone) as! SafariCollectionViewLayoutAttributes
27 | attribute.anchorPoint = self.anchorPoint
28 | return attribute
29 |
30 | }
31 |
32 | /// Overridden for Equatable compatibility
33 | override func isEqual(_ object: Any?) -> Bool {
34 | guard let object = object as? SafariCollectionViewLayoutAttributes,
35 | object.anchorPoint == self.anchorPoint else {
36 | return false
37 | }
38 | return super.isEqual(object)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/StoriesLayout.podspec:
--------------------------------------------------------------------------------
1 |
2 | Pod::Spec.new do |s|
3 | s.name = 'StoriesLayout'
4 | s.version = '0.1.1'
5 | s.summary = 'An Instagram Stories like UICollectionViewLayout'
6 | s.swift_version = '5.0'
7 | s.description = <<-DESC
8 | This is a UICollectionViewLayout that reproduce the Instagram Stories experience,
9 | just use StoriesCollectionViewLayout in your UICollectionView and subclass your cells from StoriesCollectionViewCell!
10 | DESC
11 |
12 | s.homepage = 'https://github.com/Oni-zerone/CollectionLayouts'
13 | s.license = { :type => 'MIT', :file => 'LICENSE' }
14 | s.author = { 'Andrea Altea' => 'oni.zerone@gmail.com' }
15 | s.source = { :git => 'https://github.com/Oni-zerone/CollectionLayouts.git', :tag => 'StoriesLayout-' + s.version.to_s }
16 | s.social_media_url = 'https://twitter.com/Oni_zerone'
17 |
18 | s.ios.deployment_target = '10.0'
19 |
20 | s.source_files = 'StoriesLayout/Classes/**/*'
21 | s.frameworks = 'UIKit', 'CoreGraphics'
22 | end
23 |
--------------------------------------------------------------------------------
/StoriesLayout/Assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Oni-zerone/CollectionLayouts/faef2a79540f1790916c79bb74edb0eca3689302/StoriesLayout/Assets/.gitkeep
--------------------------------------------------------------------------------
/StoriesLayout/Classes/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Oni-zerone/CollectionLayouts/faef2a79540f1790916c79bb74edb0eca3689302/StoriesLayout/Classes/.gitkeep
--------------------------------------------------------------------------------
/StoriesLayout/Classes/StoriesCollectionViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StoriesCollectionViewCell.swift
3 | // StoriesLayout
4 | //
5 | // Created by Andrea Altea on 25/05/2019.
6 | //
7 |
8 | import UIKit
9 | import CoreGraphics
10 |
11 | /**
12 | StoriesCollectionViewCell is a base class that you could use to create your custom cells with the Instagram Stories experience.
13 | */
14 | open class StoriesCollectionViewCell: UICollectionViewCell {
15 |
16 | public weak var gradientLayer: CAGradientLayer?
17 |
18 | internal func getGradientLayer() -> CAGradientLayer {
19 |
20 | if let gradientLayer = gradientLayer {
21 | return gradientLayer
22 | }
23 |
24 | let gradientLayer = CAGradientLayer()
25 | gradientLayer.opacity = 0.0
26 | gradientLayer.colors = [UIColor.black.cgColor,
27 | UIColor.black.withAlphaComponent(0.2).cgColor]
28 | gradientLayer.actions = ["opacity": NSNull()]
29 | self.layer.addSublayer(gradientLayer)
30 | self.gradientLayer = gradientLayer
31 | return gradientLayer
32 | }
33 |
34 | override open func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
35 | super.apply(layoutAttributes)
36 | guard let storiesAttributes = layoutAttributes as? StoriesLayoutAttributes else { return }
37 | self.layer.anchorPoint = storiesAttributes.anchorPoint
38 | guard let gradient = storiesAttributes.gradient else {
39 | self.gradientLayer?.opacity = 0.0
40 | return
41 | }
42 | let gradientLayer = getGradientLayer()
43 | gradientLayer.frame = self.bounds
44 | switch gradient {
45 | case .left(let percent):
46 | gradientLayer.frame = self.bounds
47 | gradientLayer.startPoint = CGPoint(x: 1.0, y: 0.5)
48 | gradientLayer.endPoint = CGPoint(x: 0.0, y: 0.5)
49 | gradientLayer.opacity = percent
50 | case .right(let percent):
51 | gradientLayer.frame = self.bounds
52 | gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
53 | gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
54 | gradientLayer.opacity = percent
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/StoriesLayout/Classes/StoriesCollectionViewLayout.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StoriesCollectionViewLayout.swift
3 | // StoriesLayout
4 | //
5 | // Created by Andrea Altea on 23/05/2019.
6 | //
7 |
8 | import UIKit
9 |
10 | /**
11 | A concrete layout object that reproduce the look and feel of the Instagram Stories layout.
12 |
13 | The items will be shown in a single row, one by one with the same dimension of the screen.
14 |
15 | Actually there isn't a specific delegate to customize layout behviors of the cells.
16 | */
17 | public class StoriesCollectionViewLayout: UICollectionViewLayout {
18 |
19 | public var baseTransform: CATransform3D = {
20 | var transform = CATransform3DIdentity
21 | transform.m34 = 1.0 / 1000
22 | return transform
23 | }()
24 |
25 | /**
26 | Defines the contentSize of the collectionView
27 |
28 | Actually StoriesCollectionViewLayout can handle a single section and the cell size is determined as the same of the collection.
29 | */
30 | override public var collectionViewContentSize: CGSize {
31 | guard let collectionView = collectionView,
32 | let dataSource = collectionView.dataSource else { return .zero }
33 |
34 | return CGSize(width: collectionView.bounds.size.width * CGFloat(dataSource.collectionView(collectionView, numberOfItemsInSection: 0)),
35 | height: collectionView.bounds.size.height)
36 | }
37 |
38 | override public func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
39 | guard let collectionView = collectionView,
40 | let dataSource = collectionView.dataSource else { return nil }
41 | let rect = CGRect(origin: collectionView.contentOffset, size: collectionView.bounds.size)
42 |
43 | let itemIndex = Int(rect.origin.x == 0 ? 0 : rect.origin.x / rect.width)
44 | var items: [UICollectionViewLayoutAttributes] = []
45 | if dataSource.collectionView(collectionView, numberOfItemsInSection: 0) > itemIndex,
46 | let firstItem = layoutAttributesForItem(at: IndexPath(item: itemIndex, section: 0)) {
47 | items.append(firstItem)
48 | }
49 |
50 | if let lastBound = items.last?.bounds,
51 | lastBound.width + lastBound.minX >= collectionView.bounds.width + collectionView.contentOffset.x {
52 | return items
53 | }
54 |
55 | if dataSource.collectionView(collectionView, numberOfItemsInSection: 0) > (itemIndex + 1),
56 | let secondItem = layoutAttributesForItem(at: IndexPath(item: itemIndex + 1, section: 0) ) {
57 | secondItem.indexPath = IndexPath(item: itemIndex + 1, section: 0)
58 | secondItem.zIndex = 1000
59 | items.append(secondItem)
60 | }
61 | return items
62 | }
63 |
64 | override public func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
65 | let attributes = StoriesLayoutAttributes(forCellWith: indexPath)
66 | guard let collection = collectionView else { return nil }
67 |
68 | attributes.frame = CGRect(x: collection.bounds.width * CGFloat(indexPath.item),
69 | y: 0,
70 | width: collection.bounds.width,
71 | height: collection.bounds.height)
72 |
73 | if attributes.frame.minX > collection.contentOffset.x &&
74 | attributes.frame.maxX > (collection.contentOffset.x + collection.bounds.width) {
75 | let rightSideOriginalTransform = -90 * CGFloat.pi / 180
76 | var rightAnimationPercentComplete = (collection.contentOffset.x / collection.bounds.width).truncatingRemainder(dividingBy: 1)
77 | rightAnimationPercentComplete = (1 - rightAnimationPercentComplete).truncatingRemainder(dividingBy: 1)
78 | let rotation = CATransform3DRotate(baseTransform, rightSideOriginalTransform * rightAnimationPercentComplete, 0.0, 1.0, 0.0)
79 | let translation = CATransform3DMakeTranslation(attributes.frame.width / -2.0, 0.0, 0.0)
80 | attributes.transform3D = CATransform3DConcat(rotation, translation)
81 | attributes.anchorPoint = CGPoint(x: 0.0, y: 0.5)
82 | attributes.gradient = .left(percent: Float(rightAnimationPercentComplete))
83 | return attributes
84 | }
85 |
86 | if attributes.frame.minX < collection.contentOffset.x &&
87 | attributes.frame.maxX < (collection.contentOffset.x + collection.bounds.width) {
88 | let leftSideOriginalTransform = 90 * CGFloat.pi / 180
89 | let leftAnimationPercentageComplete = (collection.contentOffset.x / collection.bounds.width).truncatingRemainder(dividingBy: 1)
90 | let rotation = CATransform3DRotate(baseTransform, leftSideOriginalTransform * leftAnimationPercentageComplete, 0.0, 1.0, 0.0)
91 | let translation = CATransform3DMakeTranslation(attributes.frame.width / 2.0, 0.0, 0.0)
92 | attributes.transform3D = CATransform3DConcat(rotation, translation)
93 | attributes.anchorPoint = CGPoint(x: 1.0, y: 0.5)
94 | attributes.gradient = .right(percent: Float(leftAnimationPercentageComplete))
95 | return attributes
96 | }
97 |
98 | attributes.transform3D = CATransform3DIdentity
99 | attributes.anchorPoint = CGPoint(x: 0.5, y: 0.5)
100 | return attributes
101 | }
102 |
103 | override public func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
104 | return true
105 | }
106 | }
107 |
108 |
--------------------------------------------------------------------------------
/StoriesLayout/Classes/StoriesLayoutAttributes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StoriesLayoutAttributes.swift
3 | // StoriesLayout
4 | //
5 | // Created by Andrea Altea on 25/05/2019.
6 | //
7 |
8 | import UIKit
9 | /**
10 | The custom `UICollectionViewLayoutAttributes` subclass used by `StoriesCollectionViewLayout`
11 | */
12 | public class StoriesLayoutAttributes: UICollectionViewLayoutAttributes {
13 |
14 | /**
15 | The gradient informations used by the `StoriesCollectionViewCell`
16 | */
17 | public enum Gradient: Equatable {
18 | case left(percent: Float)
19 | case right(percent: Float)
20 | }
21 |
22 | /**
23 | Anchor point is the realtive position used to calculate the rotation transformation of the cell.
24 |
25 | If you won't use the `StoriesCollectionViewCell` as your base cell class remember to override
26 |
27 | `func apply(_ layoutAttributes: UICollectionViewLayoutAttributes)` method to pass the anchorPoint property
28 | to the cell layer.
29 | */
30 | public var anchorPoint: CGPoint = CGPoint(x: 0.5, y: 0.5)
31 |
32 | /**
33 | Gradient informations to render the `CAGradientLayer` over the `UICollectionViewCell`.
34 |
35 | If you won't use the `StoriesCollectionViewCell` as your base cell class remember to override
36 |
37 | `func apply(_ layoutAttributes: UICollectionViewLayoutAttributes)` method to draw the `CAGradientLayer` if you want to show it.
38 | */
39 | public var gradient: Gradient?
40 |
41 | override public func copy(with zone: NSZone? = nil) -> Any {
42 | let attribute = super.copy(with: zone) as! StoriesLayoutAttributes
43 | attribute.anchorPoint = self.anchorPoint
44 | attribute.gradient = gradient
45 | return attribute
46 | }
47 |
48 | override public func isEqual(_ object: Any?) -> Bool {
49 | guard let object = object as? StoriesLayoutAttributes,
50 | object.anchorPoint == self.anchorPoint,
51 | object.gradient == self.gradient else { return false }
52 | return super.isEqual(object)
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/_Pods.xcodeproj:
--------------------------------------------------------------------------------
1 | Example/Pods/Pods.xcodeproj
--------------------------------------------------------------------------------