├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── STPopup.podspec ├── STPopup.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── STPopup.xcscheme ├── STPopup ├── Info.plist ├── STPopup+SwiftUI.swift ├── STPopup.h ├── STPopupController.h ├── STPopupController.m ├── STPopupControllerTransitioningFade.h ├── STPopupControllerTransitioningFade.m ├── STPopupControllerTransitioningSlideVertical.h ├── STPopupControllerTransitioningSlideVertical.m ├── STPopupLeftBarItem.h ├── STPopupLeftBarItem.m ├── STPopupNavigationBar.h ├── STPopupNavigationBar.m ├── UIResponder+STPopup.h ├── UIResponder+STPopup.m ├── UIViewController+STPopup.h └── UIViewController+STPopup.m ├── STPopupExample ├── STPopupExample.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── STPopupExample │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard │ ├── BottomSheetDemoViewController.h │ ├── BottomSheetDemoViewController.m │ ├── BulletinBoardViewController.h │ ├── BulletinBoardViewController.m │ ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── CloseIcon.imageset │ │ ├── 2x.png │ │ ├── 3x.png │ │ └── Contents.json │ ├── Contents.json │ └── LocationIcon.imageset │ │ ├── 2x.png │ │ ├── 3x.png │ │ └── Contents.json │ ├── Info.plist │ ├── MultiSelectionViewController.h │ ├── MultiSelectionViewController.m │ ├── PopupViewController1.h │ ├── PopupViewController1.m │ ├── PopupViewController2.h │ ├── PopupViewController2.m │ ├── PopupViewController3.h │ ├── PopupViewController3.m │ ├── ViewController.h │ ├── ViewController.m │ └── main.m └── STPopupSwiftUIExample ├── STPopupSwiftUIExample.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist └── STPopupSwiftUIExample ├── Assets.xcassets ├── AccentColor.colorset │ └── Contents.json ├── AppIcon.appiconset │ └── Contents.json └── Contents.json ├── ContentView.swift ├── Preview Content └── Preview Assets.xcassets │ └── Contents.json └── STPopupSwiftUIExampleApp.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | #Pods/ 27 | 28 | # General 29 | .DS_Store 30 | .AppleDouble 31 | .LSOverride 32 | 33 | # Icon must end with two \r 34 | Icon 35 | 36 | # Thumbnails 37 | ._* 38 | 39 | # Files that might appear in the root of a volume 40 | .DocumentRevisions-V100 41 | .fseventsd 42 | .Spotlight-V100 43 | .TemporaryItems 44 | .Trashes 45 | .VolumeIcon.icns 46 | .com.apple.timemachine.donotpresent 47 | 48 | # Directories potentially created on remote AFP share 49 | .AppleDB 50 | .AppleDesktop 51 | Network Trash Folder 52 | Temporary Items 53 | .apdisk 54 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode9 3 | script: 4 | - xcodebuild -project STPopup.xcodeproj -scheme STPopup -sdk iphonesimulator 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Kevin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # STPopup ![CI Status](https://img.shields.io/travis/kevin0571/STPopup.svg?style=flat) ![Version](http://img.shields.io/cocoapods/v/STPopup.svg?style=flag) ![License](https://img.shields.io/cocoapods/l/STPopup.svg?style=flag) 2 | `STPopup` provides `STPopupController`, which works just like `UINavigationController` in popup style, for both iPhone and iPad. It's written in Objective-C and compatible with Swift. 3 | 4 | **Features:** 5 | - Push/Pop view controller into/out of `STPopupController` just like `UINavigationController`. 6 | - Set navigation items through `self.navigationItem` just like using a `UINavigationController`. 7 | - Support both "Form Sheet" and "Bottom Sheet" style. 8 | - Work well with storyboard(including segue). 9 | - Customize UI by using `UIAppearance`. 10 | - Customizable popup transition style. 11 | - Auto-reposition of popup view when keyboard shows up, make sure your `UITextField`/`UITextView` won't be covered by the keyboard. 12 | - Drag navigation bar to dismiss popup view. 13 | - Support both portrait and landscape orientation in iPhone and iPad. 14 | - iOS 7+. 15 | - Compatible with Swift. 16 | 17 | ## Use Cases 18 | ![Use Cases](https://user-images.githubusercontent.com/1491282/57985696-b6e01300-7a63-11e9-91a9-b5aa55262967.jpg) 19 | 20 | ## Get Started 21 | **CocoaPods** 22 | ```ruby 23 | pod 'STPopup' 24 | ``` 25 | **Carthage** 26 | ```ruby 27 | github "kevin0571/STPopup" 28 | ``` 29 | 30 | **Import header file** 31 | Objective-C 32 | ```objc 33 | #import 34 | ``` 35 | Swift 36 | ```swift 37 | import STPopup 38 | ``` 39 | 40 | **Initialize and present STPopupController** 41 | Objective-C 42 | ```objc 43 | STPopupController *popupController = [[STPopupController alloc] initWithRootViewController:viewController]; 44 | [popupController presentInViewController:self]; 45 | ``` 46 | Swift 47 | ```swift 48 | let popupController = STPopupController(rootViewController: viewController) 49 | popupController.present(in: self) 50 | ``` 51 | 52 | **Set content size in view controller** 53 | Objective-C 54 | ```objc 55 | @implementation ViewController 56 | 57 | - (instancetype)init 58 | { 59 | if (self = [super init]) { 60 | self.title = @"View Controller"; 61 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Next" style:UIBarButtonItemStylePlain target:self action:@selector(nextBtnDidTap)]; 62 | // It's required to set content size of popup. 63 | self.contentSizeInPopup = CGSizeMake(300, 400); 64 | self.landscapeContentSizeInPopup = CGSizeMake(400, 200); 65 | } 66 | return self; 67 | } 68 | 69 | @end 70 | ``` 71 | Swift 72 | ```swift 73 | class ViewController: UIViewController { 74 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 75 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 76 | title = "View Controller" 77 | navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Next", style: .plain, target: self, action: #selector(nextBtnDidTap)) 78 | // It's required to set content size of popup. 79 | contentSizeInPopup = CGSize(width: 300, height: 400) 80 | landscapeContentSizeInPopup = CGSize(width: 400, height: 200) 81 | } 82 | } 83 | ``` 84 | **Set content size of view controller which is loaded from Storyboard** 85 | Set content size in storyboard or in `awakeFromNib`. 86 | ![Storyboard](https://user-images.githubusercontent.com/1491282/57982394-dca5f180-7a3c-11e9-8d63-3ca6c3837860.png) 87 | 88 | **Push, pop and dismiss view controllers** 89 | Objective-C 90 | ```objc 91 | [self.popupController pushViewController:[ViewController new] animated:YES]; 92 | [self.popupController popViewControllerAnimated:YES]; // Popup will be dismissed if there is only one view controller in the popup view controller stack 93 | [self.popupController dismiss]; 94 | ``` 95 | Swift 96 | ```swift 97 | popupController?.push(viewController, animated: true) 98 | popupController?.popViewController(animated: true) // Popup will be dismissed if there is only one view controller in the popup view controller stack 99 | popupController?.dismiss() 100 | ``` 101 | 102 | ![Push & Pop](https://cloud.githubusercontent.com/assets/1491282/9857915/0d4ab3ee-5b50-11e5-81bc-8fbae3ad8c06.gif) 103 | 104 | **Bottom sheet style** 105 | Objective-C 106 | ```objc 107 | STPopupController *popupController = [[STPopupController alloc] initWithRootViewController:[ViewController new]]; 108 | popupController.style = STPopupStyleBottomSheet; 109 | [popupController presentInViewController:self]; 110 | ``` 111 | Swift 112 | ```swift 113 | let popupController = STPopupController(rootViewController: viewController) 114 | popupController.style = .bottomSheet 115 | popupController.present(in: self) 116 | ``` 117 | ![Bottom Sheet](https://cloud.githubusercontent.com/assets/1491282/10417963/7649f356-7080-11e5-8f3c-0cb817b8353e.gif) 118 | 119 | **Customize popup transition style** 120 | Objective-C 121 | ```objc 122 | #pragma mark - STPopupControllerTransitioning 123 | 124 | - (NSTimeInterval)popupControllerTransitionDuration:(STPopupControllerTransitioningContext *)context 125 | { 126 | return context.action == STPopupControllerTransitioningActionPresent ? 0.5 : 0.35; 127 | } 128 | 129 | - (void)popupControllerAnimateTransition:(STPopupControllerTransitioningContext *)context completion:(void (^)())completion 130 | { 131 | // Popup will be presented with an animation sliding from right to left. 132 | UIView *containerView = context.containerView; 133 | if (context.action == STPopupControllerTransitioningActionPresent) { 134 | containerView.transform = CGAffineTransformMakeTranslation(containerView.superview.bounds.size.width - containerView.frame.origin.x, 0); 135 | 136 | [UIView animateWithDuration:[self popupControllerTransitionDuration:context] delay:0 usingSpringWithDamping:1 initialSpringVelocity:1 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 137 | context.containerView.transform = CGAffineTransformIdentity; 138 | } completion:^(BOOL finished) { 139 | completion(); 140 | }]; 141 | } 142 | else { 143 | [UIView animateWithDuration:[self popupControllerTransitionDuration:context] delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ 144 | containerView.transform = CGAffineTransformMakeTranslation(- 2 * (containerView.superview.bounds.size.width - containerView.frame.origin.x), 0); 145 | } completion:^(BOOL finished) { 146 | containerView.transform = CGAffineTransformIdentity; 147 | completion(); 148 | }]; 149 | } 150 | } 151 | 152 | // Use custom transitioning in popup controller 153 | STPopupController *popupController = [[STPopupController alloc] initWithRootViewController:viewController]; 154 | popupController.transitionStyle = STPopupTransitionStyleCustom; 155 | popupController.transitioning = self; 156 | [popupController presentInViewController:self]; 157 | ``` 158 | Swift 159 | ```swift 160 | // MARK: STPopupControllerTransitioning 161 | 162 | func popupControllerTransitionDuration(_ context: STPopupControllerTransitioningContext) -> TimeInterval { 163 | return context.action == .present ? 0.5 : 0.35 164 | } 165 | 166 | func popupControllerAnimateTransition(_ context: STPopupControllerTransitioningContext, completion: @escaping () -> Void) { 167 | // Popup will be presented with an animation sliding from right to left. 168 | let containerView = context.containerView 169 | if context.action == .present { 170 | containerView.transform = CGAffineTransform(translationX: containerView.superview!.bounds.size.width - containerView.frame.origin.x, y: 0) 171 | UIView.animate(withDuration: popupControllerTransitionDuration(context), delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseInOut, animations: { 172 | containerView.transform = .identity 173 | }, completion: { _ in 174 | completion() 175 | }); 176 | } else { 177 | UIView.animate(withDuration: popupControllerTransitionDuration(context), delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseInOut, animations: { 178 | containerView.transform = CGAffineTransform(translationX: -2 * (containerView.superview!.bounds.size.width - containerView.frame.origin.x), y: 0) 179 | }, completion: { _ in 180 | containerView.transform = .identity 181 | completion() 182 | }); 183 | } 184 | } 185 | 186 | // Use custom transitioning in popup controller 187 | let popupController = let popupController = STPopupController(rootViewController: viewController) 188 | popupController.transitionStyle = .custom 189 | popupController.transitioning = self 190 | popupController.present(in: self) 191 | ``` 192 | 193 | **Blur background** 194 | Objective-C 195 | ```objc 196 | STPopupController *popupController = [[STPopupController alloc] initWithRootViewController:[PopupViewController1 new]]; 197 | if (NSClassFromString(@"UIBlurEffect")) { 198 | UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]; 199 | popupController.backgroundView = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; 200 | } 201 | ``` 202 | Swift 203 | ```swift 204 | let popupController = let popupController = STPopupController(rootViewController: viewController) 205 | if NSClassFromString("UIBlurEffect") != nil { 206 | let blurEffect = UIBlurEffect(style: .dark) 207 | popupController.backgroundView = UIVisualEffectView(effect: blurEffect) 208 | } 209 | ``` 210 | 211 | **Action of tapping on area outside of popup** 212 | Objective-C 213 | ```objc 214 | [popupController.backgroundView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(backgroundViewDidTap)]]; 215 | ``` 216 | Swift 217 | ```swift 218 | popupController.backgroundView?.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(backgroundViewDidTap))) 219 | ``` 220 | 221 | **Customize UI** 222 | Objective-C 223 | ```objc 224 | [STPopupNavigationBar appearance].barTintColor = [UIColor colorWithRed:0.20 green:0.60 blue:0.86 alpha:1.0]; 225 | [STPopupNavigationBar appearance].tintColor = [UIColor whiteColor]; 226 | [STPopupNavigationBar appearance].barStyle = UIBarStyleDefault; 227 | [STPopupNavigationBar appearance].titleTextAttributes = @{ NSFontAttributeName: [UIFont fontWithName:@"Cochin" size:18], NSForegroundColorAttributeName: [UIColor whiteColor] }; 228 | 229 | [[UIBarButtonItem appearanceWhenContainedIn:[STPopupNavigationBar class], nil] setTitleTextAttributes:@{ NSFontAttributeName:[UIFont fontWithName:@"Cochin" size:17] } forState:UIControlStateNormal]; 230 | ``` 231 | Swift 232 | ```swift 233 | STPopupNavigationBar.appearance().barTintColor = UIColor(red: 0.2, green: 0.6, blue: 0.86, alpha: 1) 234 | STPopupNavigationBar.appearance().tintColor = .white 235 | STPopupNavigationBar.appearance().barStyle = .default 236 | STPopupNavigationBar.appearance().titleTextAttributes = [ 237 | .font: UIFont(name: "Cochin", size: 18) ?? .systemFont(ofSize: 18), 238 | .foregroundColor: UIColor.white, 239 | ] 240 | UIBarButtonItem 241 | .appearance(whenContainedInInstancesOf: [STPopupNavigationBar.self]) 242 | .setTitleTextAttributes([ 243 | .font: UIFont(name: "Cochin", size: 18) ?? .systemFont(ofSize: 18), 244 | ], for: .normal) 245 | ``` 246 | ![Customize UI](https://cloud.githubusercontent.com/assets/1491282/9911306/0f6db056-5cd4-11e5-9329-33b0cf02e1b0.png) 247 | 248 | **Auto-reposition when keyboard is showing up** 249 | This is default behavior. 250 | ![Auto-reposition](https://cloud.githubusercontent.com/assets/1491282/9858277/5b29b130-5b52-11e5-9569-7560a0853493.gif) 251 | 252 | **Drag to dismiss** 253 | This is default behavior. 254 | ![Drag to dismiss](https://cloud.githubusercontent.com/assets/1491282/9858334/b103fc96-5b52-11e5-9c3f-517367ed9386.gif) 255 | 256 | **Handle orientation change** 257 | This is default behavior. 258 | ![Orientation change](https://cloud.githubusercontent.com/assets/1491282/9858372/e6538880-5b52-11e5-8882-8705588606ba.gif) 259 | 260 | Please checkout the example project for more details. 261 | -------------------------------------------------------------------------------- /STPopup.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "STPopup" 3 | s.version = "1.8.7" 4 | s.summary = "STPopup provides STPopupController, which works just like UINavigationController in form sheet/bottom sheet style, for both iPhone and iPad." 5 | 6 | s.description = <<-DESC 7 | - Extend your view controller from UIViewController, build it in your familiar way. 8 | - Push/Pop view controller in to/out of popup view stack, and set navigation items by using self.navigationItem.leftBarButtonItem and rightBarButtonItem, just like you are using UINavigationController. 9 | - Support both "Form Sheet" and "Bottom Sheet" style. 10 | - Work well with storyboard(including segue). 11 | - Customize UI by using UIAppearance. 12 | - Fully customizable popup transition style. 13 | - Auto-reposition of popup view when keyboard is showing up, make sure your UITextField/UITextView won't be covered by the keyboard. 14 | - Drag navigation bar to dismiss popup view. 15 | - Support both portrait and landscape orientation, and both iPhone and iPad. 16 | DESC 17 | 18 | s.homepage = "https://github.com/kevin0571/STPopup" 19 | s.license = { :type => "MIT", :file => "LICENSE" } 20 | s.author = { "Kevin Lin" => "kevin_lyn@outlook.com" } 21 | 22 | s.platform = :ios, "7.0" 23 | s.source = { :git => "https://github.com/kevin0571/STPopup.git", :tag => s.version } 24 | 25 | s.source_files = "STPopup/*.{h,m}" 26 | s.public_header_files = "STPopup/*.h" 27 | end 28 | -------------------------------------------------------------------------------- /STPopup.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0A14D9941D65971B00149145 /* STPopupControllerTransitioningSlideVertical.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A14D9921D65971B00149145 /* STPopupControllerTransitioningSlideVertical.h */; }; 11 | 0A14D9951D65971B00149145 /* STPopupControllerTransitioningSlideVertical.m in Sources */ = {isa = PBXBuildFile; fileRef = 0A14D9931D65971B00149145 /* STPopupControllerTransitioningSlideVertical.m */; }; 12 | 0A14D9981D65A03000149145 /* STPopupControllerTransitioningFade.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A14D9961D65A03000149145 /* STPopupControllerTransitioningFade.h */; }; 13 | 0A14D9991D65A03000149145 /* STPopupControllerTransitioningFade.m in Sources */ = {isa = PBXBuildFile; fileRef = 0A14D9971D65A03000149145 /* STPopupControllerTransitioningFade.m */; }; 14 | 76593AFE2751288E00D1877D /* STPopup+SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76593AFD2751288E00D1877D /* STPopup+SwiftUI.swift */; }; 15 | 76D8223C1C1DB5E400FCF988 /* STPopup.h in Headers */ = {isa = PBXBuildFile; fileRef = 76D8223B1C1DB5E400FCF988 /* STPopup.h */; settings = {ATTRIBUTES = (Public, ); }; }; 16 | 76D8224D1C1DB62600FCF988 /* STPopupController.h in Headers */ = {isa = PBXBuildFile; fileRef = 76D822431C1DB62600FCF988 /* STPopupController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17 | 76D8224E1C1DB62600FCF988 /* STPopupController.m in Sources */ = {isa = PBXBuildFile; fileRef = 76D822441C1DB62600FCF988 /* STPopupController.m */; }; 18 | 76D8224F1C1DB62600FCF988 /* STPopupLeftBarItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 76D822451C1DB62600FCF988 /* STPopupLeftBarItem.h */; }; 19 | 76D822501C1DB62600FCF988 /* STPopupLeftBarItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 76D822461C1DB62600FCF988 /* STPopupLeftBarItem.m */; }; 20 | 76D822511C1DB62600FCF988 /* STPopupNavigationBar.h in Headers */ = {isa = PBXBuildFile; fileRef = 76D822471C1DB62600FCF988 /* STPopupNavigationBar.h */; settings = {ATTRIBUTES = (Public, ); }; }; 21 | 76D822521C1DB62600FCF988 /* STPopupNavigationBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 76D822481C1DB62600FCF988 /* STPopupNavigationBar.m */; }; 22 | 76D822531C1DB62600FCF988 /* UIResponder+STPopup.h in Headers */ = {isa = PBXBuildFile; fileRef = 76D822491C1DB62600FCF988 /* UIResponder+STPopup.h */; }; 23 | 76D822541C1DB62600FCF988 /* UIResponder+STPopup.m in Sources */ = {isa = PBXBuildFile; fileRef = 76D8224A1C1DB62600FCF988 /* UIResponder+STPopup.m */; }; 24 | 76D822551C1DB62600FCF988 /* UIViewController+STPopup.h in Headers */ = {isa = PBXBuildFile; fileRef = 76D8224B1C1DB62600FCF988 /* UIViewController+STPopup.h */; settings = {ATTRIBUTES = (Public, ); }; }; 25 | 76D822561C1DB62600FCF988 /* UIViewController+STPopup.m in Sources */ = {isa = PBXBuildFile; fileRef = 76D8224C1C1DB62600FCF988 /* UIViewController+STPopup.m */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 0A14D9921D65971B00149145 /* STPopupControllerTransitioningSlideVertical.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPopupControllerTransitioningSlideVertical.h; sourceTree = ""; }; 30 | 0A14D9931D65971B00149145 /* STPopupControllerTransitioningSlideVertical.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPopupControllerTransitioningSlideVertical.m; sourceTree = ""; }; 31 | 0A14D9961D65A03000149145 /* STPopupControllerTransitioningFade.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPopupControllerTransitioningFade.h; sourceTree = ""; }; 32 | 0A14D9971D65A03000149145 /* STPopupControllerTransitioningFade.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPopupControllerTransitioningFade.m; sourceTree = ""; }; 33 | 76593AFD2751288E00D1877D /* STPopup+SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "STPopup+SwiftUI.swift"; sourceTree = ""; }; 34 | 76D822381C1DB5E400FCF988 /* STPopup.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = STPopup.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | 76D8223B1C1DB5E400FCF988 /* STPopup.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = STPopup.h; sourceTree = ""; }; 36 | 76D8223D1C1DB5E400FCF988 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 37 | 76D822431C1DB62600FCF988 /* STPopupController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPopupController.h; sourceTree = ""; }; 38 | 76D822441C1DB62600FCF988 /* STPopupController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPopupController.m; sourceTree = ""; }; 39 | 76D822451C1DB62600FCF988 /* STPopupLeftBarItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPopupLeftBarItem.h; sourceTree = ""; }; 40 | 76D822461C1DB62600FCF988 /* STPopupLeftBarItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPopupLeftBarItem.m; sourceTree = ""; }; 41 | 76D822471C1DB62600FCF988 /* STPopupNavigationBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPopupNavigationBar.h; sourceTree = ""; }; 42 | 76D822481C1DB62600FCF988 /* STPopupNavigationBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPopupNavigationBar.m; sourceTree = ""; }; 43 | 76D822491C1DB62600FCF988 /* UIResponder+STPopup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIResponder+STPopup.h"; sourceTree = ""; }; 44 | 76D8224A1C1DB62600FCF988 /* UIResponder+STPopup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIResponder+STPopup.m"; sourceTree = ""; }; 45 | 76D8224B1C1DB62600FCF988 /* UIViewController+STPopup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+STPopup.h"; sourceTree = ""; }; 46 | 76D8224C1C1DB62600FCF988 /* UIViewController+STPopup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+STPopup.m"; sourceTree = ""; }; 47 | /* End PBXFileReference section */ 48 | 49 | /* Begin PBXFrameworksBuildPhase section */ 50 | 76D822341C1DB5E400FCF988 /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | /* End PBXFrameworksBuildPhase section */ 58 | 59 | /* Begin PBXGroup section */ 60 | 76D8222E1C1DB5E400FCF988 = { 61 | isa = PBXGroup; 62 | children = ( 63 | 76D8223A1C1DB5E400FCF988 /* STPopup */, 64 | 76D822391C1DB5E400FCF988 /* Products */, 65 | ); 66 | sourceTree = ""; 67 | }; 68 | 76D822391C1DB5E400FCF988 /* Products */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | 76D822381C1DB5E400FCF988 /* STPopup.framework */, 72 | ); 73 | name = Products; 74 | sourceTree = ""; 75 | }; 76 | 76D8223A1C1DB5E400FCF988 /* STPopup */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | 76D822431C1DB62600FCF988 /* STPopupController.h */, 80 | 76D822441C1DB62600FCF988 /* STPopupController.m */, 81 | 76D822451C1DB62600FCF988 /* STPopupLeftBarItem.h */, 82 | 76D822461C1DB62600FCF988 /* STPopupLeftBarItem.m */, 83 | 76D822471C1DB62600FCF988 /* STPopupNavigationBar.h */, 84 | 76D822481C1DB62600FCF988 /* STPopupNavigationBar.m */, 85 | 76D822491C1DB62600FCF988 /* UIResponder+STPopup.h */, 86 | 76D8224A1C1DB62600FCF988 /* UIResponder+STPopup.m */, 87 | 76D8224B1C1DB62600FCF988 /* UIViewController+STPopup.h */, 88 | 76D8224C1C1DB62600FCF988 /* UIViewController+STPopup.m */, 89 | 0A14D9921D65971B00149145 /* STPopupControllerTransitioningSlideVertical.h */, 90 | 0A14D9931D65971B00149145 /* STPopupControllerTransitioningSlideVertical.m */, 91 | 0A14D9961D65A03000149145 /* STPopupControllerTransitioningFade.h */, 92 | 0A14D9971D65A03000149145 /* STPopupControllerTransitioningFade.m */, 93 | 76D8223B1C1DB5E400FCF988 /* STPopup.h */, 94 | 76593AFD2751288E00D1877D /* STPopup+SwiftUI.swift */, 95 | 76D8223D1C1DB5E400FCF988 /* Info.plist */, 96 | ); 97 | path = STPopup; 98 | sourceTree = ""; 99 | }; 100 | /* End PBXGroup section */ 101 | 102 | /* Begin PBXHeadersBuildPhase section */ 103 | 76D822351C1DB5E400FCF988 /* Headers */ = { 104 | isa = PBXHeadersBuildPhase; 105 | buildActionMask = 2147483647; 106 | files = ( 107 | 76D822511C1DB62600FCF988 /* STPopupNavigationBar.h in Headers */, 108 | 0A14D9941D65971B00149145 /* STPopupControllerTransitioningSlideVertical.h in Headers */, 109 | 0A14D9981D65A03000149145 /* STPopupControllerTransitioningFade.h in Headers */, 110 | 76D822551C1DB62600FCF988 /* UIViewController+STPopup.h in Headers */, 111 | 76D8224D1C1DB62600FCF988 /* STPopupController.h in Headers */, 112 | 76D8223C1C1DB5E400FCF988 /* STPopup.h in Headers */, 113 | 76D8224F1C1DB62600FCF988 /* STPopupLeftBarItem.h in Headers */, 114 | 76D822531C1DB62600FCF988 /* UIResponder+STPopup.h in Headers */, 115 | ); 116 | runOnlyForDeploymentPostprocessing = 0; 117 | }; 118 | /* End PBXHeadersBuildPhase section */ 119 | 120 | /* Begin PBXNativeTarget section */ 121 | 76D822371C1DB5E400FCF988 /* STPopup */ = { 122 | isa = PBXNativeTarget; 123 | buildConfigurationList = 76D822401C1DB5E400FCF988 /* Build configuration list for PBXNativeTarget "STPopup" */; 124 | buildPhases = ( 125 | 76D822331C1DB5E400FCF988 /* Sources */, 126 | 76D822341C1DB5E400FCF988 /* Frameworks */, 127 | 76D822351C1DB5E400FCF988 /* Headers */, 128 | 76D822361C1DB5E400FCF988 /* Resources */, 129 | ); 130 | buildRules = ( 131 | ); 132 | dependencies = ( 133 | ); 134 | name = STPopup; 135 | productName = STPopup; 136 | productReference = 76D822381C1DB5E400FCF988 /* STPopup.framework */; 137 | productType = "com.apple.product-type.framework"; 138 | }; 139 | /* End PBXNativeTarget section */ 140 | 141 | /* Begin PBXProject section */ 142 | 76D8222F1C1DB5E400FCF988 /* Project object */ = { 143 | isa = PBXProject; 144 | attributes = { 145 | LastUpgradeCheck = 1310; 146 | ORGANIZATIONNAME = Sth4Me; 147 | TargetAttributes = { 148 | 76D822371C1DB5E400FCF988 = { 149 | CreatedOnToolsVersion = 7.2; 150 | LastSwiftMigration = 1310; 151 | }; 152 | }; 153 | }; 154 | buildConfigurationList = 76D822321C1DB5E400FCF988 /* Build configuration list for PBXProject "STPopup" */; 155 | compatibilityVersion = "Xcode 3.2"; 156 | developmentRegion = en; 157 | hasScannedForEncodings = 0; 158 | knownRegions = ( 159 | en, 160 | Base, 161 | ); 162 | mainGroup = 76D8222E1C1DB5E400FCF988; 163 | productRefGroup = 76D822391C1DB5E400FCF988 /* Products */; 164 | projectDirPath = ""; 165 | projectRoot = ""; 166 | targets = ( 167 | 76D822371C1DB5E400FCF988 /* STPopup */, 168 | ); 169 | }; 170 | /* End PBXProject section */ 171 | 172 | /* Begin PBXResourcesBuildPhase section */ 173 | 76D822361C1DB5E400FCF988 /* Resources */ = { 174 | isa = PBXResourcesBuildPhase; 175 | buildActionMask = 2147483647; 176 | files = ( 177 | ); 178 | runOnlyForDeploymentPostprocessing = 0; 179 | }; 180 | /* End PBXResourcesBuildPhase section */ 181 | 182 | /* Begin PBXSourcesBuildPhase section */ 183 | 76D822331C1DB5E400FCF988 /* Sources */ = { 184 | isa = PBXSourcesBuildPhase; 185 | buildActionMask = 2147483647; 186 | files = ( 187 | 0A14D9991D65A03000149145 /* STPopupControllerTransitioningFade.m in Sources */, 188 | 76593AFE2751288E00D1877D /* STPopup+SwiftUI.swift in Sources */, 189 | 76D822561C1DB62600FCF988 /* UIViewController+STPopup.m in Sources */, 190 | 76D822521C1DB62600FCF988 /* STPopupNavigationBar.m in Sources */, 191 | 0A14D9951D65971B00149145 /* STPopupControllerTransitioningSlideVertical.m in Sources */, 192 | 76D8224E1C1DB62600FCF988 /* STPopupController.m in Sources */, 193 | 76D822541C1DB62600FCF988 /* UIResponder+STPopup.m in Sources */, 194 | 76D822501C1DB62600FCF988 /* STPopupLeftBarItem.m in Sources */, 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | }; 198 | /* End PBXSourcesBuildPhase section */ 199 | 200 | /* Begin XCBuildConfiguration section */ 201 | 76D8223E1C1DB5E400FCF988 /* Debug */ = { 202 | isa = XCBuildConfiguration; 203 | buildSettings = { 204 | ALWAYS_SEARCH_USER_PATHS = NO; 205 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 206 | CLANG_CXX_LIBRARY = "libc++"; 207 | CLANG_ENABLE_MODULES = YES; 208 | CLANG_ENABLE_OBJC_ARC = YES; 209 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 210 | CLANG_WARN_BOOL_CONVERSION = YES; 211 | CLANG_WARN_COMMA = YES; 212 | CLANG_WARN_CONSTANT_CONVERSION = YES; 213 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 214 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 215 | CLANG_WARN_EMPTY_BODY = YES; 216 | CLANG_WARN_ENUM_CONVERSION = YES; 217 | CLANG_WARN_INFINITE_RECURSION = YES; 218 | CLANG_WARN_INT_CONVERSION = YES; 219 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 220 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 221 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 222 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 223 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 224 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 225 | CLANG_WARN_STRICT_PROTOTYPES = YES; 226 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 227 | CLANG_WARN_UNREACHABLE_CODE = YES; 228 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 229 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 230 | COPY_PHASE_STRIP = NO; 231 | CURRENT_PROJECT_VERSION = 1; 232 | DEBUG_INFORMATION_FORMAT = dwarf; 233 | ENABLE_STRICT_OBJC_MSGSEND = YES; 234 | ENABLE_TESTABILITY = YES; 235 | GCC_C_LANGUAGE_STANDARD = gnu99; 236 | GCC_DYNAMIC_NO_PIC = NO; 237 | GCC_NO_COMMON_BLOCKS = YES; 238 | GCC_OPTIMIZATION_LEVEL = 0; 239 | GCC_PREPROCESSOR_DEFINITIONS = ( 240 | "DEBUG=1", 241 | "$(inherited)", 242 | ); 243 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 244 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 245 | GCC_WARN_UNDECLARED_SELECTOR = YES; 246 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 247 | GCC_WARN_UNUSED_FUNCTION = YES; 248 | GCC_WARN_UNUSED_VARIABLE = YES; 249 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 250 | MTL_ENABLE_DEBUG_INFO = YES; 251 | ONLY_ACTIVE_ARCH = YES; 252 | SDKROOT = iphoneos; 253 | TARGETED_DEVICE_FAMILY = "1,2"; 254 | VERSIONING_SYSTEM = "apple-generic"; 255 | VERSION_INFO_PREFIX = ""; 256 | }; 257 | name = Debug; 258 | }; 259 | 76D8223F1C1DB5E400FCF988 /* Release */ = { 260 | isa = XCBuildConfiguration; 261 | buildSettings = { 262 | ALWAYS_SEARCH_USER_PATHS = NO; 263 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 264 | CLANG_CXX_LIBRARY = "libc++"; 265 | CLANG_ENABLE_MODULES = YES; 266 | CLANG_ENABLE_OBJC_ARC = YES; 267 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 268 | CLANG_WARN_BOOL_CONVERSION = YES; 269 | CLANG_WARN_COMMA = YES; 270 | CLANG_WARN_CONSTANT_CONVERSION = YES; 271 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 272 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 273 | CLANG_WARN_EMPTY_BODY = YES; 274 | CLANG_WARN_ENUM_CONVERSION = YES; 275 | CLANG_WARN_INFINITE_RECURSION = YES; 276 | CLANG_WARN_INT_CONVERSION = YES; 277 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 278 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 279 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 280 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 281 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 282 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 283 | CLANG_WARN_STRICT_PROTOTYPES = YES; 284 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 285 | CLANG_WARN_UNREACHABLE_CODE = YES; 286 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 287 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 288 | COPY_PHASE_STRIP = NO; 289 | CURRENT_PROJECT_VERSION = 1; 290 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 291 | ENABLE_NS_ASSERTIONS = NO; 292 | ENABLE_STRICT_OBJC_MSGSEND = YES; 293 | GCC_C_LANGUAGE_STANDARD = gnu99; 294 | GCC_NO_COMMON_BLOCKS = YES; 295 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 296 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 297 | GCC_WARN_UNDECLARED_SELECTOR = YES; 298 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 299 | GCC_WARN_UNUSED_FUNCTION = YES; 300 | GCC_WARN_UNUSED_VARIABLE = YES; 301 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 302 | MTL_ENABLE_DEBUG_INFO = NO; 303 | SDKROOT = iphoneos; 304 | TARGETED_DEVICE_FAMILY = "1,2"; 305 | VALIDATE_PRODUCT = YES; 306 | VERSIONING_SYSTEM = "apple-generic"; 307 | VERSION_INFO_PREFIX = ""; 308 | }; 309 | name = Release; 310 | }; 311 | 76D822411C1DB5E400FCF988 /* Debug */ = { 312 | isa = XCBuildConfiguration; 313 | buildSettings = { 314 | CLANG_ENABLE_MODULES = YES; 315 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 316 | DEFINES_MODULE = YES; 317 | DYLIB_COMPATIBILITY_VERSION = 1; 318 | DYLIB_CURRENT_VERSION = 1; 319 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 320 | INFOPLIST_FILE = STPopup/Info.plist; 321 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 322 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 323 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 324 | PRODUCT_BUNDLE_IDENTIFIER = me.sth4.STPopup; 325 | PRODUCT_NAME = "$(TARGET_NAME)"; 326 | SKIP_INSTALL = YES; 327 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 328 | SWIFT_VERSION = 5.0; 329 | }; 330 | name = Debug; 331 | }; 332 | 76D822421C1DB5E400FCF988 /* Release */ = { 333 | isa = XCBuildConfiguration; 334 | buildSettings = { 335 | CLANG_ENABLE_MODULES = YES; 336 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 337 | DEFINES_MODULE = YES; 338 | DYLIB_COMPATIBILITY_VERSION = 1; 339 | DYLIB_CURRENT_VERSION = 1; 340 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 341 | INFOPLIST_FILE = STPopup/Info.plist; 342 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 343 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 344 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 345 | PRODUCT_BUNDLE_IDENTIFIER = me.sth4.STPopup; 346 | PRODUCT_NAME = "$(TARGET_NAME)"; 347 | SKIP_INSTALL = YES; 348 | SWIFT_VERSION = 5.0; 349 | }; 350 | name = Release; 351 | }; 352 | /* End XCBuildConfiguration section */ 353 | 354 | /* Begin XCConfigurationList section */ 355 | 76D822321C1DB5E400FCF988 /* Build configuration list for PBXProject "STPopup" */ = { 356 | isa = XCConfigurationList; 357 | buildConfigurations = ( 358 | 76D8223E1C1DB5E400FCF988 /* Debug */, 359 | 76D8223F1C1DB5E400FCF988 /* Release */, 360 | ); 361 | defaultConfigurationIsVisible = 0; 362 | defaultConfigurationName = Release; 363 | }; 364 | 76D822401C1DB5E400FCF988 /* Build configuration list for PBXNativeTarget "STPopup" */ = { 365 | isa = XCConfigurationList; 366 | buildConfigurations = ( 367 | 76D822411C1DB5E400FCF988 /* Debug */, 368 | 76D822421C1DB5E400FCF988 /* Release */, 369 | ); 370 | defaultConfigurationIsVisible = 0; 371 | defaultConfigurationName = Release; 372 | }; 373 | /* End XCConfigurationList section */ 374 | }; 375 | rootObject = 76D8222F1C1DB5E400FCF988 /* Project object */; 376 | } 377 | -------------------------------------------------------------------------------- /STPopup.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /STPopup.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /STPopup.xcodeproj/xcshareddata/xcschemes/STPopup.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 52 | 53 | 59 | 60 | 66 | 67 | 68 | 69 | 71 | 72 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /STPopup/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.8.7 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /STPopup/STPopup+SwiftUI.swift: -------------------------------------------------------------------------------- 1 | // 2 | // STPopup+SwiftUI.swift 3 | // STPopup 4 | // 5 | // Created by Kevin Lin on 07/12/2021. 6 | // Copyright © 2021 Sth4Me. All rights reserved. 7 | // 8 | 9 | #if canImport(SwiftUI) 10 | 11 | import SwiftUI 12 | 13 | public typealias PopupStyle = STPopupStyle 14 | 15 | @available (iOS 13.0, *) 16 | public extension View { 17 | @ViewBuilder func popup( 18 | isPresented: Binding, 19 | style: PopupStyle = .formSheet, 20 | onDismiss: (() -> Void)? = nil, 21 | @ViewBuilder contentView: @escaping () -> ContentView 22 | ) -> some View where ContentView: View { 23 | if isPresented.wrappedValue { 24 | overlay( 25 | PopupView(isPresented: isPresented, style: style, onDismiss: onDismiss, contentView: contentView()) 26 | .frame( 27 | width: isPresented.wrappedValue ? Self.windowSize.width : 0, 28 | height: isPresented.wrappedValue ? Self.windowSize.height : 0 29 | ) 30 | ) 31 | } else { 32 | self 33 | } 34 | } 35 | 36 | // Window size is needed to properly layout content view when `sizeThatFits` is called. 37 | private static var windowSize: CGSize { 38 | if let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene, 39 | let window = scene.windows.first { 40 | return window.frame.size 41 | } else { 42 | return .zero 43 | } 44 | } 45 | } 46 | 47 | @available (iOS 13.0, *) 48 | fileprivate struct PopupView: UIViewControllerRepresentable where ContentView: View { 49 | @Binding var isPresented: Bool 50 | let style: PopupStyle 51 | let onDismiss: (() -> Void)? 52 | let contentView: ContentView 53 | 54 | func makeUIViewController(context: Context) -> PopupViewController { 55 | return PopupViewController() 56 | } 57 | 58 | func updateUIViewController(_ popupViewController: PopupViewController, context: Context) { 59 | popupViewController.contentView = contentView 60 | popupViewController.isPresented = isPresented 61 | popupViewController.style = style 62 | popupViewController.onDismiss = onDismiss 63 | popupViewController.didUpdate() 64 | } 65 | 66 | class PopupViewController: UIViewController { 67 | var contentView: ContentView? 68 | var isPresented: Bool = false 69 | var style: PopupStyle = .formSheet 70 | var onDismiss: (() -> Void)? 71 | 72 | private var hostingController: UIHostingController? 73 | private var didAppear = false 74 | 75 | override func viewDidLayoutSubviews() { 76 | super.viewDidLayoutSubviews() 77 | if let hostingController = hostingController, isPresented { 78 | hostingController.contentSizeInPopup = hostingController.sizeThatFits(in: view.bounds.size) 79 | } 80 | } 81 | 82 | override func viewDidAppear(_ animated: Bool) { 83 | super.viewDidAppear(animated) 84 | if !didAppear { 85 | didAppear = true 86 | didUpdate() 87 | } 88 | } 89 | 90 | func didUpdate() { 91 | guard isPresented else { 92 | if hostingController != nil { 93 | dismiss(animated: true) { 94 | if let onDismiss = self.onDismiss { 95 | onDismiss() 96 | } 97 | } 98 | hostingController = nil 99 | } 100 | return 101 | } 102 | guard let contentView = contentView, didAppear else { 103 | return 104 | } 105 | if let hostingController = hostingController { 106 | hostingController.popupController?.style = style 107 | hostingController.rootView = contentView 108 | hostingController.contentSizeInPopup = hostingController.sizeThatFits(in: view.bounds.size) 109 | } else { 110 | let hostingController = UIHostingController(rootView: contentView) 111 | hostingController.contentSizeInPopup = hostingController.sizeThatFits(in: view.bounds.size) 112 | hostingController.view.backgroundColor = .clear 113 | self.hostingController = hostingController 114 | 115 | let popupController = STPopupController(rootViewController: hostingController) 116 | popupController.style = style 117 | popupController.navigationBarHidden = true 118 | popupController.containerView.backgroundColor = .clear 119 | popupController.present(in: self) 120 | } 121 | } 122 | } 123 | } 124 | 125 | #endif 126 | -------------------------------------------------------------------------------- /STPopup/STPopup.h: -------------------------------------------------------------------------------- 1 | // 2 | // STPopup.h 3 | // STPopup 4 | // 5 | // Created by Kevin Lin on 13/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #ifndef STPopup_STPopup_h 10 | #define STPopup_STPopup_h 11 | 12 | #import 13 | #import 14 | #import 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /STPopup/STPopupController.h: -------------------------------------------------------------------------------- 1 | // 2 | // STPopupController.h 3 | // STPopup 4 | // 5 | // Created by Kevin Lin on 11/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | typedef NS_ENUM(NSUInteger, STPopupControllerTransitioningAction) { 15 | STPopupControllerTransitioningActionPresent, 16 | STPopupControllerTransitioningActionDismiss 17 | }; 18 | 19 | @interface STPopupControllerTransitioningContext : NSObject 20 | 21 | /** 22 | Indicating the transitioning is a "present" or "dismiss" action. 23 | */ 24 | @property (nonatomic, assign, readonly) STPopupControllerTransitioningAction action; 25 | 26 | /** 27 | Container view of popup controller. 28 | */ 29 | @property (nonatomic, strong, readonly) UIView *containerView; 30 | 31 | @end 32 | 33 | @protocol STPopupControllerTransitioning 34 | 35 | /** 36 | Return duration of transitioning, it will be used to animate transitioning of background view. 37 | */ 38 | - (NSTimeInterval)popupControllerTransitionDuration:(STPopupControllerTransitioningContext *)context; 39 | 40 | /** 41 | Animate transitioning the container view of popup controller. "completion" need to be called after transitioning is finished. 42 | Initially "containerView" will be placed at the final position with transform = CGAffineTransformIdentity if it's presenting. 43 | */ 44 | - (void)popupControllerAnimateTransition:(STPopupControllerTransitioningContext *)context completion:(void(^)(void))completion; 45 | 46 | @end 47 | 48 | typedef NS_ENUM(NSUInteger, STPopupStyle) { 49 | /** 50 | Popup will be vertically and horizontally centered. 51 | */ 52 | STPopupStyleFormSheet, 53 | /** 54 | Popup will be horizontally centered and sticked to bottom. 55 | */ 56 | STPopupStyleBottomSheet 57 | }; 58 | 59 | typedef NS_ENUM(NSUInteger, STPopupTransitionStyle) { 60 | /** 61 | Slide from bottom to center. 62 | */ 63 | STPopupTransitionStyleSlideVertical, 64 | /** 65 | Fade-in in center from transparent to opaque. 66 | */ 67 | STPopupTransitionStyleFade, 68 | /** 69 | Custom transitioning by providing an implementation of STPopupControllerTransitioning protocol 70 | */ 71 | STPopupTransitionStyleCustom 72 | }; 73 | 74 | @interface STPopupController : NSObject 75 | 76 | /** 77 | Style decides the final position of a popup. 78 | @see STPopupStyle 79 | */ 80 | @property (nonatomic, assign) STPopupStyle style; 81 | 82 | /** 83 | Transition style used in presenting and dismissing the popup. 84 | @see STPopupTransitionStyle 85 | */ 86 | @property (nonatomic, assign) STPopupTransitionStyle transitionStyle; 87 | 88 | /** 89 | Custom defined transitioning, it will be used if "transitionStyle" is set to STPopupTransitionStyleCustom 90 | */ 91 | @property (nullable, nonatomic, weak) id transitioning; 92 | 93 | /** 94 | Corner radius of the container view. 95 | */ 96 | @property (nonatomic, assign) CGFloat cornerRadius DEPRECATED_MSG_ATTRIBUTE("Use containerView.layer.cornerRadius instead"); 97 | 98 | /** 99 | Hidden status of navigation bar of popup. 100 | */ 101 | @property (nonatomic, assign) BOOL navigationBarHidden; 102 | 103 | /** 104 | Hides close button if there is only one view controller in the view controllers stack. 105 | */ 106 | @property (nonatomic, assign) BOOL hidesCloseButton; 107 | 108 | /** 109 | The insets that you use to determine the safe area for the popup. 110 | */ 111 | @property (nonatomic, assign) UIEdgeInsets safeAreaInsets; 112 | 113 | /** 114 | Navigation bar of popup. 115 | @see STPopupNavigationBar 116 | */ 117 | @property (nonatomic, strong, readonly) STPopupNavigationBar *navigationBar; 118 | 119 | /** 120 | Background view which is between popup and the view presenting popup. 121 | By default it's a UIView with background color [UIColor colorWithWhite:0 alpha:0.5]. 122 | */ 123 | @property (nullable, nonatomic, strong) UIView *backgroundView; 124 | 125 | /** 126 | Container view which is containing the navigation bar and content of top most view controller. 127 | By default its background color is set to white and clipsToBounds is set to YES. 128 | */ 129 | @property (nonatomic, strong, readonly) UIView *containerView; 130 | 131 | /** 132 | * The top view controller in the popup's controller stack. 133 | */ 134 | @property (nullable, nonatomic, strong, readonly) UIViewController *topViewController; 135 | 136 | /** 137 | Indicates if the popup is current presented. 138 | */ 139 | @property (nonatomic, assign, readonly) BOOL presented; 140 | 141 | /** 142 | Init the popup with root view controller. 143 | */ 144 | - (instancetype)initWithRootViewController:(UIViewController *)rootViewController; 145 | 146 | /** 147 | Present the popup with transition style on a given view controller. 148 | @see transitionStyle 149 | */ 150 | - (void)presentInViewController:(UIViewController *)viewController; 151 | 152 | /** 153 | Present the popup with transition style on a given view controller. 154 | Completion block will be called after the presenting transition is finished. 155 | @see transitionStyle 156 | */ 157 | - (void)presentInViewController:(UIViewController *)viewController completion:(nullable void (^)(void))completion; 158 | 159 | /** 160 | Dismiss the popup with transition style. 161 | @see transitionStyle 162 | */ 163 | - (void)dismiss; 164 | 165 | /** 166 | Dismiss the popup with transition style. 167 | Completion block will be called after dismissing transition is finished. 168 | @see transitionStyle 169 | */ 170 | - (void)dismissWithCompletion:(nullable void (^)(void))completion; 171 | 172 | /** 173 | Push a view controller into view controllers stack with animated flag. 174 | */ 175 | - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated; 176 | 177 | /** 178 | Pop the top most view controller out of view controllers stack with animated flag. 179 | */ 180 | - (void)popViewControllerAnimated:(BOOL)animated; 181 | 182 | 183 | /** 184 | Pops all view controllers from the stack until it reaches the root view controller 185 | 186 | @param animated YES if animated 187 | */ 188 | - (void)popToRootViewControllerAnimated:(BOOL)animated; 189 | 190 | /** 191 | Set hidden status of navigation bar with animated flag. 192 | @see navigationBarHidden 193 | */ 194 | - (void)setNavigationBarHidden:(BOOL)navigationBarHidden animated:(BOOL)animated; 195 | 196 | @end 197 | 198 | NS_ASSUME_NONNULL_END 199 | -------------------------------------------------------------------------------- /STPopup/STPopupController.m: -------------------------------------------------------------------------------- 1 | // 2 | // STPopupController.m 3 | // STPopup 4 | // 5 | // Created by Kevin Lin on 11/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "STPopupController.h" 10 | #import "STPopupLeftBarItem.h" 11 | #import "STPopupNavigationBar.h" 12 | #import "UIViewController+STPopup.h" 13 | #import "UIResponder+STPopup.h" 14 | #import "STPopupControllerTransitioningSlideVertical.h" 15 | #import "STPopupControllerTransitioningFade.h" 16 | 17 | @implementation STPopupControllerTransitioningContext 18 | 19 | - (instancetype)initWithContainerView:(UIView *)containerView action:(STPopupControllerTransitioningAction)action 20 | { 21 | if (self = [super init]) { 22 | _containerView = containerView; 23 | _action = action; 24 | } 25 | return self; 26 | } 27 | 28 | @end 29 | 30 | CGFloat const STPopupBottomSheetExtraHeight = 80; 31 | 32 | static NSMutableSet *_retainedPopupControllers; 33 | 34 | @protocol STPopupNavigationTouchEventDelegate 35 | 36 | - (void)popupNavigationBar:(STPopupNavigationBar *)navigationBar touchDidMoveWithOffset:(CGFloat)offset; 37 | - (void)popupNavigationBar:(STPopupNavigationBar *)navigationBar touchDidEndWithOffset:(CGFloat)offset; 38 | 39 | @end 40 | 41 | @interface STPopupNavigationBar (STInternal) 42 | 43 | @property (nonatomic, weak) id touchEventDelegate; 44 | 45 | @end 46 | 47 | @interface UIViewController (STInternal) 48 | 49 | @property (nonatomic, weak) STPopupController *popupController; 50 | 51 | @end 52 | 53 | @interface STPopupContainerViewController : UIViewController 54 | 55 | @end 56 | 57 | @implementation STPopupContainerViewController 58 | 59 | - (UIStatusBarStyle)preferredStatusBarStyle 60 | { 61 | if (self.childViewControllers.count || !self.presentingViewController) { 62 | return [super preferredStatusBarStyle]; 63 | } 64 | return [self.presentingViewController preferredStatusBarStyle]; 65 | } 66 | 67 | - (UIViewController *)childViewControllerForStatusBarHidden 68 | { 69 | return self.childViewControllers.lastObject; 70 | } 71 | 72 | - (UIViewController *)childViewControllerForStatusBarStyle 73 | { 74 | return self.childViewControllers.lastObject; 75 | } 76 | 77 | - (void)showViewController:(UIViewController *)vc sender:(id)sender 78 | { 79 | if (!CGSizeEqualToSize(vc.contentSizeInPopup, CGSizeZero) || 80 | !CGSizeEqualToSize(vc.landscapeContentSizeInPopup, CGSizeZero)) { 81 | UIViewController *childViewController = self.childViewControllers.lastObject; 82 | [childViewController.popupController pushViewController:vc animated:YES]; 83 | } 84 | else { 85 | [self presentViewController:vc animated:YES completion:nil]; 86 | } 87 | } 88 | 89 | - (void)showDetailViewController:(UIViewController *)vc sender:(id)sender 90 | { 91 | if (!CGSizeEqualToSize(vc.contentSizeInPopup, CGSizeZero) || 92 | !CGSizeEqualToSize(vc.landscapeContentSizeInPopup, CGSizeZero)) { 93 | UIViewController *childViewController = self.childViewControllers.lastObject; 94 | [childViewController.popupController pushViewController:vc animated:YES]; 95 | } 96 | else { 97 | [self presentViewController:vc animated:YES completion:nil]; 98 | } 99 | } 100 | 101 | @end 102 | 103 | @interface STPopupController () 104 | 105 | @end 106 | 107 | @implementation STPopupController 108 | { 109 | STPopupContainerViewController *_containerViewController; 110 | NSMutableArray *_viewControllers; // 111 | UIView *_contentView; 112 | UILabel *_defaultTitleLabel; 113 | STPopupLeftBarItem *_defaultLeftBarItem; 114 | NSDictionary *_keyboardInfo; 115 | BOOL _didOverrideSafeAreaInsets; 116 | BOOL _observing; 117 | 118 | // Built-in transitioning 119 | STPopupControllerTransitioningSlideVertical *_transitioningSlideVertical; 120 | STPopupControllerTransitioningFade *_transitioningFade; 121 | } 122 | 123 | + (void)load 124 | { 125 | static dispatch_once_t onceToken; 126 | dispatch_once(&onceToken, ^{ 127 | _retainedPopupControllers = [NSMutableSet new]; 128 | }); 129 | } 130 | 131 | - (instancetype)init 132 | { 133 | if (self = [super init]) { 134 | [self setup]; 135 | } 136 | return self; 137 | } 138 | 139 | - (instancetype)initWithRootViewController:(UIViewController *)rootViewController 140 | { 141 | if (self = [self init]) { 142 | [self pushViewController:rootViewController animated:NO]; 143 | } 144 | return self; 145 | } 146 | 147 | - (void)dealloc 148 | { 149 | [self destroyObservers]; 150 | for (UIViewController *viewController in _viewControllers) { 151 | viewController.popupController = nil; // Avoid crash when try to access unsafe unretained property 152 | [self destroyObserversOfViewController:viewController]; 153 | } 154 | } 155 | 156 | - (UIViewController *)topViewController 157 | { 158 | return _viewControllers.lastObject; 159 | } 160 | 161 | - (BOOL)presented 162 | { 163 | return _containerViewController.presentingViewController != nil; 164 | } 165 | 166 | - (void)setBackgroundView:(UIView *)backgroundView 167 | { 168 | [_backgroundView removeFromSuperview]; 169 | _backgroundView = backgroundView; 170 | _backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 171 | [_backgroundView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(bgViewDidTap)]]; 172 | [_containerViewController.view insertSubview:_backgroundView atIndex:0]; 173 | } 174 | 175 | - (void)setHidesCloseButton:(BOOL)hidesCloseButton 176 | { 177 | _hidesCloseButton = hidesCloseButton; 178 | [self updateNavigationBarAnimated:NO]; 179 | } 180 | 181 | - (void)setSafeAreaInsets:(UIEdgeInsets)safeAreaInsets 182 | { 183 | _safeAreaInsets = safeAreaInsets; 184 | _didOverrideSafeAreaInsets = YES; 185 | } 186 | 187 | #pragma mark - Observers 188 | 189 | - (void)setupObservers 190 | { 191 | if (_observing) { 192 | return; 193 | } 194 | _observing = YES; 195 | 196 | // Observe navigation bar 197 | [_navigationBar addObserver:self forKeyPath:NSStringFromSelector(@selector(tintColor)) options:NSKeyValueObservingOptionNew context:nil]; 198 | [_navigationBar addObserver:self forKeyPath:NSStringFromSelector(@selector(titleTextAttributes)) options:NSKeyValueObservingOptionNew context:nil]; 199 | 200 | // Observe orientation change 201 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationDidChange) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; 202 | 203 | // Observe keyboard 204 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; 205 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillChangeFrameNotification object:nil]; 206 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; 207 | 208 | // Observe responder change 209 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(firstResponderDidChange) name:STPopupFirstResponderDidChangeNotification object:nil]; 210 | } 211 | 212 | - (void)destroyObservers 213 | { 214 | if (!_observing) { 215 | return; 216 | } 217 | _observing = NO; 218 | 219 | [_navigationBar removeObserver:self forKeyPath:NSStringFromSelector(@selector(tintColor))]; 220 | [_navigationBar removeObserver:self forKeyPath:NSStringFromSelector(@selector(titleTextAttributes))]; 221 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 222 | } 223 | 224 | - (void)setupObserversForViewController:(UIViewController *)viewController 225 | { 226 | [viewController addObserver:self forKeyPath:NSStringFromSelector(@selector(contentSizeInPopup)) options:NSKeyValueObservingOptionNew context:nil]; 227 | [viewController addObserver:self forKeyPath:NSStringFromSelector(@selector(landscapeContentSizeInPopup)) options:NSKeyValueObservingOptionNew context:nil]; 228 | [viewController.navigationItem addObserver:self forKeyPath:NSStringFromSelector(@selector(title)) options:NSKeyValueObservingOptionNew context:nil]; 229 | [viewController.navigationItem addObserver:self forKeyPath:NSStringFromSelector(@selector(titleView)) options:NSKeyValueObservingOptionNew context:nil]; 230 | [viewController.navigationItem addObserver:self forKeyPath:NSStringFromSelector(@selector(leftBarButtonItem)) options:NSKeyValueObservingOptionNew context:nil]; 231 | [viewController.navigationItem addObserver:self forKeyPath:NSStringFromSelector(@selector(leftBarButtonItems)) options:NSKeyValueObservingOptionNew context:nil]; 232 | [viewController.navigationItem addObserver:self forKeyPath:NSStringFromSelector(@selector(rightBarButtonItem)) options:NSKeyValueObservingOptionNew context:nil]; 233 | [viewController.navigationItem addObserver:self forKeyPath:NSStringFromSelector(@selector(rightBarButtonItems)) options:NSKeyValueObservingOptionNew context:nil]; 234 | [viewController.navigationItem addObserver:self forKeyPath:NSStringFromSelector(@selector(hidesBackButton)) options:NSKeyValueObservingOptionNew context:nil]; 235 | } 236 | 237 | - (void)destroyObserversOfViewController:(UIViewController *)viewController 238 | { 239 | [viewController removeObserver:self forKeyPath:NSStringFromSelector(@selector(contentSizeInPopup))]; 240 | [viewController removeObserver:self forKeyPath:NSStringFromSelector(@selector(landscapeContentSizeInPopup))]; 241 | [viewController.navigationItem removeObserver:self forKeyPath:NSStringFromSelector(@selector(title))]; 242 | [viewController.navigationItem removeObserver:self forKeyPath:NSStringFromSelector(@selector(titleView))]; 243 | [viewController.navigationItem removeObserver:self forKeyPath:NSStringFromSelector(@selector(leftBarButtonItem))]; 244 | [viewController.navigationItem removeObserver:self forKeyPath:NSStringFromSelector(@selector(leftBarButtonItems))]; 245 | [viewController.navigationItem removeObserver:self forKeyPath:NSStringFromSelector(@selector(rightBarButtonItem))]; 246 | [viewController.navigationItem removeObserver:self forKeyPath:NSStringFromSelector(@selector(rightBarButtonItems))]; 247 | [viewController.navigationItem removeObserver:self forKeyPath:NSStringFromSelector(@selector(hidesBackButton))]; 248 | } 249 | 250 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 251 | { 252 | UIViewController *topViewController = self.topViewController; 253 | if (object == _navigationBar || object == topViewController.navigationItem) { 254 | if (topViewController.isViewLoaded && topViewController.view.superview) { 255 | [self updateNavigationBarAnimated:NO]; 256 | } 257 | } 258 | else if (object == topViewController) { 259 | if (topViewController.isViewLoaded && topViewController.view.superview) { 260 | [UIView animateWithDuration:0.3 delay:0 usingSpringWithDamping:1 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 261 | [self layoutContainerView]; 262 | } completion:^(BOOL finished) { 263 | [self adjustContainerViewOrigin]; 264 | }]; 265 | } 266 | } 267 | } 268 | 269 | #pragma mark - STPopupController present & dismiss & push & pop 270 | 271 | - (void)presentInViewController:(UIViewController *)viewController 272 | { 273 | [self presentInViewController:viewController completion:nil]; 274 | } 275 | 276 | - (void)presentInViewController:(UIViewController *)viewController completion:(void (^)(void))completion 277 | { 278 | if (self.presented) { 279 | return; 280 | } 281 | 282 | [self setupObservers]; 283 | 284 | [_retainedPopupControllers addObject:self]; 285 | 286 | viewController = viewController.tabBarController ? : viewController; 287 | if (@available(iOS 11.0, *)) { 288 | if (!_didOverrideSafeAreaInsets) { 289 | _safeAreaInsets = viewController.view.window.safeAreaInsets; 290 | } 291 | } 292 | [viewController presentViewController:_containerViewController animated:YES completion:completion]; 293 | } 294 | 295 | - (void)dismiss 296 | { 297 | [self dismissWithCompletion:nil]; 298 | } 299 | 300 | - (void)dismissWithCompletion:(void (^)(void))completion 301 | { 302 | if (!self.presented) { 303 | return; 304 | } 305 | 306 | [self destroyObservers]; 307 | 308 | [_containerViewController dismissViewControllerAnimated:YES completion:^{ 309 | [_retainedPopupControllers removeObject:self]; 310 | if (completion) { 311 | completion(); 312 | } 313 | }]; 314 | } 315 | 316 | - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated 317 | { 318 | if (!_viewControllers) { 319 | _viewControllers = [NSMutableArray new]; 320 | } 321 | 322 | UIViewController *topViewController = self.topViewController; 323 | viewController.popupController = self; 324 | [_viewControllers addObject:viewController]; 325 | 326 | if (self.presented) { 327 | [self transitFromViewController:topViewController toViewController:viewController animated:animated]; 328 | } 329 | [self setupObserversForViewController:viewController]; 330 | } 331 | 332 | - (void)popViewControllerAnimated:(BOOL)animated 333 | { 334 | if (_viewControllers.count <= 1) { 335 | [self dismiss]; 336 | return; 337 | } 338 | 339 | UIViewController *topViewController = self.topViewController; 340 | [self destroyObserversOfViewController:topViewController]; 341 | [_viewControllers removeObject:topViewController]; 342 | 343 | if (self.presented) { 344 | [self transitFromViewController:topViewController toViewController:self.topViewController animated:animated]; 345 | } 346 | 347 | topViewController.popupController = nil; 348 | } 349 | 350 | 351 | - (void)popToRootViewControllerAnimated:(BOOL)animated 352 | { 353 | if (_viewControllers.count <= 1) { 354 | return; 355 | } 356 | 357 | UIViewController *firstViewController = _viewControllers.firstObject; 358 | UIViewController *lastViewController = _viewControllers.lastObject; 359 | for (int i = 1; i < _viewControllers.count; i++) { 360 | [self destroyObserversOfViewController:_viewControllers[i]]; 361 | } 362 | _viewControllers = [NSMutableArray arrayWithObject:firstViewController]; 363 | 364 | if (self.presented) { 365 | [self transitFromViewController:lastViewController toViewController:firstViewController animated:animated]; 366 | } 367 | } 368 | 369 | 370 | - (void)transitFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController animated:(BOOL)animated 371 | { 372 | [fromViewController beginAppearanceTransition:NO animated:animated]; 373 | [toViewController beginAppearanceTransition:YES animated:animated]; 374 | 375 | [fromViewController willMoveToParentViewController:nil]; 376 | [_containerViewController addChildViewController:toViewController]; 377 | 378 | if (animated) { 379 | // Capture view in "fromViewController" to avoid "viewWillAppear" and "viewDidAppear" being called. 380 | UIGraphicsBeginImageContextWithOptions(fromViewController.view.bounds.size, NO, [UIScreen mainScreen].scale); 381 | [fromViewController.view drawViewHierarchyInRect:fromViewController.view.bounds afterScreenUpdates:NO]; 382 | 383 | UIImageView *capturedView = [[UIImageView alloc] initWithImage:UIGraphicsGetImageFromCurrentImageContext()]; 384 | 385 | UIGraphicsEndImageContext(); 386 | 387 | capturedView.frame = CGRectMake(_contentView.frame.origin.x, _contentView.frame.origin.y, fromViewController.view.bounds.size.width, fromViewController.view.bounds.size.height); 388 | [_containerView insertSubview:capturedView atIndex:0]; 389 | 390 | [fromViewController.view removeFromSuperview]; 391 | 392 | _containerView.userInteractionEnabled = NO; 393 | toViewController.view.alpha = 0; 394 | [UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:1 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 395 | [self layoutContainerView]; 396 | [self->_contentView addSubview:toViewController.view]; 397 | capturedView.alpha = 0; 398 | toViewController.view.alpha = 1; 399 | [self->_containerViewController setNeedsStatusBarAppearanceUpdate]; 400 | } completion:^(BOOL finished) { 401 | [capturedView removeFromSuperview]; 402 | [fromViewController removeFromParentViewController]; 403 | 404 | self->_containerView.userInteractionEnabled = YES; 405 | [toViewController didMoveToParentViewController:self->_containerViewController]; 406 | 407 | [fromViewController endAppearanceTransition]; 408 | [toViewController endAppearanceTransition]; 409 | }]; 410 | [self updateNavigationBarAnimated:animated]; 411 | } 412 | else { 413 | [self layoutContainerView]; 414 | [_contentView addSubview:toViewController.view]; 415 | [_containerViewController setNeedsStatusBarAppearanceUpdate]; 416 | [self updateNavigationBarAnimated:animated]; 417 | 418 | [fromViewController.view removeFromSuperview]; 419 | [fromViewController removeFromParentViewController]; 420 | 421 | [toViewController didMoveToParentViewController:_containerViewController]; 422 | 423 | [fromViewController endAppearanceTransition]; 424 | [toViewController endAppearanceTransition]; 425 | } 426 | } 427 | 428 | - (void)updateNavigationBarAnimated:(BOOL)animated 429 | { 430 | BOOL shouldAnimateDefaultLeftBarItem = animated && _navigationBar.topItem.leftBarButtonItem == _defaultLeftBarItem; 431 | 432 | UIViewController *topViewController = self.topViewController; 433 | UIView *lastTitleView = _navigationBar.topItem.titleView; 434 | _navigationBar.items = @[ [UINavigationItem new] ]; 435 | _navigationBar.topItem.leftBarButtonItems = topViewController.navigationItem.leftBarButtonItems ? : (topViewController.navigationItem.hidesBackButton ? nil : @[ _defaultLeftBarItem ]); 436 | _navigationBar.topItem.rightBarButtonItems = topViewController.navigationItem.rightBarButtonItems; 437 | if (self.hidesCloseButton && topViewController == _viewControllers.firstObject && 438 | _navigationBar.topItem.leftBarButtonItem == _defaultLeftBarItem) { 439 | _navigationBar.topItem.leftBarButtonItems = nil; 440 | } 441 | 442 | if (animated) { 443 | UIView *fromTitleView, *toTitleView; 444 | if (lastTitleView == _defaultTitleLabel) { 445 | UILabel *tempLabel = [[UILabel alloc] initWithFrame:_defaultTitleLabel.frame]; 446 | tempLabel.center = _navigationBar.center; 447 | tempLabel.textColor = _defaultTitleLabel.textColor; 448 | tempLabel.font = _defaultTitleLabel.font; 449 | tempLabel.attributedText = [[NSAttributedString alloc] initWithString:_defaultTitleLabel.text ? : @"" 450 | attributes:_navigationBar.titleTextAttributes]; 451 | fromTitleView = tempLabel; 452 | } 453 | else { 454 | fromTitleView = lastTitleView; 455 | } 456 | 457 | if (topViewController.navigationItem.titleView) { 458 | toTitleView = topViewController.navigationItem.titleView; 459 | } 460 | else { 461 | NSString *title = (topViewController.title ? : topViewController.navigationItem.title) ? : @""; 462 | _defaultTitleLabel = [UILabel new]; 463 | _defaultTitleLabel.attributedText = [[NSAttributedString alloc] initWithString:title 464 | attributes:_navigationBar.titleTextAttributes]; 465 | [_defaultTitleLabel sizeToFit]; 466 | toTitleView = _defaultTitleLabel; 467 | } 468 | 469 | fromTitleView.center = _navigationBar.center; 470 | [_navigationBar addSubview:fromTitleView]; 471 | _navigationBar.topItem.titleView = toTitleView; 472 | toTitleView.alpha = 0; 473 | 474 | [UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:1 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 475 | fromTitleView.alpha = 0; 476 | toTitleView.alpha = 1; 477 | } completion:^(BOOL finished) { 478 | [fromTitleView removeFromSuperview]; 479 | }]; 480 | } 481 | else { 482 | if (topViewController.navigationItem.titleView) { 483 | _navigationBar.topItem.titleView = topViewController.navigationItem.titleView; 484 | } 485 | else { 486 | NSString *title = (topViewController.title ? : topViewController.navigationItem.title) ? : @""; 487 | _defaultTitleLabel = [UILabel new]; 488 | _defaultTitleLabel.attributedText = [[NSAttributedString alloc] initWithString:title 489 | attributes:_navigationBar.titleTextAttributes]; 490 | [_defaultTitleLabel sizeToFit]; 491 | _navigationBar.topItem.titleView = _defaultTitleLabel; 492 | } 493 | } 494 | _defaultLeftBarItem.tintColor = _navigationBar.tintColor; 495 | [_defaultLeftBarItem setType:_viewControllers.count > 1 ? STPopupLeftBarItemArrow : STPopupLeftBarItemCross 496 | animated:shouldAnimateDefaultLeftBarItem]; 497 | } 498 | 499 | - (void)setNavigationBarHidden:(BOOL)navigationBarHidden 500 | { 501 | [self setNavigationBarHidden:navigationBarHidden animated:NO]; 502 | } 503 | 504 | - (void)setNavigationBarHidden:(BOOL)navigationBarHidden animated:(BOOL)animated 505 | { 506 | _navigationBarHidden = navigationBarHidden; 507 | _navigationBar.alpha = navigationBarHidden ? 1 : 0; 508 | 509 | if (!animated) { 510 | [self layoutContainerView]; 511 | _navigationBar.hidden = navigationBarHidden; 512 | return; 513 | } 514 | 515 | if (!navigationBarHidden) { 516 | _navigationBar.hidden = navigationBarHidden; 517 | } 518 | [UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:1 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 519 | self->_navigationBar.alpha = navigationBarHidden ? 0 : 1; 520 | [self layoutContainerView]; 521 | } completion:^(BOOL finished) { 522 | self->_navigationBar.hidden = navigationBarHidden; 523 | }]; 524 | } 525 | 526 | #pragma mark - UI layout 527 | 528 | - (void)layoutContainerView 529 | { 530 | CGAffineTransform lastTransform = _containerView.transform; 531 | _containerView.transform = CGAffineTransformIdentity; 532 | 533 | _backgroundView.frame = _containerViewController.view.bounds; 534 | 535 | CGFloat preferredNavigationBarHeight = [self preferredNavigationBarHeight]; 536 | CGFloat navigationBarHeight = _navigationBarHidden ? 0 : preferredNavigationBarHeight; 537 | CGSize contentSizeOfTopView = [self contentSizeOfTopView]; 538 | CGFloat containerViewWidth = contentSizeOfTopView.width; 539 | CGFloat containerViewHeight = contentSizeOfTopView.height + navigationBarHeight; 540 | CGFloat containerViewY = (_containerViewController.view.bounds.size.height - containerViewHeight) / 2; 541 | 542 | if (self.style == STPopupStyleBottomSheet) { 543 | containerViewHeight += _safeAreaInsets.bottom; 544 | containerViewY = _containerViewController.view.bounds.size.height - containerViewHeight; 545 | containerViewHeight += STPopupBottomSheetExtraHeight; 546 | } 547 | 548 | _containerView.frame = CGRectMake((_containerViewController.view.bounds.size.width - containerViewWidth) / 2, 549 | containerViewY, containerViewWidth, containerViewHeight); 550 | _navigationBar.frame = CGRectMake(0, 0, containerViewWidth, preferredNavigationBarHeight); 551 | _contentView.frame = CGRectMake(0, navigationBarHeight, contentSizeOfTopView.width, contentSizeOfTopView.height); 552 | 553 | UIViewController *topViewController = self.topViewController; 554 | topViewController.view.frame = _contentView.bounds; 555 | 556 | _containerView.transform = lastTransform; 557 | } 558 | 559 | - (CGSize)contentSizeOfTopView 560 | { 561 | UIViewController *topViewController = self.topViewController; 562 | CGSize contentSize = CGSizeZero; 563 | switch ([UIApplication sharedApplication].statusBarOrientation) { 564 | case UIInterfaceOrientationLandscapeLeft: 565 | case UIInterfaceOrientationLandscapeRight: { 566 | contentSize = topViewController.landscapeContentSizeInPopup; 567 | if (CGSizeEqualToSize(contentSize, CGSizeZero)) { 568 | contentSize = topViewController.contentSizeInPopup; 569 | } 570 | } 571 | break; 572 | default: { 573 | contentSize = topViewController.contentSizeInPopup; 574 | } 575 | break; 576 | } 577 | 578 | NSAssert(!CGSizeEqualToSize(contentSize, CGSizeZero), @"contentSizeInPopup should not be size zero."); 579 | 580 | return contentSize; 581 | } 582 | 583 | - (CGFloat)preferredNavigationBarHeight 584 | { 585 | // The preferred height of navigation bar is different between iPhone (4, 5, 6) and 6 Plus. 586 | // Create a navigation controller to get the preferred height of navigation bar. 587 | UINavigationController *navigationController = [UINavigationController new]; 588 | return navigationController.navigationBar.bounds.size.height; 589 | } 590 | 591 | #pragma mark - UI setup 592 | 593 | - (void)setup 594 | { 595 | _containerViewController = [STPopupContainerViewController new]; 596 | _containerViewController.view.backgroundColor = [UIColor clearColor]; 597 | if ([[[UIDevice currentDevice] systemVersion] compare:@"8.0" options:NSNumericSearch] != NSOrderedAscending) { 598 | _containerViewController.modalPresentationStyle = UIModalPresentationOverCurrentContext; 599 | } 600 | else { 601 | _containerViewController.modalPresentationStyle = UIModalPresentationCustom; 602 | } 603 | _containerViewController.transitioningDelegate = self; 604 | [self setupBackgroundView]; 605 | [self setupContainerView]; 606 | [self setupNavigationBar]; 607 | 608 | _transitioningSlideVertical = [STPopupControllerTransitioningSlideVertical new]; 609 | _transitioningFade = [STPopupControllerTransitioningFade new]; 610 | } 611 | 612 | - (void)setupBackgroundView 613 | { 614 | UIView *backgroundView = [UIView new]; 615 | backgroundView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5]; 616 | self.backgroundView = backgroundView; 617 | } 618 | 619 | - (void)setupContainerView 620 | { 621 | _containerView = [UIView new]; 622 | _containerView.backgroundColor = [UIColor whiteColor]; 623 | _containerView.clipsToBounds = YES; 624 | [_containerViewController.view addSubview:_containerView]; 625 | 626 | _contentView = [UIView new]; 627 | [_containerView addSubview:_contentView]; 628 | } 629 | 630 | - (void)setupNavigationBar 631 | { 632 | STPopupNavigationBar *navigationBar = [STPopupNavigationBar new]; 633 | navigationBar.touchEventDelegate = self; 634 | 635 | _navigationBar = navigationBar; 636 | [_containerView addSubview:_navigationBar]; 637 | 638 | _defaultTitleLabel = [UILabel new]; 639 | _defaultLeftBarItem = [[STPopupLeftBarItem alloc] initWithTarget:self action:@selector(leftBarItemDidTap)]; 640 | } 641 | 642 | - (void)leftBarItemDidTap 643 | { 644 | switch (_defaultLeftBarItem.type) { 645 | case STPopupLeftBarItemCross: 646 | [self dismiss]; 647 | break; 648 | case STPopupLeftBarItemArrow: 649 | [self popViewControllerAnimated:YES]; 650 | break; 651 | default: 652 | break; 653 | } 654 | } 655 | 656 | - (void)bgViewDidTap 657 | { 658 | [_containerView endEditing:YES]; 659 | } 660 | 661 | - (void)setCornerRadius:(CGFloat)cornerRadius 662 | { 663 | _cornerRadius = cornerRadius; 664 | _containerView.layer.cornerRadius = self.cornerRadius; 665 | } 666 | 667 | #pragma mark - UIApplicationDidChangeStatusBarOrientationNotification 668 | 669 | - (void)orientationDidChange 670 | { 671 | [_containerView endEditing:YES]; 672 | [UIView animateWithDuration:0.2 delay:0 usingSpringWithDamping:1 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 673 | self->_containerView.alpha = 0; 674 | } completion:^(BOOL finished) { 675 | [self layoutContainerView]; 676 | [self updateNavigationBarAnimated:NO]; 677 | [UIView animateWithDuration:0.2 delay:0 usingSpringWithDamping:1 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 678 | self->_containerView.alpha = 1; 679 | } completion:nil]; 680 | }]; 681 | } 682 | 683 | #pragma mark - UIKeyboardWillShowNotification & UIKeyboardWillHideNotification 684 | 685 | - (void)keyboardWillShow:(NSNotification *)notification 686 | { 687 | UIView *currentTextInput = [self getCurrentTextInputInView:_containerView]; 688 | if (!currentTextInput) { 689 | return; 690 | } 691 | 692 | _keyboardInfo = notification.userInfo; 693 | [self adjustContainerViewOrigin]; 694 | } 695 | 696 | - (void)keyboardWillHide:(NSNotification *)notification 697 | { 698 | _keyboardInfo = nil; 699 | 700 | NSTimeInterval duration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; 701 | UIViewAnimationCurve curve = [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] intValue]; 702 | 703 | [UIView beginAnimations:nil context:NULL]; 704 | [UIView setAnimationBeginsFromCurrentState:YES]; 705 | [UIView setAnimationCurve:curve]; 706 | [UIView setAnimationDuration:duration]; 707 | 708 | _containerView.transform = CGAffineTransformIdentity; 709 | 710 | [UIView commitAnimations]; 711 | } 712 | 713 | - (void)adjustContainerViewOrigin 714 | { 715 | if (!_keyboardInfo) { 716 | return; 717 | } 718 | 719 | UIView *currentTextInput = [self getCurrentTextInputInView:_containerView]; 720 | if (!currentTextInput) { 721 | return; 722 | } 723 | 724 | CGAffineTransform lastTransform = _containerView.transform; 725 | _containerView.transform = CGAffineTransformIdentity; // Set transform to identity for calculating a correct "minOffsetY" 726 | 727 | CGFloat textFieldBottomY = [currentTextInput convertPoint:CGPointZero toView:_containerViewController.view].y + currentTextInput.bounds.size.height; 728 | CGFloat keyboardHeight = [_keyboardInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height; 729 | // For iOS 7 730 | UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; 731 | if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1 && 732 | (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight)) { 733 | keyboardHeight = [_keyboardInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].size.width; 734 | } 735 | 736 | CGFloat offsetY = 0; 737 | if (self.style == STPopupStyleBottomSheet) { 738 | offsetY = keyboardHeight - _safeAreaInsets.bottom; 739 | } 740 | else { 741 | CGFloat statusBarHeight = [UIApplication sharedApplication].statusBarFrame.size.height; 742 | if (_containerView.bounds.size.height <= _containerViewController.view.bounds.size.height - keyboardHeight - statusBarHeight) { 743 | offsetY = _containerView.frame.origin.y - (statusBarHeight + (_containerViewController.view.bounds.size.height - keyboardHeight - statusBarHeight - _containerView.bounds.size.height) / 2); 744 | } 745 | else { 746 | CGFloat spacing = 5; 747 | offsetY = _containerView.frame.origin.y + _containerView.bounds.size.height - (_containerViewController.view.bounds.size.height - keyboardHeight - spacing); 748 | if (offsetY <= 0) { // _containerView can be totally shown, so no need to translate the origin 749 | return; 750 | } 751 | if (_containerView.frame.origin.y - offsetY < statusBarHeight) { // _containerView will be covered by status bar if the origin is translated by "offsetY" 752 | offsetY = _containerView.frame.origin.y - statusBarHeight; 753 | // currentTextField can not be totally shown if _containerView is going to repositioned with "offsetY" 754 | if (textFieldBottomY - offsetY > _containerViewController.view.bounds.size.height - keyboardHeight - spacing) { 755 | offsetY = textFieldBottomY - (_containerViewController.view.bounds.size.height - keyboardHeight - spacing); 756 | } 757 | } 758 | } 759 | } 760 | 761 | NSTimeInterval duration = [_keyboardInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; 762 | UIViewAnimationCurve curve = [_keyboardInfo[UIKeyboardAnimationCurveUserInfoKey] intValue]; 763 | 764 | _containerView.transform = lastTransform; // Restore transform 765 | 766 | [UIView beginAnimations:nil context:NULL]; 767 | [UIView setAnimationBeginsFromCurrentState:YES]; 768 | [UIView setAnimationCurve:curve]; 769 | [UIView setAnimationDuration:duration]; 770 | 771 | _containerView.transform = CGAffineTransformMakeTranslation(0, -offsetY); 772 | 773 | [UIView commitAnimations]; 774 | } 775 | 776 | - (UIView *)getCurrentTextInputInView:(UIView *)view 777 | { 778 | if ([view conformsToProtocol:@protocol(UIKeyInput)] && view.isFirstResponder) { 779 | // Quick fix for web view issue 780 | if ([view isKindOfClass:NSClassFromString(@"UIWebBrowserView")] || [view isKindOfClass:NSClassFromString(@"WKContentView")]) { 781 | return nil; 782 | } 783 | return (UIView *)view; 784 | } 785 | 786 | for (UIView *subview in view.subviews) { 787 | UIView *view = [self getCurrentTextInputInView:subview]; 788 | if (view) { 789 | return view; 790 | } 791 | } 792 | return nil; 793 | } 794 | 795 | #pragma mark - STPopupFirstResponderDidChangeNotification 796 | 797 | - (void)firstResponderDidChange 798 | { 799 | // "keyboardWillShow" won't be called if height of keyboard is not changed 800 | // Manually adjust container view origin according to last keyboard info 801 | [self adjustContainerViewOrigin]; 802 | } 803 | 804 | #pragma mark - UIViewControllerTransitioningDelegate 805 | 806 | - (id )animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source 807 | { 808 | return self; 809 | } 810 | 811 | - (id)animationControllerForDismissedController:(UIViewController *)dismissed 812 | { 813 | return self; 814 | } 815 | 816 | #pragma mark - UIViewControllerAnimatedTransitioning 817 | 818 | - (STPopupControllerTransitioningContext *)convertTransitioningContext:(id )transitionContext 819 | { 820 | STPopupControllerTransitioningAction action = STPopupControllerTransitioningActionPresent; 821 | if ([transitionContext viewControllerForKey:UITransitionContextToViewControllerKey] != _containerViewController) { 822 | action = STPopupControllerTransitioningActionDismiss; 823 | } 824 | return [[STPopupControllerTransitioningContext alloc] initWithContainerView:_containerView action:action]; 825 | } 826 | 827 | - (NSTimeInterval)transitionDuration:(id )transitionContext 828 | { 829 | STPopupControllerTransitioningContext *context = [self convertTransitioningContext:transitionContext]; 830 | switch (self.transitionStyle) { 831 | case STPopupTransitionStyleSlideVertical: 832 | return [_transitioningSlideVertical popupControllerTransitionDuration:context]; 833 | case STPopupTransitionStyleFade: 834 | return [_transitioningFade popupControllerTransitionDuration:context]; 835 | case STPopupTransitionStyleCustom: 836 | NSAssert(self.transitioning, @"transitioning should be provided if it's using STPopupTransitionStyleCustom"); 837 | return [_transitioning popupControllerTransitionDuration:context]; 838 | } 839 | } 840 | 841 | - (void)animateTransition:(id )transitionContext 842 | { 843 | UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; 844 | UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; 845 | 846 | toViewController.view.frame = fromViewController.view.frame; 847 | 848 | UIViewController *topViewController = self.topViewController; 849 | 850 | STPopupControllerTransitioningContext *context = [self convertTransitioningContext:transitionContext]; 851 | id transitioning = nil; 852 | switch (self.transitionStyle) { 853 | case STPopupTransitionStyleSlideVertical: 854 | transitioning = _transitioningSlideVertical; 855 | break; 856 | case STPopupTransitionStyleFade: 857 | transitioning = _transitioningFade; 858 | break; 859 | case STPopupTransitionStyleCustom: 860 | transitioning = self.transitioning; 861 | break; 862 | } 863 | NSAssert(transitioning, @"transitioning should be provided if it's using STPopupTransitionStyleCustom"); 864 | 865 | if (context.action == STPopupControllerTransitioningActionPresent) { 866 | [fromViewController beginAppearanceTransition:NO animated:YES]; 867 | 868 | [[transitionContext containerView] addSubview:toViewController.view]; 869 | 870 | [topViewController beginAppearanceTransition:YES animated:YES]; 871 | [toViewController addChildViewController:topViewController]; 872 | 873 | [self layoutContainerView]; 874 | [_contentView addSubview:topViewController.view]; 875 | [toViewController setNeedsStatusBarAppearanceUpdate]; 876 | [self updateNavigationBarAnimated:NO]; 877 | 878 | CGFloat lastBackgroundViewAlpha = _backgroundView.alpha; 879 | _backgroundView.alpha = 0; 880 | _backgroundView.userInteractionEnabled = NO; 881 | _containerView.userInteractionEnabled = NO; 882 | _containerView.transform = CGAffineTransformIdentity; 883 | 884 | [UIView animateWithDuration:[transitioning popupControllerTransitionDuration:context] delay:0 usingSpringWithDamping:1 initialSpringVelocity:1 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 885 | self->_backgroundView.alpha = lastBackgroundViewAlpha; 886 | } completion:nil]; 887 | 888 | [transitioning popupControllerAnimateTransition:context completion:^{ 889 | self->_backgroundView.userInteractionEnabled = YES; 890 | self->_containerView.userInteractionEnabled = YES; 891 | 892 | [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; 893 | [topViewController didMoveToParentViewController:toViewController]; 894 | [fromViewController endAppearanceTransition]; 895 | }]; 896 | } 897 | else { 898 | [toViewController beginAppearanceTransition:YES animated:YES]; 899 | 900 | [topViewController beginAppearanceTransition:NO animated:YES]; 901 | [topViewController willMoveToParentViewController:nil]; 902 | 903 | CGFloat lastBackgroundViewAlpha = _backgroundView.alpha; 904 | _backgroundView.userInteractionEnabled = NO; 905 | _containerView.userInteractionEnabled = NO; 906 | 907 | [UIView animateWithDuration:[transitioning popupControllerTransitionDuration:context] delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ 908 | self->_backgroundView.alpha = 0; 909 | } completion:nil]; 910 | 911 | [transitioning popupControllerAnimateTransition:context completion:^{ 912 | self->_backgroundView.userInteractionEnabled = YES; 913 | self->_containerView.userInteractionEnabled = YES;; 914 | 915 | [fromViewController.view removeFromSuperview]; 916 | [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; 917 | 918 | [topViewController.view removeFromSuperview]; 919 | [topViewController removeFromParentViewController]; 920 | 921 | [toViewController endAppearanceTransition]; 922 | 923 | self->_backgroundView.alpha = lastBackgroundViewAlpha; 924 | }]; 925 | } 926 | } 927 | 928 | #pragma mark - STPopupNavigationTouchEventDelegate 929 | 930 | - (void)popupNavigationBar:(STPopupNavigationBar *)navigationBar touchDidMoveWithOffset:(CGFloat)offset 931 | { 932 | [_containerView endEditing:YES]; 933 | 934 | if (self.style == STPopupStyleBottomSheet && offset < -STPopupBottomSheetExtraHeight) { 935 | return; 936 | } 937 | _containerView.transform = CGAffineTransformMakeTranslation(0, offset); 938 | } 939 | 940 | - (void)popupNavigationBar:(STPopupNavigationBar *)navigationBar touchDidEndWithOffset:(CGFloat)offset 941 | { 942 | if (offset > 150) { 943 | STPopupTransitionStyle transitionStyle = self.transitionStyle; 944 | self.transitionStyle = STPopupTransitionStyleSlideVertical; 945 | [self dismissWithCompletion:^{ 946 | self.transitionStyle = transitionStyle; 947 | }]; 948 | } 949 | else { 950 | [_containerView endEditing:YES]; 951 | [UIView animateWithDuration:0.4 delay:0 usingSpringWithDamping:0.7 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 952 | self->_containerView.transform = CGAffineTransformIdentity; 953 | } completion:nil]; 954 | } 955 | } 956 | 957 | @end 958 | -------------------------------------------------------------------------------- /STPopup/STPopupControllerTransitioningFade.h: -------------------------------------------------------------------------------- 1 | // 2 | // STPopupControllerTransitioningFade.h 3 | // STPopup 4 | // 5 | // Created by Kevin Lin on 18/8/16. 6 | // Copyright © 2016 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface STPopupControllerTransitioningFade : NSObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /STPopup/STPopupControllerTransitioningFade.m: -------------------------------------------------------------------------------- 1 | // 2 | // STPopupControllerTransitioningFade.m 3 | // STPopup 4 | // 5 | // Created by Kevin Lin on 18/8/16. 6 | // Copyright © 2016 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "STPopupControllerTransitioningFade.h" 10 | 11 | @implementation STPopupControllerTransitioningFade 12 | 13 | - (NSTimeInterval)popupControllerTransitionDuration:(STPopupControllerTransitioningContext *)context 14 | { 15 | return context.action == STPopupControllerTransitioningActionPresent ? 0.25 : 0.2; 16 | } 17 | 18 | - (void)popupControllerAnimateTransition:(STPopupControllerTransitioningContext *)context completion:(void (^)(void))completion 19 | { 20 | UIView *containerView = context.containerView; 21 | if (context.action == STPopupControllerTransitioningActionPresent) { 22 | containerView.alpha = 0; 23 | containerView.transform = CGAffineTransformMakeScale(1.05, 1.05); 24 | 25 | [UIView animateWithDuration:[self popupControllerTransitionDuration:context] delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ 26 | containerView.alpha = 1; 27 | containerView.transform = CGAffineTransformIdentity; 28 | } completion:^(BOOL finished) { 29 | completion(); 30 | }]; 31 | } 32 | else { 33 | [UIView animateWithDuration:[self popupControllerTransitionDuration:context] delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ 34 | containerView.alpha = 0; 35 | } completion:^(BOOL finished) { 36 | containerView.alpha = 1; 37 | completion(); 38 | }]; 39 | } 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /STPopup/STPopupControllerTransitioningSlideVertical.h: -------------------------------------------------------------------------------- 1 | // 2 | // STPopupControllerTransitioningSlideVertical.h 3 | // STPopup 4 | // 5 | // Created by Kevin Lin on 18/8/16. 6 | // Copyright © 2016 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface STPopupControllerTransitioningSlideVertical : NSObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /STPopup/STPopupControllerTransitioningSlideVertical.m: -------------------------------------------------------------------------------- 1 | // 2 | // STPopupControllerTransitioningSlideVertical.m 3 | // STPopup 4 | // 5 | // Created by Kevin Lin on 18/8/16. 6 | // Copyright © 2016 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "STPopupControllerTransitioningSlideVertical.h" 10 | 11 | @implementation STPopupControllerTransitioningSlideVertical 12 | 13 | - (NSTimeInterval)popupControllerTransitionDuration:(STPopupControllerTransitioningContext *)context 14 | { 15 | return context.action == STPopupControllerTransitioningActionPresent ? 0.5 : 0.35; 16 | } 17 | 18 | - (void)popupControllerAnimateTransition:(STPopupControllerTransitioningContext *)context completion:(void (^)(void))completion 19 | { 20 | UIView *containerView = context.containerView; 21 | if (context.action == STPopupControllerTransitioningActionPresent) { 22 | containerView.transform = CGAffineTransformMakeTranslation(0, containerView.superview.bounds.size.height - containerView.frame.origin.y); 23 | 24 | [UIView animateWithDuration:[self popupControllerTransitionDuration:context] delay:0 usingSpringWithDamping:1 initialSpringVelocity:1 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 25 | context.containerView.transform = CGAffineTransformIdentity; 26 | } completion:^(BOOL finished) { 27 | completion(); 28 | }]; 29 | } 30 | else { 31 | CGAffineTransform lastTransform = containerView.transform; 32 | containerView.transform = CGAffineTransformIdentity; 33 | CGFloat originY = containerView.frame.origin.y; 34 | containerView.transform = lastTransform; 35 | 36 | [UIView animateWithDuration:[self popupControllerTransitionDuration:context] delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ 37 | containerView.transform = CGAffineTransformMakeTranslation(0, containerView.superview.bounds.size.height - originY + containerView.frame.size.height); 38 | } completion:^(BOOL finished) { 39 | containerView.transform = CGAffineTransformIdentity; 40 | completion(); 41 | }]; 42 | } 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /STPopup/STPopupLeftBarItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // STPopupLeftBarItem.h 3 | // STPopup 4 | // 5 | // Created by Kevin Lin on 13/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | typedef NS_ENUM(NSUInteger, STPopupLeftBarItemType) { 14 | STPopupLeftBarItemCross, 15 | STPopupLeftBarItemArrow 16 | }; 17 | 18 | @interface STPopupLeftBarItem : UIBarButtonItem 19 | 20 | @property (nonatomic, assign) STPopupLeftBarItemType type; 21 | 22 | - (instancetype)initWithTarget:(id)target action:(SEL)action; 23 | - (void)setType:(STPopupLeftBarItemType)type animated:(BOOL)animated; 24 | 25 | @end 26 | 27 | NS_ASSUME_NONNULL_END 28 | -------------------------------------------------------------------------------- /STPopup/STPopupLeftBarItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // STPopupLeftBarItem.m 3 | // STPopup 4 | // 5 | // Created by Kevin Lin on 13/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "STPopupLeftBarItem.h" 10 | 11 | @implementation STPopupLeftBarItem 12 | { 13 | UIControl *_customView; 14 | UIView *_bar1; 15 | UIView *_bar2; 16 | } 17 | 18 | - (instancetype)initWithTarget:(id)target action:(SEL)action 19 | { 20 | _customView = [[UIControl alloc] initWithFrame:CGRectMake(0, 0, 18, 44)]; 21 | if (self = [super initWithCustomView:_customView]) { 22 | [_customView addTarget:target action:action forControlEvents:UIControlEventTouchUpInside]; 23 | _bar1 = [UIView new]; 24 | _bar1.backgroundColor = [UIColor colorWithWhite:0.4 alpha:1]; 25 | _bar1.userInteractionEnabled = NO; 26 | _bar1.layer.allowsEdgeAntialiasing = YES; 27 | [_customView addSubview:_bar1]; 28 | _bar2 = [UIView new]; 29 | _bar2.backgroundColor = [UIColor colorWithWhite:0.4 alpha:1]; 30 | _bar2.userInteractionEnabled = NO; 31 | _bar2.layer.allowsEdgeAntialiasing = YES; 32 | [_customView addSubview:_bar2]; 33 | } 34 | return self; 35 | } 36 | 37 | - (void)setType:(STPopupLeftBarItemType)type 38 | { 39 | [self setType:type animated:NO]; 40 | } 41 | 42 | - (void)setType:(STPopupLeftBarItemType)type animated:(BOOL)animated 43 | { 44 | _type = type; 45 | if (animated) { 46 | [UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:1 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 47 | [self updateLayout]; 48 | } completion:nil]; 49 | } 50 | else { 51 | [self updateLayout]; 52 | } 53 | } 54 | 55 | - (void)updateLayout 56 | { 57 | float barWidth, barHeight = 1.5, barX, bar1Y, bar2Y; 58 | switch (self.type) { 59 | case STPopupLeftBarItemCross: { 60 | barWidth = _customView.frame.size.height * 2 / 5; 61 | barX = (_customView.frame.size.width - barWidth) / 2; 62 | bar1Y = (_customView.frame.size.height - barHeight) / 2; 63 | bar2Y = bar1Y; 64 | } 65 | break; 66 | case STPopupLeftBarItemArrow: { 67 | barWidth = _customView.frame.size.height / 4; 68 | barX = (_customView.frame.size.width - barWidth) / 2 - barWidth / 2; 69 | bar1Y = (_customView.frame.size.height - barHeight) / 2 + barWidth / 2 * sin(M_PI_4); 70 | bar2Y = (_customView.frame.size.height - barHeight) / 2 - barWidth / 2 * sin(M_PI_4); 71 | } 72 | break; 73 | default: 74 | break; 75 | } 76 | _bar1.transform = CGAffineTransformIdentity; 77 | _bar2.transform = CGAffineTransformIdentity; 78 | _bar1.frame = CGRectMake(barX, bar1Y, barWidth, barHeight); 79 | _bar2.frame = CGRectMake(barX, bar2Y, barWidth, barHeight); 80 | 81 | _bar1.transform = CGAffineTransformMakeRotation(M_PI_4); 82 | _bar2.transform = CGAffineTransformMakeRotation(-M_PI_4); 83 | } 84 | 85 | - (void)setTintColor:(UIColor *)tintColor 86 | { 87 | [super setTintColor:tintColor]; 88 | _bar1.backgroundColor = tintColor; 89 | _bar2.backgroundColor = tintColor; 90 | } 91 | 92 | @end -------------------------------------------------------------------------------- /STPopup/STPopupNavigationBar.h: -------------------------------------------------------------------------------- 1 | // 2 | // STPopupNavigationBar.h 3 | // STPopup 4 | // 5 | // Created by Kevin Lin on 13/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface STPopupNavigationBar : UINavigationBar 14 | 15 | /** 16 | Indicates if the navigation bar is draggable to dissmiss the popup. 17 | Default to YES. 18 | */ 19 | @property (nonatomic, assign) BOOL draggable; 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /STPopup/STPopupNavigationBar.m: -------------------------------------------------------------------------------- 1 | // 2 | // STPopupNavigationBar.m 3 | // STPopup 4 | // 5 | // Created by Kevin Lin on 13/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "STPopupNavigationBar.h" 10 | 11 | @protocol STPopupNavigationTouchEventDelegate 12 | 13 | - (void)popupNavigationBar:(STPopupNavigationBar *)navigationBar touchDidMoveWithOffset:(CGFloat)offset; 14 | - (void)popupNavigationBar:(STPopupNavigationBar *)navigationBar touchDidEndWithOffset:(CGFloat)offset; 15 | 16 | @end 17 | 18 | @interface STPopupNavigationBar () 19 | 20 | @property (nonatomic, weak) id touchEventDelegate; 21 | 22 | @end 23 | 24 | @implementation STPopupNavigationBar 25 | { 26 | BOOL _moving; 27 | CGFloat _movingStartY; 28 | } 29 | 30 | - (instancetype)initWithFrame:(CGRect)frame 31 | { 32 | if (self = [super initWithFrame:frame]) { 33 | self.draggable = YES; 34 | } 35 | return self; 36 | } 37 | 38 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 39 | { 40 | if (!self.draggable) { 41 | [super touchesBegan:touches withEvent:event]; 42 | return; 43 | } 44 | 45 | UITouch *touch = [touches anyObject]; 46 | if ((touch.view == self || touch.view.superview == self) && !_moving) { 47 | _moving = YES; 48 | _movingStartY = [touch locationInView:self.window].y; 49 | } 50 | } 51 | 52 | - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 53 | { 54 | if (!self.draggable) { 55 | [super touchesMoved:touches withEvent:event]; 56 | return; 57 | } 58 | 59 | if (_moving) { 60 | UITouch *touch = [touches anyObject]; 61 | float offset = [touch locationInView:self.window].y - _movingStartY; 62 | if ([self.touchEventDelegate respondsToSelector:@selector(popupNavigationBar:touchDidMoveWithOffset:)]) { 63 | [self.touchEventDelegate popupNavigationBar:self touchDidMoveWithOffset:offset]; 64 | } 65 | } 66 | } 67 | 68 | - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event 69 | { 70 | if (!self.draggable) { 71 | [super touchesCancelled:touches withEvent:event]; 72 | return; 73 | } 74 | 75 | if (_moving) { 76 | UITouch *touch = [touches anyObject]; 77 | float offset = [touch locationInView:self.window].y - _movingStartY; 78 | [self movingDidEndWithOffset:offset]; 79 | } 80 | } 81 | 82 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 83 | { 84 | if (!self.draggable) { 85 | [super touchesEnded:touches withEvent:event]; 86 | return; 87 | } 88 | 89 | if (_moving) { 90 | UITouch *touch = [touches anyObject]; 91 | float offset = [touch locationInView:self.window].y - _movingStartY; 92 | [self movingDidEndWithOffset:offset]; 93 | } 94 | } 95 | 96 | - (void)movingDidEndWithOffset:(float)offset 97 | { 98 | _moving = NO; 99 | if ([self.touchEventDelegate respondsToSelector:@selector(popupNavigationBar:touchDidEndWithOffset:)]) { 100 | [self.touchEventDelegate popupNavigationBar:self touchDidEndWithOffset:offset]; 101 | } 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /STPopup/UIResponder+STPopup.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIResponder+STPopup.h 3 | // STPopup 4 | // 5 | // Created by Kevin Lin on 14/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | FOUNDATION_EXPORT NSString *const STPopupFirstResponderDidChangeNotification; 12 | 13 | @interface UIResponder (STPopup) 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /STPopup/UIResponder+STPopup.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIResponder+STPopup.m 3 | // STPopup 4 | // 5 | // Created by Kevin Lin on 14/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "UIResponder+STPopup.h" 10 | #import 11 | 12 | NSString *const STPopupFirstResponderDidChangeNotification = @"STPopupFirstResponderDidChangeNotification"; 13 | 14 | @implementation UIResponder (STPopup) 15 | 16 | + (void)load 17 | { 18 | static dispatch_once_t onceToken; 19 | dispatch_once(&onceToken, ^{ 20 | [self swizzleSelector:@selector(becomeFirstResponder) toSelector:@selector(st_becomeFirstResponder)]; 21 | }); 22 | } 23 | 24 | + (void)swizzleSelector:(SEL)originalSelector toSelector:(SEL)swizzledSelector 25 | { 26 | Class class = [self class]; 27 | 28 | Method originalMethod = class_getInstanceMethod(class, originalSelector); 29 | Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); 30 | method_exchangeImplementations(originalMethod, swizzledMethod); 31 | } 32 | 33 | - (BOOL)st_becomeFirstResponder 34 | { 35 | BOOL accepted = [self st_becomeFirstResponder]; 36 | if (accepted) { 37 | [[NSNotificationCenter defaultCenter] postNotificationName:STPopupFirstResponderDidChangeNotification object:self]; 38 | } 39 | return accepted; 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /STPopup/UIViewController+STPopup.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+STPopup.h 3 | // STPopup 4 | // 5 | // Created by Kevin Lin on 13/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @class STPopupController; 14 | 15 | @interface UIViewController (STPopup) 16 | 17 | /** 18 | Content size of popup in portrait orientation. 19 | */ 20 | @property (nonatomic, assign) IBInspectable CGSize contentSizeInPopup; 21 | 22 | /** 23 | Content size of popup in landscape orientation. 24 | */ 25 | @property (nonatomic, assign) IBInspectable CGSize landscapeContentSizeInPopup; 26 | 27 | /** 28 | Popup controller which is containing the view controller. 29 | Will be nil if the view controller is not contained in any popup controller. 30 | */ 31 | @property (nullable, nonatomic, weak, readonly) STPopupController *popupController; 32 | 33 | @end 34 | 35 | NS_ASSUME_NONNULL_END 36 | -------------------------------------------------------------------------------- /STPopup/UIViewController+STPopup.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+STPopup.m 3 | // STPopup 4 | // 5 | // Created by Kevin Lin on 13/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "UIViewController+STPopup.h" 10 | #import "STPopupController.h" 11 | #import 12 | 13 | @implementation UIViewController (STPopup) 14 | 15 | @dynamic contentSizeInPopup; 16 | @dynamic landscapeContentSizeInPopup; 17 | @dynamic popupController; 18 | 19 | + (void)load 20 | { 21 | static dispatch_once_t onceToken; 22 | dispatch_once(&onceToken, ^{ 23 | [self swizzleSelector:@selector(viewDidLoad) toSelector:@selector(st_viewDidLoad)]; 24 | [self swizzleSelector:@selector(presentViewController:animated:completion:) toSelector:@selector(st_presentViewController:animated:completion:)]; 25 | [self swizzleSelector:@selector(dismissViewControllerAnimated:completion:) toSelector:@selector(st_dismissViewControllerAnimated:completion:)]; 26 | [self swizzleSelector:@selector(presentedViewController) toSelector:@selector(st_presentedViewController)]; 27 | [self swizzleSelector:@selector(presentingViewController) toSelector:@selector(st_presentingViewController)]; 28 | }); 29 | } 30 | 31 | + (void)swizzleSelector:(SEL)originalSelector toSelector:(SEL)swizzledSelector 32 | { 33 | Class class = [self class]; 34 | 35 | Method originalMethod = class_getInstanceMethod(class, originalSelector); 36 | Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); 37 | method_exchangeImplementations(originalMethod, swizzledMethod); 38 | } 39 | 40 | - (void)st_viewDidLoad 41 | { 42 | CGSize contentSize = CGSizeZero; 43 | switch ([UIApplication sharedApplication].statusBarOrientation) { 44 | case UIInterfaceOrientationLandscapeLeft: 45 | case UIInterfaceOrientationLandscapeRight: { 46 | contentSize = self.landscapeContentSizeInPopup; 47 | if (CGSizeEqualToSize(contentSize, CGSizeZero)) { 48 | contentSize = self.contentSizeInPopup; 49 | } 50 | } 51 | break; 52 | default: { 53 | contentSize = self.contentSizeInPopup; 54 | } 55 | break; 56 | } 57 | 58 | if (!CGSizeEqualToSize(contentSize, CGSizeZero)) { 59 | self.view.frame = CGRectMake(0, 0, contentSize.width, contentSize.height); 60 | } 61 | [self st_viewDidLoad]; 62 | } 63 | 64 | - (void)st_presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion 65 | { 66 | if (!self.popupController) { 67 | [self st_presentViewController:viewControllerToPresent animated:flag completion:completion]; 68 | return; 69 | } 70 | 71 | [[self.popupController valueForKey:@"containerViewController"] presentViewController:viewControllerToPresent animated:flag completion:completion]; 72 | } 73 | 74 | - (void)st_dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion 75 | { 76 | if (!self.popupController) { 77 | [self st_dismissViewControllerAnimated:flag completion:completion]; 78 | return; 79 | } 80 | 81 | [self.popupController dismissWithCompletion:completion]; 82 | } 83 | 84 | - (UIViewController *)st_presentedViewController 85 | { 86 | if (!self.popupController) { 87 | return [self st_presentedViewController]; 88 | } 89 | return [[self.popupController valueForKey:@"containerViewController"] presentedViewController]; 90 | } 91 | 92 | - (UIViewController *)st_presentingViewController 93 | { 94 | if (!self.popupController) { 95 | return [self st_presentingViewController]; 96 | } 97 | return [[self.popupController valueForKey:@"containerViewController"] presentingViewController]; 98 | } 99 | 100 | - (void)setContentSizeInPopup:(CGSize)contentSizeInPopup 101 | { 102 | if (!CGSizeEqualToSize(CGSizeZero, contentSizeInPopup) && contentSizeInPopup.width == 0) { 103 | switch ([UIApplication sharedApplication].statusBarOrientation) { 104 | case UIInterfaceOrientationLandscapeLeft: 105 | case UIInterfaceOrientationLandscapeRight: { 106 | contentSizeInPopup.width = [UIScreen mainScreen].bounds.size.height; 107 | } 108 | break; 109 | default: { 110 | contentSizeInPopup.width = [UIScreen mainScreen].bounds.size.width; 111 | } 112 | break; 113 | } 114 | } 115 | objc_setAssociatedObject(self, @selector(contentSizeInPopup), [NSValue valueWithCGSize:contentSizeInPopup], OBJC_ASSOCIATION_RETAIN_NONATOMIC); 116 | } 117 | 118 | - (CGSize)contentSizeInPopup 119 | { 120 | return [objc_getAssociatedObject(self, @selector(contentSizeInPopup)) CGSizeValue]; 121 | } 122 | 123 | - (void)setLandscapeContentSizeInPopup:(CGSize)landscapeContentSizeInPopup 124 | { 125 | if (!CGSizeEqualToSize(CGSizeZero, landscapeContentSizeInPopup) && landscapeContentSizeInPopup.width == 0) { 126 | switch ([UIApplication sharedApplication].statusBarOrientation) { 127 | case UIInterfaceOrientationLandscapeLeft: 128 | case UIInterfaceOrientationLandscapeRight: { 129 | landscapeContentSizeInPopup.width = [UIScreen mainScreen].bounds.size.width; 130 | } 131 | break; 132 | default: { 133 | landscapeContentSizeInPopup.width = [UIScreen mainScreen].bounds.size.height; 134 | } 135 | break; 136 | } 137 | } 138 | objc_setAssociatedObject(self, @selector(landscapeContentSizeInPopup), [NSValue valueWithCGSize:landscapeContentSizeInPopup], OBJC_ASSOCIATION_RETAIN_NONATOMIC); 139 | } 140 | 141 | - (CGSize)landscapeContentSizeInPopup 142 | { 143 | return [objc_getAssociatedObject(self, @selector(landscapeContentSizeInPopup)) CGSizeValue]; 144 | } 145 | 146 | - (void)setPopupController:(STPopupController *)popupController 147 | { 148 | objc_setAssociatedObject(self, @selector(popupController), popupController, OBJC_ASSOCIATION_ASSIGN); 149 | } 150 | 151 | - (STPopupController *)popupController 152 | { 153 | STPopupController *popupController = objc_getAssociatedObject(self, @selector(popupController)); 154 | if (!popupController) { 155 | return self.parentViewController.popupController; 156 | } 157 | return popupController; 158 | } 159 | 160 | @end -------------------------------------------------------------------------------- /STPopupExample/STPopupExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0A46DC981C48AF270092C76B /* STPopup.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76D822811C1DBDB800FCF988 /* STPopup.framework */; }; 11 | 0A46DC991C48AF270092C76B /* STPopup.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 76D822811C1DBDB800FCF988 /* STPopup.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 12 | 763128D21BCA9EE8009C5D7E /* BottomSheetDemoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 763128D11BCA9EE8009C5D7E /* BottomSheetDemoViewController.m */; }; 13 | 763128D51BCAA3C5009C5D7E /* MultiSelectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 763128D41BCAA3C5009C5D7E /* MultiSelectionViewController.m */; }; 14 | 766A9BA21BA55F350062C75B /* PopupViewController3.m in Sources */ = {isa = PBXBuildFile; fileRef = 766A9BA11BA55F350062C75B /* PopupViewController3.m */; }; 15 | 7672E4B222908BB90075F798 /* BulletinBoardViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7672E4B122908BB90075F798 /* BulletinBoardViewController.m */; }; 16 | 76ACB5541BA2934D00A09710 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 76ACB5531BA2934D00A09710 /* main.m */; }; 17 | 76ACB5571BA2934D00A09710 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 76ACB5561BA2934D00A09710 /* AppDelegate.m */; }; 18 | 76ACB55A1BA2934D00A09710 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 76ACB5591BA2934D00A09710 /* ViewController.m */; }; 19 | 76ACB55D1BA2934D00A09710 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 76ACB55B1BA2934D00A09710 /* Main.storyboard */; }; 20 | 76ACB55F1BA2934D00A09710 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 76ACB55E1BA2934D00A09710 /* Images.xcassets */; }; 21 | 76ACB5621BA2934D00A09710 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 76ACB5601BA2934D00A09710 /* LaunchScreen.xib */; }; 22 | 76ACB5831BA2BE7500A09710 /* PopupViewController1.m in Sources */ = {isa = PBXBuildFile; fileRef = 76ACB5821BA2BE7500A09710 /* PopupViewController1.m */; }; 23 | 76ACB5861BA2BE8100A09710 /* PopupViewController2.m in Sources */ = {isa = PBXBuildFile; fileRef = 76ACB5851BA2BE8100A09710 /* PopupViewController2.m */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXContainerItemProxy section */ 27 | 0A46DC9A1C48AF270092C76B /* PBXContainerItemProxy */ = { 28 | isa = PBXContainerItemProxy; 29 | containerPortal = 76D8227C1C1DBDB800FCF988 /* STPopup.xcodeproj */; 30 | proxyType = 1; 31 | remoteGlobalIDString = 76D822371C1DB5E400FCF988; 32 | remoteInfo = STPopup; 33 | }; 34 | 76D822801C1DBDB800FCF988 /* PBXContainerItemProxy */ = { 35 | isa = PBXContainerItemProxy; 36 | containerPortal = 76D8227C1C1DBDB800FCF988 /* STPopup.xcodeproj */; 37 | proxyType = 2; 38 | remoteGlobalIDString = 76D822381C1DB5E400FCF988; 39 | remoteInfo = STPopup; 40 | }; 41 | 76D822821C1DBDDC00FCF988 /* PBXContainerItemProxy */ = { 42 | isa = PBXContainerItemProxy; 43 | containerPortal = 76D8227C1C1DBDB800FCF988 /* STPopup.xcodeproj */; 44 | proxyType = 1; 45 | remoteGlobalIDString = 76D822371C1DB5E400FCF988; 46 | remoteInfo = STPopup; 47 | }; 48 | /* End PBXContainerItemProxy section */ 49 | 50 | /* Begin PBXCopyFilesBuildPhase section */ 51 | 0A46DC9C1C48AF270092C76B /* Embed Frameworks */ = { 52 | isa = PBXCopyFilesBuildPhase; 53 | buildActionMask = 2147483647; 54 | dstPath = ""; 55 | dstSubfolderSpec = 10; 56 | files = ( 57 | 0A46DC991C48AF270092C76B /* STPopup.framework in Embed Frameworks */, 58 | ); 59 | name = "Embed Frameworks"; 60 | runOnlyForDeploymentPostprocessing = 0; 61 | }; 62 | /* End PBXCopyFilesBuildPhase section */ 63 | 64 | /* Begin PBXFileReference section */ 65 | 763128D01BCA9EE8009C5D7E /* BottomSheetDemoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BottomSheetDemoViewController.h; sourceTree = ""; }; 66 | 763128D11BCA9EE8009C5D7E /* BottomSheetDemoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BottomSheetDemoViewController.m; sourceTree = ""; }; 67 | 763128D31BCAA3C5009C5D7E /* MultiSelectionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MultiSelectionViewController.h; sourceTree = ""; }; 68 | 763128D41BCAA3C5009C5D7E /* MultiSelectionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MultiSelectionViewController.m; sourceTree = ""; }; 69 | 766A9BA01BA55F350062C75B /* PopupViewController3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PopupViewController3.h; sourceTree = ""; }; 70 | 766A9BA11BA55F350062C75B /* PopupViewController3.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PopupViewController3.m; sourceTree = ""; }; 71 | 7672E4B022908BB90075F798 /* BulletinBoardViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BulletinBoardViewController.h; sourceTree = ""; }; 72 | 7672E4B122908BB90075F798 /* BulletinBoardViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BulletinBoardViewController.m; sourceTree = ""; }; 73 | 76ACB54E1BA2934D00A09710 /* STPopupExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = STPopupExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 74 | 76ACB5521BA2934D00A09710 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 75 | 76ACB5531BA2934D00A09710 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 76 | 76ACB5551BA2934D00A09710 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 77 | 76ACB5561BA2934D00A09710 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 78 | 76ACB5581BA2934D00A09710 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 79 | 76ACB5591BA2934D00A09710 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 80 | 76ACB55C1BA2934D00A09710 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 81 | 76ACB55E1BA2934D00A09710 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 82 | 76ACB5611BA2934D00A09710 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 83 | 76ACB5811BA2BE7500A09710 /* PopupViewController1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PopupViewController1.h; sourceTree = ""; }; 84 | 76ACB5821BA2BE7500A09710 /* PopupViewController1.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PopupViewController1.m; sourceTree = ""; }; 85 | 76ACB5841BA2BE8100A09710 /* PopupViewController2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PopupViewController2.h; sourceTree = ""; }; 86 | 76ACB5851BA2BE8100A09710 /* PopupViewController2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PopupViewController2.m; sourceTree = ""; }; 87 | 76D8227C1C1DBDB800FCF988 /* STPopup.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = STPopup.xcodeproj; path = ../STPopup.xcodeproj; sourceTree = ""; }; 88 | /* End PBXFileReference section */ 89 | 90 | /* Begin PBXFrameworksBuildPhase section */ 91 | 76ACB54B1BA2934D00A09710 /* Frameworks */ = { 92 | isa = PBXFrameworksBuildPhase; 93 | buildActionMask = 2147483647; 94 | files = ( 95 | 0A46DC981C48AF270092C76B /* STPopup.framework in Frameworks */, 96 | ); 97 | runOnlyForDeploymentPostprocessing = 0; 98 | }; 99 | /* End PBXFrameworksBuildPhase section */ 100 | 101 | /* Begin PBXGroup section */ 102 | 76ACB5451BA2934D00A09710 = { 103 | isa = PBXGroup; 104 | children = ( 105 | 76D8227C1C1DBDB800FCF988 /* STPopup.xcodeproj */, 106 | 76ACB5501BA2934D00A09710 /* STPopupExample */, 107 | 76ACB54F1BA2934D00A09710 /* Products */, 108 | ); 109 | sourceTree = ""; 110 | }; 111 | 76ACB54F1BA2934D00A09710 /* Products */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 76ACB54E1BA2934D00A09710 /* STPopupExample.app */, 115 | ); 116 | name = Products; 117 | sourceTree = ""; 118 | }; 119 | 76ACB5501BA2934D00A09710 /* STPopupExample */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 76ACB5551BA2934D00A09710 /* AppDelegate.h */, 123 | 76ACB5561BA2934D00A09710 /* AppDelegate.m */, 124 | 76ACB5581BA2934D00A09710 /* ViewController.h */, 125 | 76ACB5591BA2934D00A09710 /* ViewController.m */, 126 | 76ACB5811BA2BE7500A09710 /* PopupViewController1.h */, 127 | 76ACB5821BA2BE7500A09710 /* PopupViewController1.m */, 128 | 76ACB5841BA2BE8100A09710 /* PopupViewController2.h */, 129 | 76ACB5851BA2BE8100A09710 /* PopupViewController2.m */, 130 | 766A9BA01BA55F350062C75B /* PopupViewController3.h */, 131 | 766A9BA11BA55F350062C75B /* PopupViewController3.m */, 132 | 763128D01BCA9EE8009C5D7E /* BottomSheetDemoViewController.h */, 133 | 763128D11BCA9EE8009C5D7E /* BottomSheetDemoViewController.m */, 134 | 763128D31BCAA3C5009C5D7E /* MultiSelectionViewController.h */, 135 | 763128D41BCAA3C5009C5D7E /* MultiSelectionViewController.m */, 136 | 7672E4B022908BB90075F798 /* BulletinBoardViewController.h */, 137 | 7672E4B122908BB90075F798 /* BulletinBoardViewController.m */, 138 | 76ACB55B1BA2934D00A09710 /* Main.storyboard */, 139 | 76ACB55E1BA2934D00A09710 /* Images.xcassets */, 140 | 76ACB5601BA2934D00A09710 /* LaunchScreen.xib */, 141 | 76ACB5511BA2934D00A09710 /* Supporting Files */, 142 | ); 143 | path = STPopupExample; 144 | sourceTree = ""; 145 | }; 146 | 76ACB5511BA2934D00A09710 /* Supporting Files */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | 76ACB5521BA2934D00A09710 /* Info.plist */, 150 | 76ACB5531BA2934D00A09710 /* main.m */, 151 | ); 152 | name = "Supporting Files"; 153 | sourceTree = ""; 154 | }; 155 | 76D8227D1C1DBDB800FCF988 /* Products */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | 76D822811C1DBDB800FCF988 /* STPopup.framework */, 159 | ); 160 | name = Products; 161 | sourceTree = ""; 162 | }; 163 | /* End PBXGroup section */ 164 | 165 | /* Begin PBXNativeTarget section */ 166 | 76ACB54D1BA2934D00A09710 /* STPopupExample */ = { 167 | isa = PBXNativeTarget; 168 | buildConfigurationList = 76ACB5711BA2934D00A09710 /* Build configuration list for PBXNativeTarget "STPopupExample" */; 169 | buildPhases = ( 170 | 76ACB54A1BA2934D00A09710 /* Sources */, 171 | 76ACB54B1BA2934D00A09710 /* Frameworks */, 172 | 76ACB54C1BA2934D00A09710 /* Resources */, 173 | 0A46DC9C1C48AF270092C76B /* Embed Frameworks */, 174 | ); 175 | buildRules = ( 176 | ); 177 | dependencies = ( 178 | 76D822831C1DBDDC00FCF988 /* PBXTargetDependency */, 179 | 0A46DC9B1C48AF270092C76B /* PBXTargetDependency */, 180 | ); 181 | name = STPopupExample; 182 | productName = STPopupExample; 183 | productReference = 76ACB54E1BA2934D00A09710 /* STPopupExample.app */; 184 | productType = "com.apple.product-type.application"; 185 | }; 186 | /* End PBXNativeTarget section */ 187 | 188 | /* Begin PBXProject section */ 189 | 76ACB5461BA2934D00A09710 /* Project object */ = { 190 | isa = PBXProject; 191 | attributes = { 192 | LastUpgradeCheck = 0930; 193 | ORGANIZATIONNAME = Sth4Me; 194 | TargetAttributes = { 195 | 76ACB54D1BA2934D00A09710 = { 196 | CreatedOnToolsVersion = 6.4; 197 | }; 198 | }; 199 | }; 200 | buildConfigurationList = 76ACB5491BA2934D00A09710 /* Build configuration list for PBXProject "STPopupExample" */; 201 | compatibilityVersion = "Xcode 3.2"; 202 | developmentRegion = English; 203 | hasScannedForEncodings = 0; 204 | knownRegions = ( 205 | en, 206 | Base, 207 | ); 208 | mainGroup = 76ACB5451BA2934D00A09710; 209 | productRefGroup = 76ACB54F1BA2934D00A09710 /* Products */; 210 | projectDirPath = ""; 211 | projectReferences = ( 212 | { 213 | ProductGroup = 76D8227D1C1DBDB800FCF988 /* Products */; 214 | ProjectRef = 76D8227C1C1DBDB800FCF988 /* STPopup.xcodeproj */; 215 | }, 216 | ); 217 | projectRoot = ""; 218 | targets = ( 219 | 76ACB54D1BA2934D00A09710 /* STPopupExample */, 220 | ); 221 | }; 222 | /* End PBXProject section */ 223 | 224 | /* Begin PBXReferenceProxy section */ 225 | 76D822811C1DBDB800FCF988 /* STPopup.framework */ = { 226 | isa = PBXReferenceProxy; 227 | fileType = wrapper.framework; 228 | path = STPopup.framework; 229 | remoteRef = 76D822801C1DBDB800FCF988 /* PBXContainerItemProxy */; 230 | sourceTree = BUILT_PRODUCTS_DIR; 231 | }; 232 | /* End PBXReferenceProxy section */ 233 | 234 | /* Begin PBXResourcesBuildPhase section */ 235 | 76ACB54C1BA2934D00A09710 /* Resources */ = { 236 | isa = PBXResourcesBuildPhase; 237 | buildActionMask = 2147483647; 238 | files = ( 239 | 76ACB55D1BA2934D00A09710 /* Main.storyboard in Resources */, 240 | 76ACB5621BA2934D00A09710 /* LaunchScreen.xib in Resources */, 241 | 76ACB55F1BA2934D00A09710 /* Images.xcassets in Resources */, 242 | ); 243 | runOnlyForDeploymentPostprocessing = 0; 244 | }; 245 | /* End PBXResourcesBuildPhase section */ 246 | 247 | /* Begin PBXSourcesBuildPhase section */ 248 | 76ACB54A1BA2934D00A09710 /* Sources */ = { 249 | isa = PBXSourcesBuildPhase; 250 | buildActionMask = 2147483647; 251 | files = ( 252 | 76ACB5861BA2BE8100A09710 /* PopupViewController2.m in Sources */, 253 | 7672E4B222908BB90075F798 /* BulletinBoardViewController.m in Sources */, 254 | 763128D21BCA9EE8009C5D7E /* BottomSheetDemoViewController.m in Sources */, 255 | 76ACB5831BA2BE7500A09710 /* PopupViewController1.m in Sources */, 256 | 763128D51BCAA3C5009C5D7E /* MultiSelectionViewController.m in Sources */, 257 | 766A9BA21BA55F350062C75B /* PopupViewController3.m in Sources */, 258 | 76ACB55A1BA2934D00A09710 /* ViewController.m in Sources */, 259 | 76ACB5571BA2934D00A09710 /* AppDelegate.m in Sources */, 260 | 76ACB5541BA2934D00A09710 /* main.m in Sources */, 261 | ); 262 | runOnlyForDeploymentPostprocessing = 0; 263 | }; 264 | /* End PBXSourcesBuildPhase section */ 265 | 266 | /* Begin PBXTargetDependency section */ 267 | 0A46DC9B1C48AF270092C76B /* PBXTargetDependency */ = { 268 | isa = PBXTargetDependency; 269 | name = STPopup; 270 | targetProxy = 0A46DC9A1C48AF270092C76B /* PBXContainerItemProxy */; 271 | }; 272 | 76D822831C1DBDDC00FCF988 /* PBXTargetDependency */ = { 273 | isa = PBXTargetDependency; 274 | name = STPopup; 275 | targetProxy = 76D822821C1DBDDC00FCF988 /* PBXContainerItemProxy */; 276 | }; 277 | /* End PBXTargetDependency section */ 278 | 279 | /* Begin PBXVariantGroup section */ 280 | 76ACB55B1BA2934D00A09710 /* Main.storyboard */ = { 281 | isa = PBXVariantGroup; 282 | children = ( 283 | 76ACB55C1BA2934D00A09710 /* Base */, 284 | ); 285 | name = Main.storyboard; 286 | sourceTree = ""; 287 | }; 288 | 76ACB5601BA2934D00A09710 /* LaunchScreen.xib */ = { 289 | isa = PBXVariantGroup; 290 | children = ( 291 | 76ACB5611BA2934D00A09710 /* Base */, 292 | ); 293 | name = LaunchScreen.xib; 294 | sourceTree = ""; 295 | }; 296 | /* End PBXVariantGroup section */ 297 | 298 | /* Begin XCBuildConfiguration section */ 299 | 76ACB56F1BA2934D00A09710 /* Debug */ = { 300 | isa = XCBuildConfiguration; 301 | buildSettings = { 302 | ALWAYS_SEARCH_USER_PATHS = NO; 303 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 304 | CLANG_CXX_LIBRARY = "libc++"; 305 | CLANG_ENABLE_MODULES = YES; 306 | CLANG_ENABLE_OBJC_ARC = YES; 307 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 308 | CLANG_WARN_BOOL_CONVERSION = YES; 309 | CLANG_WARN_COMMA = YES; 310 | CLANG_WARN_CONSTANT_CONVERSION = YES; 311 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 312 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 313 | CLANG_WARN_EMPTY_BODY = YES; 314 | CLANG_WARN_ENUM_CONVERSION = YES; 315 | CLANG_WARN_INFINITE_RECURSION = YES; 316 | CLANG_WARN_INT_CONVERSION = YES; 317 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 318 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 319 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 320 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 321 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 322 | CLANG_WARN_STRICT_PROTOTYPES = YES; 323 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 324 | CLANG_WARN_UNREACHABLE_CODE = YES; 325 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 326 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 327 | COPY_PHASE_STRIP = NO; 328 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 329 | ENABLE_STRICT_OBJC_MSGSEND = YES; 330 | ENABLE_TESTABILITY = YES; 331 | GCC_C_LANGUAGE_STANDARD = gnu99; 332 | GCC_DYNAMIC_NO_PIC = NO; 333 | GCC_NO_COMMON_BLOCKS = YES; 334 | GCC_OPTIMIZATION_LEVEL = 0; 335 | GCC_PREPROCESSOR_DEFINITIONS = ( 336 | "DEBUG=1", 337 | "$(inherited)", 338 | ); 339 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 340 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 341 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 342 | GCC_WARN_UNDECLARED_SELECTOR = YES; 343 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 344 | GCC_WARN_UNUSED_FUNCTION = YES; 345 | GCC_WARN_UNUSED_VARIABLE = YES; 346 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 347 | MTL_ENABLE_DEBUG_INFO = YES; 348 | ONLY_ACTIVE_ARCH = YES; 349 | SDKROOT = iphoneos; 350 | TARGETED_DEVICE_FAMILY = "1,2"; 351 | }; 352 | name = Debug; 353 | }; 354 | 76ACB5701BA2934D00A09710 /* Release */ = { 355 | isa = XCBuildConfiguration; 356 | buildSettings = { 357 | ALWAYS_SEARCH_USER_PATHS = NO; 358 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 359 | CLANG_CXX_LIBRARY = "libc++"; 360 | CLANG_ENABLE_MODULES = YES; 361 | CLANG_ENABLE_OBJC_ARC = YES; 362 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 363 | CLANG_WARN_BOOL_CONVERSION = YES; 364 | CLANG_WARN_COMMA = YES; 365 | CLANG_WARN_CONSTANT_CONVERSION = YES; 366 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 367 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 368 | CLANG_WARN_EMPTY_BODY = YES; 369 | CLANG_WARN_ENUM_CONVERSION = YES; 370 | CLANG_WARN_INFINITE_RECURSION = YES; 371 | CLANG_WARN_INT_CONVERSION = YES; 372 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 373 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 374 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 375 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 376 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 377 | CLANG_WARN_STRICT_PROTOTYPES = YES; 378 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 379 | CLANG_WARN_UNREACHABLE_CODE = YES; 380 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 381 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 382 | COPY_PHASE_STRIP = NO; 383 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 384 | ENABLE_NS_ASSERTIONS = NO; 385 | ENABLE_STRICT_OBJC_MSGSEND = YES; 386 | GCC_C_LANGUAGE_STANDARD = gnu99; 387 | GCC_NO_COMMON_BLOCKS = YES; 388 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 389 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 390 | GCC_WARN_UNDECLARED_SELECTOR = YES; 391 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 392 | GCC_WARN_UNUSED_FUNCTION = YES; 393 | GCC_WARN_UNUSED_VARIABLE = YES; 394 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 395 | MTL_ENABLE_DEBUG_INFO = NO; 396 | SDKROOT = iphoneos; 397 | TARGETED_DEVICE_FAMILY = "1,2"; 398 | VALIDATE_PRODUCT = YES; 399 | }; 400 | name = Release; 401 | }; 402 | 76ACB5721BA2934D00A09710 /* Debug */ = { 403 | isa = XCBuildConfiguration; 404 | buildSettings = { 405 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 406 | INFOPLIST_FILE = STPopupExample/Info.plist; 407 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 408 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 409 | PRODUCT_BUNDLE_IDENTIFIER = "me.sth4.$(PRODUCT_NAME:rfc1034identifier)"; 410 | PRODUCT_NAME = STPopupExample; 411 | }; 412 | name = Debug; 413 | }; 414 | 76ACB5731BA2934D00A09710 /* Release */ = { 415 | isa = XCBuildConfiguration; 416 | buildSettings = { 417 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 418 | INFOPLIST_FILE = STPopupExample/Info.plist; 419 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 420 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 421 | PRODUCT_BUNDLE_IDENTIFIER = "me.sth4.$(PRODUCT_NAME:rfc1034identifier)"; 422 | PRODUCT_NAME = STPopupExample; 423 | }; 424 | name = Release; 425 | }; 426 | /* End XCBuildConfiguration section */ 427 | 428 | /* Begin XCConfigurationList section */ 429 | 76ACB5491BA2934D00A09710 /* Build configuration list for PBXProject "STPopupExample" */ = { 430 | isa = XCConfigurationList; 431 | buildConfigurations = ( 432 | 76ACB56F1BA2934D00A09710 /* Debug */, 433 | 76ACB5701BA2934D00A09710 /* Release */, 434 | ); 435 | defaultConfigurationIsVisible = 0; 436 | defaultConfigurationName = Release; 437 | }; 438 | 76ACB5711BA2934D00A09710 /* Build configuration list for PBXNativeTarget "STPopupExample" */ = { 439 | isa = XCConfigurationList; 440 | buildConfigurations = ( 441 | 76ACB5721BA2934D00A09710 /* Debug */, 442 | 76ACB5731BA2934D00A09710 /* Release */, 443 | ); 444 | defaultConfigurationIsVisible = 0; 445 | defaultConfigurationName = Release; 446 | }; 447 | /* End XCConfigurationList section */ 448 | }; 449 | rootObject = 76ACB5461BA2934D00A09710 /* Project object */; 450 | } 451 | -------------------------------------------------------------------------------- /STPopupExample/STPopupExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /STPopupExample/STPopupExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // STPopupExample 4 | // 5 | // Created by Kevin Lin on 11/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // STPopupExample 4 | // 5 | // Created by Kevin Lin on 11/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/BottomSheetDemoViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BottomSheetDemoViewController.h 3 | // STPopupExample 4 | // 5 | // Created by Kevin Lin on 11/10/15. 6 | // Copyright © 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BottomSheetDemoViewController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/BottomSheetDemoViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // BottomSheetDemoViewController.m 3 | // STPopupExample 4 | // 5 | // Created by Kevin Lin on 11/10/15. 6 | // Copyright © 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "BottomSheetDemoViewController.h" 10 | #import "MultiSelectionViewController.h" 11 | #import 12 | 13 | NSString * const BottomSheetDemoFruits = @"Fruits"; 14 | NSString * const BottomSheetDemoVegetables = @"Vegetables"; 15 | 16 | @interface BottomSheetDemoSelectionCell : UITableViewCell 17 | 18 | @property (nonatomic, strong) IBOutlet UIScrollView *scrollView; 19 | @property (nonatomic, strong) IBOutlet UILabel *placeholderLabel; 20 | @property (nonatomic, strong) NSArray *selections; 21 | 22 | @end 23 | 24 | @implementation BottomSheetDemoSelectionCell 25 | { 26 | NSArray *_buttons; 27 | } 28 | 29 | - (void)setSelections:(NSArray *)selections 30 | { 31 | selections = [selections sortedArrayUsingSelector:@selector(localizedCompare:)]; 32 | _selections = selections; 33 | [_buttons makeObjectsPerformSelector:@selector(removeFromSuperview)]; 34 | 35 | self.placeholderLabel.hidden = selections.count > 0; 36 | 37 | CGFloat buttonX = 15; 38 | NSMutableArray *buttons = [NSMutableArray new]; 39 | for (NSString *selection in selections) { 40 | UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem]; 41 | button.layer.cornerRadius = 4; 42 | button.backgroundColor = button.tintColor; 43 | button.contentEdgeInsets = UIEdgeInsetsMake(5, 10, 5, 10); 44 | [button setTitle:selection forState:UIControlStateNormal]; 45 | [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 46 | [button sizeToFit]; 47 | button.frame = CGRectMake(buttonX, (self.scrollView.frame.size.height - button.frame.size.height) / 2, button.frame.size.width, button.frame.size.height); 48 | 49 | [buttons addObject:button]; 50 | [self.scrollView addSubview:button]; 51 | 52 | buttonX += button.frame.size.width + 10; 53 | } 54 | self.scrollView.contentSize = CGSizeMake(buttonX, self.scrollView.frame.size.height); 55 | 56 | _buttons = [NSArray arrayWithArray:buttons]; 57 | } 58 | 59 | @end 60 | 61 | @interface BottomSheetDemoViewController () 62 | 63 | @end 64 | 65 | @implementation BottomSheetDemoViewController 66 | { 67 | NSArray *_fruitsSelections; 68 | NSArray *_vegetablesSelections; 69 | } 70 | 71 | - (void)multiSelectionViewController:(MultiSelectionViewController *)vc didFinishWithSelections:(NSArray *)selections 72 | { 73 | if ([vc.title isEqualToString:BottomSheetDemoFruits]) { 74 | _fruitsSelections = selections; 75 | } 76 | else if ([vc.title isEqualToString:BottomSheetDemoVegetables]) { 77 | _vegetablesSelections = selections; 78 | } 79 | [self.tableView reloadData]; 80 | } 81 | 82 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 83 | { 84 | UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath]; 85 | if ([cell isKindOfClass:[BottomSheetDemoSelectionCell class]]) { 86 | BottomSheetDemoSelectionCell *selectionCell = (BottomSheetDemoSelectionCell *)cell; 87 | selectionCell.selections = [[NSArray arrayWithArray:_fruitsSelections] arrayByAddingObjectsFromArray:_vegetablesSelections]; 88 | } 89 | return cell; 90 | } 91 | 92 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 93 | { 94 | MultiSelectionViewController *destinationViewController = (MultiSelectionViewController *)segue.destinationViewController; 95 | destinationViewController.delegate = self; 96 | if ([segue.identifier isEqualToString:@"FruitsSegue"]) { 97 | destinationViewController.title = BottomSheetDemoFruits; 98 | destinationViewController.items = @[ @"Apples", @"Oranges", @"Grapes", @"Strawberries", @"Bananas", @"Lemons" ]; 99 | destinationViewController.defaultSelections = _fruitsSelections; 100 | } 101 | else if ([segue.identifier isEqualToString:@"VegetablesSegue"]) { 102 | destinationViewController.title = BottomSheetDemoVegetables; 103 | destinationViewController.items = @[ @"Cabbage", @"Turnip", @"Radish", @"Carrot", @"Lettuce", @"Potato", @"Tomato" ]; 104 | destinationViewController.defaultSelections = _vegetablesSelections; 105 | } 106 | } 107 | 108 | @end 109 | -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/BulletinBoardViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BulletinBoardViewController.h 3 | // STPopupExample 4 | // 5 | // Created by Kevin Lin on 18/05/2019. 6 | // Copyright © 2019 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface BulletinBoardViewController : UIViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/BulletinBoardViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // BulletinBoardViewController.m 3 | // STPopupExample 4 | // 5 | // Created by Kevin Lin on 18/05/2019. 6 | // Copyright © 2019 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "BulletinBoardViewController.h" 10 | 11 | #import 12 | 13 | @interface BulletinBoardViewController () 14 | 15 | @end 16 | 17 | @implementation BulletinBoardViewController 18 | 19 | - (void)awakeFromNib 20 | { 21 | [super awakeFromNib]; 22 | CGFloat margin = 20; 23 | self.contentSizeInPopup = CGSizeMake([UIScreen mainScreen].bounds.size.width - margin * 2, 480); 24 | self.view.layer.cornerRadius = 20; 25 | } 26 | 27 | - (void)viewWillAppear:(BOOL)animated 28 | { 29 | [super viewWillAppear:animated]; 30 | self.popupController.navigationBarHidden = YES; 31 | } 32 | 33 | - (IBAction)allowButtonDidTap 34 | { 35 | [self.popupController dismiss]; 36 | } 37 | 38 | - (IBAction)closeButtonDidTap 39 | { 40 | [self.popupController dismiss]; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/Images.xcassets/CloseIcon.imageset/2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin-lyn/STPopup/8c5e7c184bdfae48995a04f758dd8c55ec9b02a9/STPopupExample/STPopupExample/Images.xcassets/CloseIcon.imageset/2x.png -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/Images.xcassets/CloseIcon.imageset/3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin-lyn/STPopup/8c5e7c184bdfae48995a04f758dd8c55ec9b02a9/STPopupExample/STPopupExample/Images.xcassets/CloseIcon.imageset/3x.png -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/Images.xcassets/CloseIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | }, 22 | "properties" : { 23 | "template-rendering-intent" : "template" 24 | } 25 | } -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/Images.xcassets/LocationIcon.imageset/2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin-lyn/STPopup/8c5e7c184bdfae48995a04f758dd8c55ec9b02a9/STPopupExample/STPopupExample/Images.xcassets/LocationIcon.imageset/2x.png -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/Images.xcassets/LocationIcon.imageset/3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin-lyn/STPopup/8c5e7c184bdfae48995a04f758dd8c55ec9b02a9/STPopupExample/STPopupExample/Images.xcassets/LocationIcon.imageset/3x.png -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/Images.xcassets/LocationIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | UIInterfaceOrientationPortraitUpsideDown 39 | 40 | UISupportedInterfaceOrientations~ipad 41 | 42 | UIInterfaceOrientationPortrait 43 | UIInterfaceOrientationPortraitUpsideDown 44 | UIInterfaceOrientationLandscapeLeft 45 | UIInterfaceOrientationLandscapeRight 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/MultiSelectionViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // MultiSelectionViewController.h 3 | // STPopupExample 4 | // 5 | // Created by Kevin Lin on 11/10/15. 6 | // Copyright © 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class MultiSelectionViewController; 12 | 13 | @protocol MultiSelectionViewControllerDelegate 14 | 15 | - (void)multiSelectionViewController:(MultiSelectionViewController *)vc didFinishWithSelections:(NSArray *)selections; 16 | 17 | @end 18 | 19 | @interface MultiSelectionViewController : UITableViewController 20 | 21 | @property (nonatomic, weak) id delegate; 22 | @property (nonatomic, strong) NSArray *items; 23 | @property (nonatomic, strong) NSArray *defaultSelections; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/MultiSelectionViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // MultiSelectionViewController.m 3 | // STPopupExample 4 | // 5 | // Created by Kevin Lin on 11/10/15. 6 | // Copyright © 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "MultiSelectionViewController.h" 10 | #import 11 | 12 | @implementation MultiSelectionViewController 13 | { 14 | NSMutableSet *_mutableSelections; 15 | } 16 | 17 | - (IBAction)done:(id)sender 18 | { 19 | if ([self.delegate respondsToSelector:@selector(multiSelectionViewController:didFinishWithSelections:)]) { 20 | [self.delegate multiSelectionViewController:self didFinishWithSelections:_mutableSelections.allObjects]; 21 | } 22 | [self.popupController popViewControllerAnimated:YES]; 23 | } 24 | 25 | - (void)setDefaultSelections:(NSArray *)defaultSelections 26 | { 27 | _defaultSelections = defaultSelections; 28 | _mutableSelections = [NSMutableSet setWithArray:defaultSelections]; 29 | } 30 | 31 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 32 | { 33 | return self.items.count; 34 | } 35 | 36 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 37 | { 38 | NSString *cellId = @"Multi-Selection Cell"; 39 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId]; 40 | if (!cell) { 41 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId]; 42 | } 43 | 44 | NSString *item = self.items[indexPath.row]; 45 | 46 | cell.textLabel.text = item; 47 | cell.accessoryType = [_mutableSelections containsObject:item] ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone; 48 | return cell; 49 | } 50 | 51 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 52 | { 53 | if (!_mutableSelections) { 54 | _mutableSelections = [NSMutableSet new]; 55 | } 56 | 57 | NSString *item = self.items[indexPath.row]; 58 | if (![_mutableSelections containsObject:item]) { 59 | [_mutableSelections addObject:item]; 60 | } 61 | else { 62 | [_mutableSelections removeObject:item]; 63 | } 64 | [tableView reloadData]; 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/PopupViewController1.h: -------------------------------------------------------------------------------- 1 | // 2 | // PopupViewController1.h 3 | // STPopupExample 4 | // 5 | // Created by Kevin Lin on 11/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PopupViewController1 : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/PopupViewController1.m: -------------------------------------------------------------------------------- 1 | // 2 | // PopupViewController1.m 3 | // STPopupExample 4 | // 5 | // Created by Kevin Lin on 11/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "PopupViewController1.h" 10 | #import "PopupViewController2.h" 11 | #import 12 | 13 | @implementation PopupViewController1 14 | { 15 | UILabel *_label; 16 | } 17 | 18 | - (instancetype)init 19 | { 20 | if (self = [super init]) { 21 | self.title = @"Apple"; 22 | self.contentSizeInPopup = CGSizeMake(300, 400); 23 | self.landscapeContentSizeInPopup = CGSizeMake(400, 200); 24 | } 25 | return self; 26 | } 27 | 28 | - (void)viewDidLoad 29 | { 30 | [super viewDidLoad]; 31 | 32 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Next" style:UIBarButtonItemStylePlain target:self action:@selector(nextBtnDidTap)]; 33 | 34 | _label = [UILabel new]; 35 | _label.numberOfLines = 0; 36 | _label.text = @"Apple Inc. (commonly known as Apple) is an American multinational technology company headquartered in Cupertino, California, that designs, develops, and sells consumer electronics, computer software, and online services. Its best-known hardware products are the Mac personal computers, the iPod portable media player, the iPhone smartphone, the iPad tablet computer, and the Apple Watch smartwatch. Apple's consumer software includes the OS X and iOS operating systems, the iTunes media player, the Safari web browser, and the iLife and iWork creativity and productivity suites. Its online services include the iTunes Store, the iOS App Store and Mac App Store, and iCloud."; 37 | [self.view addSubview:_label]; 38 | } 39 | 40 | - (void)viewDidLayoutSubviews 41 | { 42 | [super viewDidLayoutSubviews]; 43 | _label.frame = CGRectMake(20, 10, self.view.frame.size.width - 40, self.view.frame.size.height - 20); 44 | } 45 | 46 | - (void)nextBtnDidTap 47 | { 48 | [self.popupController pushViewController:[[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"PopupViewController2"] animated:YES]; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/PopupViewController2.h: -------------------------------------------------------------------------------- 1 | // 2 | // PopupViewController2.h 3 | // STPopupExample 4 | // 5 | // Created by Kevin Lin on 11/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PopupViewController2 : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/PopupViewController2.m: -------------------------------------------------------------------------------- 1 | // 2 | // PopupViewController2.m 3 | // STPopupExample 4 | // 5 | // Created by Kevin Lin on 11/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "PopupViewController2.h" 10 | #import "PopupViewController3.h" 11 | #import 12 | 13 | @implementation PopupViewController2 14 | 15 | - (void)awakeFromNib 16 | { 17 | [super awakeFromNib]; 18 | self.contentSizeInPopup = CGSizeMake(300, 200); 19 | self.landscapeContentSizeInPopup = CGSizeMake(400, 200); 20 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Next" style:UIBarButtonItemStylePlain target:self action:@selector(nextBtnDidTap)]; 21 | } 22 | 23 | - (IBAction)nextBtnDidTap 24 | { 25 | [self.popupController pushViewController:[PopupViewController3 new] animated:YES]; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/PopupViewController3.h: -------------------------------------------------------------------------------- 1 | // 2 | // PopupViewController3.h 3 | // STPopupExample 4 | // 5 | // Created by Kevin Lin on 13/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PopupViewController3 : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/PopupViewController3.m: -------------------------------------------------------------------------------- 1 | // 2 | // PopupViewController3.m 3 | // STPopupExample 4 | // 5 | // Created by Kevin Lin on 13/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "PopupViewController3.h" 10 | #import 11 | 12 | @implementation PopupViewController3 13 | { 14 | UILabel *_label; 15 | UIView *_separatorView; 16 | UITextField *_textField; 17 | } 18 | 19 | - (instancetype)init 20 | { 21 | if (self = [super init]) { 22 | self.title = @"Keyboard"; 23 | self.contentSizeInPopup = CGSizeMake(300, 400); 24 | self.landscapeContentSizeInPopup = CGSizeMake(400, 200); 25 | } 26 | return self; 27 | } 28 | 29 | - (void)viewDidLoad 30 | { 31 | [super viewDidLoad]; 32 | 33 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Image" style:UIBarButtonItemStylePlain target:self action:@selector(imageBtnDidTap)]; 34 | 35 | _label = [UILabel new]; 36 | _label.numberOfLines = 0; 37 | _label.text = @"Popup view will be adjusted to appropriate position if keyboard is shown"; 38 | _label.textColor = [UIColor colorWithWhite:0.8 alpha:1]; 39 | _label.textAlignment = NSTextAlignmentCenter; 40 | [self.view addSubview:_label]; 41 | 42 | _separatorView = [UIView new]; 43 | _separatorView.backgroundColor = [UIColor colorWithWhite:0.8 alpha:1]; 44 | [self.view addSubview:_separatorView]; 45 | 46 | _textField = [UITextField new]; 47 | _textField.placeholder = @"Tap to input"; 48 | _textField.leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 0)]; 49 | _textField.leftViewMode = UITextFieldViewModeAlways; 50 | [self.view addSubview:_textField]; 51 | } 52 | 53 | - (void)viewDidLayoutSubviews 54 | { 55 | [super viewDidLayoutSubviews]; 56 | 57 | _textField.frame = CGRectMake(0, self.view.frame.size.height - 44, self.view.frame.size.width, 44); 58 | _separatorView.frame = CGRectMake(0, _textField.frame.origin.y - 0.5, self.view.frame.size.width, 0.5); 59 | _label.frame = CGRectMake(20, 10, self.view.frame.size.width - 40, self.view.frame.size.height - 20 - _textField.frame.size.height); 60 | } 61 | 62 | - (void)imageBtnDidTap 63 | { 64 | UIImagePickerController *imagePickerViewController = [UIImagePickerController new]; 65 | [self presentViewController:imagePickerViewController animated:YES completion:nil]; 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // STPopupExample 4 | // 5 | // Created by Kevin Lin on 11/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UITableViewController 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // STPopupExample 4 | // 5 | // Created by Kevin Lin on 11/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "PopupViewController1.h" 11 | #import 12 | 13 | @interface ViewController () 14 | 15 | @end 16 | 17 | @implementation ViewController 18 | 19 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 20 | { 21 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 22 | switch (indexPath.row) { 23 | case 0: { 24 | STPopupController *popupController = [[STPopupController alloc] initWithRootViewController:[PopupViewController1 new]]; 25 | popupController.containerView.layer.cornerRadius = 4; 26 | [popupController presentInViewController:self]; 27 | } 28 | break; 29 | case 1: { 30 | STPopupController *popupController = [[STPopupController alloc] initWithRootViewController:[PopupViewController1 new]]; 31 | popupController.containerView.layer.cornerRadius = 4; 32 | popupController.transitionStyle = STPopupTransitionStyleFade; 33 | [popupController presentInViewController:self]; 34 | } 35 | break; 36 | case 2: { 37 | STPopupController *popupController = [[STPopupController alloc] initWithRootViewController:[[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"PopupViewController2"]]; 38 | popupController.containerView.layer.cornerRadius = 4; 39 | [popupController presentInViewController:self]; 40 | } 41 | break; 42 | case 3: { 43 | [STPopupNavigationBar appearance].barTintColor = [UIColor colorWithRed:0.20 green:0.60 blue:0.86 alpha:1.0]; 44 | [STPopupNavigationBar appearance].tintColor = [UIColor whiteColor]; 45 | [STPopupNavigationBar appearance].barStyle = UIBarStyleDefault; 46 | [STPopupNavigationBar appearance].titleTextAttributes = @{ NSFontAttributeName: [UIFont fontWithName:@"Cochin" size:18], 47 | NSForegroundColorAttributeName: [UIColor whiteColor] }; 48 | 49 | [[UIBarButtonItem appearanceWhenContainedIn:[STPopupNavigationBar class], nil] setTitleTextAttributes:@{ NSFontAttributeName:[UIFont fontWithName:@"Cochin" size:17] } forState:UIControlStateNormal]; 50 | 51 | STPopupController *popupController = [[STPopupController alloc] initWithRootViewController:[PopupViewController1 new]]; 52 | popupController.containerView.layer.cornerRadius = 4; 53 | [popupController presentInViewController:self]; 54 | } 55 | break; 56 | case 4: { 57 | STPopupController *popupController = [[STPopupController alloc] initWithRootViewController:[PopupViewController1 new]]; 58 | popupController.containerView.layer.cornerRadius = 4; 59 | if (NSClassFromString(@"UIBlurEffect")) { 60 | UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]; 61 | popupController.backgroundView = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; 62 | popupController.backgroundView.alpha = 0.8; // This is not necessary 63 | } 64 | [popupController presentInViewController:self]; 65 | } 66 | break; 67 | case 5: { 68 | STPopupController *popupController = [[STPopupController alloc] initWithRootViewController:[[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"BottomSheetDemoViewController"]]; 69 | popupController.style = STPopupStyleBottomSheet; 70 | [popupController presentInViewController:self]; 71 | } 72 | break; 73 | case 6: { 74 | STPopupController *popupController = [[STPopupController alloc] initWithRootViewController:[PopupViewController1 new]]; 75 | popupController.transitionStyle = STPopupTransitionStyleCustom; 76 | popupController.transitioning = self; 77 | popupController.containerView.layer.cornerRadius = 4; 78 | [popupController presentInViewController:self]; 79 | } 80 | break; 81 | case 7: { 82 | STPopupController *popupController = [[STPopupController alloc] initWithRootViewController:[[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"BulletinBoardViewController"]]; 83 | popupController.style = STPopupStyleBottomSheet; 84 | popupController.containerView.backgroundColor = [UIColor clearColor]; 85 | [popupController presentInViewController:self]; 86 | } 87 | break; 88 | default: 89 | break; 90 | } 91 | } 92 | 93 | #pragma mark - STPopupControllerTransitioning 94 | 95 | - (NSTimeInterval)popupControllerTransitionDuration:(STPopupControllerTransitioningContext *)context 96 | { 97 | return context.action == STPopupControllerTransitioningActionPresent ? 0.5 : 0.35; 98 | } 99 | 100 | - (void)popupControllerAnimateTransition:(STPopupControllerTransitioningContext *)context completion:(void (^)(void))completion 101 | { 102 | UIView *containerView = context.containerView; 103 | if (context.action == STPopupControllerTransitioningActionPresent) { 104 | containerView.transform = CGAffineTransformMakeTranslation(containerView.superview.bounds.size.width - containerView.frame.origin.x, 0); 105 | 106 | [UIView animateWithDuration:[self popupControllerTransitionDuration:context] delay:0 usingSpringWithDamping:1 initialSpringVelocity:1 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 107 | context.containerView.transform = CGAffineTransformIdentity; 108 | } completion:^(BOOL finished) { 109 | completion(); 110 | }]; 111 | } 112 | else { 113 | [UIView animateWithDuration:[self popupControllerTransitionDuration:context] delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ 114 | containerView.transform = CGAffineTransformMakeTranslation(- 2 * (containerView.superview.bounds.size.width - containerView.frame.origin.x), 0); 115 | } completion:^(BOOL finished) { 116 | containerView.transform = CGAffineTransformIdentity; 117 | completion(); 118 | }]; 119 | } 120 | } 121 | 122 | @end 123 | -------------------------------------------------------------------------------- /STPopupExample/STPopupExample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // STPopup 4 | // 5 | // Created by Kevin Lin on 11/9/15. 6 | // Copyright (c) 2015 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /STPopupSwiftUIExample/STPopupSwiftUIExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 76593B17275137A900D1877D /* STPopupSwiftUIExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76593B16275137A900D1877D /* STPopupSwiftUIExampleApp.swift */; }; 11 | 76593B19275137A900D1877D /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76593B18275137A900D1877D /* ContentView.swift */; }; 12 | 76593B1B275137A900D1877D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 76593B1A275137A900D1877D /* Assets.xcassets */; }; 13 | 76593B1E275137A900D1877D /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 76593B1D275137A900D1877D /* Preview Assets.xcassets */; }; 14 | 76593B2B275137D500D1877D /* STPopup.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76593B29275137CE00D1877D /* STPopup.framework */; }; 15 | 76593B2C275137D500D1877D /* STPopup.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 76593B29275137CE00D1877D /* STPopup.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXContainerItemProxy section */ 19 | 76593B28275137CE00D1877D /* PBXContainerItemProxy */ = { 20 | isa = PBXContainerItemProxy; 21 | containerPortal = 76593B24275137CE00D1877D /* STPopup.xcodeproj */; 22 | proxyType = 2; 23 | remoteGlobalIDString = 76D822381C1DB5E400FCF988; 24 | remoteInfo = STPopup; 25 | }; 26 | /* End PBXContainerItemProxy section */ 27 | 28 | /* Begin PBXCopyFilesBuildPhase section */ 29 | 76593B2D275137D500D1877D /* Embed Frameworks */ = { 30 | isa = PBXCopyFilesBuildPhase; 31 | buildActionMask = 2147483647; 32 | dstPath = ""; 33 | dstSubfolderSpec = 10; 34 | files = ( 35 | 76593B2C275137D500D1877D /* STPopup.framework in Embed Frameworks */, 36 | ); 37 | name = "Embed Frameworks"; 38 | runOnlyForDeploymentPostprocessing = 0; 39 | }; 40 | /* End PBXCopyFilesBuildPhase section */ 41 | 42 | /* Begin PBXFileReference section */ 43 | 76593B13275137A900D1877D /* STPopupSwiftUIExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = STPopupSwiftUIExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | 76593B16275137A900D1877D /* STPopupSwiftUIExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPopupSwiftUIExampleApp.swift; sourceTree = ""; }; 45 | 76593B18275137A900D1877D /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 46 | 76593B1A275137A900D1877D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 47 | 76593B1D275137A900D1877D /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 48 | 76593B24275137CE00D1877D /* STPopup.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = STPopup.xcodeproj; path = ../STPopup.xcodeproj; sourceTree = ""; }; 49 | /* End PBXFileReference section */ 50 | 51 | /* Begin PBXFrameworksBuildPhase section */ 52 | 76593B10275137A900D1877D /* Frameworks */ = { 53 | isa = PBXFrameworksBuildPhase; 54 | buildActionMask = 2147483647; 55 | files = ( 56 | 76593B2B275137D500D1877D /* STPopup.framework in Frameworks */, 57 | ); 58 | runOnlyForDeploymentPostprocessing = 0; 59 | }; 60 | /* End PBXFrameworksBuildPhase section */ 61 | 62 | /* Begin PBXGroup section */ 63 | 76593B0A275137A900D1877D = { 64 | isa = PBXGroup; 65 | children = ( 66 | 76593B24275137CE00D1877D /* STPopup.xcodeproj */, 67 | 76593B15275137A900D1877D /* STPopupSwiftUIExample */, 68 | 76593B14275137A900D1877D /* Products */, 69 | 76593B2A275137D500D1877D /* Frameworks */, 70 | ); 71 | sourceTree = ""; 72 | }; 73 | 76593B14275137A900D1877D /* Products */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | 76593B13275137A900D1877D /* STPopupSwiftUIExample.app */, 77 | ); 78 | name = Products; 79 | sourceTree = ""; 80 | }; 81 | 76593B15275137A900D1877D /* STPopupSwiftUIExample */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | 76593B16275137A900D1877D /* STPopupSwiftUIExampleApp.swift */, 85 | 76593B18275137A900D1877D /* ContentView.swift */, 86 | 76593B1A275137A900D1877D /* Assets.xcassets */, 87 | 76593B1C275137A900D1877D /* Preview Content */, 88 | ); 89 | path = STPopupSwiftUIExample; 90 | sourceTree = ""; 91 | }; 92 | 76593B1C275137A900D1877D /* Preview Content */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 76593B1D275137A900D1877D /* Preview Assets.xcassets */, 96 | ); 97 | path = "Preview Content"; 98 | sourceTree = ""; 99 | }; 100 | 76593B25275137CE00D1877D /* Products */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 76593B29275137CE00D1877D /* STPopup.framework */, 104 | ); 105 | name = Products; 106 | sourceTree = ""; 107 | }; 108 | 76593B2A275137D500D1877D /* Frameworks */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | ); 112 | name = Frameworks; 113 | sourceTree = ""; 114 | }; 115 | /* End PBXGroup section */ 116 | 117 | /* Begin PBXNativeTarget section */ 118 | 76593B12275137A900D1877D /* STPopupSwiftUIExample */ = { 119 | isa = PBXNativeTarget; 120 | buildConfigurationList = 76593B21275137A900D1877D /* Build configuration list for PBXNativeTarget "STPopupSwiftUIExample" */; 121 | buildPhases = ( 122 | 76593B0F275137A900D1877D /* Sources */, 123 | 76593B10275137A900D1877D /* Frameworks */, 124 | 76593B11275137A900D1877D /* Resources */, 125 | 76593B2D275137D500D1877D /* Embed Frameworks */, 126 | ); 127 | buildRules = ( 128 | ); 129 | dependencies = ( 130 | ); 131 | name = STPopupSwiftUIExample; 132 | productName = STPopupSwiftUIExample; 133 | productReference = 76593B13275137A900D1877D /* STPopupSwiftUIExample.app */; 134 | productType = "com.apple.product-type.application"; 135 | }; 136 | /* End PBXNativeTarget section */ 137 | 138 | /* Begin PBXProject section */ 139 | 76593B0B275137A900D1877D /* Project object */ = { 140 | isa = PBXProject; 141 | attributes = { 142 | BuildIndependentTargetsInParallel = 1; 143 | LastSwiftUpdateCheck = 1310; 144 | LastUpgradeCheck = 1310; 145 | TargetAttributes = { 146 | 76593B12275137A900D1877D = { 147 | CreatedOnToolsVersion = 13.1; 148 | }; 149 | }; 150 | }; 151 | buildConfigurationList = 76593B0E275137A900D1877D /* Build configuration list for PBXProject "STPopupSwiftUIExample" */; 152 | compatibilityVersion = "Xcode 13.0"; 153 | developmentRegion = en; 154 | hasScannedForEncodings = 0; 155 | knownRegions = ( 156 | en, 157 | Base, 158 | ); 159 | mainGroup = 76593B0A275137A900D1877D; 160 | productRefGroup = 76593B14275137A900D1877D /* Products */; 161 | projectDirPath = ""; 162 | projectReferences = ( 163 | { 164 | ProductGroup = 76593B25275137CE00D1877D /* Products */; 165 | ProjectRef = 76593B24275137CE00D1877D /* STPopup.xcodeproj */; 166 | }, 167 | ); 168 | projectRoot = ""; 169 | targets = ( 170 | 76593B12275137A900D1877D /* STPopupSwiftUIExample */, 171 | ); 172 | }; 173 | /* End PBXProject section */ 174 | 175 | /* Begin PBXReferenceProxy section */ 176 | 76593B29275137CE00D1877D /* STPopup.framework */ = { 177 | isa = PBXReferenceProxy; 178 | fileType = wrapper.framework; 179 | path = STPopup.framework; 180 | remoteRef = 76593B28275137CE00D1877D /* PBXContainerItemProxy */; 181 | sourceTree = BUILT_PRODUCTS_DIR; 182 | }; 183 | /* End PBXReferenceProxy section */ 184 | 185 | /* Begin PBXResourcesBuildPhase section */ 186 | 76593B11275137A900D1877D /* Resources */ = { 187 | isa = PBXResourcesBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | 76593B1E275137A900D1877D /* Preview Assets.xcassets in Resources */, 191 | 76593B1B275137A900D1877D /* Assets.xcassets in Resources */, 192 | ); 193 | runOnlyForDeploymentPostprocessing = 0; 194 | }; 195 | /* End PBXResourcesBuildPhase section */ 196 | 197 | /* Begin PBXSourcesBuildPhase section */ 198 | 76593B0F275137A900D1877D /* Sources */ = { 199 | isa = PBXSourcesBuildPhase; 200 | buildActionMask = 2147483647; 201 | files = ( 202 | 76593B19275137A900D1877D /* ContentView.swift in Sources */, 203 | 76593B17275137A900D1877D /* STPopupSwiftUIExampleApp.swift in Sources */, 204 | ); 205 | runOnlyForDeploymentPostprocessing = 0; 206 | }; 207 | /* End PBXSourcesBuildPhase section */ 208 | 209 | /* Begin XCBuildConfiguration section */ 210 | 76593B1F275137A900D1877D /* Debug */ = { 211 | isa = XCBuildConfiguration; 212 | buildSettings = { 213 | ALWAYS_SEARCH_USER_PATHS = NO; 214 | CLANG_ANALYZER_NONNULL = YES; 215 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 216 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 217 | CLANG_CXX_LIBRARY = "libc++"; 218 | CLANG_ENABLE_MODULES = YES; 219 | CLANG_ENABLE_OBJC_ARC = YES; 220 | CLANG_ENABLE_OBJC_WEAK = YES; 221 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 222 | CLANG_WARN_BOOL_CONVERSION = YES; 223 | CLANG_WARN_COMMA = YES; 224 | CLANG_WARN_CONSTANT_CONVERSION = YES; 225 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 226 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 227 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 228 | CLANG_WARN_EMPTY_BODY = YES; 229 | CLANG_WARN_ENUM_CONVERSION = YES; 230 | CLANG_WARN_INFINITE_RECURSION = YES; 231 | CLANG_WARN_INT_CONVERSION = YES; 232 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 233 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 234 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 235 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 236 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 237 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 238 | CLANG_WARN_STRICT_PROTOTYPES = YES; 239 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 240 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 241 | CLANG_WARN_UNREACHABLE_CODE = YES; 242 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 243 | COPY_PHASE_STRIP = NO; 244 | DEBUG_INFORMATION_FORMAT = dwarf; 245 | ENABLE_STRICT_OBJC_MSGSEND = YES; 246 | ENABLE_TESTABILITY = YES; 247 | GCC_C_LANGUAGE_STANDARD = gnu11; 248 | GCC_DYNAMIC_NO_PIC = NO; 249 | GCC_NO_COMMON_BLOCKS = YES; 250 | GCC_OPTIMIZATION_LEVEL = 0; 251 | GCC_PREPROCESSOR_DEFINITIONS = ( 252 | "DEBUG=1", 253 | "$(inherited)", 254 | ); 255 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 256 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 257 | GCC_WARN_UNDECLARED_SELECTOR = YES; 258 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 259 | GCC_WARN_UNUSED_FUNCTION = YES; 260 | GCC_WARN_UNUSED_VARIABLE = YES; 261 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 262 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 263 | MTL_FAST_MATH = YES; 264 | ONLY_ACTIVE_ARCH = YES; 265 | SDKROOT = iphoneos; 266 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 267 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 268 | }; 269 | name = Debug; 270 | }; 271 | 76593B20275137A900D1877D /* Release */ = { 272 | isa = XCBuildConfiguration; 273 | buildSettings = { 274 | ALWAYS_SEARCH_USER_PATHS = NO; 275 | CLANG_ANALYZER_NONNULL = YES; 276 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 277 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 278 | CLANG_CXX_LIBRARY = "libc++"; 279 | CLANG_ENABLE_MODULES = YES; 280 | CLANG_ENABLE_OBJC_ARC = YES; 281 | CLANG_ENABLE_OBJC_WEAK = YES; 282 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 283 | CLANG_WARN_BOOL_CONVERSION = YES; 284 | CLANG_WARN_COMMA = YES; 285 | CLANG_WARN_CONSTANT_CONVERSION = YES; 286 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 287 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 288 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 289 | CLANG_WARN_EMPTY_BODY = YES; 290 | CLANG_WARN_ENUM_CONVERSION = YES; 291 | CLANG_WARN_INFINITE_RECURSION = YES; 292 | CLANG_WARN_INT_CONVERSION = YES; 293 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 294 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 295 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 296 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 297 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 298 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 299 | CLANG_WARN_STRICT_PROTOTYPES = YES; 300 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 301 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 302 | CLANG_WARN_UNREACHABLE_CODE = YES; 303 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 304 | COPY_PHASE_STRIP = NO; 305 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 306 | ENABLE_NS_ASSERTIONS = NO; 307 | ENABLE_STRICT_OBJC_MSGSEND = YES; 308 | GCC_C_LANGUAGE_STANDARD = gnu11; 309 | GCC_NO_COMMON_BLOCKS = YES; 310 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 311 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 312 | GCC_WARN_UNDECLARED_SELECTOR = YES; 313 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 314 | GCC_WARN_UNUSED_FUNCTION = YES; 315 | GCC_WARN_UNUSED_VARIABLE = YES; 316 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 317 | MTL_ENABLE_DEBUG_INFO = NO; 318 | MTL_FAST_MATH = YES; 319 | SDKROOT = iphoneos; 320 | SWIFT_COMPILATION_MODE = wholemodule; 321 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 322 | VALIDATE_PRODUCT = YES; 323 | }; 324 | name = Release; 325 | }; 326 | 76593B22275137A900D1877D /* Debug */ = { 327 | isa = XCBuildConfiguration; 328 | buildSettings = { 329 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 330 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 331 | CODE_SIGN_STYLE = Automatic; 332 | CURRENT_PROJECT_VERSION = 1; 333 | DEVELOPMENT_ASSET_PATHS = "\"STPopupSwiftUIExample/Preview Content\""; 334 | ENABLE_PREVIEWS = YES; 335 | GENERATE_INFOPLIST_FILE = YES; 336 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 337 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 338 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 339 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 340 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 341 | LD_RUNPATH_SEARCH_PATHS = ( 342 | "$(inherited)", 343 | "@executable_path/Frameworks", 344 | ); 345 | MARKETING_VERSION = 1.0; 346 | PRODUCT_BUNDLE_IDENTIFIER = me.sth4.STPopupSwiftUIExample.STPopupSwiftUIExample; 347 | PRODUCT_NAME = "$(TARGET_NAME)"; 348 | SWIFT_EMIT_LOC_STRINGS = YES; 349 | SWIFT_VERSION = 5.0; 350 | TARGETED_DEVICE_FAMILY = "1,2"; 351 | }; 352 | name = Debug; 353 | }; 354 | 76593B23275137A900D1877D /* Release */ = { 355 | isa = XCBuildConfiguration; 356 | buildSettings = { 357 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 358 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 359 | CODE_SIGN_STYLE = Automatic; 360 | CURRENT_PROJECT_VERSION = 1; 361 | DEVELOPMENT_ASSET_PATHS = "\"STPopupSwiftUIExample/Preview Content\""; 362 | ENABLE_PREVIEWS = YES; 363 | GENERATE_INFOPLIST_FILE = YES; 364 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 365 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 366 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 367 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 368 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 369 | LD_RUNPATH_SEARCH_PATHS = ( 370 | "$(inherited)", 371 | "@executable_path/Frameworks", 372 | ); 373 | MARKETING_VERSION = 1.0; 374 | PRODUCT_BUNDLE_IDENTIFIER = me.sth4.STPopupSwiftUIExample.STPopupSwiftUIExample; 375 | PRODUCT_NAME = "$(TARGET_NAME)"; 376 | SWIFT_EMIT_LOC_STRINGS = YES; 377 | SWIFT_VERSION = 5.0; 378 | TARGETED_DEVICE_FAMILY = "1,2"; 379 | }; 380 | name = Release; 381 | }; 382 | /* End XCBuildConfiguration section */ 383 | 384 | /* Begin XCConfigurationList section */ 385 | 76593B0E275137A900D1877D /* Build configuration list for PBXProject "STPopupSwiftUIExample" */ = { 386 | isa = XCConfigurationList; 387 | buildConfigurations = ( 388 | 76593B1F275137A900D1877D /* Debug */, 389 | 76593B20275137A900D1877D /* Release */, 390 | ); 391 | defaultConfigurationIsVisible = 0; 392 | defaultConfigurationName = Release; 393 | }; 394 | 76593B21275137A900D1877D /* Build configuration list for PBXNativeTarget "STPopupSwiftUIExample" */ = { 395 | isa = XCConfigurationList; 396 | buildConfigurations = ( 397 | 76593B22275137A900D1877D /* Debug */, 398 | 76593B23275137A900D1877D /* Release */, 399 | ); 400 | defaultConfigurationIsVisible = 0; 401 | defaultConfigurationName = Release; 402 | }; 403 | /* End XCConfigurationList section */ 404 | }; 405 | rootObject = 76593B0B275137A900D1877D /* Project object */; 406 | } 407 | -------------------------------------------------------------------------------- /STPopupSwiftUIExample/STPopupSwiftUIExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /STPopupSwiftUIExample/STPopupSwiftUIExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /STPopupSwiftUIExample/STPopupSwiftUIExample/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /STPopupSwiftUIExample/STPopupSwiftUIExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /STPopupSwiftUIExample/STPopupSwiftUIExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /STPopupSwiftUIExample/STPopupSwiftUIExample/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // STPopupSwiftUIExample 4 | // 5 | // Created by Kevin Lin on 26/11/2021. 6 | // 7 | 8 | import SwiftUI 9 | import STPopup 10 | 11 | struct ContentView: View { 12 | @State private var isPresentingFormSheetPopup = false 13 | @State private var isPresentingBottomSheetPopup = false 14 | @State private var showsAdditionalMessage = false 15 | 16 | var body: some View { 17 | NavigationView { 18 | List { 19 | Button("Show Popup(style = .formSheet)") { 20 | isPresentingFormSheetPopup = true 21 | } 22 | Button("Show Popup(style = .bottomSheet)") { 23 | isPresentingBottomSheetPopup = true 24 | } 25 | } 26 | .listStyle(.plain) 27 | .popup(isPresented: $isPresentingFormSheetPopup) { 28 | popupContent { 29 | isPresentingFormSheetPopup = false 30 | } 31 | .padding(.leading) 32 | .padding(.trailing) 33 | .background(Color.white) 34 | .cornerRadius(10) 35 | } 36 | .popup(isPresented: $isPresentingBottomSheetPopup, style: .bottomSheet) { 37 | VStack { 38 | HStack { 39 | Spacer() 40 | } 41 | popupContent { 42 | isPresentingBottomSheetPopup = false 43 | } 44 | } 45 | .background( 46 | VStack { 47 | HStack { 48 | Spacer() 49 | } 50 | Spacer() 51 | } 52 | .background(Color.white) 53 | .cornerRadius(10) 54 | .padding(.leading) 55 | .padding(.trailing) 56 | ) 57 | } 58 | .navigationTitle("STPopup+SwiftUI") 59 | .onAppear { 60 | DispatchQueue.main.asyncAfter(deadline: .now() + 5) { 61 | showsAdditionalMessage = true 62 | } 63 | } 64 | } 65 | } 66 | 67 | private func popupContent(action: @escaping () -> Void) -> some View { 68 | VStack(spacing: 10) { 69 | Image(systemName: "location") 70 | .font(.system(.largeTitle)) 71 | Text("Location Service") 72 | if showsAdditionalMessage { 73 | Text("Tap to dismiss") 74 | } 75 | Button("Enable") { 76 | action() 77 | } 78 | } 79 | .padding() 80 | } 81 | } 82 | 83 | struct ContentView_Previews: PreviewProvider { 84 | static var previews: some View { 85 | ContentView() 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /STPopupSwiftUIExample/STPopupSwiftUIExample/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /STPopupSwiftUIExample/STPopupSwiftUIExample/STPopupSwiftUIExampleApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // STPopupSwiftUIExampleApp.swift 3 | // STPopupSwiftUIExample 4 | // 5 | // Created by Kevin Lin on 26/11/2021. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct STPopupSwiftUIExampleApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | ContentView() 15 | } 16 | } 17 | } 18 | --------------------------------------------------------------------------------