├── 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 | [![Build](https://img.shields.io/badge/build-passing-green.svg?style=flat)]() 4 | [![Platform](https://img.shields.io/badge/platform-iOS-brown.svg?style=flat)]() 5 | [![Language](https://img.shields.io/badge/language-Swift%20&%20Objective%20C-blue.svg?style=flat)]() 6 | [![License](https://img.shields.io/badge/license-MIT-orange.svg?style=flat)]() 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 | [![GIF](./snapshot/preview.gif)]() 57 | 58 | [![left](./snapshot/left.png)]() 59 | 60 | [![right](./snapshot/right.png)]() 61 | 62 | [![bottom](./snapshot/bottom.png)]() 63 | 64 | [![center](./snapshot/center.png)]() 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 | --------------------------------------------------------------------------------