├── 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 | Logo 4 |

5 | 6 |

7 | 8 | Swift 4.0+ 9 | 10 | 11 | iOS 9.0 12 | 13 | 14 | Version 15 | 16 | 17 | Carthage Compatible 18 | 19 | 20 | Platform 21 | 22 | 23 | License 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 | Example 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 | layout 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 | --------------------------------------------------------------------------------