├── Demo.gif
├── Demo
├── Assets.xcassets
│ ├── Contents.json
│ ├── first.imageset
│ │ ├── first.pdf
│ │ └── Contents.json
│ ├── second.imageset
│ │ ├── second.pdf
│ │ └── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── SemiViewController.swift
├── AppDelegate.swift
├── FirstViewController.swift
├── SecondViewController.swift
├── Info.plist
└── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── Podfile
├── Demo.xcodeproj
├── xcshareddata
│ └── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── project.pbxproj
├── Source
├── UIDevice+SemiModalViewController.swift
├── ClosureWrapper.swift
├── UIView+Kit.swift
├── UIView+SemiModalViewController.swift
├── UIView+FindViewController.swift
├── PushBackAnimationGroup.swift
├── UIViewController+Options.swift
└── UIViewController+SemiModalViewController.swift
├── Demo.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ ├── IDEWorkspaceChecks.plist
│ └── WorkspaceSettings.xcsettings
├── Podfile.lock
├── Package.swift
├── .gitignore
├── SemiModalViewController.podspec
├── LICENSE
└── README.md
/Demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/muyexi/SemiModalViewController/HEAD/Demo.gif
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '9.0'
2 |
3 | target 'Demo' do
4 | use_frameworks!
5 |
6 | pod 'SemiModalViewController', :path => './'
7 | end
8 |
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/first.imageset/first.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/muyexi/SemiModalViewController/HEAD/Demo/Assets.xcassets/first.imageset/first.pdf
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/second.imageset/second.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/muyexi/SemiModalViewController/HEAD/Demo/Assets.xcassets/second.imageset/second.pdf
--------------------------------------------------------------------------------
/Demo.xcodeproj/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/Source/UIDevice+SemiModalViewController.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | extension UIDevice {
4 |
5 | static func isPad() -> Bool {
6 | return UIDevice.current.userInterfaceIdiom == .pad
7 | }
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/first.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "first.pdf"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/Demo/Assets.xcassets/second.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "second.pdf"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/Demo.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Demo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Demo.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildSystemType
6 | Original
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Demo/SemiViewController.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import UIKit
3 |
4 | class SemiViewController: UIViewController {
5 |
6 | override func awakeFromNib() {
7 | super.awakeFromNib()
8 |
9 | }
10 |
11 | @IBAction func dismiss(_ sender: AnyObject) {
12 | dismissSemiModalView()
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/Demo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | @UIApplicationMain
4 | class AppDelegate: UIResponder, UIApplicationDelegate {
5 |
6 | var window: UIWindow?
7 |
8 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
9 | return true
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - SemiModalViewController (1.0.0)
3 |
4 | DEPENDENCIES:
5 | - SemiModalViewController (from `./`)
6 |
7 | EXTERNAL SOURCES:
8 | SemiModalViewController:
9 | :path: "./"
10 |
11 | SPEC CHECKSUMS:
12 | SemiModalViewController: 6f046ecf0ff1ceb84fd2a2f53251cfdf72b8dbd6
13 |
14 | PODFILE CHECKSUM: 7a75cd15e7190183aa62892cb568390740ff2e74
15 |
16 | COCOAPODS: 1.7.3
17 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.1
2 | import PackageDescription
3 |
4 | let package = Package(
5 | name: "SemiModalViewController",
6 | platforms: [
7 | .iOS(.v8)
8 | ],
9 | products: [
10 | .library(name: "SemiModalViewController", targets: ["SemiModalViewController"]),
11 | ],
12 | targets: [
13 | .target(
14 | name: "SemiModalViewController",
15 | path: "Source"
16 | )
17 | ]
18 | )
19 |
--------------------------------------------------------------------------------
/Source/ClosureWrapper.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | class ClosureWrapper: NSObject, NSCopying {
4 | var closure: (() -> Void)?
5 |
6 | convenience init(closure: (() -> Void)?) {
7 | self.init()
8 | self.closure = closure
9 | }
10 |
11 | func copy(with zone: NSZone?) -> Any {
12 | let wrapper: ClosureWrapper = ClosureWrapper()
13 |
14 | wrapper.closure = self.closure
15 |
16 | return wrapper;
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/xcode
3 |
4 | ### Xcode ###
5 | # Xcode
6 | #
7 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
8 |
9 | ## Build generated
10 | build/
11 | DerivedData/
12 | .idea/
13 |
14 | ## Various settings
15 | *.pbxuser
16 | !default.pbxuser
17 | *.mode1v3
18 | !default.mode1v3
19 | *.mode2v3
20 | !default.mode2v3
21 | *.perspectivev3
22 | !default.perspectivev3
23 | xcuserdata/
24 |
25 | ## Other
26 | *.moved-aside
27 | *.xccheckout
28 | *.xcscmblueprint
29 | Pods/
30 | .swiftpm
31 |
--------------------------------------------------------------------------------
/SemiModalViewController.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'SemiModalViewController'
3 | s.version = '1.0.1'
4 | s.platform = :ios, '8.0'
5 | s.summary = 'Present view / view controller as bottom-half modal'
6 | s.license = 'MIT'
7 | s.homepage = 'https://github.com/muyexi/SemiModalViewController'
8 | s.author = { 'muyexi' => 'muyexi@gmail.com' }
9 | s.source = { :git => 'https://github.com/muyexi/SemiModalViewController.git', :tag => s.version }
10 | s.source_files = 'Source/*.swift'
11 | s.swift_version = '4.2'
12 | end
13 |
--------------------------------------------------------------------------------
/Source/UIView+Kit.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | extension UIView {
4 | func asImage() -> UIImage {
5 | if #available(iOS 10.0, *) {
6 | let renderer = UIGraphicsImageRenderer(bounds: bounds)
7 | return renderer.image { rendererContext in
8 | layer.render(in: rendererContext.cgContext)
9 | }
10 | } else {
11 | UIGraphicsBeginImageContext(self.frame.size)
12 | self.layer.render(in:UIGraphicsGetCurrentContext()!)
13 | let image = UIGraphicsGetImageFromCurrentImageContext()
14 | UIGraphicsEndImageContext()
15 | return UIImage(cgImage: image!.cgImage!)
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Demo/FirstViewController.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import SemiModalViewController
3 |
4 | class FirstViewController: UIViewController {
5 |
6 | override func viewDidLoad() {
7 | super.viewDidLoad()
8 |
9 | }
10 |
11 | @IBAction func show(_ sender: AnyObject) {
12 | let view = UIView(frame: UIScreen.main.bounds)
13 | view.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 300)
14 | view.backgroundColor = UIColor.red
15 |
16 | let options: [SemiModalOption : Any] = [
17 | SemiModalOption.pushParentBack: true
18 | ]
19 |
20 | presentSemiView(view, options: options) {
21 | print("Completed!")
22 | }
23 | }
24 |
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/Demo/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 | "info" : {
45 | "version" : 1,
46 | "author" : "xcode"
47 | }
48 | }
--------------------------------------------------------------------------------
/Source/UIView+SemiModalViewController.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | extension UIView {
4 |
5 | var width: CGFloat {
6 | get {
7 | return frame.size.width
8 | }
9 | set {
10 | frame = CGRect(x: x, y: y, width: newValue, height: height)
11 | }
12 | }
13 |
14 | var height: CGFloat {
15 | get {
16 | return frame.size.height
17 | }
18 | set {
19 | frame = CGRect(x: x, y: y, width: width, height: newValue)
20 | }
21 | }
22 |
23 | var x: CGFloat {
24 | get {
25 | return frame.origin.x
26 | }
27 | set {
28 | frame = CGRect(x: newValue, y: y, width: width, height: height)
29 | }
30 | }
31 |
32 | var y: CGFloat {
33 | get {
34 | return frame.origin.y
35 | }
36 | set {
37 | frame = CGRect(x: x, y: newValue, width: width, height: height)
38 | }
39 | }
40 |
41 | }
--------------------------------------------------------------------------------
/Source/UIView+FindViewController.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | extension UIView {
4 |
5 | func containingViewController() -> UIViewController {
6 | let target = (superview != nil) ? superview : self
7 | return target!.traverseResponderChainForUIViewController() as! UIViewController
8 | }
9 |
10 | func traverseResponderChainForUIViewController() -> AnyObject? {
11 | let nextResponder: UIResponder = self.next!
12 |
13 | let isViewController = nextResponder is UIViewController
14 | let isTabBarController = nextResponder is UITabBarController
15 |
16 | if isViewController && !isTabBarController {
17 | return nextResponder
18 | } else if isTabBarController {
19 | return (nextResponder as! UITabBarController).selectedViewController!
20 | } else if nextResponder is UIView {
21 | return (nextResponder as! UIView).traverseResponderChainForUIViewController()
22 | } else {
23 | return nil
24 | }
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/Demo/SecondViewController.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import SemiModalViewController
3 |
4 | class SecondViewController: UIViewController {
5 |
6 | override func viewDidLoad() {
7 | super.viewDidLoad()
8 |
9 | }
10 |
11 | @IBAction func show(_ sender: AnyObject) {
12 | let options: [SemiModalOption : Any] = [
13 | SemiModalOption.pushParentBack: false
14 | ]
15 |
16 | let storyboard = UIStoryboard(name: "Main", bundle: nil)
17 | let identifier = String(describing: SemiViewController.self)
18 | let controller = storyboard.instantiateViewController(withIdentifier: identifier)
19 |
20 | controller.view.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 200)
21 | controller.view.backgroundColor = UIColor.red
22 |
23 | presentSemiViewController(controller, options: options, completion: {
24 | print("Completed!")
25 | }, dismissBlock: {
26 | print("Dismissed!")
27 | })
28 | }
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | The MIT License (MIT)
3 |
4 | Copyright (c) 2015 Dongdong Wang
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
--------------------------------------------------------------------------------
/Demo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UIStatusBarTintParameters
34 |
35 | UINavigationBar
36 |
37 | Style
38 | UIBarStyleDefault
39 | Translucent
40 |
41 |
42 |
43 | UISupportedInterfaceOrientations
44 |
45 | UIInterfaceOrientationPortrait
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/Demo/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Source/PushBackAnimationGroup.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import UIKit
3 |
4 | public class PushBackAnimationGroup: CAAnimationGroup {
5 | public convenience init(forward: Bool, viewHeight: CGFloat, options: [SemiModalOption: Any]) {
6 | self.init()
7 |
8 | var id1 = CATransform3DIdentity
9 | id1.m34 = 1.0 / -900
10 | id1 = CATransform3DScale(id1, 0.95, 0.95, 1)
11 |
12 | let angleFactor: CGFloat = UIDevice.isPad() ? 7.5 : 15.0
13 | id1 = CATransform3DRotate(id1, angleFactor * CGFloat(Double.pi) / 180.0, 1, 0, 0)
14 |
15 | var id2 = CATransform3DIdentity
16 | id2.m34 = id1.m34
17 |
18 | let scale = CGFloat(options[.parentScale] as! Double)
19 | let tzFactor: CGFloat = UIDevice.isPad() ? -0.04 : -0.08
20 |
21 | id2 = CATransform3DTranslate(id2, 0, viewHeight * tzFactor, 0)
22 | id2 = CATransform3DScale(id2, scale, scale, 1)
23 |
24 | let animation = CABasicAnimation(keyPath: "transform")
25 | animation.toValue = NSValue(caTransform3D: id1)
26 |
27 | let animationDuration = options[.animationDuration] as! Double
28 | animation.duration = animationDuration / 2
29 | animation.fillMode = CAMediaTimingFillMode.forwards
30 | animation.isRemovedOnCompletion = false
31 | animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
32 |
33 | let animation2 = CABasicAnimation(keyPath: "transform")
34 | animation2.toValue = NSValue(caTransform3D: forward ? id2 : CATransform3DIdentity)
35 | animation2.beginTime = animation.duration
36 | animation2.duration = animation.duration
37 | animation2.fillMode = CAMediaTimingFillMode.forwards
38 | animation2.isRemovedOnCompletion = false
39 |
40 | fillMode = CAMediaTimingFillMode.forwards
41 | isRemovedOnCompletion = false
42 | duration = animation.duration * 2
43 | animations = [animation, animation2]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Source/UIViewController+Options.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | private var CustomOptions: Void?
4 |
5 | extension UIViewController {
6 |
7 | var defaultOptions: [SemiModalOption: Any] {
8 | return [
9 | .traverseParentHierarchy : true,
10 | .pushParentBack : false,
11 | .animationDuration : 0.5,
12 | .parentAlpha : 0.5,
13 | .parentScale : 0.8,
14 | .shadowOpacity : 0.5,
15 | .transitionStyle : SemiModalTransitionStyle.slideUp,
16 | .disableCancel : true
17 | ]
18 | }
19 |
20 | func registerOptions(_ options: [SemiModalOption: Any]?) {
21 | // options always save in parent viewController
22 | var targetVC: UIViewController = self
23 | while targetVC.parent != nil {
24 | targetVC = targetVC.parent!
25 | }
26 |
27 | objc_setAssociatedObject(targetVC, &CustomOptions, options, .OBJC_ASSOCIATION_RETAIN)
28 | }
29 |
30 | func options() -> [SemiModalOption: Any] {
31 | var targetVC: UIViewController = self
32 | while targetVC.parent != nil {
33 | targetVC = targetVC.parent!
34 | }
35 |
36 | if let options = objc_getAssociatedObject(targetVC, &CustomOptions) as? [SemiModalOption: Any] {
37 | var defaultOptions: [SemiModalOption: Any] = self.defaultOptions
38 | defaultOptions.merge(options) { (_, new) in new }
39 |
40 | return defaultOptions
41 | } else {
42 | return defaultOptions
43 | }
44 | }
45 |
46 | func optionForKey(_ optionKey: SemiModalOption) -> Any? {
47 | let options = self.options()
48 | let value = options[optionKey]
49 |
50 | let isValidType = value is Bool ||
51 | value is Double ||
52 | value is SemiModalTransitionStyle ||
53 | value is UIView
54 |
55 | if isValidType {
56 | return value
57 | } else {
58 | return defaultOptions[optionKey]
59 | }
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | [](https://cocoapods.org/pods/SemiModalViewController)
3 | [](https://swift.org/package-manager/)
4 | [](https://github.com/muyexi/SemiModalViewController/blob/master/LICENSE)
5 |
6 | `UIViewController` extension to present view / view controller as bottom-half modal.
7 |
8 |
9 |
10 |
11 |
12 | ## Installation
13 |
14 | ### CocoaPods
15 |
16 | ```ruby
17 | pod 'SemiModalViewController'
18 | ```
19 |
20 | ### Swift Package Manager
21 |
22 | ```swift
23 | dependencies: [
24 | .Package(url: "https://github.com/muyexi/SemiModalViewController.git", majorVersion: 0)
25 | ]
26 | ```
27 |
28 | ## Usage
29 |
30 | Present a view controller:
31 |
32 | ```swift
33 | let options = [
34 | SemiModalOption.pushParentBack: true
35 | ]
36 |
37 | let controller = UIViewController()
38 |
39 | controller.view.height = 200
40 | controller.view.backgroundColor = UIColor.redColor()
41 |
42 | presentSemiViewController(controller, options: options, completion: {
43 | print("Completed!")
44 | }, dismissBlock: {
45 | print("Dismissed!")
46 | })
47 | ```
48 |
49 | Or view:
50 |
51 | ```swift
52 | let view = UIView(frame: UIScreen.mainScreen().bounds)
53 | view.height = 300
54 | view.backgroundColor = UIColor.redColor()
55 |
56 | presentSemiView(view, options: options) {
57 | print("Completed!")
58 | }
59 | ```
60 |
61 | Dismiss a presented view / view controller:
62 |
63 | ```swift
64 | dismissSemiModalView()
65 | ```
66 |
67 | Default options:
68 |
69 | ```swift
70 | SemiModalOption.traverseParentHierarchy : true,
71 | SemiModalOption.pushParentBack : false,
72 | SemiModalOption.animationDuration : 0.5,
73 | SemiModalOption.parentAlpha : 0.5,
74 | SemiModalOption.parentScale : 0.8,
75 | SemiModalOption.shadowOpacity : 0.5,
76 | SemiModalOption.transitionStyle : .slideUp,
77 | SemiModalOption.disableCancel : true
78 | ```
79 |
80 | ## Credits
81 |
82 | SemiModalViewController is based on [KNSemiModalViewController](https://github.com/kentnguyen/KNSemiModalViewController).
83 |
84 | ## License
85 |
86 | SemiModalViewController is released under the MIT license. See LICENSE for details.
87 |
--------------------------------------------------------------------------------
/Demo/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
--------------------------------------------------------------------------------
/Source/UIViewController+SemiModalViewController.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | extension Notification.Name {
4 | static let semiModalDidShow = Notification.Name("semiModalDidShow")
5 | static let semiModalDidHide = Notification.Name("semiModalDidHide")
6 | static let semiModalWasResized = Notification.Name("semiModalWasResized")
7 | }
8 |
9 | private var semiModalViewController: Void?
10 | private var semiModalDismissBlock: Void?
11 | private var semiModalPresentingViewController: Void?
12 |
13 | private let semiModalOverlayTag = 10001
14 | private let semiModalScreenshotTag = 10002
15 | private let semiModalModalViewTag = 10003
16 |
17 | public enum SemiModalOption: String {
18 | case traverseParentHierarchy
19 | case pushParentBack
20 | case animationDuration
21 | case parentAlpha
22 | case parentScale
23 | case shadowOpacity
24 | case transitionStyle
25 | case disableCancel
26 | case backgroundView
27 | }
28 |
29 | public enum SemiModalTransitionStyle: String {
30 | case slideUp
31 | case fadeInOut
32 | case fadeIn
33 | case fadeOut
34 | }
35 |
36 | extension UIViewController {
37 |
38 | public func presentSemiViewController(_ vc: UIViewController,
39 | options: [SemiModalOption: Any]? = nil,
40 | completion: (() -> Void)? = nil,
41 | dismissBlock: (() -> Void)? = nil) {
42 | registerOptions(options)
43 |
44 | targetViewController.addChild(vc)
45 | vc.beginAppearanceTransition(true, animated: true)
46 |
47 | objc_setAssociatedObject(targetViewController, &semiModalViewController, vc, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
48 | objc_setAssociatedObject(targetViewController, &semiModalDismissBlock, ClosureWrapper(closure: dismissBlock), .OBJC_ASSOCIATION_COPY_NONATOMIC)
49 |
50 | presentSemiView(vc.view, options: options) {
51 | vc.didMove(toParent: self.targetViewController)
52 | vc.endAppearanceTransition()
53 |
54 | completion?()
55 | }
56 | }
57 |
58 | public func presentSemiView(_ view: UIView, options: [SemiModalOption: Any]? = nil, completion: (() -> Void)? = nil) {
59 | registerOptions(options)
60 |
61 | if targetView.subviews.contains(view) {
62 | return
63 | }
64 |
65 | NotificationCenter.default.addObserver(targetViewController,
66 | selector: #selector(interfaceOrientationDidChange(_:)),
67 | name: UIDevice.orientationDidChangeNotification,
68 | object: nil)
69 |
70 | let semiViewHeight = view.frame.size.height
71 | let semiViewFrame = CGRect(x: 0, y: targetView.height - semiViewHeight, width: targetView.width, height: semiViewHeight)
72 |
73 | let overlay = overlayView()
74 | targetView.addSubview(overlay)
75 |
76 | let screenshot = addOrUpdateParentScreenshotInView(overlay)
77 |
78 | let duration = optionForKey(.animationDuration) as! TimeInterval
79 | UIView.animate(withDuration: duration, animations: {
80 | screenshot.alpha = CGFloat(self.optionForKey(.parentAlpha) as! Double)
81 | })
82 |
83 | let transitionStyle = optionForKey(.transitionStyle) as! SemiModalTransitionStyle
84 | if transitionStyle == .slideUp {
85 | view.frame = semiViewFrame.offsetBy(dx: 0, dy: +semiViewHeight)
86 | } else {
87 | view.frame = semiViewFrame
88 | }
89 |
90 | if transitionStyle == .fadeIn || transitionStyle == .fadeOut {
91 | view.alpha = 0
92 | }
93 |
94 | if UIDevice.isPad() {
95 | view.autoresizingMask = [.flexibleTopMargin, .flexibleLeftMargin, .flexibleRightMargin]
96 | } else {
97 | view.autoresizingMask = [.flexibleTopMargin, .flexibleWidth]
98 | }
99 |
100 | view.tag = semiModalModalViewTag
101 | targetView.addSubview(view)
102 |
103 | view.layer.shadowColor = UIColor.black.cgColor
104 | view.layer.shadowOffset = CGSize(width: 0, height: -2)
105 | view.layer.shadowRadius = 5
106 | view.layer.shadowOpacity = Float(optionForKey(.shadowOpacity) as! Double)
107 | view.layer.shouldRasterize = true
108 | view.layer.rasterizationScale = UIScreen.main.scale
109 |
110 | UIView.animate(withDuration: duration, animations: {
111 | if transitionStyle == .slideUp {
112 | view.frame = semiViewFrame
113 | } else if transitionStyle == .fadeIn || transitionStyle == .fadeInOut {
114 | view.alpha = 1
115 | }
116 | }, completion: { finished in
117 | if finished {
118 | NotificationCenter.default.post(name: .semiModalDidShow, object: self)
119 | completion?()
120 | }
121 | })
122 | }
123 |
124 | var targetViewController: UIViewController {
125 | if let viewController = objc_getAssociatedObject(self, &semiModalPresentingViewController) as? UIViewController {
126 | return viewController
127 | } else {
128 | var viewController: UIViewController = self
129 |
130 | if optionForKey(.traverseParentHierarchy) as! Bool {
131 | while viewController.parent != nil {
132 | viewController = viewController.parent!
133 | }
134 | }
135 |
136 | objc_setAssociatedObject(self, &semiModalPresentingViewController, viewController, .OBJC_ASSOCIATION_RETAIN)
137 | return viewController
138 | }
139 | }
140 |
141 | var targetView: UIView {
142 | return targetViewController.view
143 | }
144 |
145 | @discardableResult
146 | func addOrUpdateParentScreenshotInView(_ screenshotContainer: UIView) -> UIView {
147 | let semiView = targetView.viewWithTag(semiModalModalViewTag)
148 |
149 | screenshotContainer.isHidden = true
150 | semiView?.isHidden = true
151 |
152 | var snapshotView = screenshotContainer.viewWithTag(semiModalScreenshotTag) ?? UIView()
153 | snapshotView.removeFromSuperview()
154 |
155 | let image = targetView.asImage()
156 | snapshotView = UIImageView(image: image)
157 | snapshotView.tag = semiModalScreenshotTag
158 |
159 | screenshotContainer.addSubview(snapshotView)
160 |
161 | if optionForKey(.pushParentBack) as! Bool {
162 | let animationGroup = PushBackAnimationGroup(forward: true,
163 | viewHeight: targetView.height,
164 | options: self.options())
165 | snapshotView.layer.add(animationGroup, forKey: "pushedBackAnimation")
166 | }
167 |
168 | screenshotContainer.isHidden = false
169 | semiView?.isHidden = false
170 |
171 | return snapshotView
172 | }
173 |
174 | @objc func interfaceOrientationDidChange(_ notification: Notification) {
175 | guard let overlay = targetView.viewWithTag(semiModalOverlayTag) else { return }
176 |
177 | let view = addOrUpdateParentScreenshotInView(overlay)
178 | view.alpha = CGFloat(self.optionForKey(.parentAlpha) as! Double)
179 | }
180 |
181 | @objc public func dismissSemiModalView() {
182 | dismissSemiModalViewWithCompletion(nil)
183 | }
184 |
185 | public func dismissSemiModalViewWithCompletion(_ completion: (() -> Void)?) {
186 | guard let targetView = targetViewController.view,
187 | let modal = targetView.viewWithTag(semiModalModalViewTag),
188 | let overlay = targetView.viewWithTag(semiModalOverlayTag),
189 | let transitionStyle = optionForKey(.transitionStyle) as? SemiModalTransitionStyle,
190 | let duration = optionForKey(.animationDuration) as? TimeInterval else { return }
191 |
192 | let vc = objc_getAssociatedObject(targetViewController, &semiModalViewController) as? UIViewController
193 | let dismissBlock = (objc_getAssociatedObject(targetViewController, &semiModalDismissBlock) as? ClosureWrapper)?.closure
194 |
195 | vc?.willMove(toParent: nil)
196 | vc?.beginAppearanceTransition(false, animated: true)
197 |
198 | UIView.animate(withDuration: duration, animations: {
199 | if transitionStyle == .slideUp {
200 | let originX = UIDevice.isPad() ? (targetView.width - modal.width) / 2 : 0
201 | modal.frame = CGRect(x: originX, y: targetView.height, width: modal.width, height: modal.height)
202 | } else if transitionStyle == .fadeOut || transitionStyle == .fadeInOut {
203 | modal.alpha = 0.0
204 | }
205 | }, completion: { finished in
206 | overlay.removeFromSuperview()
207 | modal.removeFromSuperview()
208 |
209 | vc?.removeFromParent()
210 | vc?.endAppearanceTransition()
211 |
212 | dismissBlock?()
213 |
214 | objc_setAssociatedObject(self.targetViewController, &semiModalDismissBlock, nil, .OBJC_ASSOCIATION_COPY_NONATOMIC)
215 | objc_setAssociatedObject(self.targetViewController, &semiModalViewController, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
216 |
217 | NotificationCenter.default.removeObserver(self.targetViewController, name: UIDevice.orientationDidChangeNotification, object: nil)
218 | })
219 |
220 | if let screenshot = overlay.subviews.first {
221 | if let pushParentBack = optionForKey(.pushParentBack) as? Bool , pushParentBack {
222 | let animationGroup = PushBackAnimationGroup(forward: false,
223 | viewHeight: targetView.height,
224 | options: self.options())
225 | screenshot.layer.add(animationGroup, forKey: "bringForwardAnimation")
226 | }
227 | UIView.animate(withDuration: duration, animations: {
228 | screenshot.alpha = 1
229 | }, completion: { finished in
230 | if finished {
231 | NotificationCenter.default.post(name: .semiModalDidHide, object: self)
232 | completion?()
233 | }
234 | })
235 | }
236 |
237 | }
238 |
239 | func overlayView() -> UIView {
240 | var view: UIView
241 | if let backgroundView = optionForKey(.backgroundView) as? UIView {
242 | view = backgroundView
243 | } else {
244 | view = UIView()
245 | }
246 |
247 | view.frame = targetView.bounds
248 | view.backgroundColor = UIColor.black
249 | view.isUserInteractionEnabled = true
250 | view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
251 | view.tag = semiModalOverlayTag
252 |
253 | if optionForKey(.disableCancel) as! Bool {
254 | let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissSemiModalView))
255 | view.addGestureRecognizer(tapGesture)
256 | }
257 |
258 | return view
259 | }
260 | }
261 |
--------------------------------------------------------------------------------
/Demo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 08B08C2B13A15184A3E63F5D /* Pods_Demo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2795497DC42E0EA345871CD8 /* Pods_Demo.framework */; };
11 | 8D4B7D531D853F1B0013459B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D4B7D4A1D853F1B0013459B /* AppDelegate.swift */; };
12 | 8D4B7D541D853F1B0013459B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8D4B7D4B1D853F1B0013459B /* Assets.xcassets */; };
13 | 8D4B7D551D853F1B0013459B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D4B7D4C1D853F1B0013459B /* LaunchScreen.storyboard */; };
14 | 8D4B7D561D853F1B0013459B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D4B7D4E1D853F1B0013459B /* Main.storyboard */; };
15 | 8D4B7D571D853F1B0013459B /* FirstViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D4B7D501D853F1B0013459B /* FirstViewController.swift */; };
16 | 8D4B7D591D853F1B0013459B /* SecondViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D4B7D521D853F1B0013459B /* SecondViewController.swift */; };
17 | 8D76E7152150D0320019CC99 /* SemiViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D76E7142150D0320019CC99 /* SemiViewController.swift */; };
18 | /* End PBXBuildFile section */
19 |
20 | /* Begin PBXFileReference section */
21 | 2795497DC42E0EA345871CD8 /* Pods_Demo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Demo.framework; sourceTree = BUILT_PRODUCTS_DIR; };
22 | 45072DEAF5EB3BCBC2B79648 /* Pods-SemiModalViewController.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SemiModalViewController.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SemiModalViewController/Pods-SemiModalViewController.debug.xcconfig"; sourceTree = ""; };
23 | 6EA5AA50658935AF6140E12F /* Pods-SemiModalViewController.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SemiModalViewController.release.xcconfig"; path = "Pods/Target Support Files/Pods-SemiModalViewController/Pods-SemiModalViewController.release.xcconfig"; sourceTree = ""; };
24 | 8D4B7D4A1D853F1B0013459B /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
25 | 8D4B7D4B1D853F1B0013459B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
26 | 8D4B7D4D1D853F1B0013459B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
27 | 8D4B7D4F1D853F1B0013459B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
28 | 8D4B7D501D853F1B0013459B /* FirstViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FirstViewController.swift; sourceTree = ""; };
29 | 8D4B7D511D853F1B0013459B /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
30 | 8D4B7D521D853F1B0013459B /* SecondViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecondViewController.swift; sourceTree = ""; };
31 | 8D642E6E1D79758E00C45795 /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; };
32 | 8D76E7142150D0320019CC99 /* SemiViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SemiViewController.swift; sourceTree = ""; };
33 | BF8C779DD9D1680FE694014C /* Pods-Demo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo.release.xcconfig"; path = "Pods/Target Support Files/Pods-Demo/Pods-Demo.release.xcconfig"; sourceTree = ""; };
34 | EC450E88D8C6359EF36F028C /* Pods-Demo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Demo/Pods-Demo.debug.xcconfig"; sourceTree = ""; };
35 | /* End PBXFileReference section */
36 |
37 | /* Begin PBXFrameworksBuildPhase section */
38 | 8D642E6B1D79758E00C45795 /* Frameworks */ = {
39 | isa = PBXFrameworksBuildPhase;
40 | buildActionMask = 2147483647;
41 | files = (
42 | 08B08C2B13A15184A3E63F5D /* Pods_Demo.framework in Frameworks */,
43 | );
44 | runOnlyForDeploymentPostprocessing = 0;
45 | };
46 | /* End PBXFrameworksBuildPhase section */
47 |
48 | /* Begin PBXGroup section */
49 | 8D4B7D491D853F1B0013459B /* Demo */ = {
50 | isa = PBXGroup;
51 | children = (
52 | 8D76E7142150D0320019CC99 /* SemiViewController.swift */,
53 | 8D4B7D4A1D853F1B0013459B /* AppDelegate.swift */,
54 | 8D4B7D4B1D853F1B0013459B /* Assets.xcassets */,
55 | 8D4B7D4C1D853F1B0013459B /* LaunchScreen.storyboard */,
56 | 8D4B7D4E1D853F1B0013459B /* Main.storyboard */,
57 | 8D4B7D501D853F1B0013459B /* FirstViewController.swift */,
58 | 8D4B7D511D853F1B0013459B /* Info.plist */,
59 | 8D4B7D521D853F1B0013459B /* SecondViewController.swift */,
60 | );
61 | path = Demo;
62 | sourceTree = "";
63 | };
64 | 8D642E651D79758E00C45795 = {
65 | isa = PBXGroup;
66 | children = (
67 | 8D4B7D491D853F1B0013459B /* Demo */,
68 | 8D642E6F1D79758E00C45795 /* Products */,
69 | EC8873D9C3439B3D5EE58432 /* Pods */,
70 | E15FBAFD47163413F3D1A7EC /* Frameworks */,
71 | );
72 | sourceTree = "";
73 | };
74 | 8D642E6F1D79758E00C45795 /* Products */ = {
75 | isa = PBXGroup;
76 | children = (
77 | 8D642E6E1D79758E00C45795 /* Demo.app */,
78 | );
79 | name = Products;
80 | sourceTree = "";
81 | };
82 | E15FBAFD47163413F3D1A7EC /* Frameworks */ = {
83 | isa = PBXGroup;
84 | children = (
85 | 2795497DC42E0EA345871CD8 /* Pods_Demo.framework */,
86 | );
87 | name = Frameworks;
88 | sourceTree = "";
89 | };
90 | EC8873D9C3439B3D5EE58432 /* Pods */ = {
91 | isa = PBXGroup;
92 | children = (
93 | 45072DEAF5EB3BCBC2B79648 /* Pods-SemiModalViewController.debug.xcconfig */,
94 | 6EA5AA50658935AF6140E12F /* Pods-SemiModalViewController.release.xcconfig */,
95 | EC450E88D8C6359EF36F028C /* Pods-Demo.debug.xcconfig */,
96 | BF8C779DD9D1680FE694014C /* Pods-Demo.release.xcconfig */,
97 | );
98 | name = Pods;
99 | sourceTree = "";
100 | };
101 | /* End PBXGroup section */
102 |
103 | /* Begin PBXNativeTarget section */
104 | 8D642E6D1D79758E00C45795 /* Demo */ = {
105 | isa = PBXNativeTarget;
106 | buildConfigurationList = 8D642E821D79758E00C45795 /* Build configuration list for PBXNativeTarget "Demo" */;
107 | buildPhases = (
108 | 92C885EEE249003944DFA33D /* [CP] Check Pods Manifest.lock */,
109 | 8D642E6A1D79758E00C45795 /* Sources */,
110 | 8D642E6B1D79758E00C45795 /* Frameworks */,
111 | 8D642E6C1D79758E00C45795 /* Resources */,
112 | A39697C889D68159C745087A /* [CP] Embed Pods Frameworks */,
113 | );
114 | buildRules = (
115 | );
116 | dependencies = (
117 | );
118 | name = Demo;
119 | productName = SemiModalViewController;
120 | productReference = 8D642E6E1D79758E00C45795 /* Demo.app */;
121 | productType = "com.apple.product-type.application";
122 | };
123 | /* End PBXNativeTarget section */
124 |
125 | /* Begin PBXProject section */
126 | 8D642E661D79758E00C45795 /* Project object */ = {
127 | isa = PBXProject;
128 | attributes = {
129 | LastSwiftUpdateCheck = 0730;
130 | LastUpgradeCheck = 0940;
131 | ORGANIZATIONNAME = muyexi;
132 | TargetAttributes = {
133 | 8D642E6D1D79758E00C45795 = {
134 | CreatedOnToolsVersion = 7.3;
135 | LastSwiftMigration = 0940;
136 | };
137 | };
138 | };
139 | buildConfigurationList = 8D642E691D79758E00C45795 /* Build configuration list for PBXProject "Demo" */;
140 | compatibilityVersion = "Xcode 3.2";
141 | developmentRegion = English;
142 | hasScannedForEncodings = 0;
143 | knownRegions = (
144 | en,
145 | Base,
146 | );
147 | mainGroup = 8D642E651D79758E00C45795;
148 | productRefGroup = 8D642E6F1D79758E00C45795 /* Products */;
149 | projectDirPath = "";
150 | projectRoot = "";
151 | targets = (
152 | 8D642E6D1D79758E00C45795 /* Demo */,
153 | );
154 | };
155 | /* End PBXProject section */
156 |
157 | /* Begin PBXResourcesBuildPhase section */
158 | 8D642E6C1D79758E00C45795 /* Resources */ = {
159 | isa = PBXResourcesBuildPhase;
160 | buildActionMask = 2147483647;
161 | files = (
162 | 8D4B7D561D853F1B0013459B /* Main.storyboard in Resources */,
163 | 8D4B7D541D853F1B0013459B /* Assets.xcassets in Resources */,
164 | 8D4B7D551D853F1B0013459B /* LaunchScreen.storyboard in Resources */,
165 | );
166 | runOnlyForDeploymentPostprocessing = 0;
167 | };
168 | /* End PBXResourcesBuildPhase section */
169 |
170 | /* Begin PBXShellScriptBuildPhase section */
171 | 92C885EEE249003944DFA33D /* [CP] Check Pods Manifest.lock */ = {
172 | isa = PBXShellScriptBuildPhase;
173 | buildActionMask = 2147483647;
174 | files = (
175 | );
176 | inputPaths = (
177 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
178 | "${PODS_ROOT}/Manifest.lock",
179 | );
180 | name = "[CP] Check Pods Manifest.lock";
181 | outputPaths = (
182 | "$(DERIVED_FILE_DIR)/Pods-Demo-checkManifestLockResult.txt",
183 | );
184 | runOnlyForDeploymentPostprocessing = 0;
185 | shellPath = /bin/sh;
186 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
187 | showEnvVarsInLog = 0;
188 | };
189 | A39697C889D68159C745087A /* [CP] Embed Pods Frameworks */ = {
190 | isa = PBXShellScriptBuildPhase;
191 | buildActionMask = 2147483647;
192 | files = (
193 | );
194 | inputPaths = (
195 | "${PODS_ROOT}/Target Support Files/Pods-Demo/Pods-Demo-frameworks.sh",
196 | "${BUILT_PRODUCTS_DIR}/SemiModalViewController/SemiModalViewController.framework",
197 | );
198 | name = "[CP] Embed Pods Frameworks";
199 | outputPaths = (
200 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SemiModalViewController.framework",
201 | );
202 | runOnlyForDeploymentPostprocessing = 0;
203 | shellPath = /bin/sh;
204 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Demo/Pods-Demo-frameworks.sh\"\n";
205 | showEnvVarsInLog = 0;
206 | };
207 | /* End PBXShellScriptBuildPhase section */
208 |
209 | /* Begin PBXSourcesBuildPhase section */
210 | 8D642E6A1D79758E00C45795 /* Sources */ = {
211 | isa = PBXSourcesBuildPhase;
212 | buildActionMask = 2147483647;
213 | files = (
214 | 8D76E7152150D0320019CC99 /* SemiViewController.swift in Sources */,
215 | 8D4B7D591D853F1B0013459B /* SecondViewController.swift in Sources */,
216 | 8D4B7D571D853F1B0013459B /* FirstViewController.swift in Sources */,
217 | 8D4B7D531D853F1B0013459B /* AppDelegate.swift in Sources */,
218 | );
219 | runOnlyForDeploymentPostprocessing = 0;
220 | };
221 | /* End PBXSourcesBuildPhase section */
222 |
223 | /* Begin PBXVariantGroup section */
224 | 8D4B7D4C1D853F1B0013459B /* LaunchScreen.storyboard */ = {
225 | isa = PBXVariantGroup;
226 | children = (
227 | 8D4B7D4D1D853F1B0013459B /* Base */,
228 | );
229 | name = LaunchScreen.storyboard;
230 | sourceTree = "";
231 | };
232 | 8D4B7D4E1D853F1B0013459B /* Main.storyboard */ = {
233 | isa = PBXVariantGroup;
234 | children = (
235 | 8D4B7D4F1D853F1B0013459B /* Base */,
236 | );
237 | name = Main.storyboard;
238 | sourceTree = "";
239 | };
240 | /* End PBXVariantGroup section */
241 |
242 | /* Begin XCBuildConfiguration section */
243 | 8D642E801D79758E00C45795 /* Debug */ = {
244 | isa = XCBuildConfiguration;
245 | buildSettings = {
246 | ALWAYS_SEARCH_USER_PATHS = NO;
247 | CLANG_ANALYZER_NONNULL = YES;
248 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
249 | CLANG_CXX_LIBRARY = "libc++";
250 | CLANG_ENABLE_MODULES = YES;
251 | CLANG_ENABLE_OBJC_ARC = YES;
252 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
253 | CLANG_WARN_BOOL_CONVERSION = YES;
254 | CLANG_WARN_COMMA = YES;
255 | CLANG_WARN_CONSTANT_CONVERSION = YES;
256 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
257 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
258 | CLANG_WARN_EMPTY_BODY = YES;
259 | CLANG_WARN_ENUM_CONVERSION = YES;
260 | CLANG_WARN_INFINITE_RECURSION = YES;
261 | CLANG_WARN_INT_CONVERSION = YES;
262 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
263 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
264 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
265 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
266 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
267 | CLANG_WARN_STRICT_PROTOTYPES = YES;
268 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
269 | CLANG_WARN_UNREACHABLE_CODE = YES;
270 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
271 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
272 | COPY_PHASE_STRIP = NO;
273 | DEBUG_INFORMATION_FORMAT = dwarf;
274 | ENABLE_STRICT_OBJC_MSGSEND = YES;
275 | ENABLE_TESTABILITY = YES;
276 | GCC_C_LANGUAGE_STANDARD = gnu99;
277 | GCC_DYNAMIC_NO_PIC = NO;
278 | GCC_NO_COMMON_BLOCKS = YES;
279 | GCC_OPTIMIZATION_LEVEL = 0;
280 | GCC_PREPROCESSOR_DEFINITIONS = (
281 | "DEBUG=1",
282 | "$(inherited)",
283 | );
284 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
285 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
286 | GCC_WARN_UNDECLARED_SELECTOR = YES;
287 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
288 | GCC_WARN_UNUSED_FUNCTION = YES;
289 | GCC_WARN_UNUSED_VARIABLE = YES;
290 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
291 | MTL_ENABLE_DEBUG_INFO = YES;
292 | ONLY_ACTIVE_ARCH = YES;
293 | SDKROOT = iphoneos;
294 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
295 | };
296 | name = Debug;
297 | };
298 | 8D642E811D79758E00C45795 /* Release */ = {
299 | isa = XCBuildConfiguration;
300 | buildSettings = {
301 | ALWAYS_SEARCH_USER_PATHS = NO;
302 | CLANG_ANALYZER_NONNULL = YES;
303 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
304 | CLANG_CXX_LIBRARY = "libc++";
305 | CLANG_ENABLE_MODULES = YES;
306 | CLANG_ENABLE_OBJC_ARC = YES;
307 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
308 | CLANG_WARN_BOOL_CONVERSION = YES;
309 | CLANG_WARN_COMMA = YES;
310 | CLANG_WARN_CONSTANT_CONVERSION = YES;
311 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
312 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
313 | CLANG_WARN_EMPTY_BODY = YES;
314 | CLANG_WARN_ENUM_CONVERSION = YES;
315 | CLANG_WARN_INFINITE_RECURSION = YES;
316 | CLANG_WARN_INT_CONVERSION = YES;
317 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
318 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
319 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
320 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
321 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
322 | CLANG_WARN_STRICT_PROTOTYPES = YES;
323 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
324 | CLANG_WARN_UNREACHABLE_CODE = YES;
325 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
326 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
327 | COPY_PHASE_STRIP = NO;
328 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
329 | ENABLE_NS_ASSERTIONS = NO;
330 | ENABLE_STRICT_OBJC_MSGSEND = YES;
331 | GCC_C_LANGUAGE_STANDARD = gnu99;
332 | GCC_NO_COMMON_BLOCKS = YES;
333 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
334 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
335 | GCC_WARN_UNDECLARED_SELECTOR = YES;
336 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
337 | GCC_WARN_UNUSED_FUNCTION = YES;
338 | GCC_WARN_UNUSED_VARIABLE = YES;
339 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
340 | MTL_ENABLE_DEBUG_INFO = NO;
341 | SDKROOT = iphoneos;
342 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
343 | VALIDATE_PRODUCT = YES;
344 | };
345 | name = Release;
346 | };
347 | 8D642E831D79758E00C45795 /* Debug */ = {
348 | isa = XCBuildConfiguration;
349 | baseConfigurationReference = EC450E88D8C6359EF36F028C /* Pods-Demo.debug.xcconfig */;
350 | buildSettings = {
351 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
352 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
353 | DEVELOPMENT_TEAM = "";
354 | INFOPLIST_FILE = "$(SRCROOT)/Demo/Info.plist";
355 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
356 | PRODUCT_BUNDLE_IDENTIFIER = im.muyexi.Demo;
357 | PRODUCT_NAME = Demo;
358 | SWIFT_SWIFT3_OBJC_INFERENCE = On;
359 | SWIFT_VERSION = 4.2;
360 | TARGETED_DEVICE_FAMILY = "1,2";
361 | };
362 | name = Debug;
363 | };
364 | 8D642E841D79758E00C45795 /* Release */ = {
365 | isa = XCBuildConfiguration;
366 | baseConfigurationReference = BF8C779DD9D1680FE694014C /* Pods-Demo.release.xcconfig */;
367 | buildSettings = {
368 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
369 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
370 | DEVELOPMENT_TEAM = "";
371 | INFOPLIST_FILE = "$(SRCROOT)/Demo/Info.plist";
372 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
373 | PRODUCT_BUNDLE_IDENTIFIER = im.muyexi.Demo;
374 | PRODUCT_NAME = Demo;
375 | SWIFT_SWIFT3_OBJC_INFERENCE = On;
376 | SWIFT_VERSION = 4.2;
377 | TARGETED_DEVICE_FAMILY = "1,2";
378 | };
379 | name = Release;
380 | };
381 | /* End XCBuildConfiguration section */
382 |
383 | /* Begin XCConfigurationList section */
384 | 8D642E691D79758E00C45795 /* Build configuration list for PBXProject "Demo" */ = {
385 | isa = XCConfigurationList;
386 | buildConfigurations = (
387 | 8D642E801D79758E00C45795 /* Debug */,
388 | 8D642E811D79758E00C45795 /* Release */,
389 | );
390 | defaultConfigurationIsVisible = 0;
391 | defaultConfigurationName = Release;
392 | };
393 | 8D642E821D79758E00C45795 /* Build configuration list for PBXNativeTarget "Demo" */ = {
394 | isa = XCConfigurationList;
395 | buildConfigurations = (
396 | 8D642E831D79758E00C45795 /* Debug */,
397 | 8D642E841D79758E00C45795 /* Release */,
398 | );
399 | defaultConfigurationIsVisible = 0;
400 | defaultConfigurationName = Release;
401 | };
402 | /* End XCConfigurationList section */
403 | };
404 | rootObject = 8D642E661D79758E00C45795 /* Project object */;
405 | }
406 |
--------------------------------------------------------------------------------