├── snapshot
├── left.png
├── bottom.png
├── center.png
├── preview.gif
└── right.png
├── ExampleObjC
├── Assets.xcassets
│ ├── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── ViewController.h
├── AppDelegate.h
├── main.m
├── Modal-ObjC
│ ├── XPModalPresentationController.h
│ ├── XPModalTransitioningDelegate.h
│ ├── XPModalPercentDrivenInteractiveTransition.h
│ ├── XPModalAnimatedTransitioning.h
│ ├── XPModalConfiguration.m
│ ├── UIViewController+XPModal.h
│ ├── XPModalConfiguration.h
│ ├── XPModalTransitioningDelegate.m
│ ├── XPModalPercentDrivenInteractiveTransition.m
│ ├── XPModalAnimatedTransitioning.m
│ ├── UIViewController+XPModal.m
│ └── XPModalPresentationController.m
├── Info.plist
├── AppDelegate.m
├── ViewController.m
└── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── Example.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── project.pbxproj
├── Example
├── ModalViewController.swift
├── Info.plist
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── AppDelegate.swift
├── ViewController.swift
└── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── LICENSE
├── .gitignore
├── README.md
└── Modal
└── UIViewController+Modal.swift
/snapshot/left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaopin/iOS-Modal/HEAD/snapshot/left.png
--------------------------------------------------------------------------------
/snapshot/bottom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaopin/iOS-Modal/HEAD/snapshot/bottom.png
--------------------------------------------------------------------------------
/snapshot/center.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaopin/iOS-Modal/HEAD/snapshot/center.png
--------------------------------------------------------------------------------
/snapshot/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaopin/iOS-Modal/HEAD/snapshot/preview.gif
--------------------------------------------------------------------------------
/snapshot/right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaopin/iOS-Modal/HEAD/snapshot/right.png
--------------------------------------------------------------------------------
/ExampleObjC/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ExampleObjC/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // ExampleObjC
4 | //
5 | // Created by xiaopin on 2018/4/23.
6 | // Copyright © 2018年 xiaopin. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UITableViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ExampleObjC/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // ExampleObjC
4 | //
5 | // Created by xiaopin on 2018/4/23.
6 | // Copyright © 2018年 xiaopin. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/ExampleObjC/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // ExampleObjC
4 | //
5 | // Created by xiaopin on 2018/4/23.
6 | // Copyright © 2018年 xiaopin. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ExampleObjC/Modal-ObjC/XPModalPresentationController.h:
--------------------------------------------------------------------------------
1 | //
2 | // XPModalPresentationController.h
3 | // https://github.com/xiaopin/iOS-Modal.git
4 | //
5 | // Created by xiaopin on 2018/4/23.
6 | // Copyright © 2018年 xiaopin. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class XPModalConfiguration;
12 |
13 | @interface XPModalPresentationController : UIPresentationController
14 |
15 | @property (nonatomic, strong) XPModalConfiguration *configuration;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/ExampleObjC/Modal-ObjC/XPModalTransitioningDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // XPModalTransitioningDelegate.h
3 | // https://github.com/xiaopin/iOS-Modal.git
4 | //
5 | // Created by xiaopin on 2018/4/23.
6 | // Copyright © 2018年 xiaopin. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class XPModalConfiguration;
12 |
13 | @interface XPModalTransitioningDelegate: NSObject
14 |
15 | + (instancetype)transitioningDelegateWithConfiguration:(XPModalConfiguration * _Nonnull)configuration;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/ExampleObjC/Modal-ObjC/XPModalPercentDrivenInteractiveTransition.h:
--------------------------------------------------------------------------------
1 | //
2 | // XPModalPercentDrivenInteractiveTransition.h
3 | // https://github.com/xiaopin/iOS-Modal.git
4 | //
5 | // Created by xiaopin on 2018/4/23.
6 | // Copyright © 2018年 xiaopin. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class XPModalConfiguration;
12 |
13 | @interface XPModalPercentDrivenInteractiveTransition : UIPercentDrivenInteractiveTransition
14 |
15 | + (instancetype)interactiveTransitionWithConfiguration:(XPModalConfiguration * _Nonnull)configuration;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/Example/ModalViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ModalViewController.swift
3 | // Example
4 | //
5 | // Created by xiaopin on 2018/4/18.
6 | //
7 |
8 | import UIKit
9 |
10 | class ModalViewController: UIViewController {
11 |
12 | override func viewDidLoad() {
13 | super.viewDidLoad()
14 |
15 | // Do any additional setup after loading the view.
16 | }
17 |
18 | override func didReceiveMemoryWarning() {
19 | super.didReceiveMemoryWarning()
20 | // Dispose of any resources that can be recreated.
21 | }
22 |
23 | @IBAction func buttonAction(_ sender: UIButton) {
24 | dismiss(animated: true, completion: nil)
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/ExampleObjC/Modal-ObjC/XPModalAnimatedTransitioning.h:
--------------------------------------------------------------------------------
1 | //
2 | // XPModalAnimatedTransitioning.h
3 | // https://github.com/xiaopin/iOS-Modal.git
4 | //
5 | // Created by xiaopin on 2018/4/23.
6 | // Copyright © 2018年 xiaopin. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class XPModalConfiguration;
12 |
13 | @interface XPModalAnimatedTransitioning: NSObject
14 |
15 | @property (nonatomic, assign, getter=isPresentation) BOOL presentation;
16 | @property (nonatomic, strong) XPModalConfiguration *configuration;
17 |
18 | + (instancetype)transitioningWithConfiguration:(XPModalConfiguration *)configuration isPresentation:(BOOL)presentation;
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018
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 |
--------------------------------------------------------------------------------
/ExampleObjC/Modal-ObjC/XPModalConfiguration.m:
--------------------------------------------------------------------------------
1 | //
2 | // XPModalConfiguration.m
3 | // https://github.com/xiaopin/iOS-Modal.git
4 | //
5 | // Created by xiaopin on 2018/4/23.
6 | // Copyright © 2018年 xiaopin. All rights reserved.
7 | //
8 |
9 | #import "XPModalConfiguration.h"
10 |
11 | @implementation XPModalConfiguration
12 |
13 | + (instancetype)defaultConfiguration {
14 | return [[XPModalConfiguration alloc] init];
15 | }
16 |
17 | - (instancetype)init {
18 | if (self = [super init]) {
19 | _direction = XPModalDirectionBottom;
20 | _contentSize= CGSizeMake(CGFLOAT_MAX, 200);
21 | _animationDuration = 0.5;
22 | _autoDismissModal = YES;
23 | _backgroundOpacity = 0.3;
24 |
25 | _enableShadow = YES;
26 | _shadowColor = [UIColor blackColor];
27 | _shadowWidth = 3.0;
28 | _shadowOpacity = 0.8;
29 | _shadowRadius = 5.0;
30 |
31 | _enableBackgroundAnimation = NO;
32 | _backgroundColor = [UIColor blackColor];
33 | _backgroundImage = nil;
34 |
35 | _enableInteractiveTransitioning = YES;
36 | }
37 | return self;
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/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 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/ExampleObjC/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 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/.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 | "info" : {
90 | "version" : 1,
91 | "author" : "xcode"
92 | }
93 | }
--------------------------------------------------------------------------------
/ExampleObjC/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 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # iOS-Modal
2 |
3 | []()
4 | []()
5 | []()
6 | []()
7 |
8 | iOS 模态窗口,内置类似淘宝添加购物车的模态视图动画,内部使用 iOS8 推出的`UIPresentationController`来实现模态窗口功能。
9 |
10 | 该项目是[`SemiModal`](https://github.com/xiaopin/SemiModal.git)的增强版,在SemiModal的基础上增加了指定模态窗口出现的位置方向(参看`ModalDirection`枚举),以适应更多的使用场景。
11 |
12 |
13 | ## TODO
14 |
15 | - 完善交互手势对滚动视图的支持
16 | - ~~提供Objective-C版本(已提供)~~
17 |
18 | ## 特性
19 |
20 | - 丰富的配置,适应多种使用场景
21 | - 支持交互式转场动画(当启动背景动画时,建议关闭该功能)
22 | - 简单易用的API,使用者只需关心 UIViewController 通过 extension 所提供的方法
23 |
24 |
25 | ## 环境要求
26 |
27 | - iOS8.0+
28 | - Swift4.0+ (Swift版本)
29 | - Xcode9.0+
30 |
31 |
32 | ## 用法
33 |
34 | - 将`UIViewController+Modal.swift`或`Modal-ObjC`文件夹拖入你的项目即可
35 |
36 | - 示例代码
37 |
38 | 1. 弹出控制器
39 |
40 | ```Swift
41 | let vc = UIViewController()
42 | vc.view.backgroundColor = .cyan
43 | presentModalViewController(vc, contentSize: CGSize(width: 200.0, height: 300.0), configuration: .default, completion: nil)
44 | ```
45 |
46 | 2. 弹出自定义视图
47 |
48 | ```Swift
49 | let view = UIView()
50 | view.backgroundColor = .brown
51 | presentModalView(view, contentSize: CGSize(width: 200.0, height: 300.0), configuration: .default, completion: nil)
52 | ```
53 |
54 | ## 演示
55 |
56 | []()
57 |
58 | []()
59 |
60 | []()
61 |
62 | []()
63 |
64 | []()
65 |
66 | ## 致谢
67 |
68 | - [KNSemiModalViewController](https://github.com/kentnguyen/KNSemiModalViewController)
69 | - [SemiModal](https://github.com/xiaopin/SemiModal.git)
70 | - [Custom View Controller Presentations and Transitions](https://developer.apple.com/library/content/samplecode/CustomTransitions/Introduction/Intro.html#//apple_ref/doc/uid/TP40015158)
71 |
72 | 感谢他们对开源社区做出的贡献。
73 |
74 | ## 协议
75 |
76 | 被许可在 MIT 协议下使用,查阅`LICENSE`文件来获得更多信息。
77 |
--------------------------------------------------------------------------------
/ExampleObjC/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // ExampleObjC
4 | //
5 | // Created by xiaopin on 2018/4/23.
6 | // Copyright © 2018年 xiaopin. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 |
11 | @interface AppDelegate ()
12 |
13 | @end
14 |
15 | @implementation AppDelegate
16 |
17 |
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
19 | // Override point for customization after application launch.
20 | return YES;
21 | }
22 |
23 |
24 | - (void)applicationWillResignActive:(UIApplication *)application {
25 | // 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.
26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
27 | }
28 |
29 |
30 | - (void)applicationDidEnterBackground:(UIApplication *)application {
31 | // 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.
32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
33 | }
34 |
35 |
36 | - (void)applicationWillEnterForeground:(UIApplication *)application {
37 | // 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.
38 | }
39 |
40 |
41 | - (void)applicationDidBecomeActive:(UIApplication *)application {
42 | // 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.
43 | }
44 |
45 |
46 | - (void)applicationWillTerminate:(UIApplication *)application {
47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
48 | }
49 |
50 |
51 | @end
52 |
--------------------------------------------------------------------------------
/Example/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Example
4 | //
5 | // Created by xiaopin on 2018/4/18.
6 | //
7 |
8 | import UIKit
9 |
10 | @UIApplicationMain
11 | class AppDelegate: UIResponder, UIApplicationDelegate {
12 |
13 | var window: UIWindow?
14 |
15 |
16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
17 | // Override point for customization after application launch.
18 | return true
19 | }
20 |
21 | func applicationWillResignActive(_ application: UIApplication) {
22 | // 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.
23 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
24 | }
25 |
26 | func applicationDidEnterBackground(_ application: UIApplication) {
27 | // 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.
28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
29 | }
30 |
31 | func applicationWillEnterForeground(_ application: UIApplication) {
32 | // 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.
33 | }
34 |
35 | func applicationDidBecomeActive(_ application: UIApplication) {
36 | // 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.
37 | }
38 |
39 | func applicationWillTerminate(_ application: UIApplication) {
40 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
41 | }
42 |
43 |
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/ExampleObjC/Modal-ObjC/UIViewController+XPModal.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+XPModal.h
3 | // https://github.com/xiaopin/iOS-Modal.git
4 | //
5 | // Created by xiaopin on 2018/4/23.
6 | // Copyright © 2018年 xiaopin. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "XPModalConfiguration.h"
11 | /** 配置的Block */
12 | typedef void (^XPModalConfigBlock)(XPModalConfiguration * _Nonnull configuration);
13 |
14 | typedef void(^XPModalCompletionHandler)(void);
15 |
16 |
17 | @interface UIViewController (XPModal)
18 |
19 | /**
20 | 显示一个模态视图控制器
21 |
22 | @param controller 模态视图控制器
23 | @param configBlock 模态窗口的配置信息
24 | @param completion 模态窗口显示完毕时的回调
25 | */
26 | - (void)presentModalWithViewController:(UIViewController *_Nonnull)controller configBlock:(XPModalConfigBlock _Nullable )configBlock completion:(XPModalCompletionHandler _Nullable)completion NS_AVAILABLE_IOS(8_0);
27 |
28 | /**
29 | 显示一个模态视图
30 |
31 | @param view 内容视图
32 | @param configBlock 模态窗口的配置信息
33 | @param completion 模态窗口显示完毕时的回调
34 | */
35 | - (void)presentModalWithView:(UIView *_Nonnull)view configBlock:(XPModalConfigBlock _Nullable )configBlock completion:(XPModalCompletionHandler _Nullable)completion NS_AVAILABLE_IOS(8_0);
36 |
37 |
38 | /**
39 | 显示一个模态视图控制器
40 |
41 | @param viewController 视图控制器
42 | @param contentSize 模态窗口大小(内部会限制宽高最大值为屏幕的宽高)
43 | @param configuration 模态窗口的配置信息
44 | @param completion 模态窗口显示完毕时的回调
45 | */
46 | - (void)presentModalWithViewController:(UIViewController * _Nonnull)viewController contentSize:(CGSize)contentSize configuration:(XPModalConfiguration * _Nonnull)configuration completion:(XPModalCompletionHandler _Nullable)completion NS_DEPRECATED_IOS(8_0, 8_0, "Use `-presentModalWithViewController:configBlock:completion:` instead") NS_AVAILABLE_IOS(8_0);
47 |
48 | /**
49 | 显示一个模态视图
50 |
51 | @param view 内容视图
52 | @param contentSize 模态窗口大小(内部会限制宽高最大值为屏幕的宽高)
53 | @param configuration 模态窗口的配置信息
54 | @param completion 模态窗口显示完毕时的回调
55 | */
56 | - (void)presentModalWithView:(UIView * _Nonnull)view contentSize:(CGSize)contentSize configuration:(XPModalConfiguration * _Nonnull)configuration completion:(XPModalCompletionHandler _Nullable)completion NS_DEPRECATED_IOS(8_0, 8_0, "Use `-presentModalWithView:configBlock:completion:` instead") NS_AVAILABLE_IOS(8_0);
57 |
58 | @end
59 |
--------------------------------------------------------------------------------
/ExampleObjC/Modal-ObjC/XPModalConfiguration.h:
--------------------------------------------------------------------------------
1 | //
2 | // XPModalConfiguration.h
3 | // https://github.com/xiaopin/iOS-Modal.git
4 | //
5 | // Created by xiaopin on 2018/4/23.
6 | // Copyright © 2018年 xiaopin. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | typedef NS_ENUM(NSInteger, XPModalDirection) {
12 | XPModalDirectionCenter,
13 | XPModalDirectionTop,
14 | XPModalDirectionRight,
15 | XPModalDirectionBottom,
16 | XPModalDirectionLeft
17 | };
18 |
19 |
20 |
21 | @interface XPModalConfiguration: NSObject
22 |
23 | /// 弹出的方向, 默认`XPModalDirectionBottom`从底部弹出
24 | @property (nonatomic, assign) XPModalDirection direction;
25 | /// 模态窗口大小(内部会限制宽高最大值为屏幕的宽高)
26 | @property (nonatomic, assign) CGSize contentSize;
27 | /// 动画时长, 默认`0.5s`
28 | @property (nonatomic, assign) NSTimeInterval animationDuration;
29 | /// 点击模态窗口之外的区域是否关闭模态窗口
30 | @property (nonatomic, assign, getter=isAutoDismissModal) BOOL autoDismissModal;
31 | /// 背景透明度, 0.0~1.0, 默认`0.3`
32 | @property (nonatomic, assign) CGFloat backgroundOpacity;
33 |
34 | /// 是否使用阴影效果
35 | @property (nonatomic, assign, getter=isEnableShadow) BOOL enableShadow;
36 | /// 阴影颜色, 默认`blackColor`
37 | @property (nonatomic, strong) UIColor *shadowColor;
38 | /// 阴影宽度, 默认`3.0`
39 | @property (nonatomic, assign) CGFloat shadowWidth;
40 | /// 阴影透明度, 0.0~1.0, 默认`0.8`
41 | @property (nonatomic, assign) CGFloat shadowOpacity;
42 | /// 阴影圆角, 默认`5.0`
43 | @property (nonatomic, assign) CGFloat shadowRadius;
44 |
45 | /// 是否启用背景动画, 默认`NO`
46 | @property (nonatomic, assign, getter=isEnableBackgroundAnimation) BOOL enableBackgroundAnimation;
47 | /// 背景颜色(需要设置`enableBackgroundAnimation`为YES)
48 | @property (nonatomic, strong) UIColor *backgroundColor;
49 | /// 背景图片(需要设置`enableBackgroundAnimation`为YES)
50 | @property (nonatomic, strong) UIImage *backgroundImage;
51 |
52 | /// 是否启用交互式转场动画(当direction == XPModalDirectionCenter时无效, 默认`YES`)
53 | @property (nonatomic, assign, getter=isEnableInteractiveTransitioning) BOOL enableInteractiveTransitioning;
54 |
55 | /// 交互手势(内部维护该手势,请忽略该属性)
56 | @property (nonatomic, strong) UIPanGestureRecognizer *panGestureRecognizer;
57 | /**
58 | 标记交互手势是否已经开始了(仅限内部使用, Internal use only)
59 |
60 | Fix: iOS9.x and iOS10.x tap gesture is failure.
61 | */
62 | @property (nonatomic, assign, getter=isStartedInteractiveTransitioning) BOOL startedInteractiveTransitioning;
63 |
64 |
65 | /// 默认配置
66 | + (instancetype)defaultConfiguration;
67 |
68 | @end
69 |
--------------------------------------------------------------------------------
/ExampleObjC/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // ExampleObjC
4 | //
5 | // Created by xiaopin on 2018/4/23.
6 | // Copyright © 2018年 xiaopin. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 | #import "UIViewController+XPModal.h"
11 |
12 | @interface ViewController ()
13 |
14 | @end
15 |
16 | @implementation ViewController
17 |
18 | - (void)viewDidLoad {
19 | [super viewDidLoad];
20 | // Do any additional setup after loading the view, typically from a nib.
21 | }
22 |
23 |
24 | - (void)didReceiveMemoryWarning {
25 | [super didReceiveMemoryWarning];
26 | // Dispose of any resources that can be recreated.
27 | }
28 |
29 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
30 | [tableView deselectRowAtIndexPath:indexPath animated:YES];
31 |
32 | UIViewController *vc = [[UIViewController alloc] init];
33 | vc.view.backgroundColor = [UIColor brownColor];
34 | [self presentModalWithViewController:vc configBlock:^(XPModalConfiguration * _Nonnull configuration) {
35 | switch (indexPath.row) {
36 | case 0:
37 | configuration.direction = XPModalDirectionTop;
38 | configuration.contentSize = CGSizeMake(CGFLOAT_MAX, 300.0);
39 | break;
40 | case 1:
41 | configuration.direction = XPModalDirectionRight;
42 | configuration.contentSize = CGSizeMake(200.0, CGFLOAT_MAX);
43 | break;
44 | case 2:
45 | configuration.direction = XPModalDirectionBottom;
46 | configuration.enableInteractiveTransitioning = NO;
47 | configuration.enableBackgroundAnimation = YES;
48 | configuration.backgroundColor = [UIColor purpleColor];
49 | configuration.contentSize = CGSizeMake(CGFLOAT_MAX, 300.0);
50 | break;
51 | case 3:
52 | configuration.direction = XPModalDirectionLeft;
53 | configuration.contentSize = CGSizeMake(200.0, [UIScreen mainScreen].bounds.size.height);
54 | break;
55 | case 4:
56 | configuration.direction = XPModalDirectionCenter;
57 | configuration.contentSize = CGSizeMake(200.0, 300.0);
58 | break;
59 | default: return;
60 | }
61 | } completion:nil];
62 |
63 | }
64 |
65 |
66 | @end
67 |
--------------------------------------------------------------------------------
/Example/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // Example
4 | //
5 | // Created by xiaopin on 2018/4/18.
6 | //
7 |
8 | import UIKit
9 |
10 | class ViewController: UITableViewController {
11 |
12 | override func viewDidLoad() {
13 | super.viewDidLoad()
14 | // Do any additional setup after loading the view, typically from a nib.
15 | }
16 |
17 | override func didReceiveMemoryWarning() {
18 | super.didReceiveMemoryWarning()
19 | // Dispose of any resources that can be recreated.
20 | }
21 |
22 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
23 | tableView.deselectRow(at: indexPath, animated: true)
24 | var size: CGSize
25 | var configuration = ModalConfiguration.default
26 | configuration.isEnableBackgroundAnimation = false
27 | switch indexPath.row {
28 | case 0:
29 | configuration.direction = .top
30 | size = CGSize(width: UIScreen.main.bounds.width, height: 300)
31 | case 1:
32 | configuration.direction = .right
33 | size = CGSize(width: 200.0, height: UIScreen.main.bounds.height)
34 | case 2:
35 | configuration.direction = .bottom
36 | configuration.isEnableBackgroundAnimation = true
37 | size = CGSize(width: UIScreen.main.bounds.width, height: 300)
38 | case 3:
39 | configuration.direction = .left
40 | size = CGSize(width: 200.0, height: UIScreen.main.bounds.height)
41 | case 4:
42 | configuration.direction = .center
43 | size = CGSize(width: 200.0, height: 300.0)
44 | default:
45 | return
46 | }
47 |
48 | // Use for UIViewController
49 | let storyboard = UIStoryboard(name: "Main", bundle: nil)
50 | let vc = storyboard.instantiateViewController(withIdentifier: "ModalViewController")
51 | presentModalViewController(vc, contentSize: size, configuration: configuration, completion: nil)
52 |
53 |
54 | // // Use for UIView
55 | // configuration.isDismissModal = false
56 | // let modalView = UIView()
57 | // modalView.backgroundColor = UIColor.cyan
58 | // presentModalView(modalView, contentSize: size, configuration: configuration, completion: nil)
59 | // DispatchQueue.main.asyncAfter(deadline: .now()+2.0) {
60 | // self.presentedViewController?.dismiss(animated: true, completion: nil)
61 | // }
62 | }
63 |
64 |
65 | }
66 |
67 |
--------------------------------------------------------------------------------
/ExampleObjC/Modal-ObjC/XPModalTransitioningDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // XPModalTransitioningDelegate.m
3 | // https://github.com/xiaopin/iOS-Modal.git
4 | //
5 | // Created by xiaopin on 2018/4/23.
6 | // Copyright © 2018年 xiaopin. All rights reserved.
7 | //
8 |
9 | #import "XPModalTransitioningDelegate.h"
10 | #import "XPModalConfiguration.h"
11 | #import "XPModalAnimatedTransitioning.h"
12 | #import "XPModalPresentationController.h"
13 | #import "XPModalPercentDrivenInteractiveTransition.h"
14 |
15 | @implementation XPModalTransitioningDelegate
16 | {
17 | XPModalConfiguration *_configuration;
18 | }
19 |
20 | + (instancetype)transitioningDelegateWithConfiguration:(XPModalConfiguration *)configuration {
21 | XPModalTransitioningDelegate *delegate = [[XPModalTransitioningDelegate alloc] init];
22 | delegate->_configuration = configuration;
23 | return delegate;
24 | }
25 |
26 | #pragma mark -
27 |
28 | - (id)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
29 | return [XPModalAnimatedTransitioning transitioningWithConfiguration:_configuration
30 | isPresentation:YES];
31 | }
32 |
33 | - (id)animationControllerForDismissedController:(UIViewController *)dismissed {
34 | return [XPModalAnimatedTransitioning transitioningWithConfiguration:_configuration
35 | isPresentation:NO];
36 | }
37 |
38 | - (id)interactionControllerForPresentation:(id)animator {
39 | return nil;
40 | }
41 |
42 | - (id)interactionControllerForDismissal:(id)animator {
43 | if (_configuration.enableInteractiveTransitioning & _configuration.isStartedInteractiveTransitioning) {
44 | return [XPModalPercentDrivenInteractiveTransition interactiveTransitionWithConfiguration:_configuration];
45 | }
46 | return nil;
47 | }
48 |
49 | - (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source {
50 | XPModalPresentationController *presentationController = [[XPModalPresentationController alloc] initWithPresentedViewController:presented presentingViewController:presenting];
51 | presentationController.configuration = _configuration;
52 | return presentationController;
53 | }
54 |
55 | @end
56 |
--------------------------------------------------------------------------------
/Example/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 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/ExampleObjC/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 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/ExampleObjC/Modal-ObjC/XPModalPercentDrivenInteractiveTransition.m:
--------------------------------------------------------------------------------
1 | //
2 | // XPModalPercentDrivenInteractiveTransition.m
3 | // https://github.com/xiaopin/iOS-Modal.git
4 | //
5 | // Created by xiaopin on 2018/4/23.
6 | // Copyright © 2018年 xiaopin. All rights reserved.
7 | //
8 |
9 | #import "XPModalPercentDrivenInteractiveTransition.h"
10 | #import "XPModalConfiguration.h"
11 |
12 | @interface XPModalPercentDrivenInteractiveTransition ()
13 |
14 | @property (nonatomic, strong) XPModalConfiguration *configuration;
15 | @property (nonatomic, strong) id transitionContext;
16 | @property (nonatomic, assign) CGPoint beganTouchPoint;
17 |
18 | @end
19 |
20 | @implementation XPModalPercentDrivenInteractiveTransition
21 |
22 | + (instancetype)interactiveTransitionWithConfiguration:(XPModalConfiguration *)configuration {
23 | XPModalPercentDrivenInteractiveTransition *interactiveTransition = [[XPModalPercentDrivenInteractiveTransition alloc] init];
24 | interactiveTransition.configuration = configuration;
25 | [configuration.panGestureRecognizer addTarget:interactiveTransition action:@selector(gestureRecognizeDidUpdate:)];
26 | return interactiveTransition;
27 | }
28 |
29 | /// 重写以便获取到上下文参数
30 | - (void)startInteractiveTransition:(id)transitionContext {
31 | _transitionContext = transitionContext;
32 | [super startInteractiveTransition:transitionContext];
33 | }
34 |
35 | /// 手势事件
36 | - (void)gestureRecognizeDidUpdate:(UIPanGestureRecognizer *)sender {
37 | switch (sender.state) {
38 | case UIGestureRecognizerStateBegan:
39 | // 开始状态由`XPModalPresentationController`进行处理,不会走到这里
40 | break;
41 | case UIGestureRecognizerStateChanged: {
42 | if (CGPointEqualToPoint(_beganTouchPoint, CGPointZero)) {
43 | _beganTouchPoint = [sender locationInView:_transitionContext.containerView];
44 | }
45 | CGFloat percent = [self percentForGesture:sender];
46 | [self updateInteractiveTransition:percent];
47 | break;
48 | }
49 | case UIGestureRecognizerStateEnded: {
50 | CGFloat percent = [self percentForGesture:sender];
51 | _beganTouchPoint = CGPointZero;
52 | if (percent >= 0.3) {
53 | [self finishInteractiveTransition];
54 | } else {
55 | [self cancelInteractiveTransition];
56 | }
57 | break;
58 | }
59 | default:
60 | _beganTouchPoint = CGPointZero;
61 | [self cancelInteractiveTransition];
62 | break;
63 | }
64 | }
65 |
66 | /// 计算手势滑动百分比
67 | - (CGFloat)percentForGesture:(UIPanGestureRecognizer *)sender {
68 | UIView *modalView = [_transitionContext viewForKey:UITransitionContextFromViewKey];
69 | CGPoint currentPoint = [sender locationInView:_transitionContext.containerView];
70 | CGFloat width = modalView.bounds.size.width;
71 | CGFloat height = modalView.bounds.size.height;
72 |
73 | switch (_configuration.direction) {
74 | case XPModalDirectionTop:
75 | if (currentPoint.y < _beganTouchPoint.y) {
76 | return (_beganTouchPoint.y - currentPoint.y) / height;
77 | }
78 | break;
79 | case XPModalDirectionRight:
80 | if (currentPoint.x > _beganTouchPoint.x) {
81 | return (currentPoint.x - _beganTouchPoint.x) / width;
82 | }
83 | break;
84 | case XPModalDirectionBottom:
85 | if (currentPoint.y > _beganTouchPoint.y) {
86 | return (currentPoint.y - _beganTouchPoint.y) / height;
87 | }
88 | break;
89 | case XPModalDirectionLeft:
90 | if (currentPoint.x < _beganTouchPoint.x) {
91 | return (_beganTouchPoint.x - currentPoint.x) / width;
92 | }
93 | break;
94 | case XPModalDirectionCenter:
95 | // Not support.
96 | break;
97 | }
98 | return 0.0;
99 | }
100 |
101 | @end
102 |
--------------------------------------------------------------------------------
/ExampleObjC/Modal-ObjC/XPModalAnimatedTransitioning.m:
--------------------------------------------------------------------------------
1 | //
2 | // XPModalAnimatedTransitioning.m
3 | // https://github.com/xiaopin/iOS-Modal.git
4 | //
5 | // Created by xiaopin on 2018/4/23.
6 | // Copyright © 2018年 xiaopin. All rights reserved.
7 | //
8 |
9 | #import "XPModalAnimatedTransitioning.h"
10 | #import "UIViewController+XPModal.h"
11 |
12 | @implementation XPModalAnimatedTransitioning
13 |
14 | + (instancetype)transitioningWithConfiguration:(XPModalConfiguration *)configuration isPresentation:(BOOL)presentation {
15 | XPModalAnimatedTransitioning *transitioning = [[XPModalAnimatedTransitioning alloc] init];
16 | transitioning.configuration = configuration;
17 | transitioning.presentation = presentation;
18 | return transitioning;
19 | }
20 |
21 | - (NSTimeInterval)transitionDuration:(nullable id)transitionContext {
22 | return self.configuration.animationDuration;
23 | }
24 |
25 | - (void)animateTransition:(nonnull id)transitionContext {
26 | UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
27 | UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
28 | UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
29 | UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
30 |
31 | BOOL isPresentation = self.isPresentation;
32 | if (isPresentation && toView) {
33 | [transitionContext.containerView addSubview:toView];
34 | }
35 |
36 | UIViewController *animatingVC = isPresentation ? toVC : fromVC;
37 | CGRect finalFrame = [transitionContext finalFrameForViewController:animatingVC];
38 | UIView *animatingView = isPresentation ? toView : fromView;
39 |
40 | switch (_configuration.direction) {
41 | case XPModalDirectionTop:
42 | animatingView.frame = isPresentation ? CGRectOffset(finalFrame, 0.0, -finalFrame.size.height) : finalFrame;
43 | break;
44 | case XPModalDirectionRight:
45 | animatingView.frame = isPresentation ? CGRectOffset(finalFrame, finalFrame.size.width, 0.0) : finalFrame;
46 | break;
47 | case XPModalDirectionBottom:
48 | animatingView.frame = isPresentation ? CGRectOffset(finalFrame, 0.0, finalFrame.size.height) : finalFrame;
49 | break;
50 | case XPModalDirectionLeft:
51 | animatingView.frame = isPresentation ? CGRectOffset(finalFrame, -finalFrame.size.width, 0.0) : finalFrame;
52 | break;
53 | case XPModalDirectionCenter:
54 | animatingView.frame = finalFrame;
55 | animatingView.alpha = isPresentation ? 0.0 : 1.0;
56 | break;
57 | }
58 |
59 | [UIView animateWithDuration:self.configuration.animationDuration delay:0.0 options:UIViewAnimationOptionAllowUserInteraction|UIViewAnimationOptionBeginFromCurrentState animations:^{
60 | switch (self.configuration.direction) {
61 | case XPModalDirectionTop:
62 | animatingView.frame = isPresentation ? finalFrame : CGRectOffset(finalFrame, 0.0, -finalFrame.size.height);
63 | break;
64 | case XPModalDirectionRight:
65 | animatingView.frame = isPresentation ? finalFrame : CGRectOffset(finalFrame, finalFrame.size.width, 0.0);
66 | break;
67 | case XPModalDirectionBottom:
68 | animatingView.frame = isPresentation ? finalFrame : CGRectOffset(finalFrame, 0.0, finalFrame.size.height);
69 | break;
70 | case XPModalDirectionLeft:
71 | animatingView.frame = isPresentation ? finalFrame : CGRectOffset(finalFrame, -finalFrame.size.width, 0.0);
72 | break;
73 | case XPModalDirectionCenter:
74 | animatingView.alpha = isPresentation ? 1.0 : 0.0;
75 | break;
76 | }
77 | } completion:^(BOOL finished) {
78 | BOOL wasCancelled = [transitionContext transitionWasCancelled];
79 | if (!self.isPresentation && !wasCancelled) {
80 | [fromView removeFromSuperview];
81 | }
82 | [transitionContext completeTransition:!wasCancelled];
83 | }];
84 | }
85 |
86 | @end
87 |
--------------------------------------------------------------------------------
/ExampleObjC/Modal-ObjC/UIViewController+XPModal.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+XPModal.m
3 | // https://github.com/xiaopin/iOS-Modal.git
4 | //
5 | // Created by xiaopin on 2018/4/23.
6 | // Copyright © 2018年 xiaopin. All rights reserved.
7 | //
8 |
9 | #import "UIViewController+XPModal.h"
10 | #import "XPModalTransitioningDelegate.h"
11 | #import
12 |
13 | #pragma mark -
14 | @implementation UIViewController (XPModal)
15 |
16 | /**
17 | 显示一个模态视图控制器
18 |
19 | @param controller 模态视图控制器
20 | @param configBlock 模态窗口的配置信息
21 | @param completion 模态窗口显示完毕时的回调
22 | */
23 | - (void)presentModalWithViewController:(UIViewController *_Nonnull)controller
24 | configBlock:(XPModalConfigBlock _Nullable )configBlock completion:(XPModalCompletionHandler _Nullable)completion{
25 | if (self.presentedViewController) { return; }
26 | XPModalConfiguration *config = [XPModalConfiguration defaultConfiguration];
27 | configBlock ? configBlock(config) : nil;
28 |
29 | controller.modalPresentationStyle = UIModalPresentationCustom;
30 | controller.preferredContentSize = config.contentSize;
31 |
32 | XPModalTransitioningDelegate *transitioningDelegate = [XPModalTransitioningDelegate transitioningDelegateWithConfiguration:config];
33 | controller.transitioningDelegate = transitioningDelegate;
34 | objc_setAssociatedObject(controller, _cmd, transitioningDelegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
35 |
36 | [self presentViewController:controller animated:true completion:completion];
37 | }
38 |
39 | /**
40 | 显示一个模态视图
41 |
42 | @param view 内容视图
43 | @param configBlock 模态窗口的配置信息
44 | @param completion 模态窗口显示完毕时的回调
45 | */
46 | - (void)presentModalWithView:(UIView *_Nonnull)view configBlock:(XPModalConfigBlock _Nullable )configBlock completion:(XPModalCompletionHandler _Nullable)completion NS_AVAILABLE_IOS(8_0) {
47 | UIViewController *modalVC = [[UIViewController alloc] init];
48 | modalVC.view.backgroundColor = [UIColor clearColor];
49 | [modalVC.view addSubview:view];
50 |
51 | view.translatesAutoresizingMaskIntoConstraints = NO;
52 | NSDictionary *views = @{@"view": view};
53 | [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:views]];
54 | [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:views]];
55 |
56 | [self presentModalWithViewController:modalVC configBlock:configBlock completion:completion];
57 | }
58 |
59 | /**
60 | 显示一个模态视图控制器(已废弃)
61 |
62 | @param viewController 视图控制器
63 | @param contentSize 模态窗口大小(内部会限制宽高最大值为屏幕的宽高)
64 | @param configuration 模态窗口的配置信息
65 | @param completion 模态窗口显示完毕时的回调
66 | */
67 | - (void)presentModalWithViewController:(UIViewController * _Nonnull)viewController contentSize:(CGSize)contentSize configuration:(XPModalConfiguration * _Nonnull)configuration completion:(XPModalCompletionHandler _Nullable)completion {
68 | NSAssert(configuration != nil, @"configuration cann't be nil.");
69 | if (self.presentedViewController) { return; }
70 | viewController.modalPresentationStyle = UIModalPresentationCustom;
71 | viewController.preferredContentSize = contentSize;
72 |
73 | XPModalTransitioningDelegate *transitioningDelegate = [XPModalTransitioningDelegate transitioningDelegateWithConfiguration:configuration];
74 | viewController.transitioningDelegate = transitioningDelegate;
75 | objc_setAssociatedObject(viewController, _cmd, transitioningDelegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
76 |
77 | [self presentViewController:viewController animated:true completion:completion];
78 | }
79 |
80 | /**
81 | 显示一个模态视图(已废弃)
82 |
83 | @param view 内容视图
84 | @param contentSize 模态窗口大小(内部会限制宽高最大值为屏幕的宽高)
85 | @param configuration 模态窗口的配置信息
86 | @param completion 模态窗口显示完毕时的回调
87 | */
88 | - (void)presentModalWithView:(UIView * _Nonnull)view contentSize:(CGSize)contentSize configuration:(XPModalConfiguration * _Nonnull)configuration completion:(XPModalCompletionHandler _Nullable)completion {
89 | UIViewController *modalVC = [[UIViewController alloc] init];
90 | modalVC.view.backgroundColor = [UIColor clearColor];
91 | [modalVC.view addSubview:view];
92 |
93 | view.translatesAutoresizingMaskIntoConstraints = NO;
94 | NSDictionary *views = @{@"view": view};
95 | [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:views]];
96 | [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:views]];
97 |
98 | [self presentModalWithViewController:modalVC contentSize:contentSize configuration:configuration completion:completion];
99 | }
100 |
101 | @end
102 |
--------------------------------------------------------------------------------
/ExampleObjC/Modal-ObjC/XPModalPresentationController.m:
--------------------------------------------------------------------------------
1 | //
2 | // XPModalPresentationController.m
3 | // https://github.com/xiaopin/iOS-Modal.git
4 | //
5 | // Created by xiaopin on 2018/4/23.
6 | // Copyright © 2018年 xiaopin. All rights reserved.
7 | //
8 |
9 | #import "XPModalPresentationController.h"
10 | #import "XPModalConfiguration.h"
11 |
12 | @interface XPModalPresentationController ()
13 |
14 | /// 背景图片视图
15 | @property (nonatomic, strong) UIImageView *backgroundView;
16 | /// 背景半透明效果视图
17 | @property (nonatomic, strong) UIView *dimmingView;
18 | /// 执行背景动画的动画视图
19 | @property (nonatomic, strong) UIView *animatingView;
20 | /// 是否正在交互
21 | @property (nonatomic, assign, getter=isInteractiving) BOOL interactiving;
22 |
23 | @end
24 |
25 |
26 | @implementation XPModalPresentationController
27 |
28 | - (instancetype)initWithPresentedViewController:(UIViewController *)presentedViewController presentingViewController:(UIViewController *)presentingViewController {
29 | self = [super initWithPresentedViewController:presentedViewController presentingViewController:presentingViewController];
30 | if (self) {
31 | _backgroundView = [[UIImageView alloc] init];
32 | _dimmingView = [[UIView alloc] init];
33 | UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureRecognizerAction:)];
34 | [_dimmingView addGestureRecognizer:tapGesture];
35 | }
36 | return self;
37 | }
38 |
39 | /// 弹窗视图的最终frame
40 | - (CGRect)frameOfPresentedViewInContainerView {
41 | CGRect presentedViewFrame = CGRectZero;
42 | CGFloat const containerWidth = self.containerView.bounds.size.width;
43 | CGFloat const containerHeight = self.containerView.bounds.size.height;
44 | CGFloat width = MIN(containerWidth, self.presentedViewController.preferredContentSize.width);
45 | CGFloat height = MIN(containerHeight, self.presentedViewController.preferredContentSize.height);
46 | presentedViewFrame.size = CGSizeMake(width, height);
47 |
48 | CGFloat x = 0.0, y = 0.0;
49 | switch (_configuration.direction) {
50 | case XPModalDirectionCenter:
51 | x = (containerWidth - width) / 2;
52 | y = (containerHeight - height) / 2;
53 | break;
54 | case XPModalDirectionTop:
55 | x = (containerWidth - width) / 2;
56 | break;
57 | case XPModalDirectionRight:
58 | x = containerWidth - width;
59 | y = (containerHeight - height) / 2;
60 | break;
61 | case XPModalDirectionBottom:
62 | x = (containerWidth - width) / 2;
63 | y = containerHeight - height;
64 | break;
65 | case XPModalDirectionLeft:
66 | y = (containerHeight - height) / 2;
67 | break;
68 | }
69 | presentedViewFrame.origin = CGPointMake(x, y);
70 |
71 | return presentedViewFrame;
72 | }
73 |
74 | - (void)containerViewWillLayoutSubviews {
75 | [super containerViewWillLayoutSubviews];
76 | _backgroundView.frame = self.containerView.bounds;
77 | _dimmingView.frame = self.containerView.bounds;
78 | self.presentedView.frame = [self frameOfPresentedViewInContainerView];
79 | }
80 |
81 | /// 即将显示弹窗(显示的动画)
82 | - (void)presentationTransitionWillBegin {
83 | [super presentationTransitionWillBegin];
84 |
85 | // 启用背景动画
86 | if (_configuration.isEnableBackgroundAnimation) {
87 | UIWindow *window = [UIApplication sharedApplication].keyWindow;
88 | NSAssert(window != nil, @"UIApplication keyWindow is nil.");
89 | UIView *snapshotView = [window snapshotViewAfterScreenUpdates:YES];
90 | _animatingView = snapshotView;
91 | [_backgroundView addSubview:snapshotView];
92 | snapshotView.translatesAutoresizingMaskIntoConstraints = NO;
93 | NSDictionary *views = @{@"snapshotView": snapshotView};
94 | [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[snapshotView]|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:views]];
95 | [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[snapshotView]|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:views]];
96 |
97 | _backgroundView.backgroundColor = _configuration.backgroundColor;
98 | _backgroundView.image = _configuration.backgroundImage;
99 | [self.containerView addSubview:_backgroundView];
100 | }
101 |
102 | // 添加阴影效果
103 | if (_configuration.isEnableShadow) {
104 | CGFloat shadowWidth = ABS(_configuration.shadowWidth);
105 | switch (_configuration.direction) {
106 | case XPModalDirectionTop:
107 | self.presentedView.layer.shadowOffset = CGSizeMake(shadowWidth, shadowWidth);
108 | break;
109 | case XPModalDirectionRight:
110 | self.presentedView.layer.shadowOffset = CGSizeMake(-shadowWidth, shadowWidth);
111 | break;
112 | default:
113 | self.presentedView.layer.shadowOffset = CGSizeMake(shadowWidth, -shadowWidth);
114 | break;
115 | }
116 | self.presentedView.layer.shadowColor = _configuration.shadowColor.CGColor;
117 | self.presentedView.layer.shadowRadius = _configuration.shadowRadius;
118 | self.presentedView.layer.shadowOpacity = _configuration.shadowOpacity;
119 | self.presentedView.layer.shouldRasterize = true;
120 | self.presentedView.layer.rasterizationScale = [UIScreen mainScreen].scale;
121 | }
122 |
123 | // 启用手势交互功能
124 | if (_configuration.isEnableInteractiveTransitioning && _configuration.direction != XPModalDirectionCenter) {
125 | UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureRecognizerAction:)];
126 | [self.containerView addGestureRecognizer:panGesture];
127 | _configuration.panGestureRecognizer = panGesture;
128 | }
129 |
130 | _dimmingView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:_configuration.backgroundOpacity];
131 | _dimmingView.alpha = 0.0;
132 | [self.containerView addSubview:_dimmingView];
133 |
134 | [self.presentedViewController.transitionCoordinator animateAlongsideTransition:^(id _Nonnull context) {
135 | self.dimmingView.alpha = 1.0;
136 | if (self.configuration.isEnableBackgroundAnimation) {
137 | CAAnimationGroup *group = [self backgroundTranslateAnimationWithForward:YES];
138 | [self.animatingView.layer addAnimation:group forKey:nil];
139 | }
140 | } completion:nil];
141 | }
142 |
143 | /// 即将关闭弹窗(消失动画)
144 | - (void)dismissalTransitionWillBegin {
145 | [super dismissalTransitionWillBegin];
146 | if (_configuration.isEnableInteractiveTransitioning && _interactiving) {
147 | return;
148 | }
149 | [self.presentedViewController.transitionCoordinator animateAlongsideTransition:^(id _Nonnull context) {
150 | self.dimmingView.alpha = 0.0;
151 | if (self.configuration.isEnableBackgroundAnimation) {
152 | CAAnimationGroup *group = [self backgroundTranslateAnimationWithForward:NO];
153 | [self.animatingView.layer addAnimation:group forKey:nil];
154 | }
155 | } completion:nil];
156 | }
157 |
158 | #pragma mark - Actions
159 |
160 | /// 背景点击关闭弹窗
161 | - (void)tapGestureRecognizerAction:(UITapGestureRecognizer *)sender {
162 | if (_configuration.autoDismissModal) {
163 | [self.presentedViewController dismissViewControllerAnimated:YES completion:nil];
164 | }
165 | }
166 |
167 | /// 滑动关闭弹窗
168 | - (void)panGestureRecognizerAction:(UIPanGestureRecognizer *)sender {
169 | switch (sender.state) {
170 | case UIGestureRecognizerStateBegan:
171 | _interactiving = YES;
172 | _configuration.startedInteractiveTransitioning = YES;
173 | [self.presentedViewController dismissViewControllerAnimated:YES completion:nil];
174 | break;
175 | case UIGestureRecognizerStateChanged:
176 | // nothing to do.
177 | break;
178 | default:
179 | _interactiving = NO;
180 | _configuration.startedInteractiveTransitioning = NO;
181 | break;
182 | }
183 | }
184 |
185 | #pragma mark - Private
186 |
187 | /// 背景动画
188 | - (CAAnimationGroup *)backgroundTranslateAnimationWithForward:(BOOL)isForward {
189 | BOOL const iPad = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad;
190 | CGFloat translateFactor = iPad ? -0.08 : -0.04;
191 | CGFloat rotateFactor = iPad ? 7.5 : 15.0;
192 | CATransform3D t1 = CATransform3DIdentity;
193 | t1.m34 = 1.0 / -900;
194 | t1 = CATransform3DScale(t1, 0.95, 0.95, 1.0);
195 | t1 = CATransform3DRotate(t1, rotateFactor * M_PI / 180.0, 1.0, 0.0, 0.0);
196 |
197 | CATransform3D t2 = CATransform3DIdentity;
198 | t2.m34 = t1.m34;
199 | t2 = CATransform3DTranslate(t2, 0.0, self.presentedViewController.view.bounds.size.height*translateFactor, 0.0);
200 | t2 = CATransform3DScale(t2, 0.8, 0.8, 1.0);
201 |
202 | CABasicAnimation *a1 = [CABasicAnimation animationWithKeyPath:@"transform"];
203 | a1.toValue = [NSValue valueWithCATransform3D:t1];
204 | a1.duration = _configuration.animationDuration / 2;
205 | a1.fillMode = kCAFillModeForwards;
206 | a1.removedOnCompletion = NO;
207 | a1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
208 |
209 | CABasicAnimation *a2 = [CABasicAnimation animationWithKeyPath:@"transform"];
210 | a2.toValue = [NSValue valueWithCATransform3D:(isForward ? t2 : CATransform3DIdentity)];
211 | a2.beginTime = a1.duration;
212 | a2.duration = a1.duration;
213 | a2.fillMode = kCAFillModeForwards;
214 | a2.removedOnCompletion = NO;
215 |
216 | CAAnimationGroup *group = [CAAnimationGroup animation];
217 | group.fillMode = kCAFillModeForwards;
218 | group.removedOnCompletion = NO;
219 | group.duration = _configuration.animationDuration;
220 | group.animations = @[a1, a2];
221 | return group;
222 | }
223 |
224 | @end
225 |
--------------------------------------------------------------------------------
/ExampleObjC/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 |
25 |
26 |
27 |
28 |
29 |
30 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
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 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
--------------------------------------------------------------------------------
/Example/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 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
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 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
--------------------------------------------------------------------------------
/Modal/UIViewController+Modal.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+Modal.swift
3 | // https://github.com/xiaopin/iOS-Modal.git
4 | //
5 | // Created by xiaopin on 2018/4/18.
6 | // Copyright © 2018年 xiaopin. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 |
12 | // MARK: *** Public ***
13 |
14 | /// 弹窗配置
15 | class ModalConfiguration {
16 |
17 | /// 弹出的方向, 默认`.bottom`从底部弹出
18 | var direction: ModalDirection = .bottom
19 | /// 动画时长, 默认`0.5s`
20 | var animationDuration: TimeInterval = 0.5
21 | /// 点击模态窗口之外的区域是否关闭模态窗口
22 | var isDismissModal: Bool = true
23 | /// 背景透明度, 0.0~1.0, 默认`0.3`
24 | var backgroundOpacity: CGFloat = 0.3
25 |
26 | /// 是否使用阴影效果
27 | var isEnableShadow = true
28 | /// 阴影颜色, 默认`.black`
29 | var shadowColor: UIColor = .black
30 | /// 阴影宽度, 默认`3.0`
31 | var shadowWidth: CGFloat = 3.0
32 | /// 阴影透明度, 0.0~1.0, 默认`0.8`
33 | var shadowOpacity: Float = 0.8
34 | /// 阴影圆角, 默认`5.0`
35 | var shadowRadius: CGFloat = 5.0
36 |
37 | /// 是否启用背景动画
38 | var isEnableBackgroundAnimation = false
39 | /// 背景颜色(需要设置`isEnableBackgroundAnimation`为true)
40 | var backgroundColor = UIColor.black
41 | /// 背景图片(需要设置`isEnableBackgroundAnimation`为true)
42 | var backgroundImage: UIImage?
43 |
44 | /// 是否启用交互式转场动画(当direction == .center时无效)
45 | var isEnableInteractiveTransitioning: Bool = true
46 | /// 标记交互式是否已经开始
47 | /// Fix: iOS9.x and iOS10.x tap gesture is failure.
48 | fileprivate var isStartedInteractiveTransitioning: Bool = false
49 | /// 交互手势
50 | fileprivate var panGestureRecognizer: UIPanGestureRecognizer?
51 |
52 | /// 默认配置
53 | static var `default`: ModalConfiguration {
54 | return ModalConfiguration()
55 | }
56 |
57 | init() {}
58 |
59 | /// 控制器弹出方向
60 | enum ModalDirection {
61 | case top, right, bottom, left, center
62 | }
63 |
64 | }
65 |
66 |
67 | /// runtime key
68 | private var key: Void?
69 |
70 | // MARK: -
71 | extension UIViewController {
72 |
73 | /// 显示一个模态视图控制器
74 | ///
75 | /// - Parameters:
76 | /// - contentViewController: 模态视图控制器
77 | /// - contentSize: 模态视图宽高
78 | /// - configuration: 模态窗口的配置信息
79 | /// - completion: 模态窗口显示完毕时的回调
80 | @available(iOS 8.0, *)
81 | func presentModalViewController(_ contentViewController: UIViewController, contentSize: CGSize, configuration: ModalConfiguration = .default, completion: (() -> Void)? = nil) {
82 | if let _ = presentedViewController { return }
83 | contentViewController.modalPresentationStyle = .custom
84 | contentViewController.preferredContentSize = contentSize
85 |
86 | let transitioningDelegate = ModalTransitioningDelegate(configuration: configuration)
87 | contentViewController.transitioningDelegate = transitioningDelegate
88 | // Keep strong references.
89 | objc_setAssociatedObject(contentViewController, &key, transitioningDelegate, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
90 |
91 | present(contentViewController, animated: true, completion: completion)
92 | }
93 |
94 |
95 | /// 显示一个模态视图控制器
96 | ///
97 | /// 内部会创建一个UIViewController并将contentView添加到该控制器的view上,并添加`距离父视图上下左右均为0`的约束.
98 | /// 如果需要手动关闭模态窗口,则`谁弹出谁负责关闭`,即`self.presentedViewController?.dismiss(animated: true, completion: nil)`
99 | ///
100 | /// - Parameters:
101 | /// - contentView: 模态视图
102 | /// - contentSize: 模态视图宽高
103 | /// - configuration: 模态窗口配置信息
104 | /// - completion: 模态窗口显示完毕时的回调
105 | @available(iOS 8.0, *)
106 | func presentModalView(_ contentView: UIView, contentSize: CGSize, configuration: ModalConfiguration = .default, completion: (() -> Void)? = nil) {
107 | let contentViewController = UIViewController()
108 | contentViewController.view.backgroundColor = .clear
109 | contentViewController.view.addSubview(contentView)
110 |
111 | let views = ["contentView": contentView]
112 | contentView.translatesAutoresizingMaskIntoConstraints = false
113 | NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|[contentView]|", options: .directionLeadingToTrailing, metrics: nil, views: views))
114 | NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:|[contentView]|", options: .directionLeadingToTrailing, metrics: nil, views: views))
115 |
116 | presentModalViewController(contentViewController, contentSize: contentSize, configuration: configuration, completion: completion)
117 | }
118 |
119 | }
120 |
121 |
122 | // MARK: *** Private ***
123 |
124 | // MARK: -
125 | private class ModalTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
126 |
127 | let configuration: ModalConfiguration
128 |
129 | init(configuration: ModalConfiguration) {
130 | self.configuration = configuration
131 | }
132 |
133 | func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
134 | let presentationController = ModalPresentationController(presentedViewController: presented, presenting: presenting, configuration: configuration)
135 | return presentationController
136 | }
137 |
138 | func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
139 | return ModalAnimatedTransitioning(configuration: configuration, isPresentation: true)
140 | }
141 |
142 | func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
143 | return ModalAnimatedTransitioning(configuration: configuration, isPresentation: false)
144 | }
145 |
146 | func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
147 | return nil
148 | }
149 |
150 | func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
151 | if configuration.isEnableInteractiveTransitioning && configuration.isStartedInteractiveTransitioning {
152 | return ModalPercentDrivenInteractiveTransition(configuration: configuration)
153 | }
154 | return nil
155 | }
156 |
157 | }
158 |
159 |
160 | // MARK: -
161 | private class ModalPercentDrivenInteractiveTransition: UIPercentDrivenInteractiveTransition {
162 |
163 | private let configuration: ModalConfiguration
164 | private var transitionContext: UIViewControllerContextTransitioning?
165 | private var beganTouchPoint: CGPoint?
166 |
167 | init(configuration: ModalConfiguration) {
168 | self.configuration = configuration
169 | super.init()
170 | guard let panGestureRecognizer = configuration.panGestureRecognizer else { return }
171 | panGestureRecognizer.addTarget(self, action: #selector(gestureRecognizeDidUpdate(_:)))
172 | }
173 |
174 | override func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) {
175 | self.transitionContext = transitionContext
176 | super.startInteractiveTransition(transitionContext)
177 | }
178 |
179 | @objc private func gestureRecognizeDidUpdate(_ sender: UIPanGestureRecognizer) {
180 | switch sender.state {
181 | case .began:
182 | // 开始状态由`ModalPresentationController`进行处理,不会走到这里
183 | break
184 | case .changed:
185 | if nil == beganTouchPoint {
186 | guard let transitionContext = transitionContext else { return }
187 | let transitionContainerView = transitionContext.containerView
188 | beganTouchPoint = sender.location(in: transitionContainerView)
189 | }
190 | update(percentForGesture(sender))
191 | case .ended:
192 | (percentForGesture(sender) > 0.3) ? finish() : cancel()
193 | beganTouchPoint = nil
194 | default:
195 | beganTouchPoint = nil
196 | cancel()
197 | }
198 | }
199 |
200 | private func percentForGesture(_ sender: UIPanGestureRecognizer) -> CGFloat {
201 | guard
202 | let transitionContext = transitionContext,
203 | let beganTouchPoint = beganTouchPoint,
204 | let modalView = transitionContext.viewController(forKey: .from)?.view
205 | else { return 0.0 }
206 |
207 | let transitionContainerView = transitionContext.containerView
208 | let currentPoint = sender.location(in: transitionContainerView)
209 | let width = modalView.bounds.width
210 | let height = modalView.bounds.height
211 |
212 | switch configuration.direction {
213 | case .top:
214 | if currentPoint.y < beganTouchPoint.y {
215 | let offset = beganTouchPoint.y - currentPoint.y
216 | return offset / height
217 | }
218 | case .right:
219 | if currentPoint.x > beganTouchPoint.x {
220 | let offset = currentPoint.x - beganTouchPoint.x
221 | return offset / width
222 | }
223 | case .bottom:
224 | if currentPoint.y > beganTouchPoint.y {
225 | let offset = currentPoint.y - beganTouchPoint.y
226 | return offset / height
227 | }
228 | case .left:
229 | if currentPoint.x < beganTouchPoint.x {
230 | let offset = beganTouchPoint.x - currentPoint.x
231 | return offset / width
232 | }
233 | case .center: // None, Not supoort
234 | break
235 | }
236 | return 0.0
237 | }
238 |
239 | }
240 |
241 |
242 | // MARK: -
243 | private class ModalAnimatedTransitioning: NSObject, UIViewControllerAnimatedTransitioning {
244 |
245 | let isPresentation: Bool
246 | let configuration: ModalConfiguration
247 |
248 | init(configuration: ModalConfiguration, isPresentation: Bool) {
249 | self.configuration = configuration
250 | self.isPresentation = isPresentation
251 | }
252 |
253 | /// 返回动画之行时长
254 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
255 | return configuration.animationDuration
256 | }
257 |
258 | /// 执行动画
259 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
260 | guard
261 | let fromVC = transitionContext.viewController(forKey: .from),
262 | let toVC = transitionContext.viewController(forKey: .to)
263 | else { return }
264 | let fromView = transitionContext.view(forKey: .from)
265 | let toView = transitionContext.view(forKey: .to)
266 |
267 | if isPresentation, let toView = toView {
268 | transitionContext.containerView.addSubview(toView)
269 | }
270 |
271 | let animatingVC = isPresentation ? toVC : fromVC
272 | let finalFrame = transitionContext.finalFrame(for: animatingVC)
273 |
274 | switch configuration.direction {
275 | case .top:
276 | animatingVC.view.frame = isPresentation ? finalFrame.offsetBy(dx: 0.0, dy: -finalFrame.height) : finalFrame
277 | case .right:
278 | animatingVC.view.frame = isPresentation ? finalFrame.offsetBy(dx: finalFrame.width, dy: 0.0) : finalFrame
279 | case .bottom:
280 | animatingVC.view.frame = isPresentation ? finalFrame.offsetBy(dx: 0.0, dy: finalFrame.height) : finalFrame
281 | case .left:
282 | animatingVC.view.frame = isPresentation ? finalFrame.offsetBy(dx: -finalFrame.width, dy: 0.0) : finalFrame
283 | case .center:
284 | animatingVC.view.frame = finalFrame
285 | animatingVC.view.alpha = isPresentation ? 0.0 : 1.0
286 | }
287 |
288 | UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0.0, options: [.allowUserInteraction, .beginFromCurrentState], animations: {
289 | switch self.configuration.direction {
290 | case .top:
291 | animatingVC.view.frame = self.isPresentation ? finalFrame : finalFrame.offsetBy(dx: 0.0, dy: -finalFrame.height)
292 | case .right:
293 | animatingVC.view.frame = self.isPresentation ? finalFrame : finalFrame.offsetBy(dx: finalFrame.width, dy: 0.0)
294 | case .bottom:
295 | animatingVC.view.frame = self.isPresentation ? finalFrame : finalFrame.offsetBy(dx: 0.0, dy: finalFrame.height)
296 | case .left:
297 | animatingVC.view.frame = self.isPresentation ? finalFrame : finalFrame.offsetBy(dx: -finalFrame.width, dy: 0.0)
298 | case .center:
299 | animatingVC.view.alpha = self.isPresentation ? 1.0 : 0.0
300 | }
301 | }) { (_) in
302 | let wasCancelled = transitionContext.transitionWasCancelled
303 | if !self.isPresentation && !wasCancelled {
304 | fromView?.removeFromSuperview()
305 | }
306 | transitionContext.completeTransition(!wasCancelled)
307 | }
308 | }
309 |
310 | }
311 |
312 |
313 | // MARK: -
314 | private class ModalPresentationController: UIPresentationController {
315 |
316 | private let configuration: ModalConfiguration
317 | private var animatingView: UIView?
318 | private let backgroundView = UIImageView()
319 | private let dimmingView = UIView()
320 | /// 是否正在交互
321 | private var isInteractiving: Bool = false
322 |
323 | init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?, configuration: ModalConfiguration) {
324 | self.configuration = configuration
325 | super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
326 |
327 | let tap = UITapGestureRecognizer(target: self, action: #selector(tapGestureRecognizerAction(_:)))
328 | dimmingView.addGestureRecognizer(tap)
329 | }
330 |
331 | /// 返回模态窗口的frame
332 | override var frameOfPresentedViewInContainerView: CGRect {
333 | guard let containerSize = containerView?.bounds.size else {
334 | return .zero
335 | }
336 | var presentedViewFrame = CGRect.zero
337 | let width = min(containerSize.width, presentedViewController.preferredContentSize.width)
338 | let height = min(containerSize.height, presentedViewController.preferredContentSize.height)
339 |
340 | presentedViewFrame.size = CGSize(width: width, height: height)
341 | let x: CGFloat, y: CGFloat
342 | switch configuration.direction {
343 | case .top:
344 | x = (containerSize.width - width) / 2
345 | y = 0.0
346 | case .right:
347 | x = containerSize.width - width
348 | y = (containerSize.height - height) / 2
349 | case .bottom:
350 | x = (containerSize.width - width) / 2
351 | y = containerSize.height - height
352 | case .left:
353 | x = 0.0
354 | y = (containerSize.height - height) / 2
355 | case .center:
356 | x = (containerSize.width - width) / 2
357 | y = (containerSize.height - height) / 2
358 | }
359 | presentedViewFrame.origin = CGPoint(x: x, y: y)
360 | return presentedViewFrame
361 | }
362 |
363 | override func containerViewWillLayoutSubviews() {
364 | super.containerViewWillLayoutSubviews()
365 | backgroundView.frame = containerView?.bounds ?? .zero
366 | dimmingView.frame = containerView?.bounds ?? .zero
367 | presentedView?.frame = frameOfPresentedViewInContainerView
368 | }
369 |
370 | override func presentationTransitionWillBegin() {
371 | super.presentationTransitionWillBegin()
372 |
373 | // 启用背景动画,需要截屏保存当前屏幕图像
374 | if configuration.isEnableBackgroundAnimation {
375 | if let window = UIApplication.shared.keyWindow,
376 | let snapshotView = window.snapshotView(afterScreenUpdates: true) {
377 | let views = ["view": snapshotView]
378 | animatingView = snapshotView
379 | backgroundView.addSubview(snapshotView)
380 | snapshotView.translatesAutoresizingMaskIntoConstraints = false
381 | NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|[view]|", options: .directionLeadingToTrailing, metrics: nil, views: views))
382 | NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:|[view]|", options: .directionLeadingToTrailing, metrics: nil, views: views))
383 | }
384 | backgroundView.backgroundColor = configuration.backgroundColor
385 | backgroundView.image = configuration.backgroundImage
386 | containerView?.addSubview(backgroundView)
387 | }
388 |
389 | // 添加阴影效果
390 | if configuration.isEnableShadow {
391 | let shadowWidth = abs(configuration.shadowWidth)
392 | presentedView?.layer.shadowColor = configuration.shadowColor.cgColor
393 | switch configuration.direction {
394 | case .top:
395 | presentedView?.layer.shadowOffset = CGSize(width: shadowWidth, height: shadowWidth)
396 | case .right:
397 | presentedView?.layer.shadowOffset = CGSize(width: -shadowWidth, height: shadowWidth)
398 | case .bottom, .left, .center:
399 | presentedView?.layer.shadowOffset = CGSize(width: shadowWidth, height: -shadowWidth)
400 | }
401 | presentedView?.layer.shadowRadius = configuration.shadowRadius
402 | presentedView?.layer.shadowOpacity = configuration.shadowOpacity
403 | presentedView?.layer.shouldRasterize = true
404 | presentedView?.layer.rasterizationScale = UIScreen.main.scale
405 | }
406 |
407 | // 启用手势交互功能,添加交互手势
408 | if configuration.isEnableInteractiveTransitioning && configuration.direction != .center {
409 | let panGestureRecognizer = UIPanGestureRecognizer(target: nil, action: nil)
410 | containerView?.addGestureRecognizer(panGestureRecognizer)
411 | self.configuration.panGestureRecognizer = panGestureRecognizer
412 | panGestureRecognizer.addTarget(self, action: #selector(gestureRecognizeDidUpdate(_:)))
413 | }
414 |
415 | dimmingView.backgroundColor = UIColor(white: 0.0, alpha: configuration.backgroundOpacity)
416 | dimmingView.alpha = 0.0
417 | containerView?.addSubview(dimmingView)
418 |
419 | // 执行背景动画
420 | presentedViewController.transitionCoordinator?.animate(alongsideTransition: { (_) in
421 | self.dimmingView.alpha = 1.0
422 | if self.configuration.isEnableBackgroundAnimation {
423 | let animation = self.backgroundTranslateAnimation(true)
424 | self.animatingView?.layer.add(animation, forKey: nil)
425 | }
426 | }, completion: nil)
427 | }
428 |
429 | override func dismissalTransitionWillBegin() {
430 | super.dismissalTransitionWillBegin()
431 | if configuration.isEnableInteractiveTransitioning && isInteractiving { return }
432 | presentedViewController.transitionCoordinator?.animate(alongsideTransition: { (_) in
433 | self.dimmingView.alpha = 0.0
434 | if self.configuration.isEnableBackgroundAnimation {
435 | let animation = self.backgroundTranslateAnimation(false)
436 | self.animatingView?.layer.add(animation, forKey: nil)
437 | }
438 | }, completion: nil)
439 | }
440 |
441 | @objc private func tapGestureRecognizerAction(_ sender: UITapGestureRecognizer) {
442 | if configuration.isDismissModal {
443 | presentedViewController.dismiss(animated: true, completion: nil)
444 | }
445 | }
446 |
447 | @objc private func gestureRecognizeDidUpdate(_ sender: UIPanGestureRecognizer) {
448 | switch sender.state {
449 | case .began:
450 | isInteractiving = true
451 | configuration.isStartedInteractiveTransitioning = true
452 | presentedViewController.dismiss(animated: true, completion: nil)
453 | case .changed:
454 | break
455 | default:
456 | isInteractiving = false
457 | configuration.isStartedInteractiveTransitioning = false
458 | }
459 | }
460 |
461 | private func backgroundTranslateAnimation(_ forward: Bool) -> CAAnimationGroup {
462 | let iPad = UI_USER_INTERFACE_IDIOM() == .pad
463 | let translateFactor: CGFloat = iPad ? -0.08 : -0.04
464 | let rotateFactor: Double = iPad ? 7.5 : 15.0
465 |
466 | var t1 = CATransform3DIdentity
467 | t1.m34 = CGFloat(1.0 / -900)
468 | t1 = CATransform3DScale(t1, 0.95, 0.95, 1.0)
469 | t1 = CATransform3DRotate(t1, CGFloat(rotateFactor * Double.pi / 180.0), 1.0, 0.0, 0.0)
470 |
471 | var t2 = CATransform3DIdentity
472 | t2.m34 = t1.m34
473 | t2 = CATransform3DTranslate(t2, 0.0, presentedViewController.view.frame.size.height * translateFactor, 0.0)
474 | t2 = CATransform3DScale(t2, 0.8, 0.8, 1.0)
475 |
476 | let animation1 = CABasicAnimation(keyPath: "transform")
477 | animation1.toValue = NSValue(caTransform3D: t1)
478 | animation1.duration = configuration.animationDuration / 2
479 | animation1.fillMode = kCAFillModeForwards
480 | animation1.isRemovedOnCompletion = false
481 | animation1.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
482 |
483 | let animation2 = CABasicAnimation(keyPath: "transform")
484 | animation2.toValue = NSValue(caTransform3D: forward ? t2 : CATransform3DIdentity)
485 | animation2.beginTime = animation1.duration
486 | animation2.duration = animation1.duration
487 | animation2.fillMode = kCAFillModeForwards
488 | animation2.isRemovedOnCompletion = false
489 |
490 | let group = CAAnimationGroup()
491 | group.fillMode = kCAFillModeForwards
492 | group.isRemovedOnCompletion = false
493 | group.duration = configuration.animationDuration
494 | group.animations = [animation1, animation2]
495 | return group
496 | }
497 |
498 | }
499 |
--------------------------------------------------------------------------------
/Example.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 48;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 12152904208D85720023E89F /* XPModalAnimatedTransitioning.m in Sources */ = {isa = PBXBuildFile; fileRef = 12152903208D85720023E89F /* XPModalAnimatedTransitioning.m */; };
11 | 12152907208D866F0023E89F /* XPModalTransitioningDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 12152906208D866F0023E89F /* XPModalTransitioningDelegate.m */; };
12 | 1215290A208D86A60023E89F /* XPModalPercentDrivenInteractiveTransition.m in Sources */ = {isa = PBXBuildFile; fileRef = 12152909208D86A60023E89F /* XPModalPercentDrivenInteractiveTransition.m */; };
13 | 1215290D208D86BA0023E89F /* XPModalPresentationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1215290C208D86BA0023E89F /* XPModalPresentationController.m */; };
14 | 12152910208D8A0A0023E89F /* XPModalConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 1215290F208D8A090023E89F /* XPModalConfiguration.m */; };
15 | 12268933208D7A9900249A08 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 12268932208D7A9900249A08 /* AppDelegate.m */; };
16 | 12268936208D7A9900249A08 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 12268935208D7A9900249A08 /* ViewController.m */; };
17 | 12268939208D7A9900249A08 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 12268937208D7A9900249A08 /* Main.storyboard */; };
18 | 1226893B208D7AA200249A08 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1226893A208D7AA200249A08 /* Assets.xcassets */; };
19 | 1226893E208D7AA200249A08 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1226893C208D7AA200249A08 /* LaunchScreen.storyboard */; };
20 | 12268941208D7AA200249A08 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 12268940208D7AA200249A08 /* main.m */; };
21 | 12268948208D7B5300249A08 /* UIViewController+XPModal.m in Sources */ = {isa = PBXBuildFile; fileRef = 12268947208D7B5300249A08 /* UIViewController+XPModal.m */; };
22 | 12E4C1902087172600BB98D4 /* ModalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E4C18F2087172600BB98D4 /* ModalViewController.swift */; };
23 | 12FBAA612086F7E300605304 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12FBAA602086F7E300605304 /* AppDelegate.swift */; };
24 | 12FBAA632086F7E300605304 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12FBAA622086F7E300605304 /* ViewController.swift */; };
25 | 12FBAA662086F7E300605304 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 12FBAA642086F7E300605304 /* Main.storyboard */; };
26 | 12FBAA682086F7E300605304 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 12FBAA672086F7E300605304 /* Assets.xcassets */; };
27 | 12FBAA6B2086F7E300605304 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 12FBAA692086F7E300605304 /* LaunchScreen.storyboard */; };
28 | 12FBAA742086F85C00605304 /* UIViewController+Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12FBAA732086F85C00605304 /* UIViewController+Modal.swift */; };
29 | /* End PBXBuildFile section */
30 |
31 | /* Begin PBXFileReference section */
32 | 12152902208D85720023E89F /* XPModalAnimatedTransitioning.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XPModalAnimatedTransitioning.h; sourceTree = ""; };
33 | 12152903208D85720023E89F /* XPModalAnimatedTransitioning.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XPModalAnimatedTransitioning.m; sourceTree = ""; };
34 | 12152905208D866F0023E89F /* XPModalTransitioningDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XPModalTransitioningDelegate.h; sourceTree = ""; };
35 | 12152906208D866F0023E89F /* XPModalTransitioningDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XPModalTransitioningDelegate.m; sourceTree = ""; };
36 | 12152908208D86A60023E89F /* XPModalPercentDrivenInteractiveTransition.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XPModalPercentDrivenInteractiveTransition.h; sourceTree = ""; };
37 | 12152909208D86A60023E89F /* XPModalPercentDrivenInteractiveTransition.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XPModalPercentDrivenInteractiveTransition.m; sourceTree = ""; };
38 | 1215290B208D86BA0023E89F /* XPModalPresentationController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XPModalPresentationController.h; sourceTree = ""; };
39 | 1215290C208D86BA0023E89F /* XPModalPresentationController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XPModalPresentationController.m; sourceTree = ""; };
40 | 1215290E208D8A090023E89F /* XPModalConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XPModalConfiguration.h; sourceTree = ""; };
41 | 1215290F208D8A090023E89F /* XPModalConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XPModalConfiguration.m; sourceTree = ""; };
42 | 1226892F208D7A9900249A08 /* ExampleObjC.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExampleObjC.app; sourceTree = BUILT_PRODUCTS_DIR; };
43 | 12268931208D7A9900249A08 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
44 | 12268932208D7A9900249A08 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
45 | 12268934208D7A9900249A08 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
46 | 12268935208D7A9900249A08 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
47 | 12268938208D7A9900249A08 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
48 | 1226893A208D7AA200249A08 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
49 | 1226893D208D7AA200249A08 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
50 | 1226893F208D7AA200249A08 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
51 | 12268940208D7AA200249A08 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
52 | 12268946208D7B5300249A08 /* UIViewController+XPModal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIViewController+XPModal.h"; sourceTree = ""; };
53 | 12268947208D7B5300249A08 /* UIViewController+XPModal.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+XPModal.m"; sourceTree = ""; };
54 | 12E4C18F2087172600BB98D4 /* ModalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalViewController.swift; sourceTree = ""; };
55 | 12FBAA5D2086F7E300605304 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
56 | 12FBAA602086F7E300605304 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
57 | 12FBAA622086F7E300605304 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
58 | 12FBAA652086F7E300605304 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
59 | 12FBAA672086F7E300605304 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
60 | 12FBAA6A2086F7E300605304 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
61 | 12FBAA6C2086F7E300605304 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
62 | 12FBAA732086F85C00605304 /* UIViewController+Modal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Modal.swift"; sourceTree = ""; };
63 | /* End PBXFileReference section */
64 |
65 | /* Begin PBXFrameworksBuildPhase section */
66 | 1226892C208D7A9900249A08 /* Frameworks */ = {
67 | isa = PBXFrameworksBuildPhase;
68 | buildActionMask = 2147483647;
69 | files = (
70 | );
71 | runOnlyForDeploymentPostprocessing = 0;
72 | };
73 | 12FBAA5A2086F7E300605304 /* Frameworks */ = {
74 | isa = PBXFrameworksBuildPhase;
75 | buildActionMask = 2147483647;
76 | files = (
77 | );
78 | runOnlyForDeploymentPostprocessing = 0;
79 | };
80 | /* End PBXFrameworksBuildPhase section */
81 |
82 | /* Begin PBXGroup section */
83 | 12268930208D7A9900249A08 /* ExampleObjC */ = {
84 | isa = PBXGroup;
85 | children = (
86 | 12268945208D7AB700249A08 /* Modal-ObjC */,
87 | 12268931208D7A9900249A08 /* AppDelegate.h */,
88 | 12268932208D7A9900249A08 /* AppDelegate.m */,
89 | 12268934208D7A9900249A08 /* ViewController.h */,
90 | 12268935208D7A9900249A08 /* ViewController.m */,
91 | 12268937208D7A9900249A08 /* Main.storyboard */,
92 | 1226893A208D7AA200249A08 /* Assets.xcassets */,
93 | 1226893C208D7AA200249A08 /* LaunchScreen.storyboard */,
94 | 1226893F208D7AA200249A08 /* Info.plist */,
95 | 12268940208D7AA200249A08 /* main.m */,
96 | );
97 | path = ExampleObjC;
98 | sourceTree = "";
99 | };
100 | 12268945208D7AB700249A08 /* Modal-ObjC */ = {
101 | isa = PBXGroup;
102 | children = (
103 | 12268946208D7B5300249A08 /* UIViewController+XPModal.h */,
104 | 12268947208D7B5300249A08 /* UIViewController+XPModal.m */,
105 | 1215290E208D8A090023E89F /* XPModalConfiguration.h */,
106 | 1215290F208D8A090023E89F /* XPModalConfiguration.m */,
107 | 12152902208D85720023E89F /* XPModalAnimatedTransitioning.h */,
108 | 12152903208D85720023E89F /* XPModalAnimatedTransitioning.m */,
109 | 12152908208D86A60023E89F /* XPModalPercentDrivenInteractiveTransition.h */,
110 | 12152909208D86A60023E89F /* XPModalPercentDrivenInteractiveTransition.m */,
111 | 1215290B208D86BA0023E89F /* XPModalPresentationController.h */,
112 | 1215290C208D86BA0023E89F /* XPModalPresentationController.m */,
113 | 12152905208D866F0023E89F /* XPModalTransitioningDelegate.h */,
114 | 12152906208D866F0023E89F /* XPModalTransitioningDelegate.m */,
115 | );
116 | path = "Modal-ObjC";
117 | sourceTree = "";
118 | };
119 | 12FBAA542086F7E300605304 = {
120 | isa = PBXGroup;
121 | children = (
122 | 12FBAA5F2086F7E300605304 /* Example */,
123 | 12268930208D7A9900249A08 /* ExampleObjC */,
124 | 12FBAA5E2086F7E300605304 /* Products */,
125 | );
126 | sourceTree = "";
127 | };
128 | 12FBAA5E2086F7E300605304 /* Products */ = {
129 | isa = PBXGroup;
130 | children = (
131 | 12FBAA5D2086F7E300605304 /* Example.app */,
132 | 1226892F208D7A9900249A08 /* ExampleObjC.app */,
133 | );
134 | name = Products;
135 | sourceTree = "";
136 | };
137 | 12FBAA5F2086F7E300605304 /* Example */ = {
138 | isa = PBXGroup;
139 | children = (
140 | 12FBAA722086F83400605304 /* Modal */,
141 | 12FBAA602086F7E300605304 /* AppDelegate.swift */,
142 | 12FBAA622086F7E300605304 /* ViewController.swift */,
143 | 12E4C18F2087172600BB98D4 /* ModalViewController.swift */,
144 | 12FBAA642086F7E300605304 /* Main.storyboard */,
145 | 12FBAA672086F7E300605304 /* Assets.xcassets */,
146 | 12FBAA692086F7E300605304 /* LaunchScreen.storyboard */,
147 | 12FBAA6C2086F7E300605304 /* Info.plist */,
148 | );
149 | path = Example;
150 | sourceTree = "";
151 | };
152 | 12FBAA722086F83400605304 /* Modal */ = {
153 | isa = PBXGroup;
154 | children = (
155 | 12FBAA732086F85C00605304 /* UIViewController+Modal.swift */,
156 | );
157 | path = Modal;
158 | sourceTree = SOURCE_ROOT;
159 | };
160 | /* End PBXGroup section */
161 |
162 | /* Begin PBXNativeTarget section */
163 | 1226892E208D7A9900249A08 /* ExampleObjC */ = {
164 | isa = PBXNativeTarget;
165 | buildConfigurationList = 12268942208D7AA200249A08 /* Build configuration list for PBXNativeTarget "ExampleObjC" */;
166 | buildPhases = (
167 | 1226892B208D7A9900249A08 /* Sources */,
168 | 1226892C208D7A9900249A08 /* Frameworks */,
169 | 1226892D208D7A9900249A08 /* Resources */,
170 | );
171 | buildRules = (
172 | );
173 | dependencies = (
174 | );
175 | name = ExampleObjC;
176 | productName = ExampleObjC;
177 | productReference = 1226892F208D7A9900249A08 /* ExampleObjC.app */;
178 | productType = "com.apple.product-type.application";
179 | };
180 | 12FBAA5C2086F7E300605304 /* Example */ = {
181 | isa = PBXNativeTarget;
182 | buildConfigurationList = 12FBAA6F2086F7E300605304 /* Build configuration list for PBXNativeTarget "Example" */;
183 | buildPhases = (
184 | 12FBAA592086F7E300605304 /* Sources */,
185 | 12FBAA5A2086F7E300605304 /* Frameworks */,
186 | 12FBAA5B2086F7E300605304 /* Resources */,
187 | );
188 | buildRules = (
189 | );
190 | dependencies = (
191 | );
192 | name = Example;
193 | productName = Example;
194 | productReference = 12FBAA5D2086F7E300605304 /* Example.app */;
195 | productType = "com.apple.product-type.application";
196 | };
197 | /* End PBXNativeTarget section */
198 |
199 | /* Begin PBXProject section */
200 | 12FBAA552086F7E300605304 /* Project object */ = {
201 | isa = PBXProject;
202 | attributes = {
203 | CLASSPREFIX = "";
204 | LastSwiftUpdateCheck = 0920;
205 | LastUpgradeCheck = 0920;
206 | TargetAttributes = {
207 | 1226892E208D7A9900249A08 = {
208 | CreatedOnToolsVersion = 9.3;
209 | ProvisioningStyle = Automatic;
210 | };
211 | 12FBAA5C2086F7E300605304 = {
212 | CreatedOnToolsVersion = 9.2;
213 | ProvisioningStyle = Automatic;
214 | };
215 | };
216 | };
217 | buildConfigurationList = 12FBAA582086F7E300605304 /* Build configuration list for PBXProject "Example" */;
218 | compatibilityVersion = "Xcode 8.0";
219 | developmentRegion = en;
220 | hasScannedForEncodings = 0;
221 | knownRegions = (
222 | en,
223 | Base,
224 | );
225 | mainGroup = 12FBAA542086F7E300605304;
226 | productRefGroup = 12FBAA5E2086F7E300605304 /* Products */;
227 | projectDirPath = "";
228 | projectRoot = "";
229 | targets = (
230 | 12FBAA5C2086F7E300605304 /* Example */,
231 | 1226892E208D7A9900249A08 /* ExampleObjC */,
232 | );
233 | };
234 | /* End PBXProject section */
235 |
236 | /* Begin PBXResourcesBuildPhase section */
237 | 1226892D208D7A9900249A08 /* Resources */ = {
238 | isa = PBXResourcesBuildPhase;
239 | buildActionMask = 2147483647;
240 | files = (
241 | 1226893E208D7AA200249A08 /* LaunchScreen.storyboard in Resources */,
242 | 1226893B208D7AA200249A08 /* Assets.xcassets in Resources */,
243 | 12268939208D7A9900249A08 /* Main.storyboard in Resources */,
244 | );
245 | runOnlyForDeploymentPostprocessing = 0;
246 | };
247 | 12FBAA5B2086F7E300605304 /* Resources */ = {
248 | isa = PBXResourcesBuildPhase;
249 | buildActionMask = 2147483647;
250 | files = (
251 | 12FBAA6B2086F7E300605304 /* LaunchScreen.storyboard in Resources */,
252 | 12FBAA682086F7E300605304 /* Assets.xcassets in Resources */,
253 | 12FBAA662086F7E300605304 /* Main.storyboard in Resources */,
254 | );
255 | runOnlyForDeploymentPostprocessing = 0;
256 | };
257 | /* End PBXResourcesBuildPhase section */
258 |
259 | /* Begin PBXSourcesBuildPhase section */
260 | 1226892B208D7A9900249A08 /* Sources */ = {
261 | isa = PBXSourcesBuildPhase;
262 | buildActionMask = 2147483647;
263 | files = (
264 | 12152910208D8A0A0023E89F /* XPModalConfiguration.m in Sources */,
265 | 12268936208D7A9900249A08 /* ViewController.m in Sources */,
266 | 12268941208D7AA200249A08 /* main.m in Sources */,
267 | 12268948208D7B5300249A08 /* UIViewController+XPModal.m in Sources */,
268 | 12268933208D7A9900249A08 /* AppDelegate.m in Sources */,
269 | 12152907208D866F0023E89F /* XPModalTransitioningDelegate.m in Sources */,
270 | 12152904208D85720023E89F /* XPModalAnimatedTransitioning.m in Sources */,
271 | 1215290A208D86A60023E89F /* XPModalPercentDrivenInteractiveTransition.m in Sources */,
272 | 1215290D208D86BA0023E89F /* XPModalPresentationController.m in Sources */,
273 | );
274 | runOnlyForDeploymentPostprocessing = 0;
275 | };
276 | 12FBAA592086F7E300605304 /* Sources */ = {
277 | isa = PBXSourcesBuildPhase;
278 | buildActionMask = 2147483647;
279 | files = (
280 | 12E4C1902087172600BB98D4 /* ModalViewController.swift in Sources */,
281 | 12FBAA632086F7E300605304 /* ViewController.swift in Sources */,
282 | 12FBAA742086F85C00605304 /* UIViewController+Modal.swift in Sources */,
283 | 12FBAA612086F7E300605304 /* AppDelegate.swift in Sources */,
284 | );
285 | runOnlyForDeploymentPostprocessing = 0;
286 | };
287 | /* End PBXSourcesBuildPhase section */
288 |
289 | /* Begin PBXVariantGroup section */
290 | 12268937208D7A9900249A08 /* Main.storyboard */ = {
291 | isa = PBXVariantGroup;
292 | children = (
293 | 12268938208D7A9900249A08 /* Base */,
294 | );
295 | name = Main.storyboard;
296 | sourceTree = "";
297 | };
298 | 1226893C208D7AA200249A08 /* LaunchScreen.storyboard */ = {
299 | isa = PBXVariantGroup;
300 | children = (
301 | 1226893D208D7AA200249A08 /* Base */,
302 | );
303 | name = LaunchScreen.storyboard;
304 | sourceTree = "";
305 | };
306 | 12FBAA642086F7E300605304 /* Main.storyboard */ = {
307 | isa = PBXVariantGroup;
308 | children = (
309 | 12FBAA652086F7E300605304 /* Base */,
310 | );
311 | name = Main.storyboard;
312 | sourceTree = "";
313 | };
314 | 12FBAA692086F7E300605304 /* LaunchScreen.storyboard */ = {
315 | isa = PBXVariantGroup;
316 | children = (
317 | 12FBAA6A2086F7E300605304 /* Base */,
318 | );
319 | name = LaunchScreen.storyboard;
320 | sourceTree = "";
321 | };
322 | /* End PBXVariantGroup section */
323 |
324 | /* Begin XCBuildConfiguration section */
325 | 12268943208D7AA200249A08 /* Debug */ = {
326 | isa = XCBuildConfiguration;
327 | buildSettings = {
328 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
329 | CLANG_ENABLE_OBJC_WEAK = YES;
330 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
331 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
332 | CODE_SIGN_STYLE = Automatic;
333 | INFOPLIST_FILE = ExampleObjC/Info.plist;
334 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
335 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
336 | PRODUCT_BUNDLE_IDENTIFIER = com.0daybug.ExampleObjC;
337 | PRODUCT_NAME = "$(TARGET_NAME)";
338 | TARGETED_DEVICE_FAMILY = "1,2";
339 | };
340 | name = Debug;
341 | };
342 | 12268944208D7AA200249A08 /* Release */ = {
343 | isa = XCBuildConfiguration;
344 | buildSettings = {
345 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
346 | CLANG_ENABLE_OBJC_WEAK = YES;
347 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
348 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
349 | CODE_SIGN_STYLE = Automatic;
350 | INFOPLIST_FILE = ExampleObjC/Info.plist;
351 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
352 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
353 | PRODUCT_BUNDLE_IDENTIFIER = com.0daybug.ExampleObjC;
354 | PRODUCT_NAME = "$(TARGET_NAME)";
355 | TARGETED_DEVICE_FAMILY = "1,2";
356 | };
357 | name = Release;
358 | };
359 | 12FBAA6D2086F7E300605304 /* Debug */ = {
360 | isa = XCBuildConfiguration;
361 | buildSettings = {
362 | ALWAYS_SEARCH_USER_PATHS = NO;
363 | CLANG_ANALYZER_NONNULL = YES;
364 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
365 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
366 | CLANG_CXX_LIBRARY = "libc++";
367 | CLANG_ENABLE_MODULES = YES;
368 | CLANG_ENABLE_OBJC_ARC = YES;
369 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
370 | CLANG_WARN_BOOL_CONVERSION = YES;
371 | CLANG_WARN_COMMA = YES;
372 | CLANG_WARN_CONSTANT_CONVERSION = YES;
373 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
374 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
375 | CLANG_WARN_EMPTY_BODY = YES;
376 | CLANG_WARN_ENUM_CONVERSION = YES;
377 | CLANG_WARN_INFINITE_RECURSION = YES;
378 | CLANG_WARN_INT_CONVERSION = YES;
379 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
380 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
381 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
382 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
383 | CLANG_WARN_STRICT_PROTOTYPES = YES;
384 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
385 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
386 | CLANG_WARN_UNREACHABLE_CODE = YES;
387 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
388 | CODE_SIGN_IDENTITY = "iPhone Developer";
389 | COPY_PHASE_STRIP = NO;
390 | DEBUG_INFORMATION_FORMAT = dwarf;
391 | ENABLE_STRICT_OBJC_MSGSEND = YES;
392 | ENABLE_TESTABILITY = YES;
393 | GCC_C_LANGUAGE_STANDARD = gnu11;
394 | GCC_DYNAMIC_NO_PIC = NO;
395 | GCC_NO_COMMON_BLOCKS = YES;
396 | GCC_OPTIMIZATION_LEVEL = 0;
397 | GCC_PREPROCESSOR_DEFINITIONS = (
398 | "DEBUG=1",
399 | "$(inherited)",
400 | );
401 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
402 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
403 | GCC_WARN_UNDECLARED_SELECTOR = YES;
404 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
405 | GCC_WARN_UNUSED_FUNCTION = YES;
406 | GCC_WARN_UNUSED_VARIABLE = YES;
407 | IPHONEOS_DEPLOYMENT_TARGET = 11.2;
408 | MTL_ENABLE_DEBUG_INFO = YES;
409 | ONLY_ACTIVE_ARCH = YES;
410 | SDKROOT = iphoneos;
411 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
412 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
413 | };
414 | name = Debug;
415 | };
416 | 12FBAA6E2086F7E300605304 /* Release */ = {
417 | isa = XCBuildConfiguration;
418 | buildSettings = {
419 | ALWAYS_SEARCH_USER_PATHS = NO;
420 | CLANG_ANALYZER_NONNULL = YES;
421 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
422 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
423 | CLANG_CXX_LIBRARY = "libc++";
424 | CLANG_ENABLE_MODULES = YES;
425 | CLANG_ENABLE_OBJC_ARC = YES;
426 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
427 | CLANG_WARN_BOOL_CONVERSION = YES;
428 | CLANG_WARN_COMMA = YES;
429 | CLANG_WARN_CONSTANT_CONVERSION = YES;
430 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
431 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
432 | CLANG_WARN_EMPTY_BODY = YES;
433 | CLANG_WARN_ENUM_CONVERSION = YES;
434 | CLANG_WARN_INFINITE_RECURSION = YES;
435 | CLANG_WARN_INT_CONVERSION = YES;
436 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
437 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
438 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
439 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
440 | CLANG_WARN_STRICT_PROTOTYPES = YES;
441 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
442 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
443 | CLANG_WARN_UNREACHABLE_CODE = YES;
444 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
445 | CODE_SIGN_IDENTITY = "iPhone Developer";
446 | COPY_PHASE_STRIP = NO;
447 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
448 | ENABLE_NS_ASSERTIONS = NO;
449 | ENABLE_STRICT_OBJC_MSGSEND = YES;
450 | GCC_C_LANGUAGE_STANDARD = gnu11;
451 | GCC_NO_COMMON_BLOCKS = YES;
452 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
453 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
454 | GCC_WARN_UNDECLARED_SELECTOR = YES;
455 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
456 | GCC_WARN_UNUSED_FUNCTION = YES;
457 | GCC_WARN_UNUSED_VARIABLE = YES;
458 | IPHONEOS_DEPLOYMENT_TARGET = 11.2;
459 | MTL_ENABLE_DEBUG_INFO = NO;
460 | SDKROOT = iphoneos;
461 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
462 | VALIDATE_PRODUCT = YES;
463 | };
464 | name = Release;
465 | };
466 | 12FBAA702086F7E300605304 /* Debug */ = {
467 | isa = XCBuildConfiguration;
468 | buildSettings = {
469 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
470 | CODE_SIGN_STYLE = Automatic;
471 | INFOPLIST_FILE = Example/Info.plist;
472 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
473 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
474 | PRODUCT_BUNDLE_IDENTIFIER = com.0daybug.Example;
475 | PRODUCT_NAME = "$(TARGET_NAME)";
476 | SWIFT_VERSION = 4.0;
477 | TARGETED_DEVICE_FAMILY = "1,2";
478 | };
479 | name = Debug;
480 | };
481 | 12FBAA712086F7E300605304 /* Release */ = {
482 | isa = XCBuildConfiguration;
483 | buildSettings = {
484 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
485 | CODE_SIGN_STYLE = Automatic;
486 | INFOPLIST_FILE = Example/Info.plist;
487 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
488 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
489 | PRODUCT_BUNDLE_IDENTIFIER = com.0daybug.Example;
490 | PRODUCT_NAME = "$(TARGET_NAME)";
491 | SWIFT_VERSION = 4.0;
492 | TARGETED_DEVICE_FAMILY = "1,2";
493 | };
494 | name = Release;
495 | };
496 | /* End XCBuildConfiguration section */
497 |
498 | /* Begin XCConfigurationList section */
499 | 12268942208D7AA200249A08 /* Build configuration list for PBXNativeTarget "ExampleObjC" */ = {
500 | isa = XCConfigurationList;
501 | buildConfigurations = (
502 | 12268943208D7AA200249A08 /* Debug */,
503 | 12268944208D7AA200249A08 /* Release */,
504 | );
505 | defaultConfigurationIsVisible = 0;
506 | defaultConfigurationName = Release;
507 | };
508 | 12FBAA582086F7E300605304 /* Build configuration list for PBXProject "Example" */ = {
509 | isa = XCConfigurationList;
510 | buildConfigurations = (
511 | 12FBAA6D2086F7E300605304 /* Debug */,
512 | 12FBAA6E2086F7E300605304 /* Release */,
513 | );
514 | defaultConfigurationIsVisible = 0;
515 | defaultConfigurationName = Release;
516 | };
517 | 12FBAA6F2086F7E300605304 /* Build configuration list for PBXNativeTarget "Example" */ = {
518 | isa = XCConfigurationList;
519 | buildConfigurations = (
520 | 12FBAA702086F7E300605304 /* Debug */,
521 | 12FBAA712086F7E300605304 /* Release */,
522 | );
523 | defaultConfigurationIsVisible = 0;
524 | defaultConfigurationName = Release;
525 | };
526 | /* End XCConfigurationList section */
527 | };
528 | rootObject = 12FBAA552086F7E300605304 /* Project object */;
529 | }
530 |
--------------------------------------------------------------------------------