├── .gitignore ├── AlertTransition.podspec ├── AlertTransition.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── AlertTransition.xcscheme ├── AlertTransition.xcworkspace └── contents.xcworkspacedata ├── AlertTransition ├── AlertTransition.h ├── AlertTransition.swift ├── Extensions.swift ├── Info.plist ├── PercentDrivenInteractiveTransition.swift ├── PresentationController.swift └── Transitions │ ├── EasyTransition │ ├── EasyPercentDrivenTransition.swift │ └── EasyTransition.swift │ ├── MenuTransition │ ├── MenuPercentDrivenTransition.swift │ └── MenuTransition.swift │ └── TrolleyTransition.swift ├── AlertTransitionDemo ├── Alerts │ ├── NormalAlertController.swift │ ├── SnapKitAlertController.swift │ └── StoryboardAlertController.swift ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── alert_dismiss.imageset │ │ ├── Contents.json │ │ ├── alert_dismiss@2x.png │ │ └── alert_dismiss@3x.png │ ├── backgroud_image.imageset │ │ ├── Contents.json │ │ └── backgroud_image.png │ ├── dismiss_button.imageset │ │ ├── Contents.json │ │ ├── dismiss_button@2x.png │ │ └── dismiss_button@3x.png │ ├── menu_background.imageset │ │ ├── Contents.json │ │ └── menu_background.png │ └── trolley_background.imageset │ │ ├── Contents.json │ │ └── trolley_background.png ├── BackgroundTypeController.swift ├── Base.lproj │ └── LaunchScreen.storyboard ├── BubbleController.swift ├── CustomTransition │ ├── BubbleTransition.swift │ └── StarWallTransition.swift ├── DifferentAlertController.swift ├── EasyStoryboardController.swift ├── EasyTransitionController.swift ├── Extensions.swift ├── Info.plist ├── Main.storyboard ├── MainController.swift ├── MenuController.swift ├── NextViewController.swift ├── StarWarsGLAnimator │ ├── Sprite.swift │ ├── SpriteRender.swift │ ├── StarWarsGLAnimator.swift │ ├── Vector2.swift │ └── ViewTexture.swift └── TrolleyController.swift ├── LICENSE ├── Media ├── BackgroundType.gif ├── BubbleTransition.gif ├── EasyTransition.gif ├── MenuTransition.gif ├── StarWarsTransition.gif ├── TrolleyTransition.gif └── changeOrientation.gif ├── Podfile ├── Podfile.lock └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | ### Xcode ### 5 | # Xcode 6 | # 7 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 8 | 9 | ## Build generated 10 | build/ 11 | DerivedData 12 | 13 | ## Various settings 14 | *.pbxuser 15 | !default.pbxuser 16 | *.mode1v3 17 | !default.mode1v3 18 | *.mode2v3 19 | !default.mode2v3 20 | *.perspectivev3 21 | !default.perspectivev3 22 | xcuserdata 23 | 24 | ## Other 25 | *.xccheckout 26 | *.moved-aside 27 | *.xcuserstate 28 | 29 | 30 | ### Objective-C ### 31 | # Xcode 32 | # 33 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 34 | 35 | ## Build generated 36 | build/ 37 | DerivedData 38 | 39 | ## Various settings 40 | *.pbxuser 41 | !default.pbxuser 42 | *.mode1v3 43 | !default.mode1v3 44 | *.mode2v3 45 | !default.mode2v3 46 | *.perspectivev3 47 | !default.perspectivev3 48 | xcuserdata 49 | 50 | ## Other 51 | *.xccheckout 52 | *.moved-aside 53 | *.xcuserstate 54 | *.xcscmblueprint 55 | 56 | ## Obj-C/Swift specific 57 | *.hmap 58 | *.ipa 59 | 60 | # CocoaPods 61 | # 62 | # We recommend against adding the Pods directory to your .gitignore. However 63 | # you should judge for yourself, the pros and cons are mentioned at: 64 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 65 | # 66 | Pods/ 67 | 68 | # Carthage 69 | # 70 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 71 | # Carthage/Checkouts 72 | 73 | Carthage/Build 74 | 75 | # fastlane 76 | # 77 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 78 | # screenshots whenever they are needed. 79 | # For more information about the recommended setup visit: 80 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 81 | 82 | fastlane/report.xml 83 | fastlane/screenshots 84 | 85 | ### Objective-C Patch ### 86 | *.xcscmblueprint 87 | 88 | -------------------------------------------------------------------------------- /AlertTransition.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "AlertTransition" 4 | s.version = "2.1.0" 5 | s.license = "MIT" 6 | s.summary = "AlertTransition is a extensible library for making view controller transitions, especially for alert transitions." 7 | 8 | s.homepage = "https://github.com/loopeer/AlertTransition" 9 | s.author = { "HanShuai" => "hanshuai@loopeer.com" } 10 | 11 | s.source = { :git => "https://github.com/loopeer/AlertTransition.git", :tag => s.version } 12 | 13 | s.requires_arc = true 14 | s.ios.deployment_target = "8.0" 15 | s.pod_target_xcconfig = { 'SWIFT_VERSION' => '4.0' } 16 | 17 | s.subspec 'Core' do |core| 18 | core.source_files = ['AlertTransition/*.swift'] 19 | end 20 | 21 | s.subspec 'Easy' do |easy| 22 | easy.source_files = ['AlertTransition/Transitions/EasyTransition/*.swift'] 23 | easy.dependency 'AlertTransition/Core' 24 | end 25 | 26 | s.subspec 'Menu' do |menu| 27 | menu.source_files = ['AlertTransition/Transitions/MenuTransition/*.swift'] 28 | menu.dependency 'AlertTransition/Core' 29 | end 30 | 31 | s.subspec 'Trolley' do |trolley| 32 | trolley.source_files = ['AlertTransition/Transitions/TrolleyTransition.swift'] 33 | trolley.dependency 'AlertTransition/Core' 34 | end 35 | 36 | # By default install just the Core subspec code 37 | s.default_subspec = ['Easy'] 38 | end 39 | -------------------------------------------------------------------------------- /AlertTransition.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 790FE7EE1EA60BEB0040D499 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 790FE7ED1EA60BEB0040D499 /* Main.storyboard */; }; 11 | 790FE8101EA64AFA0040D499 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 790FE80F1EA64AFA0040D499 /* Extensions.swift */; }; 12 | 790FE8121EA72C120040D499 /* DifferentAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 790FE8111EA72C120040D499 /* DifferentAlertController.swift */; }; 13 | 790FE8141EA72F9B0040D499 /* SnapKitAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 790FE8131EA72F9B0040D499 /* SnapKitAlertController.swift */; }; 14 | 790FE8161EA72FC50040D499 /* StoryboardAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 790FE8151EA72FC50040D499 /* StoryboardAlertController.swift */; }; 15 | 791B384E1F8CAC1500D6C0E1 /* NextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 791B384D1F8CAC1500D6C0E1 /* NextViewController.swift */; }; 16 | 793E8A141EA4D11200BA3325 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 793E8A131EA4D11200BA3325 /* AppDelegate.swift */; }; 17 | 793E8A1B1EA4D11200BA3325 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 793E8A1A1EA4D11200BA3325 /* Assets.xcassets */; }; 18 | 793E8A1E1EA4D11200BA3325 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 793E8A1C1EA4D11200BA3325 /* LaunchScreen.storyboard */; }; 19 | 793E8A361EA5B7C200BA3325 /* MainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 793E8A351EA5B7C200BA3325 /* MainController.swift */; }; 20 | 793E8A381EA5B98E00BA3325 /* EasyTransitionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 793E8A371EA5B98E00BA3325 /* EasyTransitionController.swift */; }; 21 | 793E8A3B1EA5BC5C00BA3325 /* NormalAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 793E8A3A1EA5BC5C00BA3325 /* NormalAlertController.swift */; }; 22 | 793E8A491EA602B600BA3325 /* EasyStoryboardController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 793E8A481EA602B600BA3325 /* EasyStoryboardController.swift */; }; 23 | 79474AA61EA75E7100873736 /* MenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79474AA51EA75E7100873736 /* MenuController.swift */; }; 24 | 79474AAA1EA764EC00873736 /* TrolleyController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79474AA91EA764EC00873736 /* TrolleyController.swift */; }; 25 | 79474AAC1EA7702600873736 /* BackgroundTypeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79474AAB1EA7702600873736 /* BackgroundTypeController.swift */; }; 26 | 79962C091EA779D70046B631 /* BubbleTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79962C081EA779D70046B631 /* BubbleTransition.swift */; }; 27 | 79962C0B1EA77ED80046B631 /* BubbleController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79962C0A1EA77ED80046B631 /* BubbleController.swift */; }; 28 | 79962C0F1EA789D80046B631 /* StarWallTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79962C0E1EA789D80046B631 /* StarWallTransition.swift */; }; 29 | 79962C171EA7906D0046B631 /* Sprite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79962C121EA7906D0046B631 /* Sprite.swift */; }; 30 | 79962C181EA7906D0046B631 /* SpriteRender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79962C131EA7906D0046B631 /* SpriteRender.swift */; }; 31 | 79962C191EA7906D0046B631 /* StarWarsGLAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79962C141EA7906D0046B631 /* StarWarsGLAnimator.swift */; }; 32 | 79962C1A1EA7906D0046B631 /* Vector2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79962C151EA7906D0046B631 /* Vector2.swift */; }; 33 | 79962C1B1EA7906D0046B631 /* ViewTexture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79962C161EA7906D0046B631 /* ViewTexture.swift */; }; 34 | FF864D92FE4ED8E188B7118C /* Pods_AlertTransitionDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 46D5E80FCCA63971878F53EB /* Pods_AlertTransitionDemo.framework */; }; 35 | /* End PBXBuildFile section */ 36 | 37 | /* Begin PBXFileReference section */ 38 | 3EAE73D045222165FB810F05 /* Pods-AlertTransitionDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AlertTransitionDemo.release.xcconfig"; path = "Pods/Target Support Files/Pods-AlertTransitionDemo/Pods-AlertTransitionDemo.release.xcconfig"; sourceTree = ""; }; 39 | 46D5E80FCCA63971878F53EB /* Pods_AlertTransitionDemo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AlertTransitionDemo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | 790FE7ED1EA60BEB0040D499 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 41 | 790FE80F1EA64AFA0040D499 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; 42 | 790FE8111EA72C120040D499 /* DifferentAlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DifferentAlertController.swift; sourceTree = ""; }; 43 | 790FE8131EA72F9B0040D499 /* SnapKitAlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SnapKitAlertController.swift; path = Alerts/SnapKitAlertController.swift; sourceTree = ""; }; 44 | 790FE8151EA72FC50040D499 /* StoryboardAlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StoryboardAlertController.swift; path = Alerts/StoryboardAlertController.swift; sourceTree = ""; }; 45 | 791B384D1F8CAC1500D6C0E1 /* NextViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextViewController.swift; sourceTree = ""; }; 46 | 793E8A111EA4D11200BA3325 /* AlertTransitionDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AlertTransitionDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 793E8A131EA4D11200BA3325 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 48 | 793E8A1A1EA4D11200BA3325 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 49 | 793E8A1D1EA4D11200BA3325 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 50 | 793E8A1F1EA4D11200BA3325 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 51 | 793E8A351EA5B7C200BA3325 /* MainController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainController.swift; sourceTree = ""; }; 52 | 793E8A371EA5B98E00BA3325 /* EasyTransitionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EasyTransitionController.swift; sourceTree = ""; }; 53 | 793E8A3A1EA5BC5C00BA3325 /* NormalAlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NormalAlertController.swift; path = Alerts/NormalAlertController.swift; sourceTree = ""; }; 54 | 793E8A481EA602B600BA3325 /* EasyStoryboardController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EasyStoryboardController.swift; sourceTree = ""; }; 55 | 79474AA51EA75E7100873736 /* MenuController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuController.swift; sourceTree = ""; }; 56 | 79474AA91EA764EC00873736 /* TrolleyController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrolleyController.swift; sourceTree = ""; }; 57 | 79474AAB1EA7702600873736 /* BackgroundTypeController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackgroundTypeController.swift; sourceTree = ""; }; 58 | 79962C081EA779D70046B631 /* BubbleTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BubbleTransition.swift; path = CustomTransition/BubbleTransition.swift; sourceTree = ""; }; 59 | 79962C0A1EA77ED80046B631 /* BubbleController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BubbleController.swift; sourceTree = ""; }; 60 | 79962C0E1EA789D80046B631 /* StarWallTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StarWallTransition.swift; path = CustomTransition/StarWallTransition.swift; sourceTree = ""; }; 61 | 79962C121EA7906D0046B631 /* Sprite.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Sprite.swift; path = StarWarsGLAnimator/Sprite.swift; sourceTree = ""; }; 62 | 79962C131EA7906D0046B631 /* SpriteRender.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SpriteRender.swift; path = StarWarsGLAnimator/SpriteRender.swift; sourceTree = ""; }; 63 | 79962C141EA7906D0046B631 /* StarWarsGLAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StarWarsGLAnimator.swift; path = StarWarsGLAnimator/StarWarsGLAnimator.swift; sourceTree = ""; }; 64 | 79962C151EA7906D0046B631 /* Vector2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Vector2.swift; path = StarWarsGLAnimator/Vector2.swift; sourceTree = ""; }; 65 | 79962C161EA7906D0046B631 /* ViewTexture.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ViewTexture.swift; path = StarWarsGLAnimator/ViewTexture.swift; sourceTree = ""; }; 66 | 87B043D8E060CF5513EAA5F2 /* Pods_AlertTransition.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AlertTransition.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 67 | C892AFC9C414AC1076F0A085 /* Pods-AlertTransitionDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AlertTransitionDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AlertTransitionDemo/Pods-AlertTransitionDemo.debug.xcconfig"; sourceTree = ""; }; 68 | /* End PBXFileReference section */ 69 | 70 | /* Begin PBXFrameworksBuildPhase section */ 71 | 793E8A0E1EA4D11200BA3325 /* Frameworks */ = { 72 | isa = PBXFrameworksBuildPhase; 73 | buildActionMask = 2147483647; 74 | files = ( 75 | FF864D92FE4ED8E188B7118C /* Pods_AlertTransitionDemo.framework in Frameworks */, 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | /* End PBXFrameworksBuildPhase section */ 80 | 81 | /* Begin PBXGroup section */ 82 | 2539B46ED676C34DD8F7E1AB /* Pods */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | C892AFC9C414AC1076F0A085 /* Pods-AlertTransitionDemo.debug.xcconfig */, 86 | 3EAE73D045222165FB810F05 /* Pods-AlertTransitionDemo.release.xcconfig */, 87 | ); 88 | name = Pods; 89 | sourceTree = ""; 90 | }; 91 | 58F591B8CAB4A4474D59E644 /* Frameworks */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 87B043D8E060CF5513EAA5F2 /* Pods_AlertTransition.framework */, 95 | 46D5E80FCCA63971878F53EB /* Pods_AlertTransitionDemo.framework */, 96 | ); 97 | name = Frameworks; 98 | sourceTree = ""; 99 | }; 100 | 793E89F81EA4CFE200BA3325 = { 101 | isa = PBXGroup; 102 | children = ( 103 | 793E8A121EA4D11200BA3325 /* AlertTransitionDemo */, 104 | 793E8A031EA4CFE200BA3325 /* Products */, 105 | 2539B46ED676C34DD8F7E1AB /* Pods */, 106 | 58F591B8CAB4A4474D59E644 /* Frameworks */, 107 | ); 108 | sourceTree = ""; 109 | }; 110 | 793E8A031EA4CFE200BA3325 /* Products */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 793E8A111EA4D11200BA3325 /* AlertTransitionDemo.app */, 114 | ); 115 | name = Products; 116 | sourceTree = ""; 117 | }; 118 | 793E8A121EA4D11200BA3325 /* AlertTransitionDemo */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 79962C111EA790510046B631 /* StarWarsGLAnimator */, 122 | 79962C071EA7799C0046B631 /* CustomTransitions */, 123 | 793E8A391EA5BC1900BA3325 /* Alerts */, 124 | 793E8A131EA4D11200BA3325 /* AppDelegate.swift */, 125 | 793E8A1A1EA4D11200BA3325 /* Assets.xcassets */, 126 | 793E8A1C1EA4D11200BA3325 /* LaunchScreen.storyboard */, 127 | 793E8A1F1EA4D11200BA3325 /* Info.plist */, 128 | 793E8A351EA5B7C200BA3325 /* MainController.swift */, 129 | 793E8A371EA5B98E00BA3325 /* EasyTransitionController.swift */, 130 | 793E8A481EA602B600BA3325 /* EasyStoryboardController.swift */, 131 | 790FE7ED1EA60BEB0040D499 /* Main.storyboard */, 132 | 790FE80F1EA64AFA0040D499 /* Extensions.swift */, 133 | 790FE8111EA72C120040D499 /* DifferentAlertController.swift */, 134 | 79474AA51EA75E7100873736 /* MenuController.swift */, 135 | 79474AA91EA764EC00873736 /* TrolleyController.swift */, 136 | 79474AAB1EA7702600873736 /* BackgroundTypeController.swift */, 137 | 79962C0A1EA77ED80046B631 /* BubbleController.swift */, 138 | 791B384D1F8CAC1500D6C0E1 /* NextViewController.swift */, 139 | ); 140 | path = AlertTransitionDemo; 141 | sourceTree = ""; 142 | }; 143 | 793E8A391EA5BC1900BA3325 /* Alerts */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 790FE8151EA72FC50040D499 /* StoryboardAlertController.swift */, 147 | 793E8A3A1EA5BC5C00BA3325 /* NormalAlertController.swift */, 148 | 790FE8131EA72F9B0040D499 /* SnapKitAlertController.swift */, 149 | ); 150 | name = Alerts; 151 | sourceTree = ""; 152 | }; 153 | 79962C071EA7799C0046B631 /* CustomTransitions */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 79962C081EA779D70046B631 /* BubbleTransition.swift */, 157 | 79962C0E1EA789D80046B631 /* StarWallTransition.swift */, 158 | ); 159 | name = CustomTransitions; 160 | sourceTree = ""; 161 | }; 162 | 79962C111EA790510046B631 /* StarWarsGLAnimator */ = { 163 | isa = PBXGroup; 164 | children = ( 165 | 79962C121EA7906D0046B631 /* Sprite.swift */, 166 | 79962C131EA7906D0046B631 /* SpriteRender.swift */, 167 | 79962C141EA7906D0046B631 /* StarWarsGLAnimator.swift */, 168 | 79962C151EA7906D0046B631 /* Vector2.swift */, 169 | 79962C161EA7906D0046B631 /* ViewTexture.swift */, 170 | ); 171 | name = StarWarsGLAnimator; 172 | sourceTree = ""; 173 | }; 174 | /* End PBXGroup section */ 175 | 176 | /* Begin PBXNativeTarget section */ 177 | 793E8A101EA4D11200BA3325 /* AlertTransitionDemo */ = { 178 | isa = PBXNativeTarget; 179 | buildConfigurationList = 793E8A201EA4D11200BA3325 /* Build configuration list for PBXNativeTarget "AlertTransitionDemo" */; 180 | buildPhases = ( 181 | A7B7DE3DF7AFF1C7C85A408B /* [CP] Check Pods Manifest.lock */, 182 | 793E8A0D1EA4D11200BA3325 /* Sources */, 183 | 793E8A0E1EA4D11200BA3325 /* Frameworks */, 184 | 793E8A0F1EA4D11200BA3325 /* Resources */, 185 | E5607C9777414F27DAEBC417 /* [CP] Embed Pods Frameworks */, 186 | C40D1F90111075EAE56D68B8 /* [CP] Copy Pods Resources */, 187 | ); 188 | buildRules = ( 189 | ); 190 | dependencies = ( 191 | ); 192 | name = AlertTransitionDemo; 193 | productName = AlertTransitionDemo; 194 | productReference = 793E8A111EA4D11200BA3325 /* AlertTransitionDemo.app */; 195 | productType = "com.apple.product-type.application"; 196 | }; 197 | /* End PBXNativeTarget section */ 198 | 199 | /* Begin PBXProject section */ 200 | 793E89F91EA4CFE200BA3325 /* Project object */ = { 201 | isa = PBXProject; 202 | attributes = { 203 | LastSwiftUpdateCheck = 0830; 204 | LastUpgradeCheck = 0830; 205 | ORGANIZATIONNAME = Loopeer; 206 | TargetAttributes = { 207 | 793E8A101EA4D11200BA3325 = { 208 | CreatedOnToolsVersion = 8.3.1; 209 | DevelopmentTeam = VS86A5N9W7; 210 | ProvisioningStyle = Automatic; 211 | }; 212 | }; 213 | }; 214 | buildConfigurationList = 793E89FC1EA4CFE200BA3325 /* Build configuration list for PBXProject "AlertTransition" */; 215 | compatibilityVersion = "Xcode 3.2"; 216 | developmentRegion = English; 217 | hasScannedForEncodings = 0; 218 | knownRegions = ( 219 | en, 220 | Base, 221 | ); 222 | mainGroup = 793E89F81EA4CFE200BA3325; 223 | productRefGroup = 793E8A031EA4CFE200BA3325 /* Products */; 224 | projectDirPath = ""; 225 | projectRoot = ""; 226 | targets = ( 227 | 793E8A101EA4D11200BA3325 /* AlertTransitionDemo */, 228 | ); 229 | }; 230 | /* End PBXProject section */ 231 | 232 | /* Begin PBXResourcesBuildPhase section */ 233 | 793E8A0F1EA4D11200BA3325 /* Resources */ = { 234 | isa = PBXResourcesBuildPhase; 235 | buildActionMask = 2147483647; 236 | files = ( 237 | 793E8A1E1EA4D11200BA3325 /* LaunchScreen.storyboard in Resources */, 238 | 793E8A1B1EA4D11200BA3325 /* Assets.xcassets in Resources */, 239 | 790FE7EE1EA60BEB0040D499 /* Main.storyboard in Resources */, 240 | ); 241 | runOnlyForDeploymentPostprocessing = 0; 242 | }; 243 | /* End PBXResourcesBuildPhase section */ 244 | 245 | /* Begin PBXShellScriptBuildPhase section */ 246 | A7B7DE3DF7AFF1C7C85A408B /* [CP] Check Pods Manifest.lock */ = { 247 | isa = PBXShellScriptBuildPhase; 248 | buildActionMask = 2147483647; 249 | files = ( 250 | ); 251 | inputPaths = ( 252 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 253 | "${PODS_ROOT}/Manifest.lock", 254 | ); 255 | name = "[CP] Check Pods Manifest.lock"; 256 | outputPaths = ( 257 | "$(DERIVED_FILE_DIR)/Pods-AlertTransitionDemo-checkManifestLockResult.txt", 258 | ); 259 | runOnlyForDeploymentPostprocessing = 0; 260 | shellPath = /bin/sh; 261 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 262 | showEnvVarsInLog = 0; 263 | }; 264 | C40D1F90111075EAE56D68B8 /* [CP] Copy Pods Resources */ = { 265 | isa = PBXShellScriptBuildPhase; 266 | buildActionMask = 2147483647; 267 | files = ( 268 | ); 269 | inputPaths = ( 270 | ); 271 | name = "[CP] Copy Pods Resources"; 272 | outputPaths = ( 273 | ); 274 | runOnlyForDeploymentPostprocessing = 0; 275 | shellPath = /bin/sh; 276 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AlertTransitionDemo/Pods-AlertTransitionDemo-resources.sh\"\n"; 277 | showEnvVarsInLog = 0; 278 | }; 279 | E5607C9777414F27DAEBC417 /* [CP] Embed Pods Frameworks */ = { 280 | isa = PBXShellScriptBuildPhase; 281 | buildActionMask = 2147483647; 282 | files = ( 283 | ); 284 | inputPaths = ( 285 | "${SRCROOT}/Pods/Target Support Files/Pods-AlertTransitionDemo/Pods-AlertTransitionDemo-frameworks.sh", 286 | "${BUILT_PRODUCTS_DIR}/AlertTransition/AlertTransition.framework", 287 | "${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework", 288 | ); 289 | name = "[CP] Embed Pods Frameworks"; 290 | outputPaths = ( 291 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AlertTransition.framework", 292 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SnapKit.framework", 293 | ); 294 | runOnlyForDeploymentPostprocessing = 0; 295 | shellPath = /bin/sh; 296 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AlertTransitionDemo/Pods-AlertTransitionDemo-frameworks.sh\"\n"; 297 | showEnvVarsInLog = 0; 298 | }; 299 | /* End PBXShellScriptBuildPhase section */ 300 | 301 | /* Begin PBXSourcesBuildPhase section */ 302 | 793E8A0D1EA4D11200BA3325 /* Sources */ = { 303 | isa = PBXSourcesBuildPhase; 304 | buildActionMask = 2147483647; 305 | files = ( 306 | 79474AA61EA75E7100873736 /* MenuController.swift in Sources */, 307 | 791B384E1F8CAC1500D6C0E1 /* NextViewController.swift in Sources */, 308 | 793E8A381EA5B98E00BA3325 /* EasyTransitionController.swift in Sources */, 309 | 793E8A491EA602B600BA3325 /* EasyStoryboardController.swift in Sources */, 310 | 79962C181EA7906D0046B631 /* SpriteRender.swift in Sources */, 311 | 79474AAC1EA7702600873736 /* BackgroundTypeController.swift in Sources */, 312 | 79962C091EA779D70046B631 /* BubbleTransition.swift in Sources */, 313 | 79962C0B1EA77ED80046B631 /* BubbleController.swift in Sources */, 314 | 790FE8161EA72FC50040D499 /* StoryboardAlertController.swift in Sources */, 315 | 79962C1B1EA7906D0046B631 /* ViewTexture.swift in Sources */, 316 | 79962C0F1EA789D80046B631 /* StarWallTransition.swift in Sources */, 317 | 790FE8141EA72F9B0040D499 /* SnapKitAlertController.swift in Sources */, 318 | 793E8A361EA5B7C200BA3325 /* MainController.swift in Sources */, 319 | 79474AAA1EA764EC00873736 /* TrolleyController.swift in Sources */, 320 | 793E8A141EA4D11200BA3325 /* AppDelegate.swift in Sources */, 321 | 790FE8121EA72C120040D499 /* DifferentAlertController.swift in Sources */, 322 | 793E8A3B1EA5BC5C00BA3325 /* NormalAlertController.swift in Sources */, 323 | 79962C171EA7906D0046B631 /* Sprite.swift in Sources */, 324 | 79962C191EA7906D0046B631 /* StarWarsGLAnimator.swift in Sources */, 325 | 79962C1A1EA7906D0046B631 /* Vector2.swift in Sources */, 326 | 790FE8101EA64AFA0040D499 /* Extensions.swift in Sources */, 327 | ); 328 | runOnlyForDeploymentPostprocessing = 0; 329 | }; 330 | /* End PBXSourcesBuildPhase section */ 331 | 332 | /* Begin PBXVariantGroup section */ 333 | 793E8A1C1EA4D11200BA3325 /* LaunchScreen.storyboard */ = { 334 | isa = PBXVariantGroup; 335 | children = ( 336 | 793E8A1D1EA4D11200BA3325 /* Base */, 337 | ); 338 | name = LaunchScreen.storyboard; 339 | sourceTree = ""; 340 | }; 341 | /* End PBXVariantGroup section */ 342 | 343 | /* Begin XCBuildConfiguration section */ 344 | 793E8A081EA4CFE200BA3325 /* Debug */ = { 345 | isa = XCBuildConfiguration; 346 | buildSettings = { 347 | ALWAYS_SEARCH_USER_PATHS = NO; 348 | CLANG_ANALYZER_NONNULL = YES; 349 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 350 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 351 | CLANG_CXX_LIBRARY = "libc++"; 352 | CLANG_ENABLE_MODULES = YES; 353 | CLANG_ENABLE_OBJC_ARC = YES; 354 | CLANG_WARN_BOOL_CONVERSION = YES; 355 | CLANG_WARN_CONSTANT_CONVERSION = YES; 356 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 357 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 358 | CLANG_WARN_EMPTY_BODY = YES; 359 | CLANG_WARN_ENUM_CONVERSION = YES; 360 | CLANG_WARN_INFINITE_RECURSION = YES; 361 | CLANG_WARN_INT_CONVERSION = YES; 362 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 363 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 364 | CLANG_WARN_UNREACHABLE_CODE = YES; 365 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 366 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 367 | COPY_PHASE_STRIP = NO; 368 | CURRENT_PROJECT_VERSION = 1; 369 | DEBUG_INFORMATION_FORMAT = dwarf; 370 | ENABLE_STRICT_OBJC_MSGSEND = YES; 371 | ENABLE_TESTABILITY = YES; 372 | GCC_C_LANGUAGE_STANDARD = gnu99; 373 | GCC_DYNAMIC_NO_PIC = NO; 374 | GCC_NO_COMMON_BLOCKS = YES; 375 | GCC_OPTIMIZATION_LEVEL = 0; 376 | GCC_PREPROCESSOR_DEFINITIONS = ( 377 | "DEBUG=1", 378 | "$(inherited)", 379 | ); 380 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 381 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 382 | GCC_WARN_UNDECLARED_SELECTOR = YES; 383 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 384 | GCC_WARN_UNUSED_FUNCTION = YES; 385 | GCC_WARN_UNUSED_VARIABLE = YES; 386 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 387 | MTL_ENABLE_DEBUG_INFO = YES; 388 | ONLY_ACTIVE_ARCH = YES; 389 | SDKROOT = iphoneos; 390 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 391 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 392 | TARGETED_DEVICE_FAMILY = "1,2"; 393 | VERSIONING_SYSTEM = "apple-generic"; 394 | VERSION_INFO_PREFIX = ""; 395 | }; 396 | name = Debug; 397 | }; 398 | 793E8A091EA4CFE200BA3325 /* Release */ = { 399 | isa = XCBuildConfiguration; 400 | buildSettings = { 401 | ALWAYS_SEARCH_USER_PATHS = NO; 402 | CLANG_ANALYZER_NONNULL = YES; 403 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 404 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 405 | CLANG_CXX_LIBRARY = "libc++"; 406 | CLANG_ENABLE_MODULES = YES; 407 | CLANG_ENABLE_OBJC_ARC = YES; 408 | CLANG_WARN_BOOL_CONVERSION = YES; 409 | CLANG_WARN_CONSTANT_CONVERSION = YES; 410 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 411 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 412 | CLANG_WARN_EMPTY_BODY = YES; 413 | CLANG_WARN_ENUM_CONVERSION = YES; 414 | CLANG_WARN_INFINITE_RECURSION = YES; 415 | CLANG_WARN_INT_CONVERSION = YES; 416 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 417 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 418 | CLANG_WARN_UNREACHABLE_CODE = YES; 419 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 420 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 421 | COPY_PHASE_STRIP = NO; 422 | CURRENT_PROJECT_VERSION = 1; 423 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 424 | ENABLE_NS_ASSERTIONS = NO; 425 | ENABLE_STRICT_OBJC_MSGSEND = YES; 426 | GCC_C_LANGUAGE_STANDARD = gnu99; 427 | GCC_NO_COMMON_BLOCKS = YES; 428 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 429 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 430 | GCC_WARN_UNDECLARED_SELECTOR = YES; 431 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 432 | GCC_WARN_UNUSED_FUNCTION = YES; 433 | GCC_WARN_UNUSED_VARIABLE = YES; 434 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 435 | MTL_ENABLE_DEBUG_INFO = NO; 436 | SDKROOT = iphoneos; 437 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 438 | TARGETED_DEVICE_FAMILY = "1,2"; 439 | VALIDATE_PRODUCT = YES; 440 | VERSIONING_SYSTEM = "apple-generic"; 441 | VERSION_INFO_PREFIX = ""; 442 | }; 443 | name = Release; 444 | }; 445 | 793E8A211EA4D11200BA3325 /* Debug */ = { 446 | isa = XCBuildConfiguration; 447 | baseConfigurationReference = C892AFC9C414AC1076F0A085 /* Pods-AlertTransitionDemo.debug.xcconfig */; 448 | buildSettings = { 449 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 450 | DEVELOPMENT_TEAM = VS86A5N9W7; 451 | INFOPLIST_FILE = AlertTransitionDemo/Info.plist; 452 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 453 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 454 | PRODUCT_BUNDLE_IDENTIFIER = com.loopeer.AlertTransitionDemo; 455 | PRODUCT_NAME = "$(TARGET_NAME)"; 456 | SWIFT_VERSION = 4.0; 457 | }; 458 | name = Debug; 459 | }; 460 | 793E8A221EA4D11200BA3325 /* Release */ = { 461 | isa = XCBuildConfiguration; 462 | baseConfigurationReference = 3EAE73D045222165FB810F05 /* Pods-AlertTransitionDemo.release.xcconfig */; 463 | buildSettings = { 464 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 465 | DEVELOPMENT_TEAM = VS86A5N9W7; 466 | INFOPLIST_FILE = AlertTransitionDemo/Info.plist; 467 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 468 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 469 | PRODUCT_BUNDLE_IDENTIFIER = com.loopeer.AlertTransitionDemo; 470 | PRODUCT_NAME = "$(TARGET_NAME)"; 471 | SWIFT_VERSION = 4.0; 472 | }; 473 | name = Release; 474 | }; 475 | /* End XCBuildConfiguration section */ 476 | 477 | /* Begin XCConfigurationList section */ 478 | 793E89FC1EA4CFE200BA3325 /* Build configuration list for PBXProject "AlertTransition" */ = { 479 | isa = XCConfigurationList; 480 | buildConfigurations = ( 481 | 793E8A081EA4CFE200BA3325 /* Debug */, 482 | 793E8A091EA4CFE200BA3325 /* Release */, 483 | ); 484 | defaultConfigurationIsVisible = 0; 485 | defaultConfigurationName = Release; 486 | }; 487 | 793E8A201EA4D11200BA3325 /* Build configuration list for PBXNativeTarget "AlertTransitionDemo" */ = { 488 | isa = XCConfigurationList; 489 | buildConfigurations = ( 490 | 793E8A211EA4D11200BA3325 /* Debug */, 491 | 793E8A221EA4D11200BA3325 /* Release */, 492 | ); 493 | defaultConfigurationIsVisible = 0; 494 | defaultConfigurationName = Release; 495 | }; 496 | /* End XCConfigurationList section */ 497 | }; 498 | rootObject = 793E89F91EA4CFE200BA3325 /* Project object */; 499 | } 500 | -------------------------------------------------------------------------------- /AlertTransition.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AlertTransition.xcodeproj/xcshareddata/xcschemes/AlertTransition.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 65 | 66 | 72 | 73 | 74 | 75 | 77 | 78 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /AlertTransition.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /AlertTransition/AlertTransition.h: -------------------------------------------------------------------------------- 1 | // 2 | // AlertTransition.h 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/17. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for AlertTransition. 12 | FOUNDATION_EXPORT double AlertTransitionVersionNumber; 13 | 14 | //! Project version string for AlertTransition. 15 | FOUNDATION_EXPORT const unsigned char AlertTransitionVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /AlertTransition/AlertTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlertTransition.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/17. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// If alert controller conforms to AlertFrameProtocol, the CGRect will be the final frame of alert. If alert controller do not conforms to it, alert will in center of screen, and it's height and width will be calculated from constraints. 12 | public protocol AlertFrameProtocol { 13 | var alertFrame: CGRect { get } 14 | } 15 | 16 | 17 | public extension AlertTransition { 18 | enum TransitionState { 19 | case presented, dismissed 20 | } 21 | 22 | /// Alert background Type,UIColor or UIBlurEffect. If you want a color with alpha component, use withAlphaComponent in UIColor, eg: UIColor.blue.withAlphaComponent(0.5) 23 | enum BackgroundType { 24 | case color(UIColor) 25 | case blurEffect(style: UIBlurEffectStyle, alpha: CGFloat) 26 | } 27 | } 28 | 29 | 30 | open class AlertTransition: NSObject { 31 | 32 | 33 | // MARK: - for client 34 | 35 | /// Set animation duration 36 | public var duration: TimeInterval = 0.25 37 | /// Set Alert background color or use UIBlurEffect 38 | public var backgroundType: BackgroundType = .color(UIColor(white: 0.0, alpha: 0.5)) 39 | /// If this property is YES, the alert will dismiss when you click on outside 40 | public var shouldDismissOutside = true 41 | 42 | public internal(set) weak var maskView: UIView? 43 | 44 | // MARK: - for subclass 45 | 46 | /// Set Custom UIPercentDrivenInteractiveTransition and UIPresentationController, often set in init method of subclass 47 | public var interactionTransitionType: PercentDrivenInteractiveTransition.Type? 48 | public var presentationControllerType: UIPresentationController.Type = PresentationController.self 49 | /// The InteractionTransition initialize from interactionTransitionType at suitable time 50 | public internal(set) var interactionTransition: PercentDrivenInteractiveTransition? 51 | public internal(set) var presentationController: PresentationController? 52 | 53 | /// The alert controller 54 | public internal(set) weak var toController: UIViewController! 55 | /// The controller alert from 56 | public internal(set) weak var fromController: UIViewController? 57 | 58 | /// Is current transition present or dismiss 59 | public fileprivate(set) var transitionState = TransitionState.presented 60 | 61 | 62 | // MARK: - Init 63 | 64 | public init(from controller: UIViewController? = nil) { 65 | super.init() 66 | self.fromController = controller 67 | } 68 | 69 | public override convenience init() { 70 | self.init(from: nil) 71 | } 72 | 73 | 74 | // MARK: - Methods should override by subclass 75 | 76 | /// Called in animateTransition(using transitionContext:), when animation completed don’t forget to add `context.completeTransition(true/false)` 77 | open func performPresentedTransition(presentingView: UIView, presentedView: UIView,context: UIViewControllerContextTransitioning) { 78 | // context.containerView. 79 | } 80 | open func performDismissedTransition(presentingView: UIView, presentedView: UIView,context: UIViewControllerContextTransitioning) { 81 | 82 | } 83 | } 84 | 85 | 86 | // MARK: - UIViewControllerTransitioningDelegate 87 | 88 | extension AlertTransition: UIViewControllerTransitioningDelegate { 89 | open func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 90 | transitionState = .presented 91 | return self 92 | } 93 | 94 | open func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 95 | transitionState = .dismissed 96 | return self 97 | } 98 | 99 | open func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { 100 | let presentationController: UIPresentationController = presentationControllerType.init(presentedViewController: presented, presenting: presenting) 101 | if let controller = presentationController as? PresentationController { 102 | controller.transition = self 103 | self.presentationController = controller 104 | } 105 | return presentationController 106 | } 107 | 108 | open func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { 109 | return (interactionTransition?.isTransiting ?? false) ? interactionTransition : nil 110 | } 111 | 112 | open func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { 113 | return (interactionTransition?.isTransiting ?? false) ? interactionTransition : nil 114 | } 115 | } 116 | 117 | 118 | // MARK: - UIViewControllerAnimatedTransitioning 119 | 120 | extension AlertTransition: UIViewControllerAnimatedTransitioning { 121 | open func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 122 | return duration 123 | } 124 | 125 | open func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 126 | 127 | transitionContext.toView?.frame = transitionContext.finalFrame(for: toController) 128 | 129 | if transitionState == .presented { 130 | interactionTransition?.setup(presentedView: transitionContext.toView!) 131 | 132 | performPresentedTransition(presentingView: transitionContext.fromController!.view, presentedView: transitionContext.toView!, context: transitionContext) 133 | } else { 134 | performDismissedTransition(presentingView: transitionContext.toController!.view, presentedView: transitionContext.fromView!, context: transitionContext) 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /AlertTransition/Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/17. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public final class AlertExtension { 12 | public let base: Base 13 | public init(_ base: Base) { 14 | self.base = base 15 | } 16 | } 17 | 18 | /** 19 | A type that has AlertTransition extensions. 20 | */ 21 | public protocol ExtensionCompatible { 22 | associatedtype CompatibleType 23 | var at: CompatibleType { get } 24 | } 25 | 26 | public extension ExtensionCompatible { 27 | public var at: AlertExtension { 28 | get { return AlertExtension(self) } 29 | } 30 | } 31 | 32 | extension UIViewController: ExtensionCompatible { } 33 | 34 | 35 | private var transitionKey: Void? 36 | 37 | extension AlertExtension where Base: UIViewController { 38 | 39 | public var transition: AlertTransition? { 40 | get { 41 | return objc_getAssociatedObject(base, &transitionKey) as? AlertTransition 42 | } 43 | 44 | set { 45 | objc_setAssociatedObject(base, &transitionKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 46 | 47 | guard let transtion = newValue else { return } 48 | 49 | base.transitioningDelegate = transtion 50 | base.modalPresentationStyle = .custom 51 | transtion.toController = base 52 | 53 | if let interactionTransition = transtion.interactionTransitionType?.init(transition: transtion) { 54 | interactionTransition.setup(presentingView: transtion.fromController?.view) 55 | transtion.interactionTransition = interactionTransition 56 | } 57 | } 58 | } 59 | } 60 | 61 | 62 | extension UIViewControllerContextTransitioning { 63 | var fromController: UIViewController? { 64 | return viewController(forKey: UITransitionContextViewControllerKey.from) 65 | } 66 | var toController: UIViewController? { 67 | return viewController(forKey: UITransitionContextViewControllerKey.to) 68 | } 69 | 70 | var fromView: UIView? { 71 | return view(forKey: UITransitionContextViewKey.from) 72 | } 73 | 74 | var toView: UIView? { 75 | return view(forKey: UITransitionContextViewKey.to) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /AlertTransition/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.3 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /AlertTransition/PercentDrivenInteractiveTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LPPercentDrivenInteractiveTransition.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/17. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class PercentDrivenInteractiveTransition: UIPercentDrivenInteractiveTransition { 12 | 13 | /// The transition used in 14 | public internal(set) weak var transition: AlertTransition? 15 | 16 | // flag, change this when state change 17 | public var isTransiting = false 18 | 19 | required public init(transition: AlertTransition) { 20 | self.transition = transition 21 | } 22 | 23 | // hook, called when transition is set to controller 24 | open func setup(presentingView: UIView?) { } 25 | open func setup(presentedView: UIView) { } 26 | open func deviceOrientationChanged() { } 27 | } 28 | -------------------------------------------------------------------------------- /AlertTransition/PresentationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LPPresentationController.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/17. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class PresentationController: UIPresentationController { 12 | 13 | /// The transition used in 14 | public internal(set) weak var transition: AlertTransition? 15 | 16 | /// The background view, initialized when presentationTransitionWillBegin, inserted to view hierarchy when containerViewWillLayoutSubviews 17 | public fileprivate(set) var maskView: UIView? 18 | 19 | /// Call showMaskView()、hideMaskView() manually, Usually when PercentDrivenInteractiveTransition 20 | public var manualMaskAnimation = false 21 | 22 | private var deviceOrientation = UIDeviceOrientation.portrait 23 | 24 | open override var frameOfPresentedViewInContainerView: CGRect { 25 | if let frame = (presentedViewController as? AlertFrameProtocol)?.alertFrame { 26 | return frame 27 | } 28 | 29 | let size = presentedViewController.view.systemLayoutSizeFitting(UILayoutFittingCompressedSize) 30 | // the controller view using SnapKit constraints, leading to translatesAutoresizingMaskIntoConstraints = false 31 | presentedViewController.view.translatesAutoresizingMaskIntoConstraints = true 32 | 33 | return CGRect(origin: CGPoint(x: (UIScreen.main.bounds.size.width - size.width)*0.5, y: (UIScreen.main.bounds.size.height - size.height)*0.5), size: size) 34 | } 35 | 36 | override open func presentationTransitionWillBegin() { 37 | containerView?.addSubview(presentedView!) 38 | if maskView == nil { 39 | maskView = makeMaskView() 40 | transition?.maskView = maskView 41 | } 42 | deviceOrientation = UIDevice.current.orientation 43 | } 44 | 45 | open override func dismissalTransitionWillBegin() { 46 | guard !manualMaskAnimation else { return } 47 | UIView.animate(withDuration: transition!.duration) { 48 | self.hideMaskView() 49 | } 50 | } 51 | 52 | override open func containerViewWillLayoutSubviews() { 53 | if let view = maskView, view.superview == nil, !manualMaskAnimation { 54 | containerView?.insertSubview(view, at: 0) 55 | 56 | UIView.animate(withDuration: transition!.duration, animations: { 57 | self.showMaskView() 58 | }) 59 | } 60 | 61 | guard deviceOrientation != UIDevice.current.orientation else { return } 62 | 63 | deviceOrientation = UIDevice.current.orientation 64 | /// Change Device Orientation 65 | maskView?.frame = UIScreen.main.bounds 66 | presentedView?.frame = frameOfPresentedViewInContainerView 67 | transition?.interactionTransition?.deviceOrientationChanged() 68 | } 69 | 70 | /// called in animate block, when presenting 71 | open func showMaskView() { 72 | switch transition!.backgroundType { 73 | case .color(_): 74 | maskView?.alpha = 1 75 | case .blurEffect(_, let alpha): 76 | maskView?.alpha = alpha 77 | } 78 | } 79 | 80 | /// called in animate block, when dismiss 81 | open func hideMaskView() { 82 | maskView?.alpha = 0 83 | } 84 | 85 | /// Override this method to make custom MaskView 86 | open func makeMaskView() -> UIView { 87 | var mask: UIView 88 | 89 | switch transition!.backgroundType { 90 | case .color(let color): 91 | mask = UIView() 92 | mask.backgroundColor = color 93 | case .blurEffect(let style, _): 94 | mask = UIVisualEffectView(effect: UIBlurEffect(style: style)) 95 | } 96 | 97 | mask.frame = UIScreen.main.bounds 98 | mask.alpha = 0 99 | 100 | let tap = UITapGestureRecognizer(target: self, action: #selector(close)) 101 | mask.addGestureRecognizer(tap) 102 | 103 | return mask 104 | } 105 | 106 | @objc public func close() { 107 | guard transition?.shouldDismissOutside ?? true else { 108 | return 109 | } 110 | presentedViewController.dismiss(animated: true, completion: nil) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /AlertTransition/Transitions/EasyTransition/EasyPercentDrivenTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EasyPercentDrivenTransition.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/18. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class EasyPercentDrivenTransition: PercentDrivenInteractiveTransition { 12 | 13 | var originFrame = CGRect.zero 14 | var presentedView: UIView? 15 | 16 | public override func setup(presentedView: UIView) { 17 | self.presentedView = presentedView 18 | 19 | let closeGesture = (transition as? EasyTransition)?.closeGesture ?? false 20 | if !closeGesture { 21 | let pan = UIPanGestureRecognizer(target: self, action: #selector(dismiss(pan:))) 22 | presentedView.addGestureRecognizer(pan) 23 | originFrame = presentedView.frame 24 | } 25 | } 26 | 27 | override func deviceOrientationChanged() { 28 | originFrame = presentedView?.frame ?? CGRect.zero 29 | } 30 | 31 | @objc func dismiss(pan: UIPanGestureRecognizer) { 32 | let point = pan.translation(in: pan.view?.superview) 33 | 34 | switch pan.state { 35 | 36 | case .changed: 37 | pan.view?.frame.origin.x = originFrame.minX + point.x 38 | pan.view?.frame.origin.y = originFrame.minY + point.y 39 | case .ended, .cancelled, .failed: 40 | 41 | guard fabs(Double(point.x)) <= 70 && fabs(Double(point.y)) <= 70 else { 42 | transition?.toController?.dismiss(animated: true, completion: nil) 43 | return 44 | } 45 | 46 | pan.view?.isUserInteractionEnabled = false 47 | UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0, options: .curveEaseInOut, animations: { 48 | pan.view?.frame = self.originFrame 49 | }) { (complete) in 50 | pan.view?.isUserInteractionEnabled = true 51 | } 52 | default: 53 | break 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /AlertTransition/Transitions/EasyTransition/EasyTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EasyTransition.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/18. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | public extension EasyTransition { 13 | enum AnimationType { 14 | case translation(x: CGFloat, y: CGFloat) 15 | case rotation(angle: CGFloat, anchorPoint: CGPoint) 16 | case scale(CGFloat) 17 | case alpha(CGFloat) 18 | 19 | func apply(to view: UIView) { 20 | 21 | switch self { 22 | case .translation(let x, let y): 23 | view.transform = view.transform.concatenating(CGAffineTransform(translationX: x, y: y)) 24 | case .rotation(let angle, let anchorPoint): 25 | let originOffset = CGPoint(x: anchorPoint.x - view.layer.anchorPoint.x, y: anchorPoint.y - view.layer.anchorPoint.y) 26 | let positionOffset = CGPoint(x: originOffset.x * view.layer.frame.width, y: originOffset.y * view.layer.frame.height) 27 | 28 | view.layer.anchorPoint = anchorPoint 29 | view.layer.position = CGPoint(x: view.layer.position.x + positionOffset.x, y: view.layer.position.y + positionOffset.y) 30 | view.transform = view.transform.concatenating(CGAffineTransform(rotationAngle: angle)) 31 | case .scale(let scale): 32 | view.transform = view.transform.concatenating(CGAffineTransform(scaleX: scale, y: scale)) 33 | case .alpha(let alpha): 34 | view.alpha = alpha 35 | } 36 | } 37 | } 38 | 39 | struct AnimateParams { 40 | public var duration: TimeInterval = 0.25 41 | public var damping: CGFloat = 0.7 42 | public var velocity: CGFloat = 0 43 | public var options: UIViewAnimationOptions = .curveEaseInOut 44 | } 45 | } 46 | 47 | 48 | public class EasyTransition: AlertTransition { 49 | /// Set the start state of Alert 50 | public var startTransforms: [AnimationType] = [.alpha(0), .translation(x: 0, y: 200)] 51 | /// Set the end state of Alert 52 | public var endTransforms: [AnimationType]? 53 | /// Set the state of view in presentatingController 54 | public var presentatingViewTransforms: [AnimationType]? 55 | /// Set the spring animation params in present, include duration、damping、velocity、UIViewAnimationOptions 56 | public var presentAnimateParams = AnimateParams() 57 | /// Set the spring animation params in dismiss 58 | public var dismissAnimateParams = AnimateParams() 59 | 60 | public var closeGesture = false 61 | 62 | public var disableGestureDismiss = false { 63 | didSet { 64 | guard disableGestureDismiss == true else { return } 65 | 66 | interactionTransition = nil 67 | } 68 | } 69 | 70 | public override var duration: TimeInterval { 71 | didSet { 72 | presentAnimateParams.duration = duration 73 | dismissAnimateParams.duration = duration 74 | } 75 | } 76 | 77 | public override init(from controller: UIViewController? = nil) { 78 | super.init(from: controller) 79 | 80 | interactionTransitionType = EasyPercentDrivenTransition.self 81 | } 82 | 83 | public override func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 84 | if transitionState == .presented { 85 | return presentAnimateParams.duration 86 | } else { 87 | return dismissAnimateParams.duration 88 | } 89 | } 90 | 91 | public override func performPresentedTransition(presentingView originalView: UIView, presentedView: UIView,context: UIViewControllerContextTransitioning) { 92 | 93 | apply(transforms: startTransforms, to: presentedView) 94 | 95 | UIView.animate(withDuration: presentAnimateParams.duration, delay: 0, usingSpringWithDamping: presentAnimateParams.damping, initialSpringVelocity: presentAnimateParams.velocity, options: presentAnimateParams.options, animations: { 96 | presentedView.transform = CGAffineTransform.identity 97 | presentedView.alpha = 1 98 | if let originalViewTransforms = self.presentatingViewTransforms { 99 | self.apply(transforms: originalViewTransforms, to: originalView) 100 | } 101 | }) { (complete) in 102 | if context.transitionWasCancelled { 103 | context.completeTransition(false) 104 | } else { 105 | context.completeTransition(complete) 106 | } 107 | } 108 | } 109 | 110 | public override func performDismissedTransition(presentingView originalView: UIView, presentedView: UIView,context: UIViewControllerContextTransitioning) { 111 | 112 | UIView.animate(withDuration: dismissAnimateParams.duration, delay: 0, usingSpringWithDamping: dismissAnimateParams.damping, initialSpringVelocity: dismissAnimateParams.velocity, options: dismissAnimateParams.options, animations: { 113 | self.apply(transforms: self.endTransforms ?? self.startTransforms, to: presentedView) 114 | originalView.transform = CGAffineTransform.identity 115 | }) { (complete) in 116 | if context.transitionWasCancelled { 117 | context.completeTransition(false) 118 | } else { 119 | context.completeTransition(complete) 120 | } 121 | } 122 | } 123 | 124 | func apply(transforms: [AnimationType], to view: UIView) { 125 | transforms.forEach { $0.apply(to: view) } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /AlertTransition/Transitions/MenuTransition/MenuPercentDrivenTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuPercentDrivenTransition.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/19. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MenuPercentDrivenTransition: PercentDrivenInteractiveTransition { 12 | var transitionWidth: CGFloat = UIScreen.main.bounds.width 13 | 14 | override func setup(presentingView: UIView?) { 15 | let pan = UIPanGestureRecognizer(target: self, action: #selector(presentPan(pan:))) 16 | pan.delegate = self 17 | presentingView?.addGestureRecognizer(pan) 18 | 19 | if let width = (transition?.toController as? AlertFrameProtocol)?.alertFrame.size.width { 20 | transitionWidth = width 21 | } 22 | } 23 | 24 | func setupDismissView(view: UIView) { 25 | let pan = UIPanGestureRecognizer(target: self, action: #selector(dismissPan(pan:))) 26 | view.addGestureRecognizer(pan) 27 | } 28 | 29 | @objc func presentPan(pan: UIPanGestureRecognizer) { 30 | let offset = pan.translation(in: pan.view?.superview).x 31 | let persent = offset / transitionWidth 32 | 33 | switch pan.state { 34 | case .began: 35 | isTransiting = true 36 | 37 | if let owningController = transition?.toController { 38 | transition?.fromController?.present(owningController, animated: true, completion: nil) 39 | } 40 | case .changed: 41 | if persent >= 1 { 42 | update(0.999) 43 | return 44 | } 45 | update(persent) 46 | case .ended, .cancelled, .failed: 47 | isTransiting = false 48 | 49 | if persent > 0.5 { 50 | finish() 51 | } else { 52 | cancel() 53 | } 54 | default: 55 | break 56 | } 57 | } 58 | 59 | @objc func dismissPan(pan: UIPanGestureRecognizer) { 60 | let offset = -pan.translation(in: pan.view?.superview).x 61 | let persent = offset / transitionWidth 62 | 63 | switch pan.state { 64 | case .began: 65 | isTransiting = true 66 | transition?.fromController?.dismiss(animated: true, completion: nil) 67 | case .changed: 68 | if persent >= 1 { 69 | update(0.999) 70 | return 71 | } 72 | update(persent) 73 | case .ended, .cancelled, .failed: 74 | isTransiting = false 75 | 76 | if persent > 0.5 { 77 | finish() 78 | } else { 79 | cancel() 80 | } 81 | default: 82 | break 83 | } 84 | } 85 | } 86 | 87 | extension MenuPercentDrivenTransition: UIGestureRecognizerDelegate { 88 | func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { 89 | guard let count = (transition?.fromController as? UINavigationController)?.childViewControllers.count, count == 1 else { 90 | return false 91 | } 92 | guard let view = transition?.fromController?.view else { 93 | return false 94 | } 95 | return gestureRecognizer.location(in: view).x < 40 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /AlertTransition/Transitions/MenuTransition/MenuTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuTransition.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/19. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class MenuTransition: AlertTransition { 12 | 13 | public internal(set) var originSuperView: UIView? 14 | 15 | public override init(from controller: UIViewController?) { 16 | super.init(from: controller) 17 | interactionTransitionType = MenuPercentDrivenTransition.self 18 | backgroundType = .color(.clear) 19 | } 20 | 21 | public override func performPresentedTransition(presentingView: UIView, presentedView: UIView, context: UIViewControllerContextTransitioning) { 22 | presentationController?.manualMaskAnimation = true 23 | originSuperView = presentingView.superview 24 | context.containerView.addSubview(presentingView) 25 | context.containerView.addSubview(maskView!) 26 | (interactionTransition as? MenuPercentDrivenTransition)?.setupDismissView(view: maskView!) 27 | 28 | presentedView.frame.origin.x = -presentedView.frame.width / 2 29 | presentingView.frame.origin.x = 0 30 | maskView?.frame = presentingView.frame 31 | 32 | // HACK: If zero, the animation briefly flashes in iOS 11. UIViewPropertyAnimators (iOS 10+) may resolve this. 33 | let delay = (interactionTransition?.isTransiting ?? false) ? duration : 0 34 | 35 | UIView.animate(withDuration: duration, delay: delay, options: [.curveLinear], animations: { 36 | presentedView.transform = CGAffineTransform(translationX: presentedView.frame.width / 2, y: 0) 37 | presentingView.frame.origin.x = presentedView.frame.width 38 | self.maskView?.frame = presentingView.frame 39 | self.presentationController?.showMaskView() 40 | }) { (complete) in 41 | if context.transitionWasCancelled { 42 | context.completeTransition(false) 43 | self.originSuperView?.addSubview(presentingView) 44 | }else{ 45 | context.completeTransition(complete) 46 | } 47 | } 48 | } 49 | 50 | public override func performDismissedTransition(presentingView: UIView, presentedView: UIView, context: UIViewControllerContextTransitioning) { 51 | 52 | // HACK: If zero, the animation briefly flashes in iOS 11. UIViewPropertyAnimators (iOS 10+) may resolve this. 53 | let delay = (interactionTransition?.isTransiting ?? false) ? duration : 0 54 | 55 | UIView.animate(withDuration: duration, delay: delay, options: [.curveLinear], animations: { 56 | self.dismissActions() 57 | }) { (complete) in 58 | if context.transitionWasCancelled { 59 | context.completeTransition(false) 60 | }else{ 61 | context.completeTransition(complete) 62 | self.originSuperView?.addSubview(presentingView) 63 | } 64 | } 65 | } 66 | 67 | public func dismissActions() { 68 | toController.view.transform = CGAffineTransform.identity 69 | fromController?.view.frame.origin.x = 0 70 | self.maskView?.frame = fromController?.view.frame ?? CGRect.zero 71 | self.presentationController?.hideMaskView() 72 | } 73 | 74 | public func push(controller: UIViewController, from navi: UINavigationController? = nil) { 75 | guard let naviController = navi ?? (fromController as? UINavigationController) else { return } 76 | 77 | CATransaction.begin() 78 | CATransaction.setCompletionBlock( { () -> Void in 79 | self.toController.dismiss(animated: true, completion: nil) 80 | }) 81 | UIView.animate(withDuration: duration, animations: { () -> Void in 82 | self.dismissActions() 83 | }) 84 | naviController.pushViewController(controller, animated: true) 85 | CATransaction.commit() 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /AlertTransition/Transitions/TrolleyTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TrolleyTransition.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/19. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class TrolleyTransition: AlertTransition { 12 | 13 | public var m34: CGFloat = 1.0 / -6000 14 | public var scale: CGFloat = 0.85 15 | public var rotate: CGFloat = 15.0 * CGFloat(Double.pi)/180.0 16 | public var zTranslate: CGFloat = -100.0 17 | 18 | public override init(from controller: UIViewController?) { 19 | super.init(from: controller) 20 | duration = 0.5 21 | } 22 | 23 | public override func performPresentedTransition(presentingView: UIView, presentedView: UIView, context: UIViewControllerContextTransitioning) { 24 | presentedView.frame.origin.y = UIScreen.main.bounds.height 25 | 26 | UIView.animate(withDuration: duration, animations: { 27 | presentedView.transform = CGAffineTransform(translationX: 0, y: -presentedView.frame.height) 28 | }) 29 | 30 | UIView.animate(withDuration: duration/2, animations: { 31 | presentingView.layer.transform = self.firstTransform() 32 | }) { (complete) in 33 | UIView.animate(withDuration: self.duration/2, animations: { 34 | presentingView.layer.transform = self.secondTransform() 35 | }, completion: { (complete) in 36 | context.completeTransition(complete) 37 | }) 38 | } 39 | } 40 | 41 | public override func performDismissedTransition(presentingView: UIView, presentedView: UIView, context: UIViewControllerContextTransitioning) { 42 | 43 | UIView.animate(withDuration: duration, animations: { 44 | presentedView.transform = CGAffineTransform.identity 45 | }) 46 | 47 | UIView.animate(withDuration: duration/2, animations: { 48 | presentingView.layer.transform = self.firstTransform() 49 | }) { (complete) in 50 | UIView.animate(withDuration: self.duration/2, animations: { 51 | presentingView.layer.transform = CATransform3DIdentity 52 | }, completion: { (complete) in 53 | context.completeTransition(complete) 54 | }) 55 | } 56 | } 57 | 58 | private func firstTransform() -> CATransform3D { 59 | var form = CATransform3DIdentity 60 | form.m34 = m34 61 | form = CATransform3DScale(form, scale, scale, 1) 62 | form = CATransform3DRotate(form, rotate, 1, 0, 0) 63 | form = CATransform3DTranslate(form, 0, 0, zTranslate) 64 | return form 65 | } 66 | 67 | private func secondTransform() -> CATransform3D { 68 | var form = CATransform3DIdentity 69 | form.m34 = firstTransform().m34 70 | form = CATransform3DTranslate(form, 0, -20, 0) 71 | form = CATransform3DScale(form, scale, scale, 1) 72 | return form 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /AlertTransitionDemo/Alerts/NormalAlertController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NormalAlertController.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/18. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AlertTransition 11 | 12 | class NormalAlertController: UIViewController, AlertFrameProtocol { 13 | 14 | var alertFrame: CGRect { 15 | let x = (UIScreen.main.bounds.size.width - 200) / 2 16 | let y = (UIScreen.main.bounds.size.height - 250) / 2 17 | return CGRect(x: x, y: y, width: 200, height: 250) 18 | } 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | 23 | view.backgroundColor = .white 24 | 25 | let image = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 150)) 26 | image.backgroundColor = .randomColor 27 | 28 | let title = UIView(frame: CGRect(x: 15, y: image.frame.maxY + 15, width: 100, height: 10)) 29 | title.backgroundColor = UIColor(red: 0.74, green: 0.74, blue: 0.74, alpha: 1) 30 | 31 | let content1 = UIView(frame: CGRect(x: 15, y: title.frame.maxY + 10, width: 170, height: 10)) 32 | content1.backgroundColor = UIColor(red: 0.93, green: 0.93, blue: 0.93, alpha: 1) 33 | 34 | let content2 = UIView(frame: CGRect(x: 15, y: content1.frame.maxY + 10, width: 150, height: 10)) 35 | content2.backgroundColor = UIColor(red: 0.93, green: 0.93, blue: 0.93, alpha: 1) 36 | 37 | let content3 = UIView(frame: CGRect(x: 15, y: content2.frame.maxY + 10, width: 170, height: 10)) 38 | content3.backgroundColor = UIColor(red: 0.93, green: 0.93, blue: 0.93, alpha: 1) 39 | 40 | let button = UIButton(type: .custom) 41 | button.setImage(#imageLiteral(resourceName: "alert_dismiss"), for: .normal) 42 | button.addTarget(self, action: #selector(buttonClicked(sender:)), for: .touchUpInside) 43 | button.sizeToFit() 44 | button.frame.origin = CGPoint(x: 155, y: 10) 45 | 46 | view.addSubview(image) 47 | view.addSubview(title) 48 | view.addSubview(content1) 49 | view.addSubview(content2) 50 | view.addSubview(content3) 51 | view.addSubview(button) 52 | } 53 | 54 | @objc func buttonClicked(sender: UIButton) { 55 | dismiss(animated: true, completion: nil) 56 | } 57 | 58 | deinit { 59 | print("---deinit---") 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /AlertTransitionDemo/Alerts/SnapKitAlertController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKitAlertController.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/19. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SnapKit 11 | 12 | class SnapKitAlertController: UIViewController { 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | 17 | view.backgroundColor = .white 18 | 19 | let image = UIView(frame: CGRect.zero) 20 | image.backgroundColor = .randomColor 21 | 22 | let title = UIView(frame: CGRect.zero) 23 | title.backgroundColor = UIColor(red: 0.74, green: 0.74, blue: 0.74, alpha: 1) 24 | 25 | let content1 = UIView(frame: CGRect.zero) 26 | content1.backgroundColor = UIColor(red: 0.93, green: 0.93, blue: 0.93, alpha: 1) 27 | 28 | let content2 = UIView(frame: CGRect.zero) 29 | content2.backgroundColor = UIColor(red: 0.93, green: 0.93, blue: 0.93, alpha: 1) 30 | 31 | let content3 = UIView(frame: CGRect.zero) 32 | content3.backgroundColor = UIColor(red: 0.93, green: 0.93, blue: 0.93, alpha: 1) 33 | 34 | let button = UIButton(type: .custom) 35 | button.setImage(#imageLiteral(resourceName: "alert_dismiss"), for: .normal) 36 | button.addTarget(self, action: #selector(buttonClicked(sender:)), for: .touchUpInside) 37 | button.sizeToFit() 38 | button.frame.origin = CGPoint(x: 155, y: 10) 39 | 40 | view.addSubview(image) 41 | view.addSubview(title) 42 | view.addSubview(content1) 43 | view.addSubview(content2) 44 | view.addSubview(content3) 45 | view.addSubview(button) 46 | 47 | image.snp.makeConstraints { make in 48 | make.width.equalTo(200).priority(999) 49 | make.height.equalTo(150) 50 | make.left.top.right.equalToSuperview() 51 | } 52 | 53 | title.snp.makeConstraints { make in 54 | make.width.equalTo(100) 55 | make.height.equalTo(10) 56 | make.left.equalToSuperview().offset(15) 57 | make.top.equalTo(image.snp.bottom).offset(15) 58 | } 59 | 60 | content1.snp.makeConstraints { make in 61 | make.width.equalTo(170) 62 | make.height.equalTo(10) 63 | make.left.equalToSuperview().offset(15) 64 | make.top.equalTo(title.snp.bottom).offset(15) 65 | } 66 | 67 | content2.snp.makeConstraints { make in 68 | make.width.equalTo(150) 69 | make.height.equalTo(10) 70 | make.left.equalToSuperview().offset(15) 71 | make.top.equalTo(content1.snp.bottom).offset(15) 72 | } 73 | 74 | content3.snp.makeConstraints { make in 75 | make.width.equalTo(170) 76 | make.height.equalTo(10) 77 | make.left.equalToSuperview().offset(15) 78 | make.top.equalTo(content2.snp.bottom).offset(15) 79 | make.bottom.equalToSuperview().offset(-15).priority(999) 80 | } 81 | } 82 | 83 | @objc func buttonClicked(sender: UIButton) { 84 | dismiss(animated: true, completion: nil) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /AlertTransitionDemo/Alerts/StoryboardAlertController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StoryboardAlertController.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/18. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class StoryboardAlertController: UIViewController { 12 | 13 | @IBOutlet weak var imageView: UIView! 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | 18 | imageView.backgroundColor = .randomColor 19 | } 20 | 21 | @IBAction func dismiss() { 22 | dismiss(animated: true, completion: nil) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /AlertTransitionDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // AlertTransitionDemo 4 | // 5 | // Created by 韩帅 on 2017/4/17. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | 19 | window = UIWindow(frame: UIScreen.main.bounds) 20 | window?.rootViewController = UINavigationController(rootViewController: MainController()) 21 | window?.makeKeyAndVisible() 22 | 23 | return true 24 | } 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /AlertTransitionDemo/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" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /AlertTransitionDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /AlertTransitionDemo/Assets.xcassets/alert_dismiss.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "alert_dismiss@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "alert_dismiss@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /AlertTransitionDemo/Assets.xcassets/alert_dismiss.imageset/alert_dismiss@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loopeer/AlertTransition/05c67f56bcc1d6069f1903e39948412281b69b6b/AlertTransitionDemo/Assets.xcassets/alert_dismiss.imageset/alert_dismiss@2x.png -------------------------------------------------------------------------------- /AlertTransitionDemo/Assets.xcassets/alert_dismiss.imageset/alert_dismiss@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loopeer/AlertTransition/05c67f56bcc1d6069f1903e39948412281b69b6b/AlertTransitionDemo/Assets.xcassets/alert_dismiss.imageset/alert_dismiss@3x.png -------------------------------------------------------------------------------- /AlertTransitionDemo/Assets.xcassets/backgroud_image.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "backgroud_image.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /AlertTransitionDemo/Assets.xcassets/backgroud_image.imageset/backgroud_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loopeer/AlertTransition/05c67f56bcc1d6069f1903e39948412281b69b6b/AlertTransitionDemo/Assets.xcassets/backgroud_image.imageset/backgroud_image.png -------------------------------------------------------------------------------- /AlertTransitionDemo/Assets.xcassets/dismiss_button.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "dismiss_button@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "dismiss_button@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /AlertTransitionDemo/Assets.xcassets/dismiss_button.imageset/dismiss_button@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loopeer/AlertTransition/05c67f56bcc1d6069f1903e39948412281b69b6b/AlertTransitionDemo/Assets.xcassets/dismiss_button.imageset/dismiss_button@2x.png -------------------------------------------------------------------------------- /AlertTransitionDemo/Assets.xcassets/dismiss_button.imageset/dismiss_button@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loopeer/AlertTransition/05c67f56bcc1d6069f1903e39948412281b69b6b/AlertTransitionDemo/Assets.xcassets/dismiss_button.imageset/dismiss_button@3x.png -------------------------------------------------------------------------------- /AlertTransitionDemo/Assets.xcassets/menu_background.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "menu_background.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /AlertTransitionDemo/Assets.xcassets/menu_background.imageset/menu_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loopeer/AlertTransition/05c67f56bcc1d6069f1903e39948412281b69b6b/AlertTransitionDemo/Assets.xcassets/menu_background.imageset/menu_background.png -------------------------------------------------------------------------------- /AlertTransitionDemo/Assets.xcassets/trolley_background.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "trolley_background.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /AlertTransitionDemo/Assets.xcassets/trolley_background.imageset/trolley_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loopeer/AlertTransition/05c67f56bcc1d6069f1903e39948412281b69b6b/AlertTransitionDemo/Assets.xcassets/trolley_background.imageset/trolley_background.png -------------------------------------------------------------------------------- /AlertTransitionDemo/BackgroundTypeController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BackgroundTypeController.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/19. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AlertTransition 11 | 12 | 13 | class BackgroundTypeController: UITableViewController { 14 | 15 | var titles = ["blur-extraLight + alpha", "blur-light + alpha", "blur-dark + alpha", "custom color + alpha"] 16 | 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | 21 | title = "BackgroundType" 22 | view.layer.contents = #imageLiteral(resourceName: "backgroud_image").cgImage 23 | 24 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: UITableViewCell.description()) 25 | tableView.tableFooterView = UIView() 26 | tableView.backgroundColor = .clear 27 | } 28 | } 29 | 30 | extension BackgroundTypeController { 31 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 32 | return titles.count 33 | } 34 | 35 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 36 | let cell = tableView.dequeueReusableCell(withIdentifier: UITableViewCell.description(), for: indexPath) 37 | cell.backgroundColor = .clear 38 | cell.textLabel?.backgroundColor = .clear 39 | cell.textLabel?.text = titles[indexPath.row] 40 | return cell 41 | } 42 | 43 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 44 | tableView.deselectRow(at: indexPath, animated: false) 45 | 46 | 47 | let alert = NormalAlertController() 48 | 49 | let transition = EasyTransition() 50 | transition.startTransforms = [.alpha(0), .rotation(angle: 0.75, anchorPoint: CGPoint(x: 0, y: 0)), .scale(0.5), .translation(x: 0, y: 200)] 51 | 52 | switch indexPath.row { 53 | case 0: 54 | transition.backgroundType = .blurEffect(style: .extraLight, alpha: 0.9) 55 | case 1: 56 | transition.backgroundType = .blurEffect(style: .light, alpha: 0.9) 57 | case 2: 58 | transition.backgroundType = .blurEffect(style: .dark, alpha: 0.9) 59 | case 3: 60 | transition.backgroundType = .color(UIColor.blue.withAlphaComponent(0.5)) 61 | default: 62 | break 63 | } 64 | 65 | alert.at.transition = transition 66 | 67 | present(alert, animated: true, completion: nil) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /AlertTransitionDemo/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 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /AlertTransitionDemo/BubbleController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BubbleController.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/19. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AlertTransition 11 | 12 | class BubbleController: UIViewController, AlertFrameProtocol { 13 | 14 | var alertFrame: CGRect { return UIScreen.main.bounds } 15 | 16 | var label: UILabel! 17 | var dismissButton: UIButton! 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | 22 | view.backgroundColor = .red 23 | 24 | label = UILabel() 25 | label.text = "Hello!" 26 | label.font = UIFont.systemFont(ofSize: 50) 27 | label.textColor = .white 28 | label.sizeToFit() 29 | label.center = CGPoint(x: UIScreen.main.bounds.width / 2, y: 100) 30 | 31 | dismissButton = UIButton(type: .custom) 32 | dismissButton.backgroundColor = UIColor.white 33 | dismissButton.setImage(#imageLiteral(resourceName: "dismiss_button"), for: .normal) 34 | dismissButton.bounds = CGRect(x: 0, y: 0, width: 60, height: 60) 35 | dismissButton.layer.cornerRadius = 30 36 | dismissButton.center = CGPoint(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height - 50) 37 | dismissButton.addTarget(self, action: #selector(buttonClicked(sender:)), for: .touchUpInside) 38 | 39 | view.addSubview(label) 40 | view.addSubview(dismissButton) 41 | } 42 | 43 | @objc func buttonClicked(sender: UIButton) { 44 | dismiss(animated: true, completion: nil) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /AlertTransitionDemo/CustomTransition/BubbleTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BubbleTransition.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/19. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AlertTransition 11 | 12 | public class BubbleTransition: AlertTransition { 13 | 14 | var startingPoint = CGPoint.zero { 15 | didSet { 16 | bubble.center = startingPoint 17 | } 18 | } 19 | 20 | var bubbleColor: UIColor = .white 21 | 22 | fileprivate(set) var bubble = UIView() 23 | 24 | public override func performPresentedTransition(presentingView: UIView, presentedView: UIView, context: UIViewControllerContextTransitioning) { 25 | let originalCenter = presentedView.center 26 | let originalSize = presentedView.frame.size 27 | 28 | bubble = UIView() 29 | bubble.frame = frameForBubble(originalCenter, size: originalSize, start: startingPoint) 30 | bubble.layer.cornerRadius = bubble.frame.size.height / 2 31 | bubble.center = startingPoint 32 | bubble.transform = CGAffineTransform(scaleX: 0.001, y: 0.001) 33 | bubble.backgroundColor = bubbleColor 34 | context.containerView.insertSubview(bubble, at: 0) 35 | 36 | presentedView.center = startingPoint 37 | presentedView.transform = CGAffineTransform(scaleX: 0.001, y: 0.001) 38 | presentedView.alpha = 0 39 | 40 | UIView.animate(withDuration: duration, animations: { 41 | self.bubble.transform = CGAffineTransform.identity 42 | presentedView.transform = CGAffineTransform.identity 43 | presentedView.alpha = 1 44 | presentedView.center = originalCenter 45 | }, completion: { (_) in 46 | context.completeTransition(true) 47 | 48 | self.fromController?.endAppearanceTransition() 49 | self.toController.endAppearanceTransition() 50 | }) 51 | } 52 | 53 | public override func performDismissedTransition(presentingView: UIView, presentedView: UIView, context: UIViewControllerContextTransitioning) { 54 | bubble.frame = frameForBubble(presentedView.center, size: presentedView.frame.size, start: startingPoint) 55 | bubble.layer.cornerRadius = bubble.frame.size.height / 2 56 | bubble.center = startingPoint 57 | 58 | UIView.animate(withDuration: duration, animations: { 59 | self.bubble.transform = CGAffineTransform(scaleX: 0.001, y: 0.001) 60 | presentedView.transform = CGAffineTransform(scaleX: 0.001, y: 0.001) 61 | presentedView.center = self.startingPoint 62 | presentedView.alpha = 0 63 | 64 | }, completion: { (_) in 65 | self.bubble.removeFromSuperview() 66 | context.completeTransition(true) 67 | 68 | self.fromController?.endAppearanceTransition() 69 | self.toController.endAppearanceTransition() 70 | }) 71 | } 72 | 73 | func frameForBubble(_ originalCenter: CGPoint, size originalSize: CGSize, start: CGPoint) -> CGRect { 74 | let lengthX = fmax(start.x, originalSize.width - start.x) 75 | let lengthY = fmax(start.y, originalSize.height - start.y) 76 | let offset = sqrt(lengthX * lengthX + lengthY * lengthY) * 2 77 | let size = CGSize(width: offset, height: offset) 78 | 79 | return CGRect(origin: CGPoint.zero, size: size) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /AlertTransitionDemo/CustomTransition/StarWallTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StarWallTransition.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/19. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AlertTransition 11 | 12 | class StarWallTransition: AlertTransition { 13 | 14 | override func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 15 | return StarWarsGLAnimator() 16 | } 17 | 18 | 19 | override func performPresentedTransition(presentingView: UIView, presentedView: UIView, context: UIViewControllerContextTransitioning) { 20 | 21 | presentedView.alpha = 0 22 | 23 | UIView.animate(withDuration: duration, animations: { 24 | presentedView.alpha = 1 25 | }) { complete in 26 | if context.transitionWasCancelled { 27 | context.completeTransition(false) 28 | } else { 29 | context.completeTransition(complete) 30 | } 31 | } 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /AlertTransitionDemo/DifferentAlertController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DifferentAlertController.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/19. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AlertTransition 11 | 12 | class DifferentAlertController: UITableViewController { 13 | 14 | var titles = ["Alert (AlertFrameProtocol)", "Alert (constraints, storyboard)", "Alert (constraints, SnapKit)"] 15 | 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | title = "AlertTransition" 21 | 22 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: UITableViewCell.description()) 23 | tableView.tableFooterView = UIView() 24 | } 25 | } 26 | 27 | extension DifferentAlertController { 28 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 29 | return titles.count 30 | } 31 | 32 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 33 | let cell = tableView.dequeueReusableCell(withIdentifier: UITableViewCell.description(), for: indexPath) 34 | cell.textLabel?.text = titles[indexPath.row] 35 | return cell 36 | } 37 | 38 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 39 | tableView.deselectRow(at: indexPath, animated: false) 40 | 41 | 42 | let alert: UIViewController 43 | 44 | switch indexPath.row { 45 | case 0: 46 | alert = NormalAlertController() 47 | case 1: 48 | let storyBoard = UIStoryboard(name: "Main", bundle: nil) 49 | alert = storyBoard.instantiateViewController(withIdentifier: "StoryboardAlertController") as UIViewController 50 | default: 51 | alert = SnapKitAlertController() 52 | } 53 | 54 | let transition = EasyTransition() 55 | transition.startTransforms = [.alpha(0), .rotation(angle: 0.75, anchorPoint: CGPoint(x: 0, y: 0)), .scale(0.5), .translation(x: 0, y: 200)] 56 | alert.at.transition = transition 57 | 58 | present(alert, animated: true, completion: nil) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /AlertTransitionDemo/EasyStoryboardController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EasyStoryboardController.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/18. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AlertTransition 11 | 12 | class EasyStoryboardController: UITableViewController { 13 | 14 | 15 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 16 | let controller = segue.destination 17 | 18 | let transition = EasyTransition() 19 | transition.startTransforms = [.alpha(0), .rotation(angle: 0.75, anchorPoint: CGPoint(x: 0, y: 0)), .scale(0.5), .translation(x: 0, y: 200)] 20 | 21 | controller.at.transition = transition 22 | } 23 | 24 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 25 | tableView.deselectRow(at: indexPath, animated: false) 26 | 27 | guard indexPath.row != 3 else { return } 28 | 29 | 30 | let storyBoard = UIStoryboard(name: "Main", bundle: nil) 31 | let alert = storyBoard.instantiateViewController(withIdentifier: "StoryboardAlertController") as UIViewController 32 | let transition = EasyTransition() 33 | 34 | switch indexPath.row { 35 | case 0: 36 | transition.startTransforms = [.translation(x: 0, y: -100), .alpha(0)] 37 | transition.endTransforms = [.translation(x: 0, y: 100), .alpha(0)] 38 | case 1: 39 | transition.startTransforms = [.scale(0.5), .alpha(0)] 40 | case 2: 41 | transition.startTransforms = [.rotation(angle: 1.5, anchorPoint: CGPoint(x: 0, y: 0)), .alpha(0)] 42 | default: 43 | break 44 | } 45 | 46 | alert.at.transition = transition 47 | present(alert, animated: true, completion: nil) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /AlertTransitionDemo/EasyTransitionController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EasyTransitionController.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/18. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AlertTransition 11 | 12 | class EasyTransitionController: UITableViewController { 13 | 14 | let titles = ["transition", "rotation", "scale", "alpha", "transition + alpha", "scale + alpha", "rotation + alpha", "transition + rotation", "transition + rotation + scale + alpha", "presentatingViewTransforms", "presentAnimateParams"] 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | title = "EasyTransition" 20 | 21 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: UITableViewCell.description()) 22 | tableView.tableFooterView = UIView() 23 | } 24 | } 25 | 26 | extension EasyTransitionController { 27 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 28 | return titles.count 29 | } 30 | 31 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 32 | let cell = tableView.dequeueReusableCell(withIdentifier: UITableViewCell.description(), for: indexPath) 33 | cell.textLabel?.text = titles[indexPath.row] 34 | return cell 35 | } 36 | 37 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 38 | tableView.deselectRow(at: indexPath, animated: false) 39 | 40 | let alert = NormalAlertController() 41 | let transition = EasyTransition() 42 | 43 | switch indexPath.row { 44 | case 0: 45 | transition.startTransforms = [.translation(x: 0, y: 500)] 46 | transition.closeGesture = true 47 | case 1: 48 | transition.startTransforms = [.rotation(angle: CGFloat(Double.pi/2), anchorPoint: CGPoint(x: 0, y: 0))] 49 | case 2: 50 | transition.startTransforms = [.scale(0.1)] 51 | case 3: 52 | transition.startTransforms = [.alpha(0)] 53 | case 4: 54 | transition.startTransforms = [.translation(x: 0, y: -100), .alpha(0)] 55 | transition.endTransforms = [.translation(x: 0, y: 100), .alpha(0)] 56 | case 5: 57 | transition.startTransforms = [.scale(0.5), .alpha(0)] 58 | case 6: 59 | transition.startTransforms = [.rotation(angle: CGFloat(Double.pi/2), anchorPoint: CGPoint(x: 0, y: 0)), .alpha(0)] 60 | case 7: 61 | transition.startTransforms = [.rotation(angle: -0.75, anchorPoint: CGPoint(x: 0, y: 0)), .translation(x: 0, y: -300)] 62 | transition.endTransforms = [.scale(0.5), .alpha(0)] 63 | case 8: 64 | transition.startTransforms = [.alpha(0), .rotation(angle: 0.75, anchorPoint: CGPoint(x: 0, y: 0)), .scale(0.5), .translation(x: 0, y: 200)] 65 | case 9: 66 | transition.startTransforms = [.scale(0.5), .alpha(0)] 67 | transition.presentatingViewTransforms = [.scale(0.9)] 68 | case 10: 69 | transition.startTransforms = [.translation(x: 0, y: 500)] 70 | transition.presentAnimateParams.damping = 0.3 71 | default: 72 | break 73 | } 74 | 75 | alert.at.transition = transition 76 | present(alert, animated: true, completion: nil) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /AlertTransitionDemo/Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/18. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIColor { 12 | static var randomColor: UIColor { 13 | func randomFloat() -> CGFloat { 14 | return CGFloat(Double(arc4random()) / 0x100000000) 15 | } 16 | 17 | return UIColor(red: randomFloat(), green: randomFloat(), blue: randomFloat(), alpha: 1) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /AlertTransitionDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0.3 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeRight 33 | UIInterfaceOrientationLandscapeLeft 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /AlertTransitionDemo/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 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 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 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /AlertTransitionDemo/MainController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainController.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/18. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AlertTransition 11 | 12 | class MainController: UITableViewController { 13 | 14 | var titles = ["EasyTransition", "EasyTransition use in Storyboard", "Different Alert Implementation", "BackgroundType", "MenuTransition", "TrolleyTransition", "BubbleTransition", "StarWarsTransition"] 15 | var menuController = MenuController() 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | title = "AlertTransition" 21 | 22 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: UITableViewCell.description()) 23 | 24 | let promptLabel = UILabel() 25 | promptLabel.font = UIFont.systemFont(ofSize: 14) 26 | promptLabel.textColor = UIColor.lightText 27 | promptLabel.text = "← Swipe Screen Edges in View" 28 | promptLabel.sizeToFit() 29 | promptLabel.backgroundColor = .red 30 | tableView.tableFooterView = promptLabel 31 | 32 | let transition = MenuTransition(from: navigationController) 33 | transition.backgroundType = .color(UIColor.black.withAlphaComponent(0.5)) 34 | menuController.at.transition = transition 35 | } 36 | } 37 | 38 | extension MainController { 39 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 40 | return titles.count 41 | } 42 | 43 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 44 | let cell = tableView.dequeueReusableCell(withIdentifier: UITableViewCell.description(), for: indexPath) 45 | cell.textLabel?.text = titles[indexPath.row] 46 | cell.selectionStyle = .none 47 | return cell 48 | } 49 | 50 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 51 | tableView.deselectRow(at: indexPath, animated: false) 52 | 53 | switch indexPath.row { 54 | case 0: 55 | navigationController?.pushViewController(EasyTransitionController(), animated: true) 56 | case 1: 57 | let storyBoard = UIStoryboard(name: "Main", bundle: nil) 58 | let storyboardController = storyBoard.instantiateViewController(withIdentifier: "EasyStoryboardController") as UIViewController 59 | navigationController?.pushViewController(storyboardController, animated: true) 60 | case 2: 61 | navigationController?.pushViewController(DifferentAlertController(), animated: true) 62 | case 3: 63 | navigationController?.pushViewController(BackgroundTypeController(), animated: true) 64 | case 4: 65 | navigationController?.present(menuController, animated: true, completion: nil) 66 | case 5: 67 | let trolleyController = TrolleyController() 68 | trolleyController.at.transition = TrolleyTransition() 69 | present(trolleyController, animated: true, completion: nil) 70 | case 6: 71 | let alert = BubbleController() 72 | 73 | let transition = BubbleTransition() 74 | transition.bubbleColor = .red 75 | transition.startingPoint = CGPoint(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height - 50) 76 | 77 | alert.at.transition = transition 78 | 79 | present(alert, animated: true, completion: nil) 80 | 81 | case 7: 82 | 83 | let alert = NormalAlertController() 84 | alert.at.transition = StarWallTransition() 85 | present(alert, animated: true, completion: nil) 86 | 87 | default: 88 | break 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /AlertTransitionDemo/MenuController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuController.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/19. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AlertTransition 11 | 12 | 13 | class MenuController: UIViewController, AlertFrameProtocol { 14 | 15 | var alertFrame: CGRect { 16 | let width = UIScreen.main.bounds.size.height / 1136 * 511 17 | return CGRect(x: 0, y: 0, width: width, height: UIScreen.main.bounds.size.height) 18 | } 19 | 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | 24 | view.layer.contents = #imageLiteral(resourceName: "menu_background").cgImage 25 | 26 | let tap = UITapGestureRecognizer(target: self, action: #selector(viewDidTapped(tap:))) 27 | view.addGestureRecognizer(tap) 28 | } 29 | 30 | @objc func viewDidTapped(tap: UITapGestureRecognizer) { 31 | (self.at.transition as? MenuTransition)?.push(controller: NextViewController()) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /AlertTransitionDemo/NextViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NextViewController.swift 3 | // AlertTransitionDemo 4 | // 5 | // Created by 韩帅 on 2017/10/10. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class NextViewController: UIViewController { 12 | 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | 17 | title = "NextViewController" 18 | 19 | view.backgroundColor = .randomColor 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /AlertTransitionDemo/StarWarsGLAnimator/Sprite.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Artem Sidorenko on 10/8/15. 3 | // Copyright © 2015 Yalantis. All rights reserved. 4 | // 5 | // Licensed under the MIT license: http://opensource.org/licenses/MIT 6 | // Latest version can be found at https://github.com/Yalantis/StarWars.iOS 7 | // 8 | 9 | import GLKit 10 | 11 | struct TexturedVertex { 12 | var geometryVertex = Vector2() 13 | var textureVertex = Vector2() 14 | } 15 | 16 | struct TexturedQuad { 17 | var bl = TexturedVertex() 18 | var br = TexturedVertex() { didSet { _br = br } } 19 | var tl = TexturedVertex() { didSet { _tl = tl } } 20 | var tr = TexturedVertex() 21 | 22 | // openGL optimization. it uses triangles to draw. 23 | // so we duplicate 2 vertex, so it have 6 vertex to draw two triangles 24 | fileprivate var _br = TexturedVertex() 25 | fileprivate var _tl = TexturedVertex() 26 | } 27 | 28 | struct Sprite { 29 | var quad = TexturedQuad() 30 | var moveVelocity = Vector2() 31 | 32 | mutating func slice(_ rect: CGRect, textureSize: CGSize) { 33 | quad.bl.geometryVertex = Vector2(x: 0, y: 0) 34 | quad.br.geometryVertex = Vector2(x: rect.size.width, y: 0) 35 | quad.tl.geometryVertex = Vector2(x: 0, y: rect.size.height) 36 | quad.tr.geometryVertex = Vector2(x: rect.size.width, y: rect.size.height) 37 | 38 | quad.bl.textureVertex = Vector2(x: rect.origin.x / textureSize.width, y: rect.origin.y / textureSize.height) 39 | quad.br.textureVertex = Vector2(x: (rect.origin.x + rect.size.width) / textureSize.width, y: rect.origin.y / textureSize.height) 40 | quad.tl.textureVertex = Vector2(x: rect.origin.x / textureSize.width, y: (rect.origin.y + rect.size.height) / textureSize.height) 41 | quad.tr.textureVertex = Vector2(x: (rect.origin.x + rect.size.width) / textureSize.width, y: (rect.origin.y + rect.size.height) / textureSize.height) 42 | 43 | position += Vector2(rect.origin) 44 | } 45 | 46 | var position = Vector2() { 47 | didSet { 48 | let diff = position - oldValue 49 | quad.bl.geometryVertex += diff 50 | quad.br.geometryVertex += diff 51 | quad.tl.geometryVertex += diff 52 | quad.tr.geometryVertex += diff 53 | } 54 | } 55 | 56 | mutating func update(_ tick: TimeInterval) { 57 | position += moveVelocity * Float32(tick) 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /AlertTransitionDemo/StarWarsGLAnimator/SpriteRender.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Artem Sidorenko on 10/8/15. 3 | // Copyright © 2015 Yalantis. All rights reserved. 4 | // 5 | // Licensed under the MIT license: http://opensource.org/licenses/MIT 6 | // Latest version can be found at https://github.com/Yalantis/StarWars.iOS 7 | // 8 | 9 | import GLKit 10 | 11 | class SpriteRender { 12 | 13 | fileprivate let texture: ViewTexture 14 | fileprivate let effect: GLKBaseEffect 15 | 16 | init(texture: ViewTexture, effect: GLKBaseEffect) { 17 | self.texture = texture 18 | self.effect = effect 19 | } 20 | 21 | func render(_ sprites: [Sprite]) { 22 | effect.texture2d0.name = self.texture.name 23 | effect.texture2d0.enabled = 1 24 | 25 | effect.prepareToDraw() 26 | 27 | var vertex = sprites.map { $0.quad } 28 | 29 | glEnableVertexAttribArray(GLuint(GLKVertexAttrib.position.rawValue)) 30 | glEnableVertexAttribArray(GLuint(GLKVertexAttrib.texCoord0.rawValue)) 31 | 32 | withUnsafePointer(to: &vertex[0].bl.geometryVertex) { offset in 33 | glVertexAttribPointer(GLuint(GLKVertexAttrib.position.rawValue), 2, GLenum(GL_FLOAT), GLboolean(UInt8(GL_FALSE)), GLsizei(MemoryLayout.size), offset) 34 | } 35 | withUnsafePointer(to: &vertex[0].bl.textureVertex) { offset in 36 | glVertexAttribPointer(GLuint(GLKVertexAttrib.texCoord0.rawValue), 2, GLenum(GL_FLOAT), GLboolean(UInt8(GL_FALSE)), GLsizei(MemoryLayout.size), offset) 37 | } 38 | 39 | glDrawArrays(GLenum(GL_TRIANGLES), 0, GLsizei(vertex.count * 6)) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /AlertTransitionDemo/StarWarsGLAnimator/StarWarsGLAnimator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Artem Sidorenko on 9/14/15. 3 | // Copyright © 2015 Yalantis. All rights reserved. 4 | // 5 | // Licensed under the MIT license: http://opensource.org/licenses/MIT 6 | // Latest version can be found at https://github.com/Yalantis/StarWars.iOS 7 | // 8 | 9 | import UIKit 10 | import GLKit 11 | 12 | open class StarWarsGLAnimator: NSObject, UIViewControllerAnimatedTransitioning { 13 | 14 | open var duration: TimeInterval = 2 15 | open var spriteWidth: CGFloat = 8 16 | 17 | fileprivate var sprites: [Sprite] = [] 18 | fileprivate var glContext: EAGLContext! 19 | fileprivate var effect: GLKBaseEffect! 20 | fileprivate var glView: GLKView! 21 | fileprivate var displayLink: CADisplayLink! 22 | fileprivate var lastUpdateTime: TimeInterval? 23 | fileprivate var startTransitionTime: TimeInterval! 24 | fileprivate var transitionContext: UIViewControllerContextTransitioning! 25 | fileprivate var render: SpriteRender! 26 | 27 | open func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 28 | return duration 29 | } 30 | 31 | open func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 32 | let containerView = transitionContext.containerView 33 | let fromView = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!.view 34 | let toView = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!.view 35 | 36 | func randomFloatBetween(_ smallNumber: CGFloat, and bigNumber: CGFloat) -> Float { 37 | let diff = bigNumber - smallNumber 38 | return Float((CGFloat(arc4random()) / 100.0).truncatingRemainder(dividingBy: diff) + smallNumber) 39 | } 40 | 41 | self.glContext = EAGLContext(api: .openGLES2) 42 | EAGLContext.setCurrent(glContext) 43 | 44 | glView = GLKView(frame: containerView.bounds, context: glContext) 45 | glView.enableSetNeedsDisplay = true 46 | glView.delegate = self 47 | glView.isOpaque = false 48 | containerView.addSubview(glView) 49 | 50 | let texture = ViewTexture() 51 | texture.setupOpenGL() 52 | texture.render(view: containerView) 53 | 54 | effect = GLKBaseEffect() 55 | let projectionMatrix = GLKMatrix4MakeOrtho(0, Float(texture.width), 0, Float(texture.height), -1, 1) 56 | effect.transform.projectionMatrix = projectionMatrix 57 | 58 | render = SpriteRender(texture: texture, effect: effect) 59 | 60 | let size = CGSize(width: CGFloat(texture.width), height: CGFloat(texture.height)) 61 | 62 | let scale = UIScreen.main.scale 63 | let width = spriteWidth * scale 64 | let height = width 65 | 66 | let fromViewWidth = fromView?.frame.width ?? 0 67 | let fromViewHeight = fromView?.frame.height ?? 0 68 | let startX = ((containerView.frame.width - fromViewWidth) / 2) / containerView.frame.width * size.width 69 | let xWidth = fromViewWidth / containerView.frame.width * size.width - width 70 | let startY = ((containerView.frame.height - fromViewHeight) / 2) / containerView.frame.height * size.height 71 | let yHeight = fromViewHeight / containerView.frame.height * size.height - height 72 | 73 | for x in stride(from: startX, through: xWidth + startX, by: width) { 74 | for y in stride(from: startY, through: yHeight + startY, by: height) { 75 | let region = CGRect(x: x, y: y, width: width, height: height) 76 | var sprite = Sprite() 77 | sprite.slice(region, textureSize: size) 78 | sprite.moveVelocity = Vector2(x: randomFloatBetween(-100, and: 100), y: randomFloatBetween(-CGFloat(texture.height)*1.3/CGFloat(duration), and: -CGFloat(texture.height)/CGFloat(duration))) 79 | 80 | sprites.append(sprite) 81 | } 82 | } 83 | fromView?.alpha = 0 84 | self.transitionContext = transitionContext 85 | 86 | displayLink = CADisplayLink(target: self, selector: #selector(StarWarsGLAnimator.displayLinkTick(_:))) 87 | displayLink.isPaused = false 88 | displayLink.add(to: RunLoop.main, forMode: RunLoopMode.commonModes) 89 | 90 | self.startTransitionTime = Date.timeIntervalSinceReferenceDate 91 | } 92 | 93 | open func animationEnded(_ transitionCompleted: Bool) { 94 | displayLink.invalidate() 95 | displayLink = nil 96 | } 97 | 98 | @objc func displayLinkTick(_ displayLink: CADisplayLink) { 99 | if let lastUpdateTime = lastUpdateTime { 100 | let timeSinceLastUpdate = Date.timeIntervalSinceReferenceDate - lastUpdateTime 101 | self.lastUpdateTime = Date.timeIntervalSinceReferenceDate 102 | for index in 0.. duration { 110 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 111 | } 112 | } 113 | } 114 | 115 | extension StarWarsGLAnimator: GLKViewDelegate { 116 | 117 | public func glkView(_ view: GLKView, drawIn rect: CGRect) { 118 | glClearColor(0, 0, 0, 0) 119 | glClear(UInt32(GL_COLOR_BUFFER_BIT)) 120 | glBlendFunc(GLenum(GL_SRC_ALPHA), GLenum(GL_ONE_MINUS_SRC_ALPHA)) 121 | glEnable(GLenum(GL_BLEND)) 122 | 123 | render.render(self.sprites) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /AlertTransitionDemo/StarWarsGLAnimator/Vector2.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Artem Sidorenko on 10/9/15. 3 | // Copyright © 2015 Yalantis. All rights reserved. 4 | // 5 | // Licensed under the MIT license: http://opensource.org/licenses/MIT 6 | // Latest version can be found at https://github.com/Yalantis/StarWars.iOS 7 | // 8 | 9 | import UIKit 10 | 11 | struct Vector2 { 12 | 13 | var x : Float32 = 0.0 14 | var y : Float32 = 0.0 15 | 16 | init() { 17 | 18 | x = 0.0 19 | y = 0.0 20 | } 21 | 22 | init(value: Float32) { 23 | 24 | x = value 25 | y = value 26 | } 27 | 28 | init(x: Float32 ,y: Float32) { 29 | 30 | self.x = x 31 | self.y = y 32 | } 33 | 34 | init(x: CGFloat, y: CGFloat) { 35 | self.init(x: Float32(x), y: Float32(y)) 36 | } 37 | 38 | init(x: Int, y: Int) { 39 | self.init(x: Float32(x), y: Float32(y)) 40 | } 41 | 42 | init(other: Vector2) { 43 | 44 | x = other.x 45 | y = other.y 46 | } 47 | 48 | init(_ other: CGPoint) { 49 | x = Float32(other.x) 50 | y = Float32(other.y) 51 | } 52 | } 53 | 54 | extension Vector2: CustomStringConvertible { 55 | 56 | var description: String { return "[\(x),\(y)]" } 57 | } 58 | 59 | extension Vector2 : Equatable { 60 | 61 | func isFinite() -> Bool { 62 | 63 | return x.isFinite && y.isFinite 64 | } 65 | 66 | func distance(_ other: Vector2) -> Float32 { 67 | 68 | let result = self - other; 69 | return sqrt( result.dot(result) ) 70 | } 71 | 72 | mutating func normalize() { 73 | 74 | let m = magnitude() 75 | 76 | if m > 0 { 77 | 78 | let il:Float32 = 1.0 / m 79 | 80 | x *= il 81 | y *= il 82 | } 83 | } 84 | 85 | func magnitude() -> Float32 { 86 | 87 | return sqrtf( x*x + y*y ) 88 | } 89 | 90 | func dot( _ v: Vector2 ) -> Float32 { 91 | 92 | return x * v.x + y * v.y 93 | } 94 | 95 | mutating func lerp( _ a: Vector2, b: Vector2, coef : Float32) { 96 | 97 | let result = a + ( b - a) * coef 98 | 99 | x = result.x 100 | y = result.y 101 | } 102 | } 103 | 104 | func ==(lhs: Vector2, rhs: Vector2) -> Bool { 105 | 106 | return (lhs.x == rhs.x) && (lhs.y == rhs.y) 107 | } 108 | 109 | func * (left: Vector2, right : Float32) -> Vector2 { 110 | 111 | return Vector2(x:left.x * right, y:left.y * right) 112 | } 113 | 114 | func * (left: Vector2, right : Vector2) -> Vector2 { 115 | 116 | return Vector2(x:left.x * right.x, y:left.y * right.y) 117 | } 118 | 119 | func / (left: Vector2, right : Float32) -> Vector2 { 120 | 121 | return Vector2(x:left.x / right, y:left.y / right) 122 | } 123 | 124 | func / (left: Vector2, right : Vector2) -> Vector2 { 125 | 126 | return Vector2(x:left.x / right.x, y:left.y / right.y) 127 | } 128 | 129 | func + (left: Vector2, right: Vector2) -> Vector2 { 130 | 131 | return Vector2(x:left.x + right.x, y:left.y + right.y) 132 | } 133 | 134 | func - (left: Vector2, right: Vector2) -> Vector2 { 135 | 136 | return Vector2(x:left.x - right.x, y:left.y - right.y) 137 | } 138 | 139 | func + (left: Vector2, right: Float32) -> Vector2 { 140 | 141 | return Vector2(x:left.x + right, y:left.y + right) 142 | } 143 | 144 | func - (left: Vector2, right: Float32) -> Vector2 { 145 | 146 | return Vector2(x:left.x - right, y:left.y - right) 147 | } 148 | 149 | func += (left: inout Vector2, right: Vector2) { 150 | 151 | left = left + right 152 | } 153 | 154 | func -= (left: inout Vector2, right: Vector2) { 155 | 156 | left = left - right 157 | } 158 | 159 | func *= (left: inout Vector2, right: Vector2) { 160 | 161 | left = left * right 162 | } 163 | 164 | func /= (left: inout Vector2, right: Vector2) { 165 | 166 | left = left / right 167 | } 168 | 169 | -------------------------------------------------------------------------------- /AlertTransitionDemo/StarWarsGLAnimator/ViewTexture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Artem Sidorenko on 10/9/15. 3 | // Copyright © 2015 Yalantis. All rights reserved. 4 | // 5 | // Licensed under the MIT license: http://opensource.org/licenses/MIT 6 | // Latest version can be found at https://github.com/Yalantis/StarWars.iOS 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewTexture { 12 | var name: GLuint = 0 13 | var width: GLsizei = 0 14 | var height: GLsizei = 0 15 | 16 | func setupOpenGL() { 17 | glGenTextures(1, &name) 18 | glBindTexture(GLenum(GL_TEXTURE_2D), name) 19 | 20 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MIN_FILTER), GLint(GL_LINEAR)) 21 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MAG_FILTER), GLint(GL_LINEAR)) 22 | 23 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_S), GLint(GL_CLAMP_TO_EDGE)) 24 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_T), GLint(GL_CLAMP_TO_EDGE)) 25 | glBindTexture(GLenum(GL_TEXTURE_2D), 0); 26 | } 27 | 28 | deinit { 29 | if name != 0 { 30 | glDeleteTextures(1, &name) 31 | } 32 | } 33 | 34 | func render(view: UIView) { 35 | let scale = UIScreen.main.scale 36 | width = GLsizei(view.layer.bounds.size.width * scale) 37 | height = GLsizei(view.layer.bounds.size.height * scale) 38 | 39 | var texturePixelBuffer = [GLubyte](repeating: 0, count: Int(height * width * 4)) 40 | let colorSpace = CGColorSpaceCreateDeviceRGB() 41 | 42 | withUnsafeMutablePointer(to: &texturePixelBuffer[0]) { texturePixelBuffer in 43 | let context = CGContext(data: texturePixelBuffer, 44 | width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: Int(width * 4), space: colorSpace, 45 | bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Big.rawValue)! 46 | context.scaleBy(x: scale, y: scale) 47 | 48 | UIGraphicsPushContext(context) 49 | view.drawHierarchy(in: view.layer.bounds, afterScreenUpdates: false) 50 | UIGraphicsPopContext() 51 | 52 | glBindTexture(GLenum(GL_TEXTURE_2D), name); 53 | 54 | glTexImage2D(GLenum(GL_TEXTURE_2D), 0, GLint(GL_RGBA), width, height, 0, GLenum(GL_RGBA), GLenum(GL_UNSIGNED_BYTE), texturePixelBuffer) 55 | glBindTexture(GLenum(GL_TEXTURE_2D), 0); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /AlertTransitionDemo/TrolleyController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TrolleyController.swift 3 | // AlertTransition 4 | // 5 | // Created by 韩帅 on 2017/4/19. 6 | // Copyright © 2017年 Loopeer. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AlertTransition 11 | 12 | class TrolleyController: UIViewController, AlertFrameProtocol { 13 | 14 | var alertFrame: CGRect { 15 | let height = UIScreen.main.bounds.width / 638 * 893 16 | return CGRect(x: 0, y: UIScreen.main.bounds.height - height, width: UIScreen.main.bounds.width, height: height) 17 | } 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | 22 | view.layer.contents = #imageLiteral(resourceName: "trolley_background").cgImage 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Loopeer 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 | -------------------------------------------------------------------------------- /Media/BackgroundType.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loopeer/AlertTransition/05c67f56bcc1d6069f1903e39948412281b69b6b/Media/BackgroundType.gif -------------------------------------------------------------------------------- /Media/BubbleTransition.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loopeer/AlertTransition/05c67f56bcc1d6069f1903e39948412281b69b6b/Media/BubbleTransition.gif -------------------------------------------------------------------------------- /Media/EasyTransition.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loopeer/AlertTransition/05c67f56bcc1d6069f1903e39948412281b69b6b/Media/EasyTransition.gif -------------------------------------------------------------------------------- /Media/MenuTransition.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loopeer/AlertTransition/05c67f56bcc1d6069f1903e39948412281b69b6b/Media/MenuTransition.gif -------------------------------------------------------------------------------- /Media/StarWarsTransition.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loopeer/AlertTransition/05c67f56bcc1d6069f1903e39948412281b69b6b/Media/StarWarsTransition.gif -------------------------------------------------------------------------------- /Media/TrolleyTransition.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loopeer/AlertTransition/05c67f56bcc1d6069f1903e39948412281b69b6b/Media/TrolleyTransition.gif -------------------------------------------------------------------------------- /Media/changeOrientation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loopeer/AlertTransition/05c67f56bcc1d6069f1903e39948412281b69b6b/Media/changeOrientation.gif -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'AlertTransitionDemo' do 5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | pod 'SnapKit', '~> 4.0.0' 9 | pod 'AlertTransition/Easy', path: '.' 10 | pod 'AlertTransition/Menu', path: '.' 11 | pod 'AlertTransition/Trolley', path: '.' 12 | 13 | end 14 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - AlertTransition/Core (2.1.0) 3 | - AlertTransition/Easy (2.1.0): 4 | - AlertTransition/Core 5 | - AlertTransition/Menu (2.1.0): 6 | - AlertTransition/Core 7 | - AlertTransition/Trolley (2.1.0): 8 | - AlertTransition/Core 9 | - SnapKit (4.0.0) 10 | 11 | DEPENDENCIES: 12 | - AlertTransition/Easy (from `.`) 13 | - AlertTransition/Menu (from `.`) 14 | - AlertTransition/Trolley (from `.`) 15 | - SnapKit (~> 4.0.0) 16 | 17 | EXTERNAL SOURCES: 18 | AlertTransition: 19 | :path: . 20 | 21 | SPEC CHECKSUMS: 22 | AlertTransition: 228ab96e7fd52a99ef92b74ce18d924a457b0594 23 | SnapKit: a42d492c16e80209130a3379f73596c3454b7694 24 | 25 | PODFILE CHECKSUM: 8c409f68cedfa091490114bbb09309d1c6e8fa1a 26 | 27 | COCOAPODS: 1.3.1 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # AlertTransition 3 | 4 | ![Xcode 8.2+](https://img.shields.io/badge/Xcode-8.2%2B-blue.svg) 5 | ![iOS 8.0+](https://img.shields.io/badge/iOS-8.0%2B-blue.svg) 6 | ![Swift 3.0+](https://img.shields.io/badge/Swift-3.0%2B-orange.svg) 7 | ![Swift 4](https://img.shields.io/badge/Swift-4-orange.svg) 8 | [![Carthage compatible](https://img.shields.io/badge/Carthage-Compatible-brightgreen.svg?style=flat)](https://github.com/Carthage/Carthage) 9 | [![Version](https://img.shields.io/cocoapods/v/AlertTransition.svg?style=flat)](https://cocoapods.org/pods/AlertTransition) 10 | 11 | **AlertTransition** is a extensible library for making view controller transitions, especially for alert transitions. 12 | 13 | ## Overview 14 | 15 | **AlertTransition** is a super class, make basic structure, and no default animation realized. 16 | 17 | **EasyTransition** is a subclass of **AlertTransition**. with it, you can compose custom transition with translation, scale, rotation and alpha. You can also change background with custom color or UIBlurEffect. It support changing device orientation. And it is easy to use both in code or storyboard. 18 | 19 | 20 | 21 | 24 | 27 | 30 | 31 |
22 | 23 | 25 | 26 | 28 | 29 |
32 | 33 | **MenuTransition** is a subclass of **AlertTransition**. With it, you can make a side menu with a few line of codes. 34 | 35 | 36 | 37 | **TrolleyTransition** is a subclass of **AlertTransition**. With it, you can make a trolley with a few line of codes. 38 | 39 | 40 | 41 | ***You can subclass AlertTransition, and write your custom transition***. And you can also wrap other transition effect with AlertTransition, such as [BubbleTransition](https://github.com/andreamazz/BubbleTransition) or [StarWars.iOS](https://github.com/Yalantis/StarWars.iOS). 42 | 43 | 44 | 45 | 48 | 51 | 52 |
46 | 47 | 49 | 50 |
53 | 54 | ## AlertTransition 55 | 56 | ### How To Use 57 | 58 | It is pretty simple to use AlertTransition 59 | 60 | ```swift 61 | // First, initialize your presented controller 62 | let alert = SomeController() 63 | // Second, initialize a subclass of AlertTransition, such as EasyTransition, configure your controller with it 64 | alert.at.transition = EasyTransition() 65 | // Present your controller, Amazing!! 66 | present(alert, animated: true, completion: nil) 67 | ``` 68 | 69 | If you use storyboard 70 | 71 | ```swift 72 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 73 | 74 | // First, find presented controller 75 | let controller = segue.destination 76 | // Second, initialize a subclass of AlertTransition, such as EasyTransition, configure your controller with it 77 | controller.at.transition = EasyTransition() 78 | } 79 | ``` 80 | 81 | ```swift 82 | @IBAction func dismissButtonClicked() { 83 | dismiss(animated: true, completion: nil) 84 | } 85 | ``` 86 | 87 | ### Frame of Presented Controller 88 | 89 | How to set the frame of presented controller? Conform to **AlertFrameProtocol** or **Self-sizing** 90 | 91 | #### AlertFrameProtocol 92 | 93 | Conforms to AlertFrameProtocol, provide your desired frame through property **alertFrame** 94 | 95 | ```swift 96 | class SomeController: UIViewController, AlertFrameProtocol { 97 | 98 | var alertFrame: CGRect { 99 | let x = (UIScreen.main.bounds.size.width - 200) / 2 100 | let y = (UIScreen.main.bounds.size.height - 250) / 2 101 | return CGRect(x: x, y: y, width: 200, height: 250) 102 | } 103 | } 104 | ``` 105 | 106 | #### Self-sizing 107 | 108 | You need an unbroken chain of constraints and views (with defined heights) to fill the area between the controller view’s top edge and its bottom edge, and you also need one (with defined widths) between the controller view’s left edge and its right edge. If you know how to write [Self-sizing Cell](https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithSelf-SizingTableViewCells.html), this will be easy to you. 109 | 110 | AlertTransition will calculate view's width and height, and set the presented controller in center of screen. 111 | 112 | Sometimes, there maybe constraint conflicts. Among the chain of constraints, pick one constraint and reduce it's priority (eg: from 1000 to 999), everything will be fine. 113 | 114 | You can find demo code in SnapKitAlertController.swift and Main.storyboard. 115 | 116 | ### Change background 117 | 118 | You can change alert background with ***backgroundType***. Effect gif is the second image above. 119 | 120 | ```swift 121 | let alert = SomeController() 122 | 123 | // It is a property of AlertTransition, you can use any subclass, just use EasyTransition as an example 124 | let transition = EasyTransition() 125 | transition.backgroundType = .blurEffect(style: .extraLight, alpha: 0.9) 126 | // transition.backgroundType = .blurEffect(style: .light, alpha: 0.9) 127 | // transition.backgroundType = .blurEffect(style: .dark, alpha: 0.9) 128 | // transition.backgroundType = .color(UIColor.blue.withAlphaComponent(0.5)) 129 | 130 | alert.at.transition = transition 131 | present(alert, animated: true, completion: nil) 132 | ``` 133 | 134 | ## EasyTransition 135 | 136 | You can easily compose your custom transition with a enum named **AnimationType** 137 | 138 | ```swift 139 | let alert = SomeController() 140 | let transition = EasyTransition() 141 | transition.startTransforms = [.rotation(angle: CGFloat(Double.pi/2), anchorPoint: CGPoint(x: 0, y: 0)), .alpha(0)] 142 | alert.at.transition = transition 143 | present(alert, animated: true, completion: nil) 144 | ``` 145 | 146 | EasyTransition use **startTransforms** and final frame of presented controller's view, to calculate the state of start. The above sample code, the view will rotate pi / 2 with anchoPoint in left top corner, and alpha 0. 147 | 148 | There is also a property named **endTransforms**, it will have same value with **startTransforms** in default. 149 | 150 | You can also change duration, damping, velocity, curve of the animation with **presentAnimateParams** and **dismissAnimateParams** 151 | 152 | ```swift 153 | let alert = SomeController() 154 | let transition = EasyTransition() 155 | transition.presentAnimateParams.damping = 0.3 156 | alert.at.transition = transition 157 | present(alert, animated: true, completion: nil) 158 | ``` 159 | 160 | ## MenuTransition 161 | 162 | There are only three steps to make a side menu. Set frame and change background just like other *AlertTransition*. 163 | 164 | ```swift 165 | class MainController: UIViewController { 166 | // First, hold your menu controller in your main controller 167 | var menuController = MenuController() 168 | 169 | override func viewDidLoad() { 170 | super.viewDidLoad() 171 | 172 | // second, initialize MenuTransition with presenting controller 173 | let transition = MenuTransition(from: navigationController) 174 | // third, set MenuTransition to your menuController 175 | menuController.at.transition = transition 176 | } 177 | } 178 | ``` 179 | 180 | When you select an item at side menu, you want to push a controller from main controller. Push like this to make suitable animation. 181 | 182 | ```swift 183 | (self.at.transition as? MenuTransition)?.push(controller: NextViewController()) 184 | ``` 185 | 186 | ## Write custom AlertTransition 187 | 188 | In most cases, you only need to override func **performPresentedTransition** and **performDismissedTransition**. Apply your animation to **presentingView** and **presentedView**, such as TrolleyTransition: 189 | 190 | ```swift 191 | public class TrolleyTransition: AlertTransition { 192 | 193 | public override init(from controller: UIViewController?) { 194 | super.init(from: controller) 195 | duration = 0.5 196 | } 197 | 198 | public override func performPresentedTransition(presentingView: UIView, presentedView: UIView, context: UIViewControllerContextTransitioning) { 199 | presentedView.frame.origin.y = UIScreen.main.bounds.height 200 | 201 | UIView.animate(withDuration: duration/2, animations: { 202 | presentingView.layer.transform = self.firstTransform() 203 | }) { (complete) in 204 | UIView.animate(withDuration: self.duration/2, animations: { 205 | presentingView.layer.transform = self.secondTransform() 206 | presentedView.transform = CGAffineTransform(translationX: 0, y: -presentedView.frame.height) 207 | }, completion: { (complete) in 208 | context.completeTransition(complete) 209 | }) 210 | } 211 | } 212 | 213 | public override func performDismissedTransition(presentingView: UIView, presentedView: UIView, context: UIViewControllerContextTransitioning) { 214 | 215 | UIView.animate(withDuration: duration/2, animations: { 216 | presentedView.transform = CGAffineTransform.identity 217 | presentingView.layer.transform = self.firstTransform() 218 | }) { (complete) in 219 | UIView.animate(withDuration: self.duration/2, animations: { 220 | presentingView.layer.transform = CATransform3DIdentity 221 | }, completion: { (complete) in 222 | context.completeTransition(complete) 223 | }) 224 | } 225 | } 226 | 227 | private func firstTransform() -> CATransform3D { 228 | var form = CATransform3DIdentity 229 | form.m34 = 1.0 / -900 230 | form = CATransform3DScale(form, 0.9, 0.9, 1) 231 | form = CATransform3DRotate(form, 15.0 * CGFloat(Double.pi)/180.0, 1, 0, 0) 232 | form = CATransform3DTranslate(form, 0, 0, -100.0) 233 | return form 234 | } 235 | 236 | private func secondTransform() -> CATransform3D { 237 | var form = CATransform3DIdentity 238 | form.m34 = firstTransform().m34 239 | form = CATransform3DTranslate(form, 0, -20, 0) 240 | form = CATransform3DScale(form, 0.9, 0.9, 1) 241 | return form 242 | } 243 | } 244 | ``` 245 | 246 | If you want add UIPercentDrivenInteractiveTransition, or has custom UIPresentationController, set **interactionTransitionType** and **presentationControllerType** at init method. 247 | 248 | ```swift 249 | public override init(from controller: UIViewController? = nil) { 250 | super.init(from: controller) 251 | 252 | interactionTransitionType = EasyPercentDrivenTransition.self 253 | presentationControllerType = SomePresentationController.self 254 | } 255 | ``` 256 | 257 | If you write an amazing custom transition, please submit a pull requests. We looking forward to accumulate custom transitions. And with AlertTransition, we can easily change from one custom transition to another. 258 | 259 | ## Getting involved 260 | 261 | * If you **want to contribute** please feel free to **submit pull requests**, even if you find some spell error in README, because I am not good at English. 262 | * If you **have a feature request** please **open an issue**. 263 | * If you **found a bug** or **need help** please **check older issues before submitting an issue**. 264 | 265 | ## Installation 266 | 267 | ### CocoaPods 268 | 269 | [CocoaPods](https://cocoapods.org/) is a dependency manager for Cocoa projects. 270 | 271 | Specify AlertTransition into your project's Podfile: 272 | 273 | ```ruby 274 | source 'https://github.com/CocoaPods/Specs.git' 275 | platform :ios, '8.0' 276 | use_frameworks! 277 | 278 | target '' do 279 | #Swift3 280 | #pod 'AlertTransition', "~> 1.0.4" 281 | #Swift4 282 | pod 'AlertTransition', "~> 2.1.0" 283 | end 284 | ``` 285 | 286 | If you only want EasyTransition, MenuTransition or TrolleyTransition, you can pod them alone like this: 287 | 288 | ```ruby 289 | pod 'AlertTransition/Easy', "~> 2.1.0" 290 | pod 'AlertTransition/Menu', "~> 2.1.0" 291 | pod 'AlertTransition/Trolley', "~> 2.1.0" 292 | ``` 293 | 294 | Then run the following command: 295 | 296 | ```sh 297 | $ pod install 298 | ``` 299 | 300 | ### Carthage 301 | 302 | [Carthage](https://github.com/Carthage/Carthage) is a simple, decentralized 303 | dependency manager for Cocoa. 304 | 305 | Specify AlertTransition into your project's Carthage: 306 | 307 | ``` 308 | github "loopeer/AlertTransition" ~> 2.1.0 309 | ``` 310 | --------------------------------------------------------------------------------