├── README
├── main.png
├── example.gif
├── layout.png
├── toolbar.png
└── allLayout.png
├── Example
├── Assets.xcassets
│ ├── Contents.json
│ ├── main.imageset
│ │ ├── main.png
│ │ └── Contents.json
│ ├── login.imageset
│ │ ├── icons8-enter-50.png
│ │ └── Contents.json
│ ├── move.imageset
│ │ ├── icons8-expand-50.png
│ │ └── Contents.json
│ ├── report.imageset
│ │ ├── icons8-info-50.png
│ │ └── Contents.json
│ ├── add.imageset
│ │ ├── icons8-plus-math-50.png
│ │ └── Contents.json
│ ├── email.imageset
│ │ ├── icons8-new-post-50.png
│ │ └── Contents.json
│ ├── google.imageset
│ │ ├── icons8-google-50.png
│ │ └── Contents.json
│ ├── mylist.imageset
│ │ ├── icons8-picture-50.png
│ │ └── Contents.json
│ ├── user.imageset
│ │ ├── icons8-customer-64.png
│ │ └── Contents.json
│ ├── facebook.imageset
│ │ ├── icons8-facebook-50.png
│ │ └── Contents.json
│ ├── twitter.imageset
│ │ ├── icons8-twitter-50.png
│ │ └── Contents.json
│ ├── like.imageset
│ │ ├── icons8-heart-outline-50.png
│ │ └── Contents.json
│ ├── more.imageset
│ │ ├── icons8-menu-vertical-48.png
│ │ └── Contents.json
│ ├── send.imageset
│ │ ├── icons8-secured-letter-50.png
│ │ └── Contents.json
│ ├── loading.imageset
│ │ ├── icons8-loading-filled-50.png
│ │ └── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── FullViewController.swift
├── XIB
│ ├── DescriptionFooterView.swift
│ ├── TitleHeaderView.swift
│ ├── DescriptionFooterView.xib
│ └── TitleHeaderView.xib
├── Info.plist
├── AppDelegate.swift
├── ReportViewController.swift
├── ViewController.swift
├── LoginViewController.swift
├── CteateListViewController.swift
├── MoveItemViewController.swift
├── WriteViewController.swift
├── ListContentsViewController.swift
├── MylistViewController.swift
├── LoadingViewController.swift
└── SendViewController.swift
├── Tests
├── LinuxMain.swift
└── SheetTests
│ └── SheetTests.swift
├── Sources
├── Sheet.swift
├── Animator
│ ├── SheetMoveAnimator.swift
│ ├── SheetModalScaleAnimator.swift
│ └── SheetFadeAnimator.swift
├── Extensions
│ ├── Optional+Extension.swift
│ ├── UIEdgeInsets+Extension.swift
│ └── Notification+Extension.swift
├── Manager
│ ├── SheetManager.swift
│ └── SheetOptions.swift
├── Layout
│ ├── SheetLayoutElement.swift
│ ├── SheetDecorationView.swift
│ ├── SheetLayoutAttributes.swift
│ ├── SheetLayoutSettings.swift
│ └── SheetContentsLayout.swift
└── Controller
│ ├── SheetContentsViewController.swift
│ └── SheetNavigationController.swift
├── Sheet.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcshareddata
│ └── xcschemes
│ │ └── Sheet-iOS.xcscheme
└── project.pbxproj
├── Sheet.podspec
├── Configs
├── SheetTests.plist
└── Sheet.plist
├── Package.swift
├── LICENSE
├── .gitignore
└── README.md
/README/main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ParkGwangBeom/Sheet/HEAD/README/main.png
--------------------------------------------------------------------------------
/README/example.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ParkGwangBeom/Sheet/HEAD/README/example.gif
--------------------------------------------------------------------------------
/README/layout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ParkGwangBeom/Sheet/HEAD/README/layout.png
--------------------------------------------------------------------------------
/README/toolbar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ParkGwangBeom/Sheet/HEAD/README/toolbar.png
--------------------------------------------------------------------------------
/README/allLayout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ParkGwangBeom/Sheet/HEAD/README/allLayout.png
--------------------------------------------------------------------------------
/Example/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Tests/LinuxMain.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import SheetTests
3 |
4 | XCTMain([
5 | testCase(SheetTests.allTests),
6 | ])
7 |
--------------------------------------------------------------------------------
/Example/Assets.xcassets/main.imageset/main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ParkGwangBeom/Sheet/HEAD/Example/Assets.xcassets/main.imageset/main.png
--------------------------------------------------------------------------------
/Example/Assets.xcassets/login.imageset/icons8-enter-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ParkGwangBeom/Sheet/HEAD/Example/Assets.xcassets/login.imageset/icons8-enter-50.png
--------------------------------------------------------------------------------
/Example/Assets.xcassets/move.imageset/icons8-expand-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ParkGwangBeom/Sheet/HEAD/Example/Assets.xcassets/move.imageset/icons8-expand-50.png
--------------------------------------------------------------------------------
/Example/Assets.xcassets/report.imageset/icons8-info-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ParkGwangBeom/Sheet/HEAD/Example/Assets.xcassets/report.imageset/icons8-info-50.png
--------------------------------------------------------------------------------
/Example/Assets.xcassets/add.imageset/icons8-plus-math-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ParkGwangBeom/Sheet/HEAD/Example/Assets.xcassets/add.imageset/icons8-plus-math-50.png
--------------------------------------------------------------------------------
/Example/Assets.xcassets/email.imageset/icons8-new-post-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ParkGwangBeom/Sheet/HEAD/Example/Assets.xcassets/email.imageset/icons8-new-post-50.png
--------------------------------------------------------------------------------
/Example/Assets.xcassets/google.imageset/icons8-google-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ParkGwangBeom/Sheet/HEAD/Example/Assets.xcassets/google.imageset/icons8-google-50.png
--------------------------------------------------------------------------------
/Example/Assets.xcassets/mylist.imageset/icons8-picture-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ParkGwangBeom/Sheet/HEAD/Example/Assets.xcassets/mylist.imageset/icons8-picture-50.png
--------------------------------------------------------------------------------
/Example/Assets.xcassets/user.imageset/icons8-customer-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ParkGwangBeom/Sheet/HEAD/Example/Assets.xcassets/user.imageset/icons8-customer-64.png
--------------------------------------------------------------------------------
/Example/Assets.xcassets/facebook.imageset/icons8-facebook-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ParkGwangBeom/Sheet/HEAD/Example/Assets.xcassets/facebook.imageset/icons8-facebook-50.png
--------------------------------------------------------------------------------
/Example/Assets.xcassets/twitter.imageset/icons8-twitter-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ParkGwangBeom/Sheet/HEAD/Example/Assets.xcassets/twitter.imageset/icons8-twitter-50.png
--------------------------------------------------------------------------------
/Example/Assets.xcassets/like.imageset/icons8-heart-outline-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ParkGwangBeom/Sheet/HEAD/Example/Assets.xcassets/like.imageset/icons8-heart-outline-50.png
--------------------------------------------------------------------------------
/Example/Assets.xcassets/more.imageset/icons8-menu-vertical-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ParkGwangBeom/Sheet/HEAD/Example/Assets.xcassets/more.imageset/icons8-menu-vertical-48.png
--------------------------------------------------------------------------------
/Example/Assets.xcassets/send.imageset/icons8-secured-letter-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ParkGwangBeom/Sheet/HEAD/Example/Assets.xcassets/send.imageset/icons8-secured-letter-50.png
--------------------------------------------------------------------------------
/Example/Assets.xcassets/loading.imageset/icons8-loading-filled-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ParkGwangBeom/Sheet/HEAD/Example/Assets.xcassets/loading.imageset/icons8-loading-filled-50.png
--------------------------------------------------------------------------------
/Sources/Sheet.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Sheet.swift
3 | // Interactive
4 | //
5 | // Created by gwangbeom on {TODAY}.
6 | // Copyright © 2018 Interactive. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
--------------------------------------------------------------------------------
/Sheet.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Sources/Animator/SheetMoveAnimator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SheetMoveAnimator.swift
3 | // Sheeeeeeeeet
4 | //
5 | // Created by Gwangbeom on 2018. 9. 30..
6 | // Copyright © 2018년 GwangBeom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | // Coming Soon...
12 |
13 |
--------------------------------------------------------------------------------
/Sheet.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Sources/Extensions/Optional+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Optional+Extension.swift
3 | // Example
4 | //
5 | // Created by Gwangbeom on 2018. 10. 1..
6 | // Copyright © 2018년 Interactive. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Optional {
12 |
13 | public var value: Wrapped? {
14 | return self
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Example/FullViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FullViewController.swift
3 | // Example
4 | //
5 | // Created by ArLupin on 24/04/2019.
6 | // Copyright © 2019 Interactive. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Sheet
11 |
12 | class FullViewController: UIViewController, SheetContent {
13 |
14 | @IBOutlet weak var scrollView: UIScrollView!
15 | }
16 |
--------------------------------------------------------------------------------
/Example/Assets.xcassets/main.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "main.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/XIB/DescriptionFooterView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DescriptionFooterView.swift
3 | // Sheeeeeeeeet
4 | //
5 | // Created by Gwangbeom on 2018. 10. 1..
6 | // Copyright © 2018년 GwangBeom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class DescriptionFooterView: UICollectionReusableView {
12 |
13 | override func awakeFromNib() {
14 | super.awakeFromNib()
15 | backgroundColor = .clear
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Example/Assets.xcassets/add.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icons8-plus-math-50.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/Assets.xcassets/email.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "icons8-new-post-50.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/Assets.xcassets/google.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "icons8-google-50.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/Assets.xcassets/login.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icons8-enter-50.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/Assets.xcassets/move.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icons8-expand-50.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/Assets.xcassets/mylist.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "icons8-picture-50.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/Assets.xcassets/report.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icons8-info-50.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/Assets.xcassets/user.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icons8-customer-64.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/Assets.xcassets/facebook.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "icons8-facebook-50.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/Assets.xcassets/like.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icons8-heart-outline-50.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/Assets.xcassets/more.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "icons8-menu-vertical-48.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/Assets.xcassets/send.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icons8-secured-letter-50.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/Assets.xcassets/twitter.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "icons8-twitter-50.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Sources/Manager/SheetManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SheetManager.swift
3 | // Example
4 | //
5 | // Created by Gwangbeom on 2018. 10. 1..
6 | // Copyright © 2018년 Interactive. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | public class SheetManager {
12 |
13 | public static let shared = SheetManager()
14 |
15 | public var options = SheetOptions()
16 |
17 | public var animationOption = SheetAnimationOption()
18 | }
19 |
--------------------------------------------------------------------------------
/Example/Assets.xcassets/loading.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icons8-loading-filled-50.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Sources/Extensions/UIEdgeInsets+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIEdgeInsets+Extension.swift
3 | // Example
4 | //
5 | // Created by Gwangbeom on 2018. 10. 1..
6 | // Copyright © 2018년 Interactive. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIEdgeInsets {
12 |
13 | static var safeAreaInsets: UIEdgeInsets {
14 | guard #available(iOS 11.0, *) else {
15 | return .zero
16 | }
17 | return UIApplication.shared.delegate?.window?.value?.safeAreaInsets ?? .zero
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/Layout/SheetLayoutElement.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LayoutElement.swift
3 | // Example
4 | //
5 | // Created by Gwangbeom on 2018. 10. 1..
6 | // Copyright © 2018년 Interactive. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /**ㅇ
12 | ContentsView layout elements
13 | */
14 | public enum SheetLayoutElement: String {
15 | case header
16 | case footer
17 | case cell
18 | case sectionHeader
19 | case sectionFooter
20 |
21 | public var id: String {
22 | return self.rawValue
23 | }
24 |
25 | public var kind: String {
26 | return "Kind\(self.rawValue.capitalized)"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Tests/SheetTests/SheetTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SheetTests.swift
3 | // Interactive
4 | //
5 | // Created by gwangbeom on {TODAY}.
6 | // Copyright © 2018 Interactive. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import XCTest
11 | import Sheet
12 |
13 | class SheetTests: XCTestCase {
14 | func testExample() {
15 | // This is an example of a functional test case.
16 | // Use XCTAssert and related functions to verify your tests produce the correct results.
17 | //// XCTAssertEqual(Sheet().text, "Hello, World!")
18 | }
19 |
20 | static var allTests = [
21 | ("testExample", testExample),
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/Sheet.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "Sheet"
3 | s.version = "0.7.0"
4 | s.swift_version = '4.2'
5 | s.summary = "Navigationable Action Sheet"
6 | s.description = "💦 Navigable custom action sheet like Flipboard"
7 | s.homepage = "https://github.com/ParkGwangBeom/Sheet"
8 | s.license = { :type => "MIT", :file => "LICENSE" }
9 | s.author = { "gwangbeom" => "battlerhkqo@naver.com" }
10 | s.ios.deployment_target = "9.0"
11 | s.source = { :git => "https://github.com/ParkGwangBeom/Sheet.git", :tag => s.version.to_s }
12 | s.source_files = "Sources/**/*"
13 | s.frameworks = "Foundation"
14 | end
15 |
--------------------------------------------------------------------------------
/Configs/SheetTests.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/XIB/TitleHeaderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TitleHeaderView.swift
3 | // Sheeeeeeeeet
4 | //
5 | // Created by Gwangbeom on 2018. 10. 1..
6 | // Copyright © 2018년 GwangBeom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Sheet
11 |
12 | class TitleHeaderView: UICollectionReusableView {
13 | @IBOutlet weak var titleLabel: UILabel!
14 | @IBOutlet weak var backgroundView: UIView!
15 |
16 | override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
17 | guard let attributes = layoutAttributes as? SheetLayoutAttributes else { return }
18 |
19 | UIView.animate(withDuration: 0.1, delay: 0, options: [.curveLinear, .beginFromCurrentState, .allowUserInteraction], animations: {
20 | self.backgroundView.alpha = attributes.contentOffset.y < 0 ? 0 : 1
21 | }, completion: nil)
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Sources/Extensions/Notification+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Notification+Extension.swift
3 | // Example
4 | //
5 | // Created by Gwangbeom on 2018. 10. 1..
6 | // Copyright © 2018년 Interactive. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension Notification {
12 |
13 | func keyboardAnimation(_ animations: @escaping (CGSize) -> Void, completion: @escaping (Bool, CGSize) -> Void) {
14 | let duration = userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval ?? 0
15 | let curve = userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt ?? 0
16 | let keyboardRect = userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect ?? .zero
17 |
18 | UIView.animate(withDuration: duration, delay: 0, options: UIView.AnimationOptions(rawValue: curve), animations: {
19 | animations(keyboardRect.size)
20 | }, completion: { flag in
21 | completion(flag, keyboardRect.size)
22 | })
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/Layout/SheetDecorationView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SheetDecorationView.swift
3 | // Sheeeeeeeeet
4 | //
5 | // Created by Gwangbeom on 2018. 9. 29..
6 | // Copyright © 2018년 GwangBeom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SheetDecorationView : UICollectionReusableView {
12 |
13 | static var KIND = "SheetDecorationView"
14 | static var REUSE_ID = "SheetDecorationView"
15 |
16 | override var reuseIdentifier: String? {
17 | return SheetDecorationView.REUSE_ID
18 | }
19 |
20 | override init(frame: CGRect) {
21 | super.init(frame: frame)
22 | prepare()
23 | }
24 |
25 | required init(coder aDecoder: NSCoder) {
26 | super.init(coder: aDecoder)!
27 | prepare()
28 | }
29 |
30 | private func prepare() {
31 | clipsToBounds = true
32 | backgroundColor = SheetManager.shared.options.sheetBackgroundColor
33 | layer.cornerRadius = SheetManager.shared.options.cornerRadius
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Configs/Sheet.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 | $(MARKETING_VERSION)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSHumanReadableCopyright
24 | Copyright © 2018 gwangbeom. All rights reserved.
25 | NSPrincipalClass
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:4.0
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "Sheet",
8 | products: [
9 | // Products define the executables and libraries produced by a package, and make them visible to other packages.
10 | .library(
11 | name: "Sheet",
12 | targets: ["Sheet"]),
13 | ],
14 | dependencies: [
15 | // Dependencies declare other packages that this package depends on.
16 | // .package(url: /* package url */, from: "1.0.0"),
17 | ],
18 | targets: [
19 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
20 | // Targets can depend on other targets in this package, and on products in packages which this package depends on.
21 | .target(
22 | name: "Sheet",
23 | dependencies: []),
24 | .testTarget(
25 | name: "SheetTests",
26 | dependencies: ["Sheet"]),
27 | ]
28 | )
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 gwangbeom
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/Sources/Layout/SheetLayoutAttributes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SheetLayoutAttributes.swift
3 | // Sheeeeeeeeet
4 | //
5 | // Created by Gwangbeom on 2018. 10. 1..
6 | // Copyright © 2018년 GwangBeom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | public class SheetLayoutAttributes: UICollectionViewLayoutAttributes {
12 |
13 | public var initialOrigin: CGPoint = .zero
14 | public var contentOffset: CGPoint = .zero
15 |
16 | public override func copy(with zone: NSZone?) -> Any {
17 | guard let copiedAttributes = super.copy(with: zone) as? SheetLayoutAttributes else {
18 | return super.copy(with: zone)
19 | }
20 | copiedAttributes.initialOrigin = initialOrigin
21 | copiedAttributes.contentOffset = contentOffset
22 | return copiedAttributes
23 | }
24 |
25 | public override func isEqual(_ object: Any?) -> Bool {
26 | guard let otherAttributes = object as? SheetLayoutAttributes else {
27 | return false
28 | }
29 | if otherAttributes.initialOrigin != initialOrigin || otherAttributes.contentOffset != contentOffset {
30 | return false
31 | }
32 |
33 | return super.isEqual(object)
34 | }
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/Sources/Animator/SheetModalScaleAnimator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SheetModalScaleAnimator.swift
3 | // Sheeeeeeeeet
4 | //
5 | // Created by Gwangbeom on 2018. 9. 30..
6 | // Copyright © 2018년 GwangBeom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SheetModalScaleAnimator: NSObject, UIViewControllerAnimatedTransitioning {
12 |
13 | var present = true
14 |
15 | init(present: Bool) {
16 | super.init()
17 | self.present = present
18 | }
19 |
20 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
21 | return 0.4
22 | }
23 |
24 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
25 | let containerView = transitionContext.containerView
26 | let fromView = transitionContext.viewController(forKey: .from)?.view
27 |
28 | let toView = transitionContext.view(forKey: .to)
29 | toView?.frame = containerView.bounds
30 | containerView.addSubview(toView!)
31 |
32 | UIView.animate(withDuration: 0.2, animations: {
33 | fromView?.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
34 | }) { _ in
35 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Example/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | # Swift Package Manager
36 | #
37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38 | # Packages/
39 | # Package.pins
40 | .build/
41 |
42 | # CocoaPods
43 | #
44 | # We recommend against adding the Pods directory to your .gitignore. However
45 | # you should judge for yourself, the pros and cons are mentioned at:
46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
47 | #
48 | # Pods/
49 |
50 | # Carthage
51 | #
52 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
53 | # Carthage/Checkouts
54 |
55 | Carthage/Build
56 |
57 | # fastlane
58 | #
59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
60 | # screenshots whenever they are needed.
61 | # For more information about the recommended setup visit:
62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
63 |
64 | fastlane/report.xml
65 | fastlane/Preview.html
66 | fastlane/screenshots
67 | fastlane/test_output
68 |
--------------------------------------------------------------------------------
/Example/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/Sources/Layout/SheetLayoutSettings.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SheetLayoutSettings.swift
3 | // Sheeeeeeeeet
4 | //
5 | // Created by Gwangbeom on 2018. 10. 1..
6 | // Copyright © 2018년 GwangBeom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | public struct SheetLayoutSettings {
12 |
13 | var topMargin: CGFloat = 0
14 |
15 | public var minTopMargin: CGFloat = 0
16 |
17 | /// Whether the header view is stiky. Defaults to true
18 | public var isHeaderStretchy = true
19 |
20 | /// Whether the section header view is stiky. Defaults to false
21 | public var isSectionHeaderStretchy = false
22 |
23 | /// header view size
24 | public var headerSize: CGSize?
25 |
26 | /// footer view size
27 | public var footerSize: CGSize?
28 |
29 | /// cell size
30 | public var itemSize: ((IndexPath) -> CGSize)?
31 |
32 | /// section header size
33 | public var sectionHeaderSize: ((IndexPath) -> CGSize)?
34 |
35 | /// section footer size
36 | public var sectionFooterSize: ((IndexPath) -> CGSize)?
37 | }
38 |
39 | //protocol SheetLayoutDelegate: AnyObject {
40 | //
41 | // func sheetLayout(_ layout: SheetContentsLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
42 | //
43 | // func sheetLayoutHeaderSize(_ layout: SheetContentsLayout) -> CGSize?
44 | //
45 | // func sheetLayoutFooterSize(_ layout: SheetContentsLayout) -> CGSize?
46 | //
47 | // func sheetLayout(_ layout: SheetContentsLayout, referenceSizeForHeaderInSection section: Int) -> CGSize?
48 | //
49 | // func sheetLayout(_ layout: SheetContentsLayout, referenceSizeForFooterInSection section: Int) -> CGSize?
50 | //}
51 | //
52 | //extension SheetLayoutDelegate {
53 | //
54 | // func sheetLayoutHeaderSize(_ layout: SheetContentsLayout) -> CGSize? {
55 | // return nil
56 | // }
57 | //
58 | // func sheetLayoutFooterSize(_ layout: SheetContentsLayout) -> CGSize? {
59 | // return nil
60 | // }
61 | //
62 | // func sheetLayout(_ layout: SheetContentsLayout, referenceSizeForHeaderInSection section: Int) -> CGSize? {
63 | // return nil
64 | // }
65 | //
66 | // func sheetLayout(_ layout: SheetContentsLayout, referenceSizeForFooterInSection section: Int) -> CGSize? {
67 | // return nil
68 | // }
69 | //}
70 |
--------------------------------------------------------------------------------
/Example/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Example
4 | //
5 | // Created by Gwangbeom on 2018. 10. 1..
6 | // Copyright © 2018년 Interactive. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(_ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/Example/ReportViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ReportViewController.swift
3 | // Sheeeeeeeeet
4 | //
5 | // Created by Gwangbeom on 2018. 10. 1..
6 | // Copyright © 2018년 GwangBeom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Sheet
11 |
12 | class ReportViewController: SheetContentsViewController {
13 |
14 | var items: [String] = [
15 | "This is off list",
16 | "This list has intrusive advertising",
17 | "This infringes on intellectual property rights",
18 | "This is offensive or abusive",
19 | "This is sexually explicit",
20 | "This is otherwise objectionable"
21 | ]
22 |
23 | override var visibleContentsHeight: CGFloat {
24 | return 1000
25 | }
26 |
27 | override func setupSheetLayout(_ layout: SheetContentsLayout) {
28 | layout.settings.itemSize = { indexPath in
29 | let height: CGFloat = indexPath.section == 0 ? 90 : 50
30 | return CGSize(width: UIScreen.main.bounds.width, height: height)
31 | }
32 | layout.sectionInset.bottom = 15
33 | }
34 |
35 | override func numberOfSections(in collectionView: UICollectionView) -> Int {
36 | return 2
37 | }
38 |
39 | override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
40 | if section == 0 {
41 | return 1
42 | }
43 | return items.count
44 | }
45 |
46 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
47 | if indexPath.section == 0 {
48 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "title", for: indexPath)
49 | return cell
50 | }
51 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? ReportCell
52 | cell?.reportLabel.text = items[indexPath.item]
53 | return cell ?? UICollectionViewCell()
54 | }
55 |
56 | override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
57 | dismiss(animated: true)
58 | }
59 |
60 | @IBAction func tappedBackButton(_ sender: Any) {
61 | navigationController?.popViewController(animated: true)
62 | }
63 |
64 | @IBAction func tappedDoneButton(_ sender: Any) {
65 | dismiss(animated: true)
66 | }
67 | }
68 |
69 | class ReportCell: UICollectionViewCell {
70 | @IBOutlet weak var reportLabel: UILabel!
71 | }
72 |
--------------------------------------------------------------------------------
/Example/XIB/DescriptionFooterView.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/Example/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // Sheeeeeeeeet
4 | //
5 | // Created by Gwangbeom on 2018. 9. 26..
6 | // Copyright © 2018년 GwangBeom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Sheet
11 |
12 | class ViewController: UIViewController {
13 |
14 | @IBOutlet weak var collectionView: UICollectionView!
15 |
16 | @IBAction func tappedEtc(_ sender: Any) {
17 | let vc = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ListContentsViewController")
18 | let navigation = SheetNavigationController(rootViewController: vc)
19 | navigation.onDismissed = {
20 | print("✅ SheetNavigationController is dismissed")
21 | }
22 | present(navigation, animated: false, completion: nil)
23 | }
24 |
25 | override var prefersStatusBarHidden: Bool {
26 | return true
27 | }
28 | }
29 |
30 | extension ViewController: UICollectionViewDataSource {
31 |
32 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
33 | return 40
34 | }
35 |
36 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
37 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "menu", for: indexPath) as? MenuCell
38 | let red = CGFloat(arc4random() % 255) / 255
39 | let green = CGFloat(arc4random() % 255) / 255
40 | let blue = CGFloat(arc4random() % 255) / 255
41 | cell?.itemImageView.backgroundColor = UIColor(red: red, green: green, blue: blue, alpha: 1)
42 | return cell ?? UICollectionViewCell()
43 | }
44 |
45 | func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
46 | return collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "main", for: indexPath)
47 | }
48 | }
49 |
50 | extension ViewController: UICollectionViewDelegateFlowLayout {
51 |
52 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
53 | return CGSize(width: collectionView.bounds.width, height: 340)
54 | }
55 |
56 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
57 | let width = (collectionView.bounds.width - 30) / 2
58 | return CGSize(width: width, height: 200)
59 | }
60 | }
61 |
62 | class MenuCell: UICollectionViewCell {
63 |
64 | @IBOutlet weak var itemImageView: UIImageView!
65 | @IBOutlet weak var titleLabel: UILabel!
66 |
67 | override func awakeFromNib() {
68 | super.awakeFromNib()
69 | layer.cornerRadius = 10
70 | layer.borderWidth = 0.5
71 | layer.borderColor = UIColor.groupTableViewBackground.cgColor
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/Example/LoginViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoginViewController.swift
3 | // Sheeeeeeeeet
4 | //
5 | // Created by Gwangbeom on 2018. 10. 1..
6 | // Copyright © 2018년 GwangBeom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Sheet
11 |
12 | struct LoginMenu {
13 | let image: UIImage?
14 | let color: UIColor
15 | }
16 |
17 | class LoginViewController: SheetContentsViewController {
18 |
19 | var items: [LoginMenu] = [
20 | LoginMenu(image: #imageLiteral(resourceName: "twitter"), color: #colorLiteral(red: 0.2588235438, green: 0.7568627596, blue: 0.9686274529, alpha: 1)),
21 | LoginMenu(image: #imageLiteral(resourceName: "facebook"), color: #colorLiteral(red: 0.2196078449, green: 0.007843137719, blue: 0.8549019694, alpha: 1)),
22 | LoginMenu(image: #imageLiteral(resourceName: "google"), color: #colorLiteral(red: 0.9372549057, green: 0.3490196168, blue: 0.1921568662, alpha: 1)),
23 | LoginMenu(image: #imageLiteral(resourceName: "email"), color: #colorLiteral(red: 0.7450980544, green: 0.1568627506, blue: 0.07450980693, alpha: 1))
24 | ]
25 |
26 | override var visibleContentsHeight: CGFloat {
27 | return 600
28 | }
29 |
30 | override func registCollectionElement() {
31 | let nib = UINib(nibName: "DescriptionFooterView", bundle: nil)
32 | collectionView?.register(nib, forSupplementaryViewOfKind: SheetLayoutElement.footer.kind, withReuseIdentifier: SheetLayoutElement.footer.id)
33 | }
34 |
35 | override func setupSheetLayout(_ layout: SheetContentsLayout) {
36 | layout.settings.itemSize = { _ in
37 | return CGSize(width: UIScreen.main.bounds.width, height: 60)
38 | }
39 | layout.sectionInset = UIEdgeInsets(top: 35, left: 0, bottom: 15, right: 0)
40 | layout.minimumLineSpacing = 7
41 | layout.settings.footerSize = CGSize(width: UIScreen.main.bounds.width, height: 45)
42 | }
43 |
44 | override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
45 | return items.count
46 | }
47 |
48 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
49 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? LoginMenuCell
50 | let item = items[indexPath.item]
51 | cell?.loginButton.setImage(item.image, for: .normal)
52 | cell?.loginButton.backgroundColor = item.color
53 | return cell ?? UICollectionViewCell()
54 | }
55 |
56 | override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
57 | switch kind {
58 | case SheetLayoutElement.footer.kind:
59 | let footerView = collectionView.dequeueReusableSupplementaryView(
60 | ofKind: kind,
61 | withReuseIdentifier: SheetLayoutElement.footer.id,
62 | for: indexPath) as? DescriptionFooterView
63 | return footerView ?? UICollectionReusableView()
64 | default: return UICollectionReusableView()
65 | }
66 | }
67 |
68 | @IBAction func tappedLogin(_ sender: Any) {
69 | dismiss(animated: true)
70 | }
71 | }
72 |
73 | class LoginMenuCell: UICollectionViewCell {
74 | @IBOutlet weak var loginButton: UIButton!
75 |
76 | override func awakeFromNib() {
77 | super.awakeFromNib()
78 | loginButton.layer.cornerRadius = 3
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Example/CteateListViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CteateListViewController.swift
3 | // Sheeeeeeeeet
4 | //
5 | // Created by Gwangbeom on 2018. 10. 1..
6 | // Copyright © 2018년 GwangBeom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Sheet
11 |
12 | class CteateListViewController: SheetContentsViewController {
13 |
14 | @IBOutlet weak var customToolBar: UIView!
15 |
16 | override var visibleContentsHeight: CGFloat {
17 | return 370
18 | }
19 |
20 | override var sheetToolBar: UIView? {
21 | return customToolBar
22 | }
23 |
24 | override func viewDidLoad() {
25 | super.viewDidLoad()
26 | registNotifications()
27 |
28 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
29 | let cell = self.collectionView?.cellForItem(at: IndexPath(row: 0, section: 0)) as? CreateCell
30 | cell?.titleTextField.becomeFirstResponder()
31 | }
32 | }
33 |
34 | deinit {
35 | NotificationCenter.default.removeObserver(self)
36 | }
37 |
38 | override func setupSheetLayout(_ layout: SheetContentsLayout) {
39 | layout.settings.itemSize = { _ in
40 | return CGSize(width: UIScreen.main.bounds.width, height: 260)
41 | }
42 | }
43 |
44 | override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
45 | return 1
46 | }
47 |
48 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
49 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
50 | return cell
51 | }
52 |
53 | @IBAction func tappedBackButton(_ sender: Any) {
54 | navigationController?.popViewController(animated: true)
55 | }
56 |
57 | @IBAction func tappedDoneButton(_ sender: Any) {
58 | dismiss(animated: true)
59 | }
60 | }
61 |
62 | extension CteateListViewController {
63 |
64 | func registNotifications() {
65 | NotificationCenter.default.addObserver(self, selector: #selector(willShowKeyboard), name: UIResponder.keyboardWillShowNotification, object: nil)
66 | NotificationCenter.default.addObserver(self, selector: #selector(willHideKeyboard), name: UIResponder.keyboardWillHideNotification, object: nil)
67 | }
68 |
69 | @objc
70 | func willShowKeyboard(notification: Notification) {
71 | notification.keyboardAnimation({ [weak self] size in
72 | self?.collectionView?.frame.origin.y -= size.height
73 | self?.sheetNavigationController?.toolBarBottomConstraint?.constant -= size.height
74 | self?.sheetNavigationController?.toolBarHeightConstraint?.constant = SheetManager.shared.options.sheetToolBarHeight
75 | self?.sheetNavigationController?.view.layoutIfNeeded()
76 | }, completion: nil)
77 | }
78 |
79 | @objc
80 | func willHideKeyboard(notification: Notification) {
81 | notification.keyboardAnimation({ [weak self] size in
82 | self?.collectionView?.frame.origin.y += size.height
83 | self?.sheetNavigationController?.toolBarBottomConstraint?.constant += size.height
84 | self?.sheetNavigationController?.toolBarHeightConstraint?.constant = self?.collectionView?.contentInset.bottom ?? 0
85 | self?.sheetNavigationController?.view.layoutIfNeeded()
86 | }, completion: nil)
87 | }
88 | }
89 |
90 | class CreateCell: UICollectionViewCell {
91 | @IBOutlet weak var titleTextField: UITextField!
92 | @IBOutlet weak var descriptionTextField: UITextField!
93 | }
94 |
--------------------------------------------------------------------------------
/Example/XIB/TitleHeaderView.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 |
46 |
--------------------------------------------------------------------------------
/Sources/Manager/SheetOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SheetOptions.swift
3 | // Sheeeeeeeeet
4 | //
5 | // Created by Gwangbeom on 2018. 9. 28..
6 | // Copyright © 2018년 GwangBeom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | public enum SheetPresentTransitionType {
12 |
13 | case none
14 |
15 | case scale
16 |
17 | public func animator(present: Bool) -> UIViewControllerAnimatedTransitioning? {
18 | switch self {
19 | case .none: return nil
20 | case .scale: return SheetModalScaleAnimator(present: present)
21 | }
22 | }
23 | }
24 |
25 | public struct ToolBarItem {
26 |
27 | public init() { }
28 |
29 | /// Default Sheet ToolBar font
30 | public var font: UIFont = .systemFont(ofSize: 15)
31 |
32 | /// Default Sheet ToolBar title color
33 | public var titleColor: UIColor = .white
34 |
35 | /// Default Sheet ToolBar line hidden property
36 | public var isLineHidden: Bool = false
37 |
38 | /// Default Sheet ToolBar line color
39 | public var lineColor: UIColor = #colorLiteral(red: 0.1176470588, green: 0.1176470588, blue: 0.1176470588, alpha: 1)
40 |
41 | /// Default Sheet ToolBar `Close` Title
42 | public var defaultCloseTitle: String = "Close"
43 |
44 | /// Default Sheet ToolBar `Back` Title
45 | public var defaultBackTitle: String = "Back"
46 | }
47 |
48 | public struct SheetOptions {
49 |
50 | public init() { }
51 |
52 | // Button
53 | /// Sheet ToolBar Background Color
54 | public var defaultToolBarBackgroundColor: UIColor = .black
55 |
56 | /// Sheet ToolBar Item
57 | public var defaultToolBarItem = ToolBarItem()
58 |
59 | /// Sheet ToolBar height. Defaults to 50
60 | public var sheetToolBarHeight: CGFloat = 50
61 |
62 | /// Sheet ToolBar hide property. Defaults to false
63 | public var isToolBarHidden = false
64 |
65 | // Sheet
66 | /// Sheet top corner radius. Defaults to 0
67 | public var cornerRadius: CGFloat = 0
68 |
69 | /// Sheet visible contents height. If contentSize height is less than defaultVisibleContentHeight, contentSize height is applied. Defaults to 240
70 | public var defaultVisibleContentHeight: CGFloat = 240
71 |
72 | // etc
73 | /// Sheet NavigationController present transition style. Defaults to .scale
74 | public var presentTransitionType: SheetPresentTransitionType = .scale
75 |
76 | /// sheet dimming view background color
77 | public var dimmingViewBackgroundColor = UIColor.black.withAlphaComponent(0.3)
78 |
79 | /// sheet background color
80 | public var sheetBackgroundColor: UIColor = .white
81 | }
82 |
83 | public struct SheetAnimationOption {
84 |
85 | public init() { }
86 |
87 | public var presentAnimationItem = AnimationItem()
88 |
89 | public var pushAnimationItem = AnimationItem(duration: 0.5, springDumping: 0.8, initialSpringVelocity: 1)
90 | }
91 |
92 | public struct AnimationItem {
93 |
94 | public init(duration: TimeInterval = 0.6, springDumping: CGFloat = 0.8, initialSpringVelocity: CGFloat = 1, options: UIView.AnimationOptions = [.curveEaseOut]) {
95 | self.duration = duration
96 | self.springDumping = springDumping
97 | self.initialSpringVelocity = initialSpringVelocity
98 | self.options = options
99 | }
100 |
101 | /// sheet present animation duration. Defaults to 0.6
102 | public var duration: TimeInterval = 0.6
103 |
104 | /// sheet present animation spring dumping value. Defaults to 0.8
105 | public var springDumping: CGFloat = 0.8
106 |
107 | /// sheet present animation initial spring velocity. Defaults to 1
108 | public var initialSpringVelocity: CGFloat = 1
109 |
110 | /// sheet present animation options.
111 | public var options: UIView.AnimationOptions = [.curveEaseOut]
112 | }
113 |
--------------------------------------------------------------------------------
/Sheet.xcodeproj/xcshareddata/xcschemes/Sheet-iOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
34 |
40 |
41 |
42 |
43 |
44 |
50 |
51 |
52 |
53 |
54 |
55 |
65 |
66 |
72 |
73 |
74 |
75 |
76 |
77 |
83 |
84 |
90 |
91 |
92 |
93 |
95 |
96 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/Example/MoveItemViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MoveItemViewController.swift
3 | // Example
4 | //
5 | // Created by Gwangbeom on 2018. 10. 1..
6 | // Copyright © 2018년 Interactive. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Sheet
11 |
12 | class MoveItemViewController: SheetContentsViewController {
13 |
14 | var items: [UIColor] = [
15 | #colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1), #colorLiteral(red: 0.09019608051, green: 0, blue: 0.3019607961, alpha: 1), #colorLiteral(red: 0.3098039329, green: 0.2039215714, blue: 0.03921568766, alpha: 1), #colorLiteral(red: 0.9098039269, green: 0.4784313738, blue: 0.6431372762, alpha: 1), #colorLiteral(red: 0.721568644, green: 0.8862745166, blue: 0.5921568871, alpha: 1), #colorLiteral(red: 0.2745098174, green: 0.4862745106, blue: 0.1411764771, alpha: 1)
16 | ]
17 |
18 | override func viewDidLoad() {
19 | super.viewDidLoad()
20 | let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPressGesture))
21 | collectionView?.addGestureRecognizer(longPressGesture)
22 | }
23 |
24 | @objc
25 | func handleLongPressGesture(recognizer: UILongPressGestureRecognizer) {
26 | switch recognizer.state {
27 | case .began:
28 | guard let selectedIndexPath = collectionView?.indexPathForItem(at: recognizer.location(in: collectionView)) else {
29 | break
30 | }
31 | collectionView?.beginInteractiveMovementForItem(at: selectedIndexPath)
32 | case .changed:
33 | collectionView?.updateInteractiveMovementTargetPosition(recognizer.location(in: recognizer.view))
34 | case .ended:
35 | collectionView?.endInteractiveMovement()
36 | default:
37 | collectionView?.cancelInteractiveMovement()
38 | }
39 | }
40 |
41 | override func registCollectionElement() {
42 | let nib = UINib(nibName: "TitleHeaderView", bundle: nil)
43 | collectionView?.register(nib, forSupplementaryViewOfKind: SheetLayoutElement.header.kind, withReuseIdentifier: SheetLayoutElement.header.id)
44 | }
45 |
46 | override func setupSheetLayout(_ layout: SheetContentsLayout) {
47 | layout.settings.itemSize = { indexPath in
48 | let width = (UIScreen.main.bounds.width - 30) / 2
49 | return CGSize(width: width, height: width)
50 | }
51 | layout.settings.headerSize = CGSize(width: UIScreen.main.bounds.width, height: 60)
52 | layout.minimumLineSpacing = 10
53 | layout.minimumInteritemSpacing = 10
54 | layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 20, right: 10)
55 | }
56 |
57 | override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
58 | return items.count
59 | }
60 |
61 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
62 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
63 | cell.contentView.backgroundColor = items[indexPath.item]
64 | return cell
65 | }
66 |
67 | override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
68 | switch kind {
69 | case SheetLayoutElement.header.kind:
70 | let headerView = collectionView.dequeueReusableSupplementaryView(
71 | ofKind: kind,
72 | withReuseIdentifier: SheetLayoutElement.header.id,
73 | for: indexPath) as? TitleHeaderView
74 | headerView?.titleLabel.text = "MOVE ITEM"
75 | return headerView ?? UICollectionReusableView()
76 | default: return UICollectionReusableView()
77 | }
78 | }
79 |
80 | override func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool {
81 | return true
82 | }
83 |
84 | override func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
85 |
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/Example/WriteViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WriteViewController.swift
3 | // Sheeeeeeeeet
4 | //
5 | // Created by Gwangbeom on 2018. 9. 30..
6 | // Copyright © 2018년 GwangBeom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Sheet
11 |
12 | class WriteViewController: SheetContentsViewController {
13 |
14 | @IBOutlet weak var customToolBar: UIView!
15 |
16 | override var visibleContentsHeight: CGFloat {
17 | return 370
18 | }
19 |
20 | override var sheetToolBar: UIView? {
21 | return customToolBar
22 | }
23 |
24 | override func viewDidLoad() {
25 | super.viewDidLoad()
26 | registNotifications()
27 | }
28 |
29 | deinit {
30 | NotificationCenter.default.removeObserver(self)
31 | }
32 |
33 | override func setupSheetLayout(_ layout: SheetContentsLayout) {
34 | layout.settings.itemSize = { _ in
35 | return CGSize(width: UIScreen.main.bounds.width, height: 220)
36 | }
37 | }
38 |
39 | override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
40 | return 1
41 | }
42 |
43 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
44 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
45 | return cell
46 | }
47 |
48 | @IBAction func tappedBackButton(_ sender: Any) {
49 | navigationController?.popViewController(animated: true)
50 | }
51 |
52 | @IBAction func tappedDoneButton(_ sender: Any) {
53 | dismiss(animated: true)
54 | }
55 | }
56 |
57 | extension WriteViewController {
58 |
59 | func registNotifications() {
60 | NotificationCenter.default.addObserver(self, selector: #selector(willShowKeyboard), name: UIResponder.keyboardWillShowNotification, object: nil)
61 | NotificationCenter.default.addObserver(self, selector: #selector(willHideKeyboard), name: UIResponder.keyboardWillHideNotification, object: nil)
62 | }
63 |
64 | @objc
65 | func willShowKeyboard(notification: Notification) {
66 | notification.keyboardAnimation({ [weak self] size in
67 | self?.collectionView?.frame.origin.y = -size.height
68 | self?.sheetNavigationController?.toolBarBottomConstraint?.constant -= size.height
69 | self?.sheetNavigationController?.toolBarHeightConstraint?.constant = SheetManager.shared.options.sheetToolBarHeight
70 | self?.sheetNavigationController?.view.layoutIfNeeded()
71 | }, completion: nil)
72 | }
73 |
74 | @objc
75 | func willHideKeyboard(notification: Notification) {
76 | notification.keyboardAnimation({ [weak self] size in
77 | self?.collectionView?.frame.origin.y += size.height
78 | self?.sheetNavigationController?.toolBarBottomConstraint?.constant += size.height
79 | self?.sheetNavigationController?.toolBarHeightConstraint?.constant = self?.collectionView?.contentInset.bottom ?? 0
80 | self?.sheetNavigationController?.view.layoutIfNeeded()
81 | }, completion: nil)
82 | }
83 | }
84 |
85 | class WriteCell: UICollectionViewCell {
86 | @IBOutlet weak var textField: UITextField!
87 | @IBOutlet weak var userImageView: UIImageView!
88 |
89 | override func awakeFromNib() {
90 | super.awakeFromNib()
91 | userImageView.layer.cornerRadius = 35 / 2
92 | }
93 | }
94 |
95 | extension Notification {
96 |
97 | func keyboardAnimation(_ animations: @escaping (CGSize) -> Void, completion: ((Bool, CGSize) -> Void)?) {
98 | let duration = userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval ?? 0
99 | let curve = userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt ?? 0
100 | let keyboardRect = userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect ?? .zero
101 |
102 | UIView.animate(withDuration: duration, delay: 0, options: UIView.AnimationOptions(rawValue: curve), animations: {
103 | animations(keyboardRect.size)
104 | }, completion: { flag in
105 | completion?(flag, keyboardRect.size)
106 | })
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/Example/ListContentsViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListContentsViewController.swift
3 | // Sheeeeeeeeet
4 | //
5 | // Created by Gwangbeom on 2018. 9. 29..
6 | // Copyright © 2018년 GwangBeom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Sheet
11 |
12 | struct ListItem {
13 | let image: UIImage?
14 | let title: String
15 | }
16 |
17 | class ListContentsViewController: SheetContentsViewController {
18 |
19 | var items: [ListItem] = [
20 | ListItem(image: #imageLiteral(resourceName: "like"), title: "Like"),
21 | ListItem(image: #imageLiteral(resourceName: "add"), title: "Add to mylist"),
22 | ListItem(image: #imageLiteral(resourceName: "send"), title: "Send to Firends"),
23 | ListItem(image: #imageLiteral(resourceName: "report"), title: "Report"),
24 | ListItem(image: #imageLiteral(resourceName: "move"), title: "Move"),
25 | ListItem(image: #imageLiteral(resourceName: "login"), title: "Login"),
26 | ListItem(image: #imageLiteral(resourceName: "loading"), title: "Loading")
27 | ]
28 |
29 | override var visibleContentsHeight: CGFloat {
30 | return 320
31 | }
32 |
33 | override func viewDidLoad() {
34 | super.viewDidLoad()
35 |
36 | // default toolbar custom
37 | // (sheetToolBar as? UIButton)?.setTitleColor(.green, for: .normal)
38 | }
39 |
40 | override func setupSheetLayout(_ layout: SheetContentsLayout) {
41 | layout.settings.itemSize = { _ in
42 | return CGSize(width: UIScreen.main.bounds.width, height: 55)
43 | }
44 | layout.sectionInset = UIEdgeInsets(top: 15, left: 0, bottom: 15, right: 0)
45 | }
46 |
47 | override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
48 | return items.count
49 | }
50 |
51 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
52 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? ListCell
53 | let item = items[indexPath.item]
54 | cell?.titleLabel.text = item.title
55 | cell?.itemImageView.image = item.image
56 | return cell ?? UICollectionViewCell()
57 | }
58 |
59 | override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
60 | if indexPath.item == 1 {
61 | let vc = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "FullViewController")
62 | navigationController?.pushViewController(vc, animated: true)
63 | } else if indexPath.item == 2 {
64 | let vc = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SendViewController")
65 | navigationController?.pushViewController(vc, animated: true)
66 | } else if indexPath.item == 3 {
67 | let vc = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ReportViewController")
68 | navigationController?.pushViewController(vc, animated: true)
69 | } else if indexPath.item == 4 {
70 | let vc = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MoveItemViewController")
71 | navigationController?.pushViewController(vc, animated: true)
72 | } else if indexPath.item == 5 {
73 | let vc = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LoginViewController")
74 | navigationController?.pushViewController(vc, animated: true)
75 | } else if indexPath.item == 6 {
76 | let vc = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LoadingViewController")
77 | navigationController?.pushViewController(vc, animated: true)
78 | } else {
79 | let item = items[indexPath.item]
80 | dismiss(animated: true) {
81 | let alertView = UIAlertView(title: nil, message: "Tapped \(item.title)", delegate: nil, cancelButtonTitle: "Done")
82 | alertView.show()
83 | }
84 | }
85 | }
86 | }
87 |
88 | class ListCell: UICollectionViewCell {
89 | @IBOutlet weak var titleLabel: UILabel!
90 | @IBOutlet weak var itemImageView: UIImageView!
91 | }
92 |
--------------------------------------------------------------------------------
/Sources/Animator/SheetFadeAnimator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SheetFadeAnimator.swift
3 | // Sheeeeeeeeet
4 | //
5 | // Created by Gwangbeom on 2018. 9. 26..
6 | // Copyright © 2018년 GwangBeom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SheetFadeAnimator: NSObject, UIViewControllerAnimatedTransitioning {
12 |
13 | var isPush: Bool = true
14 | var onReady: (() -> Void)?
15 | var onComplete: (() -> Void)?
16 |
17 | private var options: SheetOptions {
18 | return SheetManager.shared.options
19 | }
20 |
21 | private var animationOption: SheetAnimationOption {
22 | return SheetManager.shared.animationOption
23 | }
24 |
25 | private var toTopMargin: CGFloat = 0
26 | private var fromTopMargin: CGFloat = 0
27 |
28 | init(to: CGFloat, from: CGFloat) {
29 | super.init()
30 | toTopMargin = to
31 | fromTopMargin = from
32 | }
33 |
34 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
35 | return animationOption.pushAnimationItem.duration
36 | }
37 |
38 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
39 | let containerView = transitionContext.containerView
40 | let fromViewController = transitionContext.viewController(forKey: .from)
41 | let toViewController = transitionContext.viewController(forKey: .to)
42 |
43 | let fromContainer = fromViewController?.view
44 |
45 | let fromContent = fromViewController as? SheetContent
46 | let toContent = toViewController as? SheetContent
47 |
48 | let backgroundView = UIView()
49 | backgroundView.backgroundColor = SheetManager.shared.options.sheetBackgroundColor
50 |
51 | backgroundView.layer.cornerRadius = options.cornerRadius
52 | backgroundView.frame = CGRect(x: 0, y: fromTopMargin, width: containerView.bounds.width, height: containerView.bounds.height)
53 | containerView.insertSubview(backgroundView, at: 0)
54 |
55 | let toView = transitionContext.view(forKey: .to) ?? UIView()
56 | toView.alpha = 0
57 | containerView.addSubview(toView)
58 |
59 | let diff = fromTopMargin - toTopMargin
60 |
61 | var toLayoutTopMargin: CGFloat = 0
62 | if let toSheetContentViewController = toViewController as? SheetContentsViewController {
63 | let toLayout = toSheetContentViewController.collectionView.collectionViewLayout as? SheetContentsLayout
64 | toLayoutTopMargin = toLayout?.settings.topMargin ?? 0
65 | toSheetContentViewController.collectionView.contentOffset.y = isPush ? -diff : toLayoutTopMargin - fromTopMargin
66 | } else {
67 | toView.transform = CGAffineTransform.init(translationX: 0, y: isPush ? diff : -toLayoutTopMargin + fromTopMargin)
68 | // toContent?.contentScrollView.contentOffset.y = isPush ? -diff : toLayoutTopMargin - fromTopMargin
69 | }
70 |
71 | // toContent?.contentScrollView.contentOffset.y = isPush ? -diff : toLayoutTopMargin - fromTopMargin
72 |
73 | onReady?()
74 |
75 | UIView.animate(withDuration: animationOption.pushAnimationItem.duration, delay: 0, usingSpringWithDamping: animationOption.pushAnimationItem.springDumping, initialSpringVelocity: animationOption.pushAnimationItem.initialSpringVelocity, options: animationOption.pushAnimationItem.options, animations: {
76 | fromContainer?.alpha = 0
77 | if let fromSheetViewController = fromViewController as? SheetContentsViewController {
78 | fromSheetViewController.collectionView.contentOffset.y += diff
79 | } else {
80 | fromViewController?.view.transform = CGAffineTransform.init(translationX: 0, y: -diff)
81 | }
82 |
83 |
84 | backgroundView.frame = CGRect(x: 0, y: self.toTopMargin, width: containerView.bounds.width, height: containerView.bounds.height)
85 |
86 | toView.alpha = 1
87 |
88 | if let toSheetViewController = toViewController as? SheetContentsViewController {
89 | toSheetViewController.collectionView.contentOffset.y = self.isPush ? 0 : toLayoutTopMargin - self.toTopMargin
90 | } else {
91 | toView.transform = .identity
92 | }
93 |
94 | }) { _ in
95 | self.onComplete?()
96 | backgroundView.removeFromSuperview()
97 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/Example/MylistViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MylistViewController.swift
3 | // Sheeeeeeeeet
4 | //
5 | // Created by Gwangbeom on 2018. 9. 29..
6 | // Copyright © 2018년 GwangBeom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Sheet
11 |
12 | struct MyListItem {
13 | let iamge: UIImage?
14 | let title: String
15 | let isPrivacy: Bool
16 | }
17 |
18 | class MylistViewController: SheetContentsViewController {
19 |
20 | var items: [MyListItem] = [
21 | MyListItem(iamge: nil, title: "Trevi", isPrivacy: true),
22 | MyListItem(iamge: nil, title: "Moomin Love", isPrivacy: false),
23 | MyListItem(iamge: nil, title: "Sheet", isPrivacy: false),
24 | MyListItem(iamge: nil, title: "Trevi", isPrivacy: true),
25 | MyListItem(iamge: nil, title: "Moomin Love", isPrivacy: false),
26 | ]
27 |
28 | override var isFullScreenContent: Bool {
29 | return true
30 | }
31 |
32 | override var isToolBarHidden: Bool {
33 | return true
34 | }
35 |
36 | override func registCollectionElement() {
37 | let nib = UINib(nibName: "TitleHeaderView", bundle: nil)
38 | collectionView?.register(nib, forSupplementaryViewOfKind: SheetLayoutElement.header.kind, withReuseIdentifier: SheetLayoutElement.header.id)
39 | }
40 |
41 | override func setupSheetLayout(_ layout: SheetContentsLayout) {
42 | layout.settings.itemSize = { indexPath in
43 | let height: CGFloat = indexPath.section == 0 ? 30 : 60
44 | return CGSize(width: UIScreen.main.bounds.width, height: height)
45 | }
46 | layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 15, right: 0)
47 | layout.settings.headerSize = CGSize(width: UIScreen.main.bounds.width, height: 60 + 44)
48 | layout.settings.isHeaderStretchy = true
49 | }
50 |
51 | override func numberOfSections(in collectionView: UICollectionView) -> Int {
52 | return 3
53 | }
54 |
55 | override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
56 | if section == 0 {
57 | return 1
58 | } else if section == 1 {
59 | return items.count
60 | } else {
61 | return 1
62 | }
63 | }
64 |
65 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
66 | if indexPath.section == 0 {
67 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "title", for: indexPath)
68 | return cell
69 | } else if indexPath.section == 1 {
70 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? MylistCell
71 | let item = items[indexPath.item]
72 | cell?.listTitleLabel.text = item.title
73 | cell?.privacyLabel.text = item.isPrivacy ? "Privacy" : "Public"
74 | return cell ?? UICollectionViewCell()
75 | }
76 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "create", for: indexPath)
77 | return cell
78 | }
79 |
80 | override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
81 | switch kind {
82 | case SheetLayoutElement.header.kind:
83 | let headerView = collectionView.dequeueReusableSupplementaryView(
84 | ofKind: kind,
85 | withReuseIdentifier: SheetLayoutElement.header.id,
86 | for: indexPath) as? TitleHeaderView
87 | headerView?.titleLabel.text = "ADD TO MYLIST"
88 | return headerView ?? UICollectionReusableView()
89 | default: return UICollectionReusableView()
90 | }
91 | }
92 |
93 | override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
94 | if indexPath.section == 1 {
95 | let vc = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "WriteViewController")
96 | navigationController?.pushViewController(vc, animated: true)
97 | } else if indexPath.section == 2 {
98 | let vc = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "CteateListViewController")
99 | navigationController?.pushViewController(vc, animated: true)
100 | }
101 | }
102 |
103 | @IBAction func tappedBackButton(_ sender: Any) {
104 | navigationController?.popViewController(animated: true)
105 | }
106 | }
107 |
108 | class MylistCell: UICollectionViewCell {
109 |
110 | @IBOutlet weak var listTitleLabel: UILabel!
111 | @IBOutlet weak var privacyLabel: UILabel!
112 | }
113 |
--------------------------------------------------------------------------------
/Example/LoadingViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoadingViewController.swift
3 | // Sheeeeeeeeet
4 | //
5 | // Created by Gwangbeom on 2018. 10. 1..
6 | // Copyright © 2018년 GwangBeom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Sheet
11 |
12 | enum LoadingState {
13 | case loading
14 | case finish
15 | }
16 |
17 | class LoadingViewController: SheetContentsViewController {
18 |
19 | var items: [String] = [
20 | "Interactive (Organization)",
21 | "Windless",
22 | "TransitionableTab",
23 | "ISPageControl"
24 | ]
25 |
26 | var loaingState: LoadingState = .loading {
27 | didSet {
28 | let cell = collectionView?.cellForItem(at: IndexPath(item: 0, section: 0)) as? LaodingCell
29 | switch loaingState {
30 | case .loading:
31 | cell?.indicator.startAnimating()
32 | case .finish:
33 | cell?.indicator.stopAnimating()
34 | reload()
35 | }
36 | }
37 | }
38 |
39 | override func viewDidLoad() {
40 | super.viewDidLoad()
41 |
42 | self.loaingState = .loading
43 | DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
44 | self.loaingState = .finish
45 | }
46 | }
47 |
48 | override func registCollectionElement() {
49 | let nib = UINib(nibName: "TitleHeaderView", bundle: nil)
50 | collectionView?.register(nib, forSupplementaryViewOfKind: SheetLayoutElement.header.kind, withReuseIdentifier: SheetLayoutElement.header.id)
51 | }
52 |
53 | override func setupSheetLayout(_ layout: SheetContentsLayout) {
54 | layout.settings.itemSize = { [weak self] _ in
55 | let height: CGFloat = self?.loaingState == .loading ? 150 : 61
56 | return CGSize(width: UIScreen.main.bounds.width, height: height)
57 | }
58 | layout.settings.sectionHeaderSize = { _ in
59 | return CGSize(width: UIScreen.main.bounds.width, height: 30)
60 | }
61 | layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 30, right: 0)
62 | layout.settings.headerSize = CGSize(width: UIScreen.main.bounds.width, height: 60)
63 | }
64 |
65 | override var visibleContentsHeight: CGFloat {
66 | return 350 + 41
67 | }
68 |
69 | override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
70 | return loaingState == .loading ? 1 : items.count
71 | }
72 |
73 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
74 | if loaingState == .loading {
75 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? LaodingCell
76 | return cell ?? UICollectionViewCell()
77 | } else {
78 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "text", for: indexPath) as? ReportCell
79 | cell?.reportLabel.text = items[indexPath.item]
80 | return cell ?? UICollectionViewCell()
81 | }
82 | }
83 |
84 | override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
85 | switch kind {
86 | case UICollectionView.elementKindSectionHeader:
87 | let headerView = collectionView.dequeueReusableSupplementaryView(
88 | ofKind: kind,
89 | withReuseIdentifier: "header",
90 | for: indexPath)
91 | return headerView
92 | case SheetLayoutElement.header.kind:
93 | let headerView = collectionView.dequeueReusableSupplementaryView(
94 | ofKind: kind,
95 | withReuseIdentifier: SheetLayoutElement.header.id,
96 | for: indexPath) as? TitleHeaderView
97 | headerView?.titleLabel.text = "LOADING"
98 | return headerView ?? UICollectionReusableView()
99 | default: return UICollectionReusableView()
100 | }
101 | }
102 |
103 | override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
104 | if indexPath.item == 0 {
105 | let url = URL(string: "https://github.com/Interactive-Studio")
106 | UIApplication.shared.open(url!, options: [:], completionHandler: nil)
107 | } else if indexPath.item == 1 {
108 | let url = URL(string: "https://github.com/Interactive-Studio/Windless")
109 | UIApplication.shared.open(url!, options: [:], completionHandler: nil)
110 |
111 | } else if indexPath.item == 2 {
112 | let url = URL(string: "https://github.com/Interactive-Studio/TransitionableTab")
113 | UIApplication.shared.open(url!, options: [:], completionHandler: nil)
114 | } else {
115 | let url = URL(string: "https://github.com/Interactive-Studio/ISPageControl")
116 | UIApplication.shared.open(url!, options: [:], completionHandler: nil)
117 | }
118 | }
119 | }
120 |
121 | class LaodingCell: UICollectionViewCell {
122 | @IBOutlet weak var indicator: UIActivityIndicatorView!
123 | }
124 |
--------------------------------------------------------------------------------
/Example/SendViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SendViewController.swift
3 | // Sheeeeeeeeet
4 | //
5 | // Created by Gwangbeom on 2018. 9. 29..
6 | // Copyright © 2018년 GwangBeom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Sheet
11 |
12 | struct Frined {
13 | let image: UIImage?
14 | let name: String
15 | }
16 |
17 | class SendViewController: SheetContentsViewController, FristsListContainerCellDelegate {
18 |
19 | var frinesLsit: [Frined] = [
20 | Frined(image: nil, name: "John"),
21 | Frined(image: nil, name: "Mark"),
22 | Frined(image: nil, name: "Steve"),
23 | Frined(image: nil, name: "Apple")
24 | ]
25 |
26 | @IBOutlet weak var customToolBar: UIView!
27 | @IBOutlet weak var doneButton: UIButton!
28 |
29 | override var visibleContentsHeight: CGFloat {
30 | return 500
31 | }
32 |
33 | override var sheetToolBar: UIView? {
34 | return customToolBar
35 | }
36 |
37 | override func registCollectionElement() {
38 | let nib = UINib(nibName: "TitleHeaderView", bundle: nil)
39 | collectionView?.register(nib, forSupplementaryViewOfKind: SheetLayoutElement.header.kind, withReuseIdentifier: SheetLayoutElement.header.id)
40 | }
41 |
42 | override func setupSheetLayout(_ layout: SheetContentsLayout) {
43 | layout.settings.itemSize = { indexPath in
44 | let height: CGFloat = indexPath.item == 0 ? 30 : 170
45 | return CGSize(width: UIScreen.main.bounds.width, height: height)
46 | }
47 | layout.settings.headerSize = CGSize(width: UIScreen.main.bounds.width, height: 60)
48 | layout.settings.isHeaderStretchy = true
49 | }
50 |
51 | override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
52 | return 2
53 | }
54 |
55 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
56 | if indexPath.item == 0 {
57 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "title", for: indexPath)
58 | return cell
59 | }
60 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? FristsListContainerCell
61 | cell?.frinesLsit = frinesLsit
62 | cell?.delegate = self
63 | return cell ?? UICollectionViewCell()
64 | }
65 |
66 | override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
67 | switch kind {
68 | case SheetLayoutElement.header.kind:
69 | let headerView = collectionView.dequeueReusableSupplementaryView(
70 | ofKind: kind,
71 | withReuseIdentifier: SheetLayoutElement.header.id,
72 | for: indexPath) as? TitleHeaderView
73 | headerView?.titleLabel.text = "SEND"
74 | return headerView ?? UICollectionReusableView()
75 | default: return UICollectionReusableView()
76 | }
77 | }
78 |
79 | @IBAction func tappedBackButton(_ sender: Any) {
80 | navigationController?.popViewController(animated: true)
81 | }
82 |
83 | @IBAction func tappedDone(_ sender: Any) {
84 | dismiss(animated: true)
85 | }
86 |
87 | func tappedFriend(isSelected: Bool) {
88 | controlToolBar(isShow: false, animated: true)
89 | doneButton.isEnabled = isSelected
90 | }
91 | }
92 |
93 | class FriendCell: UICollectionViewCell {
94 | @IBOutlet weak var userImageView: UIImageView!
95 | @IBOutlet weak var nameLabel: UILabel!
96 |
97 | override func awakeFromNib() {
98 | super.awakeFromNib()
99 | userImageView.layer.cornerRadius = userImageView.bounds.width / 2
100 | }
101 | }
102 |
103 | protocol FristsListContainerCellDelegate: AnyObject {
104 |
105 | func tappedFriend(isSelected: Bool)
106 | }
107 |
108 | class FristsListContainerCell: UICollectionViewCell {
109 |
110 | @IBOutlet weak var collectionView: UICollectionView!
111 | weak var delegate: FristsListContainerCellDelegate?
112 |
113 | var frinesLsit: [Frined] = [] {
114 | didSet {
115 | collectionView.reloadData()
116 | }
117 | }
118 |
119 | override func awakeFromNib() {
120 | super.awakeFromNib()
121 | collectionView.dataSource = self
122 | collectionView.delegate = self
123 | }
124 | }
125 |
126 | extension FristsListContainerCell: UICollectionViewDataSource {
127 |
128 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
129 | return frinesLsit.count
130 | }
131 |
132 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
133 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? FriendCell
134 | let friend = frinesLsit[indexPath.item]
135 | cell?.nameLabel.text = friend.name
136 | return cell ?? UICollectionViewCell()
137 | }
138 | }
139 |
140 | extension FristsListContainerCell: UICollectionViewDelegate {
141 |
142 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
143 | delegate?.tappedFriend(isSelected: true)
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | 📑 SHEET helps you easily create a wide variety of action sheets with navigation features used in the Flipboard App
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | ## Installation
40 |
41 | ### CocoaPods
42 | ```bash
43 | pod 'Sheet', '~> 0.7.0'
44 | ```
45 |
46 | ### Carthage
47 | ```bash
48 | github "ParkGwangBeom/Sheet" ~> 0.7.0
49 | ```
50 |
51 | ### Manually
52 |
53 | If you prefer not to use either of the aforementioned dependency managers, you can integrate Sheet into your project manually.
54 |
55 | ## Usage
56 |
57 | Implementing the contents of a Sheet is similar to implementing an existing UICollectionViewController.
58 | Simply make your view controller subclass of SheetContentsViewController.
59 |
60 | ```
61 | import Sheet
62 |
63 | class ViewController: SheetContentsViewController {
64 |
65 | /// Sheet visible contents height. If contentSize height is less than visibleContentsHeight, contentSize height is applied.
66 | override var visibleContentsHeight: CGFloat {
67 | return 600
68 | }
69 |
70 | /// Give CollectionView a chance to regulate Supplementray Element
71 | override func registCollectionElement() {
72 | let nib = UINib(nibName: "TitleHeaderView", bundle: nil)
73 | collectionView?.register(nib, forSupplementaryViewOfKind: SheetLayoutElement.header.kind, withReuseIdentifier: SheetLayoutElement.header.id)
74 | }
75 |
76 | /// Provide an opportunity to set default settings for collectionview custom layout
77 | override func setupSheetLayout(_ layout: SheetContentsLayout) {
78 | layout.settings.itemSize = { indexPath in
79 | let height: CGFloat = indexPath.section == 0 ? 30 : 60
80 | return CGSize(width: UIScreen.main.bounds.width, height: height)
81 | }
82 | layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 15, right: 0)
83 | layout.settings.headerSize = CGSize(width: UIScreen.main.bounds.width, height: 60)
84 | layout.settings.isHeaderStretchy = true
85 | }
86 |
87 | override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
88 | return 10
89 | }
90 |
91 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
92 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
93 | ...
94 | return cell
95 | }
96 |
97 | override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
98 | ...
99 | }
100 |
101 | ...
102 | }
103 | ```
104 |
105 | You can use the custom action sheet transition using the default api provided by UIKit such as present, push, pop.
106 | > 🔥 **However, do not use the dismiss of the NavigationController, but use the close (duration: completion :) function.**
107 | ```
108 | // present
109 | let contentsViewController = ViewController()
110 | let sheetNavigation = SheetNavigationController(rootViewController: contentsViewController)
111 | present(sheetNavigation, animated: true, completion: nil)
112 |
113 | // push
114 | let nextContentsViewController = NextContentsViewController()
115 | navigationController?.pushViewController(nextContentsViewController, animated: true)
116 |
117 | // pop
118 | navigationController?.popViewController(animated: true)
119 | ```
120 | **See the Example project for more details.**
121 |
122 |
123 | ## Layout
124 | **Sheet** basically has Navigation structure. All children should inherit from **SheetContentsViewController**. SheetContentsViewController inherits **UICollectionViewController** by default, and its layout is like the following image.
125 |
126 |
127 |
128 |
129 |
130 | **Please refer to Example Code for detailed setting of image layout.**
131 |
132 |
133 | ## Advanced
134 | Easily customizable by SheetContents.
135 |
136 | ### Options
137 |
138 | | Property | Type | Default Value |
139 | | -------- | --- | --- |
140 | | `defaultToolBarBackgroundColor` | `UIColor` | `.black` |
141 | | `sheetToolBarHeight` | `CGFloat`| `50`|
142 | | `isSheetToolBarHidden` | `Bool` | `false`|
143 | | `cornerRadius` | `CGFloat` | `0`|
144 | | `defaultVisibleContentHeight` | `CGFloat` | `240`|
145 | | `dimmingViewBackgroundColor` | `UIColor` | `.black.withAlphaComponent(0.3)`|
146 | | `sheetBackgroundColor` | `UIColor` | `.white`|
147 | | `presentTransitionType` | `SheetPresentTransitionType` | `.scale`|
148 |
149 | ### Layout Settings
150 |
151 | | Property | Type |
152 | | -------- | --- |
153 | | `headerSize` | `CGSize?` |
154 | | `footerSize` | `CGSize?`|
155 | | `itemSize` | `((IndexPath) -> CGSize)?` |
156 | | `sectionHeaderSize` | `((IndexPath) -> CGSize)?` |
157 | | `sectionFooterSize` | `((IndexPath) -> CGSize)?` |
158 |
159 | ### SheetContentsViewController
160 |
161 | | Property | Type |
162 | | -------- | --- |
163 | | `sheetToolBar` | `UIView` |
164 |
165 | | Method | Explanation |
166 | | -------- | --- |
167 | | `func registCollectionElement()` | `Give CollectionView a chance to regulate Supplementray Element` |
168 | | `func setupSheetLayout()` | `Provide an opportunity to set default settings for collectionview custom layout`|
169 | | `func reload()` | `Help reload CollectionView and adjust the height of the content.`|
170 | | `func close(completion: (() -> Void)? = nil)` | `Sheet Dismiss`|
171 |
172 | ### Custom ToolBar
173 |
174 | The built-in toolbar consists of a single button.
175 |
176 | | Default ToolBar |
177 | |------- |
178 | |
|
179 |
180 | Setting up a Custom ToolBar is very simple.
181 | ```
182 | sheetToolBar = CustomToolBar()
183 | ```
184 |
185 | ## Author
186 |
187 | - GwangBeom Park ([@gwangbeom](https://github.com/ParkGwangBeom))
188 |
189 | ## License
190 |
191 | Sheet is released under the MIT license. See LICENSE for details.
192 |
193 |
--------------------------------------------------------------------------------
/Sources/Controller/SheetContentsViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SheetContentsViewController.swift
3 | // Sheeeeeeeeet
4 | //
5 | // Created by Gwangbeom on 2018. 9. 29..
6 | // Copyright © 2018년 GwangBeom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | open class SheetContentsViewController: UICollectionViewController, SheetContent {
12 |
13 | private var options: SheetOptions {
14 | return SheetManager.shared.options
15 | }
16 |
17 | private var layout = SheetContentsLayout()
18 |
19 | public var topMargin: CGFloat = 0
20 |
21 | public var contentScrollView: UIScrollView {
22 | return collectionView
23 | }
24 |
25 | open var isFullScreenContent: Bool {
26 | return false
27 | }
28 |
29 | /// Sheet ToolBar hide property. Defaults to false
30 | open var isToolBarHidden: Bool {
31 | return options.isToolBarHidden
32 | }
33 |
34 | /// Sheet visible contents height. If contentSize height is less than visibleContentsHeight, contentSize height is applied.
35 | open var visibleContentsHeight: CGFloat {
36 | return SheetManager.shared.options.defaultVisibleContentHeight
37 | }
38 |
39 | open var sheetToolBar: UIView? {
40 | return nil
41 | }
42 |
43 | open override func viewDidLoad() {
44 | super.viewDidLoad()
45 | registCollectionElement()
46 | setupSheetLayout(layout)
47 | setupViews()
48 | }
49 |
50 | open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
51 | super.viewWillTransition(to: size, with: coordinator)
52 |
53 | updateTopMargin()
54 | }
55 |
56 | /// Give CollectionView a chance to regulate Supplementray Element
57 | open func registCollectionElement() {
58 |
59 | }
60 |
61 | /// Provide an opportunity to set default settings for collectionview custom layout
62 | open func setupSheetLayout(_ layout: SheetContentsLayout) {
63 |
64 | }
65 |
66 | open override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
67 | fatalError("Implement with override as required")
68 | }
69 |
70 | open override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
71 | fatalError("Implement with override as required")
72 | }
73 |
74 | open override func scrollViewDidScroll(_ scrollView: UIScrollView) {
75 | topMargin = max(layout.settings.topMargin - scrollView.contentOffset.y, 0)
76 | }
77 |
78 | open override func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) {
79 | let velocity = scrollView.panGestureRecognizer.velocity(in: nil)
80 | if scrollView.contentOffset.y <= -10 && velocity.y >= 400 {
81 | let diff = UIScreen.main.bounds.height - topMargin
82 | let duration = min(0.3, diff / velocity.y)
83 | sheetNavigationController?.close(duration: TimeInterval(duration))
84 | }
85 | }
86 |
87 | open override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
88 | if sheetNavigationController == nil {
89 | super.dismiss(animated: flag, completion: completion)
90 | } else {
91 | sheetNavigationController?.close(completion: completion)
92 | }
93 | }
94 | }
95 |
96 | extension SheetContentsViewController {
97 |
98 | public var sheetNavigationController: SheetNavigationController? {
99 | return navigationController as? SheetNavigationController
100 | }
101 |
102 | public var isRootViewController: Bool {
103 | return self == navigationController?.viewControllers.first
104 | }
105 |
106 | public func reload() {
107 | let before = topMargin
108 | setupSheetLayout(layout)
109 | collectionView?.reloadData()
110 | updateTopMargin()
111 | collectionView?.collectionViewLayout.invalidateLayout()
112 | let after = topMargin
113 | let diff = before - after
114 |
115 | collectionView?.transform = CGAffineTransform(translationX: 0, y: diff)
116 |
117 | UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 1, options: [.curveLinear], animations: {
118 | self.collectionView?.transform = .identity
119 | }, completion: nil)
120 | }
121 | }
122 |
123 | // MARK: Views
124 | private extension SheetContentsViewController {
125 |
126 | func setupViews() {
127 | view.backgroundColor = .clear
128 | setupContainerView()
129 | setupDimmingView()
130 | }
131 |
132 | func setupContainerView() {
133 | let bottomToolBarHeight: CGFloat = isToolBarHidden ? UIEdgeInsets.safeAreaInsets.bottom : SheetManager.shared.options.sheetToolBarHeight + UIEdgeInsets.safeAreaInsets.bottom
134 |
135 | collectionView?.delaysContentTouches = true
136 | collectionView?.collectionViewLayout.invalidateLayout()
137 | collectionView?.setCollectionViewLayout(layout, animated: false)
138 | collectionView?.delegate = self
139 | collectionView?.dataSource = self
140 | collectionView?.alwaysBounceVertical = true
141 | collectionView?.showsVerticalScrollIndicator = false
142 | collectionView?.contentInset.bottom = bottomToolBarHeight
143 | collectionView?.backgroundColor = .clear
144 | if #available(iOS 11.0, *) {
145 | collectionView?.contentInsetAdjustmentBehavior = .never
146 | }
147 | updateTopMargin()
148 | }
149 |
150 | func updateTopMargin() {
151 | let bottomToolBarHeight: CGFloat = isToolBarHidden ? UIEdgeInsets.safeAreaInsets.bottom : SheetManager.shared.options.sheetToolBarHeight + UIEdgeInsets.safeAreaInsets.bottom
152 | collectionView?.layoutIfNeeded()
153 |
154 | let screenHeight = UIScreen.main.bounds.height
155 | let contentHeight = collectionView?.contentSize.height ?? 0
156 | let visibleHeight = min(contentHeight - layout.settings.topMargin, visibleContentsHeight)
157 |
158 | topMargin = isFullScreenContent ? 0 : max(screenHeight - layout.settings.minTopMargin - visibleHeight - bottomToolBarHeight, 0)
159 | layout.settings.topMargin = topMargin
160 | }
161 |
162 | func setupDimmingView() {
163 | let dimmingButton = UIButton()
164 | collectionView?.insertSubview(dimmingButton, at: 0)
165 | dimmingButton.translatesAutoresizingMaskIntoConstraints = false
166 | dimmingButton.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
167 | dimmingButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
168 | dimmingButton.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
169 | dimmingButton.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
170 |
171 | dimmingButton.backgroundColor = .clear
172 | dimmingButton.addTarget(self, action: #selector(tappedBackground), for: .touchUpInside)
173 | }
174 | }
175 |
176 | // MARK: Events
177 | private extension SheetContentsViewController {
178 |
179 | @objc
180 | func tappedBackground() {
181 | sheetNavigationController?.close()
182 | }
183 |
184 | @objc
185 | func tappedDefaultButton() {
186 | if isRootViewController {
187 | sheetNavigationController?.close()
188 | } else {
189 | navigationController?.popViewController(animated: true)
190 | }
191 | }
192 | }
193 |
194 | public protocol SheetContent {
195 |
196 | var topMargin: CGFloat { get set }
197 |
198 | var isToolBarHidden: Bool { get }
199 |
200 | var visibleContentsHeight: CGFloat { get }
201 |
202 | var sheetToolBar: UIView? { get }
203 | }
204 |
205 | public extension SheetContent {
206 |
207 | var topMargin: CGFloat {
208 | get { return 0 }
209 | set {}
210 | }
211 |
212 | var isToolBarHidden: Bool { return SheetManager.shared.options.isToolBarHidden }
213 |
214 | var sheetToolBar: UIView? { return nil }
215 |
216 | var visibleContentsHeight: CGFloat { return SheetManager.shared.options.defaultVisibleContentHeight }
217 |
218 | }
219 |
220 | extension SheetContent where Self: UIViewController {
221 |
222 | public func controlToolBar(isShow: Bool, animated: Bool) {
223 | let sheetNavi = navigationController as? SheetNavigationController
224 | sheetNavi?.controlToolBar(isShow: isShow, animated: animated)
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/Sources/Layout/SheetContentsLayout.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SheetContentsLayout.swift
3 | // VibeMusic
4 | //
5 | // Created by Gwangbeom on 2018. 6. 29..
6 | // Copyright © 2018년 VIBE. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | // https://www.raywenderlich.com/527-custom-uicollectionviewlayout-tutorial-with-parallax
12 | public class SheetContentsLayout: UICollectionViewFlowLayout {
13 |
14 | public var settings = SheetLayoutSettings()
15 | private var oldBounds: CGRect = .zero
16 | private var contentHeight: CGFloat = 0
17 | private var cache: [SheetLayoutElement: [IndexPath: SheetLayoutAttributes]] = [:]
18 | private var visibleLayoutAttributes: [SheetLayoutAttributes] = []
19 |
20 | private var collectionViewHeight: CGFloat {
21 | return collectionView?.frame.height ?? 0
22 | }
23 |
24 | private var collectionViewWidth: CGFloat {
25 | return collectionView?.frame.width ?? 0
26 | }
27 |
28 | private var contentOffset: CGPoint {
29 | return collectionView?.contentOffset ?? .zero
30 | }
31 |
32 | public override var collectionViewContentSize: CGSize {
33 | return CGSize(width: collectionViewWidth, height: contentHeight)
34 | }
35 |
36 | public override init() {
37 | super.init()
38 | minimumLineSpacing = 0
39 | minimumInteritemSpacing = 0
40 | sectionInset = .zero
41 | scrollDirection = .vertical
42 | register(SheetDecorationView.self, forDecorationViewOfKind: SheetDecorationView.KIND)
43 | }
44 |
45 | public required init?(coder aDecoder: NSCoder) {
46 | fatalError("init(coder:) has not been implemented")
47 | }
48 | }
49 |
50 | extension SheetContentsLayout {
51 |
52 | public override func prepare() {
53 | guard let collectionView = collectionView else {
54 | return
55 | }
56 |
57 | prepareCache()
58 | contentHeight = settings.topMargin
59 | oldBounds = collectionView.bounds
60 |
61 | if let headerSize = settings.headerSize {
62 | let headerAttributes = SheetLayoutAttributes(forSupplementaryViewOfKind: SheetLayoutElement.header.kind, with: IndexPath(item: 0, section: 0))
63 | prepareSupplementrayElement(size: headerSize, type: .header, attributes: headerAttributes)
64 | }
65 |
66 | for section in 0..= collectionView.bounds.width {
86 | x = sectionInset.left
87 | y += itemSize.height + minimumLineSpacing
88 | }
89 |
90 | rect.size = itemSize
91 | attributes.frame = rect
92 |
93 | attributes.zIndex = 0
94 | contentHeight = attributes.frame.maxY
95 | cache[.cell]?[cellIndexPath] = attributes
96 | }
97 |
98 | if let sectionFooterSize = settings.sectionFooterSize?(IndexPath(item: 1, section: section)) {
99 | let sectionFooterAttributes = SheetLayoutAttributes(forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, with: IndexPath(item: 1, section: section))
100 | prepareSupplementrayElement(size: sectionFooterSize, type: .sectionFooter, attributes: sectionFooterAttributes)
101 | }
102 | }
103 |
104 | if let footerSize = settings.footerSize {
105 | let footerAttributes = SheetLayoutAttributes(forSupplementaryViewOfKind: SheetLayoutElement.footer.kind, with: IndexPath(item: 0, section: 0))
106 | prepareSupplementrayElement(size: footerSize, type: .footer, attributes: footerAttributes)
107 | }
108 |
109 | contentHeight += sectionInset.bottom
110 | }
111 |
112 | private func prepareCache() {
113 | cache.removeAll(keepingCapacity: true)
114 | cache[.cell] = [:]
115 | cache[.header] = [:]
116 | cache[.footer] = [:]
117 | cache[.sectionHeader] = [:]
118 | cache[.sectionFooter] = [:]
119 | }
120 |
121 | private func prepareSupplementrayElement(size: CGSize, type: SheetLayoutElement, attributes: SheetLayoutAttributes) {
122 | guard size != .zero else { return }
123 | attributes.initialOrigin = CGPoint(x: 0, y: contentHeight)
124 | attributes.frame = CGRect(origin: attributes.initialOrigin, size: size)
125 | attributes.zIndex = type == .header ? 2 : 1
126 | contentHeight += size.height
127 | cache[type]?[attributes.indexPath] = attributes
128 | }
129 | }
130 |
131 | extension SheetContentsLayout {
132 |
133 | public override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
134 | if oldBounds.size != newBounds.size {
135 | cache.removeAll(keepingCapacity: true)
136 | }
137 | return true
138 | }
139 |
140 | public override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
141 | switch elementKind {
142 | case UICollectionView.elementKindSectionHeader:
143 | return cache[.sectionHeader]?[indexPath]
144 | case UICollectionView.elementKindSectionFooter:
145 | return cache[.sectionFooter]?[indexPath]
146 | case SheetLayoutElement.header.kind:
147 | return cache[.header]?[indexPath]
148 | case SheetLayoutElement.footer.kind:
149 | return cache[.footer]?[indexPath]
150 | default:
151 | return super.layoutAttributesForSupplementaryView(ofKind: elementKind, at: indexPath)
152 | }
153 | }
154 |
155 | public override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
156 | return cache[.cell]?[indexPath]
157 | }
158 |
159 | public override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
160 | guard let collectionView = collectionView else {
161 | return nil
162 | }
163 | visibleLayoutAttributes.removeAll(keepingCapacity: true)
164 |
165 | for (type, elementInfos) in cache {
166 | for (indexPath, attributes) in elementInfos {
167 | attributes.transform = .identity
168 | updateSupplementaryViews(type, attributes: attributes, collectionView: collectionView, indexPath: indexPath)
169 | if attributes.frame.intersects(rect) {
170 | visibleLayoutAttributes.append(attributes)
171 | }
172 | }
173 | }
174 |
175 | for section in 0.. Void)?
29 |
30 | private var backgroundView: UIView?
31 | var sheetToolBarContainerView: UIView?
32 | private var topMargins: [CGFloat] = []
33 |
34 | public override init(rootViewController: UIViewController) {
35 | guard let rootViewController = rootViewController as? SheetContentsViewController else {
36 | fatalError("rootViewController is not a SheetContentsViewController")
37 | }
38 | super.init(rootViewController: rootViewController)
39 | }
40 |
41 | public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
42 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
43 | }
44 |
45 | required public init?(coder aDecoder: NSCoder) {
46 | fatalError("init(coder:) has not been implemented")
47 | }
48 |
49 | public override func viewDidLoad() {
50 | super.viewDidLoad()
51 | setupViews()
52 | }
53 |
54 |
55 | private var isFirstCall: Bool = false
56 | public override func viewWillAppear(_ animated: Bool) {
57 | super.viewWillAppear(animated)
58 |
59 | let rootViewController = viewControllers.first as? SheetContentsViewController
60 | rootViewController?.reload()
61 | let contentSize = rootViewController?.collectionView?.contentSize ?? .zero
62 | let visibleContentsHeight = rootViewController?.visibleContentsHeight ?? 0
63 | let visibleHeight = min(contentSize.height, visibleContentsHeight)
64 | rootViewController?.collectionView?.transform = CGAffineTransform(translationX: 0, y: visibleHeight + options.sheetToolBarHeight)
65 | sheetToolBarContainerView?.transform = CGAffineTransform(translationX: 0, y: SheetManager.shared.options.sheetToolBarHeight + UIEdgeInsets.safeAreaInsets.bottom)
66 |
67 |
68 | UIView.animate(withDuration: animationItem.duration, delay: 0, usingSpringWithDamping: animationItem.springDumping, initialSpringVelocity: animationItem.initialSpringVelocity, options: animationItem.options, animations: {
69 | rootViewController?.collectionView?.transform = .identity
70 | }, completion: nil)
71 |
72 | UIView.animate(withDuration: 0.3, animations: {
73 | self.backgroundView?.alpha = 1
74 | self.sheetToolBarContainerView?.transform = .identity
75 | }) { _ in
76 | self.isFirstCall = true
77 | }
78 | }
79 |
80 | public override var prefersStatusBarHidden: Bool {
81 | return presentingViewController?.prefersStatusBarHidden ?? false
82 | }
83 |
84 | public override func pushViewController(_ viewController: UIViewController, animated: Bool) {
85 | guard viewController is SheetContent else {
86 | fatalError("viewController is not a SheetContentsViewController")
87 | }
88 | super.pushViewController(viewController, animated: animated)
89 | }
90 |
91 | public override func popViewController(animated: Bool) -> UIViewController? {
92 | if let contentsViewController = currentContentsViewController {
93 | let originY = contentsViewController.collectionView?.frame.origin.y ?? 0
94 | contentsViewController.topMargin = max(contentsViewController.topMargin + originY, 0)
95 | }
96 | return super.popViewController(animated: animated)
97 | }
98 |
99 | func close(duration: TimeInterval = 0.2, completion: (() -> Void)? = nil) {
100 | let contentsViewController = viewControllers.last
101 | contentsViewController?.view.isHidden = true
102 |
103 | let topMargin = (contentsViewController as? SheetContent)?.topMargin ?? 0
104 | let snapShot = contentsViewController?.view.snapshotView(afterScreenUpdates: false) ?? UIView()
105 | snapShot.frame = view.bounds
106 |
107 | view.insertSubview(snapShot, at: 2)
108 |
109 | UIView.animateKeyframes(withDuration: duration, delay: 0, options: [.calculationModeLinear], animations: {
110 | UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1) {
111 | let diff = UIScreen.main.bounds.height - topMargin
112 | snapShot.transform = CGAffineTransform(translationX: 0, y: diff)
113 | self.backgroundView?.alpha = 0
114 | self.presentingViewController?.view.transform = .identity
115 | }
116 |
117 | UIView.addKeyframe(withRelativeStartTime: 0.6, relativeDuration: 0.4) {
118 | self.sheetToolBarContainerView?.transform = CGAffineTransform(translationX: 0, y: self.options.sheetToolBarHeight + UIEdgeInsets.safeAreaInsets.bottom)
119 | }
120 | }) { _ in
121 | snapShot.removeFromSuperview()
122 | self.dismiss(animated: false) {
123 | self.onDismissed?()
124 | completion?()
125 | }
126 | }
127 | }
128 |
129 | func controlToolBar(isShow: Bool, animated: Bool) {
130 | let bottomConstraint = isShow ? 0 : SheetManager.shared.options.sheetToolBarHeight + UIEdgeInsets.safeAreaInsets.bottom
131 | if animated {
132 | UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.75, initialSpringVelocity: 1, options: [.curveEaseOut], animations: {
133 | self.toolBarBottomConstraint?.constant = bottomConstraint
134 | self.view.layoutIfNeeded()
135 | }) { _ in
136 | }
137 | } else {
138 | self.toolBarBottomConstraint?.constant = bottomConstraint
139 | }
140 | }
141 | }
142 |
143 | extension SheetNavigationController {
144 |
145 | public var currentContentsViewController: SheetContentsViewController? {
146 | return viewControllers.last as? SheetContentsViewController
147 | }
148 | }
149 |
150 | private extension SheetNavigationController {
151 |
152 | func setupViews() {
153 | view.backgroundColor = .clear
154 | setupNavigation()
155 | setupBackgroundView()
156 | setupSheetToolBarContainerView()
157 | }
158 |
159 | func setupNavigation() {
160 | delegate = self
161 | transitioningDelegate = self
162 | isNavigationBarHidden = true
163 | modalPresentationStyle = .overFullScreen
164 | }
165 |
166 | func setupBackgroundView() {
167 | let backgroundView = UIView()
168 | view.insertSubview(backgroundView, at: 0)
169 | backgroundView.translatesAutoresizingMaskIntoConstraints = false
170 | backgroundView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
171 | backgroundView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
172 | backgroundView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
173 | backgroundView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
174 |
175 | backgroundView.alpha = 0
176 | backgroundView.backgroundColor = options.dimmingViewBackgroundColor
177 | self.backgroundView = backgroundView
178 | }
179 |
180 | func setupSheetToolBarContainerView() {
181 | guard !options.isToolBarHidden else {
182 | return
183 | }
184 | let containerView = UIView()
185 | containerView.backgroundColor = options.defaultToolBarBackgroundColor
186 | containerView.translatesAutoresizingMaskIntoConstraints = false
187 | view.addSubview(containerView)
188 | containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
189 | containerView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
190 | toolBarHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: UIEdgeInsets.safeAreaInsets.bottom + options.sheetToolBarHeight)
191 | toolBarHeightConstraint?.isActive = true
192 | toolBarBottomConstraint = containerView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
193 | toolBarBottomConstraint?.isActive = true
194 | self.sheetToolBarContainerView = containerView
195 | }
196 |
197 | @objc
198 | func tappedDefaultButton() {
199 | if viewControllers.count == 1 {
200 | close()
201 | } else {
202 | _ = popViewController(animated: true)
203 | }
204 | }
205 |
206 | func updateSheetToolBar(toolBarView: UIView?) {
207 | var toolBarTemp = toolBarView
208 | if toolBarTemp == nil {
209 | let closeButton = UIButton(type: .system)
210 | let title = viewControllers.count == 1 ? options.defaultToolBarItem.defaultCloseTitle : options.defaultToolBarItem.defaultBackTitle
211 | closeButton.setTitle(title, for: .normal)
212 | closeButton.titleLabel?.font = options.defaultToolBarItem.font
213 | closeButton.setTitleColor(options.defaultToolBarItem.titleColor, for: .normal)
214 | closeButton.addTarget(self, action: #selector(tappedDefaultButton), for: .touchUpInside)
215 |
216 | if !options.defaultToolBarItem.isLineHidden {
217 | let border = CALayer()
218 | border.backgroundColor = options.defaultToolBarItem.lineColor.cgColor
219 | border.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 0.5)
220 | closeButton.layer.addSublayer(border)
221 | }
222 |
223 | toolBarTemp = closeButton
224 | }
225 |
226 | guard let containerView = sheetToolBarContainerView, let toolBar = toolBarTemp else {
227 | return
228 | }
229 |
230 | // TODO: Custom Transition...
231 | toolBar.backgroundColor = options.defaultToolBarBackgroundColor
232 | toolBar.translatesAutoresizingMaskIntoConstraints = false
233 | toolBar.alpha = 0
234 | containerView.addSubview(toolBar)
235 | toolBar.heightAnchor.constraint(equalToConstant: options.sheetToolBarHeight).isActive = true
236 | toolBar.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
237 | toolBar.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
238 | toolBar.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
239 |
240 | UIView.animate(withDuration: 0.25, animations: {
241 | toolBar.alpha = 1
242 | }) { _ in
243 | if containerView.subviews.count > 1 {
244 | containerView.subviews.first?.removeFromSuperview()
245 | }
246 | }
247 | }
248 | }
249 |
250 | extension SheetNavigationController: UIViewControllerTransitioningDelegate {
251 |
252 | open func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
253 | return options.presentTransitionType.animator(present: true)
254 | }
255 |
256 | open func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
257 | return nil
258 | }
259 | }
260 |
261 | extension SheetNavigationController: UINavigationControllerDelegate {
262 |
263 | public func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
264 | let sheetContent = viewController as? SheetContent
265 | if sheetContent?.isToolBarHidden ?? false {
266 | let isRootViewController = viewController == viewControllers.first
267 | if isRootViewController {
268 | self.toolBarBottomConstraint?.constant = SheetManager.shared.options.sheetToolBarHeight + UIEdgeInsets.safeAreaInsets.bottom
269 | self.view.layoutIfNeeded()
270 | } else {
271 | controlToolBar(isShow: false, animated: true)
272 | // UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.75, initialSpringVelocity: 1, options: [.curveEaseOut], animations: {
273 | // self.toolBarBottomConstraint?.constant = SheetManager.shared.options.sheetToolBarHeight + UIEdgeInsets.safeAreaInsets.bottom
274 | // self.view.layoutIfNeeded()
275 | // }) { _ in
276 | //
277 | // }
278 | }
279 | } else {
280 | updateSheetToolBar(toolBarView: sheetContent?.sheetToolBar)
281 | if isFirstCall {
282 | controlToolBar(isShow: true, animated: true)
283 | // UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.75, initialSpringVelocity: 1, options: [.curveEaseOut], animations: {
284 | // self.toolBarBottomConstraint?.constant = 0
285 | // self.view.layoutIfNeeded()
286 | // }) { _ in
287 | //
288 | // }
289 | }
290 | }
291 | }
292 |
293 | public func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
294 |
295 | toVC.view.layoutIfNeeded()
296 |
297 | let fromTopMargin: CGFloat = (fromVC as? SheetContent)?.topMargin ?? 0
298 | var toTopMargin: CGFloat = (toVC as? SheetContent)?.topMargin ?? 0
299 |
300 | if operation == .push {
301 | topMargins.append(fromTopMargin)
302 | } else {
303 | toTopMargin = topMargins.removeLast()
304 | }
305 |
306 | let animator = SheetFadeAnimator(to: toTopMargin, from: fromTopMargin)
307 | animator.isPush = operation == .push
308 | animator.onReady = { }
309 | animator.onComplete = {
310 | fromVC.view.alpha = 1
311 | }
312 | return animator
313 | }
314 | }
315 |
--------------------------------------------------------------------------------
/Sheet.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 47;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 0814D809269D7DD80039424F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4AEF0F3E2162371700FF034E /* Main.storyboard */; };
11 | 4AEF0EF8216235F200FF034E /* SheetFadeAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0EEB216235F100FF034E /* SheetFadeAnimator.swift */; };
12 | 4AEF0EF9216235F200FF034E /* SheetModalScaleAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0EEC216235F100FF034E /* SheetModalScaleAnimator.swift */; };
13 | 4AEF0EFA216235F200FF034E /* SheetMoveAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0EED216235F100FF034E /* SheetMoveAnimator.swift */; };
14 | 4AEF0EFB216235F200FF034E /* SheetContentsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0EEF216235F100FF034E /* SheetContentsViewController.swift */; };
15 | 4AEF0EFC216235F200FF034E /* SheetNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0EF0216235F100FF034E /* SheetNavigationController.swift */; };
16 | 4AEF0EFD216235F200FF034E /* SheetLayoutSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0EF2216235F100FF034E /* SheetLayoutSettings.swift */; };
17 | 4AEF0EFE216235F200FF034E /* SheetDecorationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0EF3216235F100FF034E /* SheetDecorationView.swift */; };
18 | 4AEF0EFF216235F200FF034E /* SheetLayoutAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0EF4216235F100FF034E /* SheetLayoutAttributes.swift */; };
19 | 4AEF0F00216235F200FF034E /* SheetContentsLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0EF5216235F100FF034E /* SheetContentsLayout.swift */; };
20 | 4AEF0F01216235F200FF034E /* SheetOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0EF7216235F100FF034E /* SheetOptions.swift */; };
21 | 4AEF0F09216236A600FF034E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0F08216236A600FF034E /* AppDelegate.swift */; };
22 | 4AEF0F10216236A700FF034E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4AEF0F0F216236A700FF034E /* Assets.xcassets */; };
23 | 4AEF0F18216236C900FF034E /* Sheet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* Sheet.framework */; };
24 | 4AEF0F19216236C900FF034E /* Sheet.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* Sheet.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
25 | 4AEF0F2E2162370400FF034E /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0F1D2162370300FF034E /* ViewController.swift */; };
26 | 4AEF0F2F2162370400FF034E /* ReportViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0F1E2162370300FF034E /* ReportViewController.swift */; };
27 | 4AEF0F302162370400FF034E /* CteateListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0F1F2162370300FF034E /* CteateListViewController.swift */; };
28 | 4AEF0F312162370400FF034E /* MylistViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0F202162370300FF034E /* MylistViewController.swift */; };
29 | 4AEF0F322162370400FF034E /* TitleHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4AEF0F222162370300FF034E /* TitleHeaderView.xib */; };
30 | 4AEF0F332162370400FF034E /* DescriptionFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0F232162370300FF034E /* DescriptionFooterView.swift */; };
31 | 4AEF0F342162370400FF034E /* TitleHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0F242162370300FF034E /* TitleHeaderView.swift */; };
32 | 4AEF0F352162370400FF034E /* DescriptionFooterView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4AEF0F252162370300FF034E /* DescriptionFooterView.xib */; };
33 | 4AEF0F362162370400FF034E /* ListContentsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0F262162370400FF034E /* ListContentsViewController.swift */; };
34 | 4AEF0F372162370400FF034E /* WriteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0F272162370400FF034E /* WriteViewController.swift */; };
35 | 4AEF0F392162370400FF034E /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0F2B2162370400FF034E /* LoginViewController.swift */; };
36 | 4AEF0F3A2162370400FF034E /* LoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0F2C2162370400FF034E /* LoadingViewController.swift */; };
37 | 4AEF0F3B2162370400FF034E /* SendViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0F2D2162370400FF034E /* SendViewController.swift */; };
38 | 4AEF0F4221623F4600FF034E /* SheetManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0F4021623F0D00FF034E /* SheetManager.swift */; };
39 | 4AEF0F4521623FDD00FF034E /* SheetLayoutElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0F4321623F6500FF034E /* SheetLayoutElement.swift */; };
40 | 4AEF0F482162423A00FF034E /* Notification+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0F462162423700FF034E /* Notification+Extension.swift */; };
41 | 4AEF0F4B2162441200FF034E /* UIEdgeInsets+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0F49216243C000FF034E /* UIEdgeInsets+Extension.swift */; };
42 | 4AEF0F4E216244A100FF034E /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0F4C2162449F00FF034E /* Optional+Extension.swift */; };
43 | 4AEF0F5121624BF400FF034E /* MoveItemViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF0F5021624BF400FF034E /* MoveItemViewController.swift */; };
44 | 8933C7851EB5B820000D00A4 /* Sheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7841EB5B820000D00A4 /* Sheet.swift */; };
45 | CE4924A3226FFD9C00033244 /* FullViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE4924A2226FFD9C00033244 /* FullViewController.swift */; };
46 | /* End PBXBuildFile section */
47 |
48 | /* Begin PBXContainerItemProxy section */
49 | 4AEF0F1A216236C900FF034E /* PBXContainerItemProxy */ = {
50 | isa = PBXContainerItemProxy;
51 | containerPortal = 52D6D9731BEFF229002C0205 /* Project object */;
52 | proxyType = 1;
53 | remoteGlobalIDString = 52D6D97B1BEFF229002C0205;
54 | remoteInfo = "Sheet-iOS";
55 | };
56 | /* End PBXContainerItemProxy section */
57 |
58 | /* Begin PBXCopyFilesBuildPhase section */
59 | 4AEF0F1C216236CA00FF034E /* Embed Frameworks */ = {
60 | isa = PBXCopyFilesBuildPhase;
61 | buildActionMask = 2147483647;
62 | dstPath = "";
63 | dstSubfolderSpec = 10;
64 | files = (
65 | 4AEF0F19216236C900FF034E /* Sheet.framework in Embed Frameworks */,
66 | );
67 | name = "Embed Frameworks";
68 | runOnlyForDeploymentPostprocessing = 0;
69 | };
70 | /* End PBXCopyFilesBuildPhase section */
71 |
72 | /* Begin PBXFileReference section */
73 | 4AEF0EEB216235F100FF034E /* SheetFadeAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SheetFadeAnimator.swift; sourceTree = ""; };
74 | 4AEF0EEC216235F100FF034E /* SheetModalScaleAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SheetModalScaleAnimator.swift; sourceTree = ""; };
75 | 4AEF0EED216235F100FF034E /* SheetMoveAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SheetMoveAnimator.swift; sourceTree = ""; };
76 | 4AEF0EEF216235F100FF034E /* SheetContentsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SheetContentsViewController.swift; sourceTree = ""; };
77 | 4AEF0EF0216235F100FF034E /* SheetNavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SheetNavigationController.swift; sourceTree = ""; };
78 | 4AEF0EF2216235F100FF034E /* SheetLayoutSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SheetLayoutSettings.swift; sourceTree = ""; };
79 | 4AEF0EF3216235F100FF034E /* SheetDecorationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SheetDecorationView.swift; sourceTree = ""; };
80 | 4AEF0EF4216235F100FF034E /* SheetLayoutAttributes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SheetLayoutAttributes.swift; sourceTree = ""; };
81 | 4AEF0EF5216235F100FF034E /* SheetContentsLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SheetContentsLayout.swift; sourceTree = ""; };
82 | 4AEF0EF7216235F100FF034E /* SheetOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SheetOptions.swift; sourceTree = ""; };
83 | 4AEF0F06216236A600FF034E /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
84 | 4AEF0F08216236A600FF034E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
85 | 4AEF0F0F216236A700FF034E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
86 | 4AEF0F14216236A700FF034E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
87 | 4AEF0F1D2162370300FF034E /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
88 | 4AEF0F1E2162370300FF034E /* ReportViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportViewController.swift; sourceTree = ""; };
89 | 4AEF0F1F2162370300FF034E /* CteateListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CteateListViewController.swift; sourceTree = ""; };
90 | 4AEF0F202162370300FF034E /* MylistViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MylistViewController.swift; sourceTree = ""; };
91 | 4AEF0F222162370300FF034E /* TitleHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TitleHeaderView.xib; sourceTree = ""; };
92 | 4AEF0F232162370300FF034E /* DescriptionFooterView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DescriptionFooterView.swift; sourceTree = ""; };
93 | 4AEF0F242162370300FF034E /* TitleHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TitleHeaderView.swift; sourceTree = ""; };
94 | 4AEF0F252162370300FF034E /* DescriptionFooterView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DescriptionFooterView.xib; sourceTree = ""; };
95 | 4AEF0F262162370400FF034E /* ListContentsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListContentsViewController.swift; sourceTree = ""; };
96 | 4AEF0F272162370400FF034E /* WriteViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WriteViewController.swift; sourceTree = ""; };
97 | 4AEF0F2B2162370400FF034E /* LoginViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; };
98 | 4AEF0F2C2162370400FF034E /* LoadingViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadingViewController.swift; sourceTree = ""; };
99 | 4AEF0F2D2162370400FF034E /* SendViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendViewController.swift; sourceTree = ""; };
100 | 4AEF0F3E2162371700FF034E /* Main.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; };
101 | 4AEF0F4021623F0D00FF034E /* SheetManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SheetManager.swift; sourceTree = ""; };
102 | 4AEF0F4321623F6500FF034E /* SheetLayoutElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SheetLayoutElement.swift; sourceTree = ""; };
103 | 4AEF0F462162423700FF034E /* Notification+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+Extension.swift"; sourceTree = ""; };
104 | 4AEF0F49216243C000FF034E /* UIEdgeInsets+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIEdgeInsets+Extension.swift"; sourceTree = ""; };
105 | 4AEF0F4C2162449F00FF034E /* Optional+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Extension.swift"; sourceTree = ""; };
106 | 4AEF0F5021624BF400FF034E /* MoveItemViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveItemViewController.swift; sourceTree = ""; };
107 | 52D6D97C1BEFF229002C0205 /* Sheet.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Sheet.framework; sourceTree = BUILT_PRODUCTS_DIR; };
108 | 8933C7841EB5B820000D00A4 /* Sheet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sheet.swift; sourceTree = ""; };
109 | 8933C7891EB5B82A000D00A4 /* SheetTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SheetTests.swift; sourceTree = ""; };
110 | AD2FAA261CD0B6D800659CF4 /* Sheet.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Sheet.plist; sourceTree = ""; };
111 | AD2FAA281CD0B6E100659CF4 /* SheetTests.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = SheetTests.plist; sourceTree = ""; };
112 | CE4924A2226FFD9C00033244 /* FullViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullViewController.swift; sourceTree = ""; };
113 | /* End PBXFileReference section */
114 |
115 | /* Begin PBXFrameworksBuildPhase section */
116 | 4AEF0F03216236A600FF034E /* Frameworks */ = {
117 | isa = PBXFrameworksBuildPhase;
118 | buildActionMask = 2147483647;
119 | files = (
120 | 4AEF0F18216236C900FF034E /* Sheet.framework in Frameworks */,
121 | );
122 | runOnlyForDeploymentPostprocessing = 0;
123 | };
124 | 52D6D9781BEFF229002C0205 /* Frameworks */ = {
125 | isa = PBXFrameworksBuildPhase;
126 | buildActionMask = 2147483647;
127 | files = (
128 | );
129 | runOnlyForDeploymentPostprocessing = 0;
130 | };
131 | /* End PBXFrameworksBuildPhase section */
132 |
133 | /* Begin PBXGroup section */
134 | 4AEF0EEA216235F100FF034E /* Animator */ = {
135 | isa = PBXGroup;
136 | children = (
137 | 4AEF0EEB216235F100FF034E /* SheetFadeAnimator.swift */,
138 | 4AEF0EEC216235F100FF034E /* SheetModalScaleAnimator.swift */,
139 | 4AEF0EED216235F100FF034E /* SheetMoveAnimator.swift */,
140 | );
141 | path = Animator;
142 | sourceTree = "";
143 | };
144 | 4AEF0EEE216235F100FF034E /* Controller */ = {
145 | isa = PBXGroup;
146 | children = (
147 | 4AEF0EF0216235F100FF034E /* SheetNavigationController.swift */,
148 | 4AEF0EEF216235F100FF034E /* SheetContentsViewController.swift */,
149 | );
150 | path = Controller;
151 | sourceTree = "";
152 | };
153 | 4AEF0EF1216235F100FF034E /* Layout */ = {
154 | isa = PBXGroup;
155 | children = (
156 | 4AEF0F4321623F6500FF034E /* SheetLayoutElement.swift */,
157 | 4AEF0EF2216235F100FF034E /* SheetLayoutSettings.swift */,
158 | 4AEF0EF3216235F100FF034E /* SheetDecorationView.swift */,
159 | 4AEF0EF4216235F100FF034E /* SheetLayoutAttributes.swift */,
160 | 4AEF0EF5216235F100FF034E /* SheetContentsLayout.swift */,
161 | );
162 | path = Layout;
163 | sourceTree = "";
164 | };
165 | 4AEF0EF6216235F100FF034E /* Manager */ = {
166 | isa = PBXGroup;
167 | children = (
168 | 4AEF0F4021623F0D00FF034E /* SheetManager.swift */,
169 | 4AEF0EF7216235F100FF034E /* SheetOptions.swift */,
170 | );
171 | path = Manager;
172 | sourceTree = "";
173 | };
174 | 4AEF0F07216236A600FF034E /* Example */ = {
175 | isa = PBXGroup;
176 | children = (
177 | 4AEF0F212162370300FF034E /* XIB */,
178 | 4AEF0F3E2162371700FF034E /* Main.storyboard */,
179 | 4AEF0F1D2162370300FF034E /* ViewController.swift */,
180 | 4AEF0F262162370400FF034E /* ListContentsViewController.swift */,
181 | 4AEF0F1F2162370300FF034E /* CteateListViewController.swift */,
182 | 4AEF0F2C2162370400FF034E /* LoadingViewController.swift */,
183 | 4AEF0F2B2162370400FF034E /* LoginViewController.swift */,
184 | 4AEF0F202162370300FF034E /* MylistViewController.swift */,
185 | 4AEF0F1E2162370300FF034E /* ReportViewController.swift */,
186 | 4AEF0F2D2162370400FF034E /* SendViewController.swift */,
187 | 4AEF0F272162370400FF034E /* WriteViewController.swift */,
188 | 4AEF0F5021624BF400FF034E /* MoveItemViewController.swift */,
189 | 4AEF0F08216236A600FF034E /* AppDelegate.swift */,
190 | 4AEF0F0F216236A700FF034E /* Assets.xcassets */,
191 | 4AEF0F14216236A700FF034E /* Info.plist */,
192 | CE4924A2226FFD9C00033244 /* FullViewController.swift */,
193 | );
194 | path = Example;
195 | sourceTree = "";
196 | };
197 | 4AEF0F212162370300FF034E /* XIB */ = {
198 | isa = PBXGroup;
199 | children = (
200 | 4AEF0F242162370300FF034E /* TitleHeaderView.swift */,
201 | 4AEF0F222162370300FF034E /* TitleHeaderView.xib */,
202 | 4AEF0F232162370300FF034E /* DescriptionFooterView.swift */,
203 | 4AEF0F252162370300FF034E /* DescriptionFooterView.xib */,
204 | );
205 | path = XIB;
206 | sourceTree = "";
207 | };
208 | 4AEF0F4F216244CD00FF034E /* Extensions */ = {
209 | isa = PBXGroup;
210 | children = (
211 | 4AEF0F462162423700FF034E /* Notification+Extension.swift */,
212 | 4AEF0F49216243C000FF034E /* UIEdgeInsets+Extension.swift */,
213 | 4AEF0F4C2162449F00FF034E /* Optional+Extension.swift */,
214 | );
215 | path = Extensions;
216 | sourceTree = "";
217 | };
218 | 52D6D9721BEFF229002C0205 = {
219 | isa = PBXGroup;
220 | children = (
221 | 8933C7811EB5B7E0000D00A4 /* Sources */,
222 | 8933C7831EB5B7EB000D00A4 /* Tests */,
223 | 52D6D99C1BEFF38C002C0205 /* Configs */,
224 | 4AEF0F07216236A600FF034E /* Example */,
225 | 52D6D97D1BEFF229002C0205 /* Products */,
226 | );
227 | sourceTree = "";
228 | };
229 | 52D6D97D1BEFF229002C0205 /* Products */ = {
230 | isa = PBXGroup;
231 | children = (
232 | 52D6D97C1BEFF229002C0205 /* Sheet.framework */,
233 | 4AEF0F06216236A600FF034E /* Example.app */,
234 | );
235 | name = Products;
236 | sourceTree = "";
237 | };
238 | 52D6D99C1BEFF38C002C0205 /* Configs */ = {
239 | isa = PBXGroup;
240 | children = (
241 | DD7502721C68FC1B006590AF /* Frameworks */,
242 | DD7502731C68FC20006590AF /* Tests */,
243 | );
244 | path = Configs;
245 | sourceTree = "";
246 | };
247 | 8933C7811EB5B7E0000D00A4 /* Sources */ = {
248 | isa = PBXGroup;
249 | children = (
250 | 4AEF0EF6216235F100FF034E /* Manager */,
251 | 4AEF0EF1216235F100FF034E /* Layout */,
252 | 4AEF0EEE216235F100FF034E /* Controller */,
253 | 4AEF0EEA216235F100FF034E /* Animator */,
254 | 4AEF0F4F216244CD00FF034E /* Extensions */,
255 | 8933C7841EB5B820000D00A4 /* Sheet.swift */,
256 | );
257 | path = Sources;
258 | sourceTree = "";
259 | };
260 | 8933C7831EB5B7EB000D00A4 /* Tests */ = {
261 | isa = PBXGroup;
262 | children = (
263 | 8933C7891EB5B82A000D00A4 /* SheetTests.swift */,
264 | );
265 | name = Tests;
266 | path = Tests/SheetTests;
267 | sourceTree = "";
268 | };
269 | DD7502721C68FC1B006590AF /* Frameworks */ = {
270 | isa = PBXGroup;
271 | children = (
272 | AD2FAA261CD0B6D800659CF4 /* Sheet.plist */,
273 | );
274 | name = Frameworks;
275 | sourceTree = "";
276 | };
277 | DD7502731C68FC20006590AF /* Tests */ = {
278 | isa = PBXGroup;
279 | children = (
280 | AD2FAA281CD0B6E100659CF4 /* SheetTests.plist */,
281 | );
282 | name = Tests;
283 | sourceTree = "";
284 | };
285 | /* End PBXGroup section */
286 |
287 | /* Begin PBXHeadersBuildPhase section */
288 | 52D6D9791BEFF229002C0205 /* Headers */ = {
289 | isa = PBXHeadersBuildPhase;
290 | buildActionMask = 2147483647;
291 | files = (
292 | );
293 | runOnlyForDeploymentPostprocessing = 0;
294 | };
295 | /* End PBXHeadersBuildPhase section */
296 |
297 | /* Begin PBXNativeTarget section */
298 | 4AEF0F05216236A600FF034E /* Example */ = {
299 | isa = PBXNativeTarget;
300 | buildConfigurationList = 4AEF0F15216236A700FF034E /* Build configuration list for PBXNativeTarget "Example" */;
301 | buildPhases = (
302 | 4AEF0F02216236A600FF034E /* Sources */,
303 | 4AEF0F03216236A600FF034E /* Frameworks */,
304 | 4AEF0F04216236A600FF034E /* Resources */,
305 | 4AEF0F1C216236CA00FF034E /* Embed Frameworks */,
306 | );
307 | buildRules = (
308 | );
309 | dependencies = (
310 | 4AEF0F1B216236C900FF034E /* PBXTargetDependency */,
311 | );
312 | name = Example;
313 | productName = Example;
314 | productReference = 4AEF0F06216236A600FF034E /* Example.app */;
315 | productType = "com.apple.product-type.application";
316 | };
317 | 52D6D97B1BEFF229002C0205 /* Sheet-iOS */ = {
318 | isa = PBXNativeTarget;
319 | buildConfigurationList = 52D6D9901BEFF229002C0205 /* Build configuration list for PBXNativeTarget "Sheet-iOS" */;
320 | buildPhases = (
321 | 52D6D9771BEFF229002C0205 /* Sources */,
322 | 52D6D9781BEFF229002C0205 /* Frameworks */,
323 | 52D6D9791BEFF229002C0205 /* Headers */,
324 | 52D6D97A1BEFF229002C0205 /* Resources */,
325 | );
326 | buildRules = (
327 | );
328 | dependencies = (
329 | );
330 | name = "Sheet-iOS";
331 | productName = Sheet;
332 | productReference = 52D6D97C1BEFF229002C0205 /* Sheet.framework */;
333 | productType = "com.apple.product-type.framework";
334 | };
335 | /* End PBXNativeTarget section */
336 |
337 | /* Begin PBXProject section */
338 | 52D6D9731BEFF229002C0205 /* Project object */ = {
339 | isa = PBXProject;
340 | attributes = {
341 | LastSwiftUpdateCheck = 0930;
342 | LastUpgradeCheck = 1000;
343 | ORGANIZATIONNAME = Interactive;
344 | TargetAttributes = {
345 | 4AEF0F05216236A600FF034E = {
346 | CreatedOnToolsVersion = 9.3;
347 | DevelopmentTeam = SV5LTS7ED5;
348 | ProvisioningStyle = Automatic;
349 | };
350 | 52D6D97B1BEFF229002C0205 = {
351 | CreatedOnToolsVersion = 7.1;
352 | LastSwiftMigration = 0800;
353 | };
354 | };
355 | };
356 | buildConfigurationList = 52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "Sheet" */;
357 | compatibilityVersion = "Xcode 6.3";
358 | developmentRegion = English;
359 | hasScannedForEncodings = 0;
360 | knownRegions = (
361 | English,
362 | en,
363 | Base,
364 | );
365 | mainGroup = 52D6D9721BEFF229002C0205;
366 | productRefGroup = 52D6D97D1BEFF229002C0205 /* Products */;
367 | projectDirPath = "";
368 | projectRoot = "";
369 | targets = (
370 | 52D6D97B1BEFF229002C0205 /* Sheet-iOS */,
371 | 4AEF0F05216236A600FF034E /* Example */,
372 | );
373 | };
374 | /* End PBXProject section */
375 |
376 | /* Begin PBXResourcesBuildPhase section */
377 | 4AEF0F04216236A600FF034E /* Resources */ = {
378 | isa = PBXResourcesBuildPhase;
379 | buildActionMask = 2147483647;
380 | files = (
381 | 4AEF0F352162370400FF034E /* DescriptionFooterView.xib in Resources */,
382 | 0814D809269D7DD80039424F /* Main.storyboard in Resources */,
383 | 4AEF0F322162370400FF034E /* TitleHeaderView.xib in Resources */,
384 | 4AEF0F10216236A700FF034E /* Assets.xcassets in Resources */,
385 | );
386 | runOnlyForDeploymentPostprocessing = 0;
387 | };
388 | 52D6D97A1BEFF229002C0205 /* Resources */ = {
389 | isa = PBXResourcesBuildPhase;
390 | buildActionMask = 2147483647;
391 | files = (
392 | );
393 | runOnlyForDeploymentPostprocessing = 0;
394 | };
395 | /* End PBXResourcesBuildPhase section */
396 |
397 | /* Begin PBXSourcesBuildPhase section */
398 | 4AEF0F02216236A600FF034E /* Sources */ = {
399 | isa = PBXSourcesBuildPhase;
400 | buildActionMask = 2147483647;
401 | files = (
402 | 4AEF0F372162370400FF034E /* WriteViewController.swift in Sources */,
403 | 4AEF0F362162370400FF034E /* ListContentsViewController.swift in Sources */,
404 | 4AEF0F332162370400FF034E /* DescriptionFooterView.swift in Sources */,
405 | 4AEF0F2E2162370400FF034E /* ViewController.swift in Sources */,
406 | 4AEF0F342162370400FF034E /* TitleHeaderView.swift in Sources */,
407 | 4AEF0F312162370400FF034E /* MylistViewController.swift in Sources */,
408 | 4AEF0F09216236A600FF034E /* AppDelegate.swift in Sources */,
409 | 4AEF0F302162370400FF034E /* CteateListViewController.swift in Sources */,
410 | 4AEF0F3B2162370400FF034E /* SendViewController.swift in Sources */,
411 | 4AEF0F5121624BF400FF034E /* MoveItemViewController.swift in Sources */,
412 | 4AEF0F3A2162370400FF034E /* LoadingViewController.swift in Sources */,
413 | 4AEF0F392162370400FF034E /* LoginViewController.swift in Sources */,
414 | 4AEF0F2F2162370400FF034E /* ReportViewController.swift in Sources */,
415 | CE4924A3226FFD9C00033244 /* FullViewController.swift in Sources */,
416 | );
417 | runOnlyForDeploymentPostprocessing = 0;
418 | };
419 | 52D6D9771BEFF229002C0205 /* Sources */ = {
420 | isa = PBXSourcesBuildPhase;
421 | buildActionMask = 2147483647;
422 | files = (
423 | 4AEF0EFE216235F200FF034E /* SheetDecorationView.swift in Sources */,
424 | 4AEF0F4E216244A100FF034E /* Optional+Extension.swift in Sources */,
425 | 4AEF0F4521623FDD00FF034E /* SheetLayoutElement.swift in Sources */,
426 | 4AEF0F4221623F4600FF034E /* SheetManager.swift in Sources */,
427 | 4AEF0F4B2162441200FF034E /* UIEdgeInsets+Extension.swift in Sources */,
428 | 4AEF0EF8216235F200FF034E /* SheetFadeAnimator.swift in Sources */,
429 | 4AEF0F482162423A00FF034E /* Notification+Extension.swift in Sources */,
430 | 4AEF0EFF216235F200FF034E /* SheetLayoutAttributes.swift in Sources */,
431 | 4AEF0EFB216235F200FF034E /* SheetContentsViewController.swift in Sources */,
432 | 4AEF0EFA216235F200FF034E /* SheetMoveAnimator.swift in Sources */,
433 | 8933C7851EB5B820000D00A4 /* Sheet.swift in Sources */,
434 | 4AEF0EF9216235F200FF034E /* SheetModalScaleAnimator.swift in Sources */,
435 | 4AEF0EFD216235F200FF034E /* SheetLayoutSettings.swift in Sources */,
436 | 4AEF0EFC216235F200FF034E /* SheetNavigationController.swift in Sources */,
437 | 4AEF0F01216235F200FF034E /* SheetOptions.swift in Sources */,
438 | 4AEF0F00216235F200FF034E /* SheetContentsLayout.swift in Sources */,
439 | );
440 | runOnlyForDeploymentPostprocessing = 0;
441 | };
442 | /* End PBXSourcesBuildPhase section */
443 |
444 | /* Begin PBXTargetDependency section */
445 | 4AEF0F1B216236C900FF034E /* PBXTargetDependency */ = {
446 | isa = PBXTargetDependency;
447 | target = 52D6D97B1BEFF229002C0205 /* Sheet-iOS */;
448 | targetProxy = 4AEF0F1A216236C900FF034E /* PBXContainerItemProxy */;
449 | };
450 | /* End PBXTargetDependency section */
451 |
452 | /* Begin XCBuildConfiguration section */
453 | 4AEF0F16216236A700FF034E /* Debug */ = {
454 | isa = XCBuildConfiguration;
455 | buildSettings = {
456 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
457 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
458 | CLANG_ANALYZER_NONNULL = YES;
459 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
460 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
461 | CLANG_ENABLE_OBJC_WEAK = YES;
462 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
463 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
464 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
465 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
466 | CODE_SIGN_IDENTITY = "iPhone Developer";
467 | CODE_SIGN_STYLE = Automatic;
468 | DEVELOPMENT_TEAM = SV5LTS7ED5;
469 | GCC_C_LANGUAGE_STANDARD = gnu11;
470 | INFOPLIST_FILE = Example/Info.plist;
471 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
472 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
473 | PRODUCT_BUNDLE_IDENTIFIER = com.Gwangbeom.Example;
474 | PRODUCT_NAME = "$(TARGET_NAME)";
475 | PROVISIONING_PROFILE_SPECIFIER = "";
476 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
477 | SWIFT_VERSION = 4.2;
478 | TARGETED_DEVICE_FAMILY = "1,2";
479 | };
480 | name = Debug;
481 | };
482 | 4AEF0F17216236A700FF034E /* Release */ = {
483 | isa = XCBuildConfiguration;
484 | buildSettings = {
485 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
486 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
487 | CLANG_ANALYZER_NONNULL = YES;
488 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
489 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
490 | CLANG_ENABLE_OBJC_WEAK = YES;
491 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
492 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
493 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
494 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
495 | CODE_SIGN_IDENTITY = "iPhone Developer";
496 | CODE_SIGN_STYLE = Automatic;
497 | DEVELOPMENT_TEAM = SV5LTS7ED5;
498 | GCC_C_LANGUAGE_STANDARD = gnu11;
499 | INFOPLIST_FILE = Example/Info.plist;
500 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
501 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
502 | PRODUCT_BUNDLE_IDENTIFIER = com.Gwangbeom.Example;
503 | PRODUCT_NAME = "$(TARGET_NAME)";
504 | PROVISIONING_PROFILE_SPECIFIER = "";
505 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
506 | SWIFT_VERSION = 4.2;
507 | TARGETED_DEVICE_FAMILY = "1,2";
508 | };
509 | name = Release;
510 | };
511 | 52D6D98E1BEFF229002C0205 /* Debug */ = {
512 | isa = XCBuildConfiguration;
513 | buildSettings = {
514 | ALWAYS_SEARCH_USER_PATHS = NO;
515 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
516 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
517 | CLANG_CXX_LIBRARY = "libc++";
518 | CLANG_ENABLE_MODULES = YES;
519 | CLANG_ENABLE_OBJC_ARC = YES;
520 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
521 | CLANG_WARN_BOOL_CONVERSION = YES;
522 | CLANG_WARN_COMMA = YES;
523 | CLANG_WARN_CONSTANT_CONVERSION = YES;
524 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
525 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
526 | CLANG_WARN_EMPTY_BODY = YES;
527 | CLANG_WARN_ENUM_CONVERSION = YES;
528 | CLANG_WARN_INFINITE_RECURSION = YES;
529 | CLANG_WARN_INT_CONVERSION = YES;
530 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
531 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
532 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
533 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
534 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
535 | CLANG_WARN_STRICT_PROTOTYPES = YES;
536 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
537 | CLANG_WARN_UNREACHABLE_CODE = YES;
538 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
539 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
540 | COPY_PHASE_STRIP = NO;
541 | CURRENT_PROJECT_VERSION = 1;
542 | DEBUG_INFORMATION_FORMAT = dwarf;
543 | ENABLE_STRICT_OBJC_MSGSEND = YES;
544 | ENABLE_TESTABILITY = YES;
545 | GCC_C_LANGUAGE_STANDARD = gnu99;
546 | GCC_DYNAMIC_NO_PIC = NO;
547 | GCC_NO_COMMON_BLOCKS = YES;
548 | GCC_OPTIMIZATION_LEVEL = 0;
549 | GCC_PREPROCESSOR_DEFINITIONS = (
550 | "DEBUG=1",
551 | "$(inherited)",
552 | );
553 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
554 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
555 | GCC_WARN_UNDECLARED_SELECTOR = YES;
556 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
557 | GCC_WARN_UNUSED_FUNCTION = YES;
558 | GCC_WARN_UNUSED_VARIABLE = YES;
559 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
560 | MTL_ENABLE_DEBUG_INFO = YES;
561 | ONLY_ACTIVE_ARCH = YES;
562 | SDKROOT = iphoneos;
563 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
564 | SWIFT_VERSION = 4.2;
565 | TARGETED_DEVICE_FAMILY = "1,2";
566 | VERSIONING_SYSTEM = "apple-generic";
567 | VERSION_INFO_PREFIX = "";
568 | };
569 | name = Debug;
570 | };
571 | 52D6D98F1BEFF229002C0205 /* Release */ = {
572 | isa = XCBuildConfiguration;
573 | buildSettings = {
574 | ALWAYS_SEARCH_USER_PATHS = NO;
575 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
576 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
577 | CLANG_CXX_LIBRARY = "libc++";
578 | CLANG_ENABLE_MODULES = YES;
579 | CLANG_ENABLE_OBJC_ARC = YES;
580 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
581 | CLANG_WARN_BOOL_CONVERSION = YES;
582 | CLANG_WARN_COMMA = YES;
583 | CLANG_WARN_CONSTANT_CONVERSION = YES;
584 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
585 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
586 | CLANG_WARN_EMPTY_BODY = YES;
587 | CLANG_WARN_ENUM_CONVERSION = YES;
588 | CLANG_WARN_INFINITE_RECURSION = YES;
589 | CLANG_WARN_INT_CONVERSION = YES;
590 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
591 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
592 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
593 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
594 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
595 | CLANG_WARN_STRICT_PROTOTYPES = YES;
596 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
597 | CLANG_WARN_UNREACHABLE_CODE = YES;
598 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
599 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
600 | COPY_PHASE_STRIP = NO;
601 | CURRENT_PROJECT_VERSION = 1;
602 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
603 | ENABLE_NS_ASSERTIONS = NO;
604 | ENABLE_STRICT_OBJC_MSGSEND = YES;
605 | GCC_C_LANGUAGE_STANDARD = gnu99;
606 | GCC_NO_COMMON_BLOCKS = YES;
607 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
608 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
609 | GCC_WARN_UNDECLARED_SELECTOR = YES;
610 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
611 | GCC_WARN_UNUSED_FUNCTION = YES;
612 | GCC_WARN_UNUSED_VARIABLE = YES;
613 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
614 | MTL_ENABLE_DEBUG_INFO = NO;
615 | SDKROOT = iphoneos;
616 | SWIFT_VERSION = 4.2;
617 | TARGETED_DEVICE_FAMILY = "1,2";
618 | VALIDATE_PRODUCT = YES;
619 | VERSIONING_SYSTEM = "apple-generic";
620 | VERSION_INFO_PREFIX = "";
621 | };
622 | name = Release;
623 | };
624 | 52D6D9911BEFF229002C0205 /* Debug */ = {
625 | isa = XCBuildConfiguration;
626 | buildSettings = {
627 | APPLICATION_EXTENSION_API_ONLY = NO;
628 | CLANG_ENABLE_MODULES = YES;
629 | CODE_SIGN_IDENTITY = "iPhone Developer";
630 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
631 | DEFINES_MODULE = YES;
632 | DYLIB_COMPATIBILITY_VERSION = 1;
633 | DYLIB_CURRENT_VERSION = 1;
634 | DYLIB_INSTALL_NAME_BASE = "@rpath";
635 | INFOPLIST_FILE = Configs/Sheet.plist;
636 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
637 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
638 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
639 | MARKETING_VERSION = 0.7.0;
640 | ONLY_ACTIVE_ARCH = NO;
641 | PRODUCT_BUNDLE_IDENTIFIER = "com.Sheet.Sheet-iOS";
642 | PRODUCT_NAME = Sheet;
643 | SKIP_INSTALL = YES;
644 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
645 | SWIFT_VERSION = 4.2;
646 | TARGETED_DEVICE_FAMILY = 1;
647 | };
648 | name = Debug;
649 | };
650 | 52D6D9921BEFF229002C0205 /* Release */ = {
651 | isa = XCBuildConfiguration;
652 | buildSettings = {
653 | APPLICATION_EXTENSION_API_ONLY = NO;
654 | CLANG_ENABLE_MODULES = YES;
655 | CODE_SIGN_IDENTITY = "iPhone Developer";
656 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
657 | DEFINES_MODULE = YES;
658 | DYLIB_COMPATIBILITY_VERSION = 1;
659 | DYLIB_CURRENT_VERSION = 1;
660 | DYLIB_INSTALL_NAME_BASE = "@rpath";
661 | INFOPLIST_FILE = Configs/Sheet.plist;
662 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
663 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
664 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
665 | MARKETING_VERSION = 0.7.0;
666 | PRODUCT_BUNDLE_IDENTIFIER = "com.Sheet.Sheet-iOS";
667 | PRODUCT_NAME = Sheet;
668 | SKIP_INSTALL = YES;
669 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
670 | SWIFT_VERSION = 4.2;
671 | TARGETED_DEVICE_FAMILY = 1;
672 | };
673 | name = Release;
674 | };
675 | /* End XCBuildConfiguration section */
676 |
677 | /* Begin XCConfigurationList section */
678 | 4AEF0F15216236A700FF034E /* Build configuration list for PBXNativeTarget "Example" */ = {
679 | isa = XCConfigurationList;
680 | buildConfigurations = (
681 | 4AEF0F16216236A700FF034E /* Debug */,
682 | 4AEF0F17216236A700FF034E /* Release */,
683 | );
684 | defaultConfigurationIsVisible = 0;
685 | defaultConfigurationName = Release;
686 | };
687 | 52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "Sheet" */ = {
688 | isa = XCConfigurationList;
689 | buildConfigurations = (
690 | 52D6D98E1BEFF229002C0205 /* Debug */,
691 | 52D6D98F1BEFF229002C0205 /* Release */,
692 | );
693 | defaultConfigurationIsVisible = 0;
694 | defaultConfigurationName = Release;
695 | };
696 | 52D6D9901BEFF229002C0205 /* Build configuration list for PBXNativeTarget "Sheet-iOS" */ = {
697 | isa = XCConfigurationList;
698 | buildConfigurations = (
699 | 52D6D9911BEFF229002C0205 /* Debug */,
700 | 52D6D9921BEFF229002C0205 /* Release */,
701 | );
702 | defaultConfigurationIsVisible = 0;
703 | defaultConfigurationName = Release;
704 | };
705 | /* End XCConfigurationList section */
706 | };
707 | rootObject = 52D6D9731BEFF229002C0205 /* Project object */;
708 | }
709 |
--------------------------------------------------------------------------------