├── .gitignore ├── KLCPopup.podspec ├── KLCPopup ├── KLCPopup.h └── KLCPopup.m ├── KLCPopupExample ├── KLCPopupExample.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── KLCPopupExample │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── icon-120.png │ │ │ ├── icon-152.png │ │ │ ├── icon-29.png │ │ │ ├── icon-40.png │ │ │ ├── icon-58-1.png │ │ │ ├── icon-58.png │ │ │ ├── icon-76.png │ │ │ ├── icon-80-1.png │ │ │ └── icon-80.png │ │ └── LaunchImage.launchimage │ │ │ ├── Contents.json │ │ │ ├── launch_ipad_landscape.png │ │ │ ├── launch_ipad_landscape@2x.png │ │ │ ├── launch_ipad_portrait.png │ │ │ ├── launch_ipad_portrait@2x.png │ │ │ ├── launch_iphone_2x.png │ │ │ └── launch_iphone_r4.png │ ├── KLCPopupExample-Info.plist │ ├── KLCPopupExample-Prefix.pch │ ├── ViewController.h │ ├── ViewController.m │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m └── KLCPopupExampleTests │ ├── KLCPopupExampleTests-Info.plist │ ├── KLCPopupExampleTests.m │ └── en.lproj │ └── InfoPlist.strings ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Xcode 4 | # 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | *.moved-aside 17 | DerivedData 18 | *.hmap 19 | *.ipa 20 | *.xcuserstate 21 | 22 | 23 | # CocoaPods 24 | # 25 | # We recommend against adding the Pods directory to your .gitignore. However 26 | # you should judge for yourself, the pros and cons are mentioned at: 27 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control? 28 | # 29 | # Pods/ 30 | 31 | -------------------------------------------------------------------------------- /KLCPopup.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "KLCPopup" 3 | s.version = "1.1" 4 | s.summary = "KLCPopup is a simple and flexible iOS class for presenting any custom view as a popup" 5 | s.homepage = "https://github.com/jmascia/KLCPopup" 6 | s.author = {"Jeff Mascia" => "http://jeffmascia.com"} 7 | s.source_files = 'KLCPopup', 'KLCPopup/*.{h,m}' 8 | s.source = {:git => 'https://github.com/jmascia/KLCPopup.git', :tag => s.version.to_s} 9 | s.frameworks = 'UIKit' 10 | s.requires_arc = true 11 | s.platform = :ios, '7.0' 12 | s.license = { 13 | :type => 'MIT', 14 | :file => 'LICENSE' 15 | } 16 | 17 | end -------------------------------------------------------------------------------- /KLCPopup/KLCPopup.h: -------------------------------------------------------------------------------- 1 | // KLCPopup.h 2 | // 3 | // Created by Jeff Mascia 4 | // Copyright (c) 2013-2014 Kullect Inc. (http://kullect.com) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | 25 | // KLCPopupShowType: Controls how the popup will be presented. 26 | typedef NS_ENUM(NSInteger, KLCPopupShowType) { 27 | KLCPopupShowTypeNone = 0, 28 | KLCPopupShowTypeFadeIn, 29 | KLCPopupShowTypeGrowIn, 30 | KLCPopupShowTypeShrinkIn, 31 | KLCPopupShowTypeSlideInFromTop, 32 | KLCPopupShowTypeSlideInFromBottom, 33 | KLCPopupShowTypeSlideInFromLeft, 34 | KLCPopupShowTypeSlideInFromRight, 35 | KLCPopupShowTypeBounceIn, 36 | KLCPopupShowTypeBounceInFromTop, 37 | KLCPopupShowTypeBounceInFromBottom, 38 | KLCPopupShowTypeBounceInFromLeft, 39 | KLCPopupShowTypeBounceInFromRight, 40 | }; 41 | 42 | // KLCPopupDismissType: Controls how the popup will be dismissed. 43 | typedef NS_ENUM(NSInteger, KLCPopupDismissType) { 44 | KLCPopupDismissTypeNone = 0, 45 | KLCPopupDismissTypeFadeOut, 46 | KLCPopupDismissTypeGrowOut, 47 | KLCPopupDismissTypeShrinkOut, 48 | KLCPopupDismissTypeSlideOutToTop, 49 | KLCPopupDismissTypeSlideOutToBottom, 50 | KLCPopupDismissTypeSlideOutToLeft, 51 | KLCPopupDismissTypeSlideOutToRight, 52 | KLCPopupDismissTypeBounceOut, 53 | KLCPopupDismissTypeBounceOutToTop, 54 | KLCPopupDismissTypeBounceOutToBottom, 55 | KLCPopupDismissTypeBounceOutToLeft, 56 | KLCPopupDismissTypeBounceOutToRight, 57 | }; 58 | 59 | 60 | 61 | // KLCPopupHorizontalLayout: Controls where the popup will come to rest horizontally. 62 | typedef NS_ENUM(NSInteger, KLCPopupHorizontalLayout) { 63 | KLCPopupHorizontalLayoutCustom = 0, 64 | KLCPopupHorizontalLayoutLeft, 65 | KLCPopupHorizontalLayoutLeftOfCenter, 66 | KLCPopupHorizontalLayoutCenter, 67 | KLCPopupHorizontalLayoutRightOfCenter, 68 | KLCPopupHorizontalLayoutRight, 69 | }; 70 | 71 | // KLCPopupVerticalLayout: Controls where the popup will come to rest vertically. 72 | typedef NS_ENUM(NSInteger, KLCPopupVerticalLayout) { 73 | KLCPopupVerticalLayoutCustom = 0, 74 | KLCPopupVerticalLayoutTop, 75 | KLCPopupVerticalLayoutAboveCenter, 76 | KLCPopupVerticalLayoutCenter, 77 | KLCPopupVerticalLayoutBelowCenter, 78 | KLCPopupVerticalLayoutBottom, 79 | }; 80 | 81 | // KLCPopupMaskType 82 | typedef NS_ENUM(NSInteger, KLCPopupMaskType) { 83 | KLCPopupMaskTypeNone = 0, // Allow interaction with underlying views. 84 | KLCPopupMaskTypeClear, // Don't allow interaction with underlying views. 85 | KLCPopupMaskTypeDimmed, // Don't allow interaction with underlying views, dim background. 86 | }; 87 | 88 | // KLCPopupLayout structure and maker functions 89 | struct KLCPopupLayout { 90 | KLCPopupHorizontalLayout horizontal; 91 | KLCPopupVerticalLayout vertical; 92 | }; 93 | typedef struct KLCPopupLayout KLCPopupLayout; 94 | 95 | extern KLCPopupLayout KLCPopupLayoutMake(KLCPopupHorizontalLayout horizontal, KLCPopupVerticalLayout vertical); 96 | 97 | extern const KLCPopupLayout KLCPopupLayoutCenter; 98 | 99 | 100 | 101 | @interface KLCPopup : UIView 102 | 103 | // This is the view that you want to appear in Popup. 104 | // - Must provide contentView before or in willStartShowing. 105 | // - Must set desired size of contentView before or in willStartShowing. 106 | @property (nonatomic, strong) UIView* contentView; 107 | 108 | // Animation transition for presenting contentView. default = shrink in 109 | @property (nonatomic, assign) KLCPopupShowType showType; 110 | 111 | // Animation transition for dismissing contentView. default = shrink out 112 | @property (nonatomic, assign) KLCPopupDismissType dismissType; 113 | 114 | // Mask prevents background touches from passing to underlying views. default = dimmed. 115 | @property (nonatomic, assign) KLCPopupMaskType maskType; 116 | 117 | // Overrides alpha value for dimmed background mask. default = 0.5 118 | @property (nonatomic, assign) CGFloat dimmedMaskAlpha; 119 | 120 | // If YES, then popup will get dismissed when background is touched. default = YES. 121 | @property (nonatomic, assign) BOOL shouldDismissOnBackgroundTouch; 122 | 123 | // If YES, then popup will get dismissed when content view is touched. default = NO. 124 | @property (nonatomic, assign) BOOL shouldDismissOnContentTouch; 125 | 126 | // Block gets called after show animation finishes. Be sure to use weak reference for popup within the block to avoid retain cycle. 127 | @property (nonatomic, copy) void (^didFinishShowingCompletion)(); 128 | 129 | // Block gets called when dismiss animation starts. Be sure to use weak reference for popup within the block to avoid retain cycle. 130 | @property (nonatomic, copy) void (^willStartDismissingCompletion)(); 131 | 132 | // Block gets called after dismiss animation finishes. Be sure to use weak reference for popup within the block to avoid retain cycle. 133 | @property (nonatomic, copy) void (^didFinishDismissingCompletion)(); 134 | 135 | // Convenience method for creating popup with default values (mimics UIAlertView). 136 | + (KLCPopup*)popupWithContentView:(UIView*)contentView; 137 | 138 | // Convenience method for creating popup with custom values. 139 | + (KLCPopup*)popupWithContentView:(UIView*)contentView 140 | showType:(KLCPopupShowType)showType 141 | dismissType:(KLCPopupDismissType)dismissType 142 | maskType:(KLCPopupMaskType)maskType 143 | dismissOnBackgroundTouch:(BOOL)shouldDismissOnBackgroundTouch 144 | dismissOnContentTouch:(BOOL)shouldDismissOnContentTouch; 145 | 146 | // Dismisses all the popups in the app. Use as a fail-safe for cleaning up. 147 | + (void)dismissAllPopups; 148 | 149 | // Show popup with center layout. Animation determined by showType. 150 | - (void)show; 151 | 152 | // Show with specified layout. 153 | - (void)showWithLayout:(KLCPopupLayout)layout; 154 | 155 | // Show and then dismiss after duration. 0.0 or less will be considered infinity. 156 | - (void)showWithDuration:(NSTimeInterval)duration; 157 | 158 | // Show with layout and dismiss after duration. 159 | - (void)showWithLayout:(KLCPopupLayout)layout duration:(NSTimeInterval)duration; 160 | 161 | // Show centered at point in view's coordinate system. If view is nil use screen base coordinates. 162 | - (void)showAtCenter:(CGPoint)center inView:(UIView*)view; 163 | 164 | // Show centered at point in view's coordinate system, then dismiss after duration. 165 | - (void)showAtCenter:(CGPoint)center inView:(UIView *)view withDuration:(NSTimeInterval)duration; 166 | 167 | // Dismiss popup. Uses dismissType if animated is YES. 168 | - (void)dismiss:(BOOL)animated; 169 | 170 | 171 | #pragma mark Subclassing 172 | @property (nonatomic, strong, readonly) UIView *backgroundView; 173 | @property (nonatomic, strong, readonly) UIView *containerView; 174 | @property (nonatomic, assign, readonly) BOOL isBeingShown; 175 | @property (nonatomic, assign, readonly) BOOL isShowing; 176 | @property (nonatomic, assign, readonly) BOOL isBeingDismissed; 177 | 178 | - (void)willStartShowing; 179 | - (void)didFinishShowing; 180 | - (void)willStartDismissing; 181 | - (void)didFinishDismissing; 182 | 183 | @end 184 | 185 | 186 | #pragma mark - UIView Category 187 | @interface UIView(KLCPopup) 188 | - (void)forEachPopupDoBlock:(void (^)(KLCPopup* popup))block; 189 | - (void)dismissPresentingPopup; 190 | @end 191 | 192 | -------------------------------------------------------------------------------- /KLCPopup/KLCPopup.m: -------------------------------------------------------------------------------- 1 | // KLCPopup.m 2 | // 3 | // Created by Jeff Mascia 4 | // Copyright (c) 2013-2014 Kullect Inc. (http://kullect.com) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | 25 | #import "KLCPopup.h" 26 | 27 | static NSInteger const kAnimationOptionCurveIOS7 = (7 << 16); 28 | 29 | KLCPopupLayout KLCPopupLayoutMake(KLCPopupHorizontalLayout horizontal, KLCPopupVerticalLayout vertical) 30 | { 31 | KLCPopupLayout layout; 32 | layout.horizontal = horizontal; 33 | layout.vertical = vertical; 34 | return layout; 35 | } 36 | 37 | const KLCPopupLayout KLCPopupLayoutCenter = { KLCPopupHorizontalLayoutCenter, KLCPopupVerticalLayoutCenter }; 38 | 39 | 40 | @interface NSValue (KLCPopupLayout) 41 | + (NSValue*)valueWithKLCPopupLayout:(KLCPopupLayout)layout; 42 | - (KLCPopupLayout)KLCPopupLayoutValue; 43 | @end 44 | 45 | 46 | @interface KLCPopup () { 47 | // views 48 | UIView* _backgroundView; 49 | UIView* _containerView; 50 | 51 | // state flags 52 | BOOL _isBeingShown; 53 | BOOL _isShowing; 54 | BOOL _isBeingDismissed; 55 | } 56 | 57 | - (void)updateForInterfaceOrientation; 58 | - (void)didChangeStatusBarOrientation:(NSNotification*)notification; 59 | 60 | // Used for calling dismiss:YES from selector because you can't pass primitives, thanks objc 61 | - (void)dismiss; 62 | 63 | @end 64 | 65 | 66 | @implementation KLCPopup 67 | 68 | @synthesize backgroundView = _backgroundView; 69 | @synthesize containerView = _containerView; 70 | @synthesize isBeingShown = _isBeingShown; 71 | @synthesize isShowing = _isShowing; 72 | @synthesize isBeingDismissed = _isBeingDismissed; 73 | 74 | 75 | - (void)dealloc { 76 | [NSObject cancelPreviousPerformRequestsWithTarget:self]; 77 | 78 | // stop listening to notifications 79 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 80 | } 81 | 82 | 83 | - (id)init { 84 | return [self initWithFrame:[[UIScreen mainScreen] bounds]]; 85 | } 86 | 87 | 88 | - (id)initWithFrame:(CGRect)frame { 89 | self = [super initWithFrame:frame]; 90 | if (self) { 91 | 92 | self.userInteractionEnabled = YES; 93 | self.backgroundColor = [UIColor clearColor]; 94 | self.alpha = 0; 95 | self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 96 | self.autoresizesSubviews = YES; 97 | 98 | self.shouldDismissOnBackgroundTouch = YES; 99 | self.shouldDismissOnContentTouch = NO; 100 | 101 | self.showType = KLCPopupShowTypeShrinkIn; 102 | self.dismissType = KLCPopupDismissTypeShrinkOut; 103 | self.maskType = KLCPopupMaskTypeDimmed; 104 | self.dimmedMaskAlpha = 0.5; 105 | 106 | _isBeingShown = NO; 107 | _isShowing = NO; 108 | _isBeingDismissed = NO; 109 | 110 | _backgroundView = [[UIView alloc] init]; 111 | _backgroundView.backgroundColor = [UIColor clearColor]; 112 | _backgroundView.userInteractionEnabled = NO; 113 | _backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 114 | _backgroundView.frame = self.bounds; 115 | 116 | _containerView = [[UIView alloc] init]; 117 | _containerView.autoresizesSubviews = NO; 118 | _containerView.userInteractionEnabled = YES; 119 | _containerView.backgroundColor = [UIColor clearColor]; 120 | 121 | [self addSubview:_backgroundView]; 122 | [self addSubview:_containerView]; 123 | 124 | // register for notifications 125 | [[NSNotificationCenter defaultCenter] addObserver:self 126 | selector:@selector(didChangeStatusBarOrientation:) 127 | name:UIApplicationDidChangeStatusBarFrameNotification 128 | object:nil]; 129 | } 130 | return self; 131 | } 132 | 133 | 134 | #pragma mark - UIView 135 | 136 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { 137 | 138 | UIView* hitView = [super hitTest:point withEvent:event]; 139 | if (hitView == self) { 140 | 141 | // Try to dismiss if backgroundTouch flag set. 142 | if (_shouldDismissOnBackgroundTouch) { 143 | [self dismiss:YES]; 144 | } 145 | 146 | // If no mask, then return nil so touch passes through to underlying views. 147 | if (_maskType == KLCPopupMaskTypeNone) { 148 | return nil; 149 | } else { 150 | return hitView; 151 | } 152 | 153 | } else { 154 | 155 | // If view is within containerView and contentTouch flag set, then try to hide. 156 | if ([hitView isDescendantOfView:_containerView]) { 157 | if (_shouldDismissOnContentTouch) { 158 | [self dismiss:YES]; 159 | } 160 | } 161 | return hitView; 162 | } 163 | } 164 | 165 | 166 | #pragma mark - Class Public 167 | 168 | + (KLCPopup*)popupWithContentView:(UIView*)contentView 169 | { 170 | KLCPopup* popup = [[[self class] alloc] init]; 171 | popup.contentView = contentView; 172 | return popup; 173 | } 174 | 175 | 176 | + (KLCPopup*)popupWithContentView:(UIView*)contentView 177 | showType:(KLCPopupShowType)showType 178 | dismissType:(KLCPopupDismissType)dismissType 179 | maskType:(KLCPopupMaskType)maskType 180 | dismissOnBackgroundTouch:(BOOL)shouldDismissOnBackgroundTouch 181 | dismissOnContentTouch:(BOOL)shouldDismissOnContentTouch 182 | { 183 | KLCPopup* popup = [[[self class] alloc] init]; 184 | popup.contentView = contentView; 185 | popup.showType = showType; 186 | popup.dismissType = dismissType; 187 | popup.maskType = maskType; 188 | popup.shouldDismissOnBackgroundTouch = shouldDismissOnBackgroundTouch; 189 | popup.shouldDismissOnContentTouch = shouldDismissOnContentTouch; 190 | return popup; 191 | } 192 | 193 | 194 | + (void)dismissAllPopups { 195 | NSArray* windows = [[UIApplication sharedApplication] windows]; 196 | for (UIWindow* window in windows) { 197 | [window forEachPopupDoBlock:^(KLCPopup *popup) { 198 | [popup dismiss:NO]; 199 | }]; 200 | } 201 | } 202 | 203 | 204 | #pragma mark - Public 205 | 206 | - (void)show { 207 | [self showWithLayout:KLCPopupLayoutCenter]; 208 | } 209 | 210 | 211 | - (void)showWithLayout:(KLCPopupLayout)layout { 212 | [self showWithLayout:layout duration:0.0]; 213 | } 214 | 215 | 216 | - (void)showWithDuration:(NSTimeInterval)duration { 217 | [self showWithLayout:KLCPopupLayoutCenter duration:duration]; 218 | } 219 | 220 | 221 | - (void)showWithLayout:(KLCPopupLayout)layout duration:(NSTimeInterval)duration { 222 | NSDictionary* parameters = @{@"layout" : [NSValue valueWithKLCPopupLayout:layout], 223 | @"duration" : @(duration)}; 224 | [self showWithParameters:parameters]; 225 | } 226 | 227 | 228 | - (void)showAtCenter:(CGPoint)center inView:(UIView*)view { 229 | [self showAtCenter:center inView:view withDuration:0.0]; 230 | } 231 | 232 | 233 | - (void)showAtCenter:(CGPoint)center inView:(UIView *)view withDuration:(NSTimeInterval)duration { 234 | NSMutableDictionary* parameters = [NSMutableDictionary dictionary]; 235 | [parameters setValue:[NSValue valueWithCGPoint:center] forKey:@"center"]; 236 | [parameters setValue:@(duration) forKey:@"duration"]; 237 | [parameters setValue:view forKey:@"view"]; 238 | [self showWithParameters:[NSDictionary dictionaryWithDictionary:parameters]]; 239 | } 240 | 241 | 242 | - (void)dismiss:(BOOL)animated { 243 | 244 | if (_isShowing && !_isBeingDismissed) { 245 | _isBeingShown = NO; 246 | _isShowing = NO; 247 | _isBeingDismissed = YES; 248 | 249 | // cancel previous dismiss requests (i.e. the dismiss after duration call). 250 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(dismiss) object:nil]; 251 | 252 | [self willStartDismissing]; 253 | 254 | if (self.willStartDismissingCompletion != nil) { 255 | self.willStartDismissingCompletion(); 256 | } 257 | 258 | dispatch_async( dispatch_get_main_queue(), ^{ 259 | 260 | // Animate background if needed 261 | void (^backgroundAnimationBlock)(void) = ^(void) { 262 | _backgroundView.alpha = 0.0; 263 | }; 264 | 265 | if (animated && (_showType != KLCPopupShowTypeNone)) { 266 | // Make fade happen faster than motion. Use linear for fades. 267 | [UIView animateWithDuration:0.15 268 | delay:0 269 | options:UIViewAnimationOptionCurveLinear 270 | animations:backgroundAnimationBlock 271 | completion:NULL]; 272 | } else { 273 | backgroundAnimationBlock(); 274 | } 275 | 276 | // Setup completion block 277 | void (^completionBlock)(BOOL) = ^(BOOL finished) { 278 | 279 | [self removeFromSuperview]; 280 | 281 | _isBeingShown = NO; 282 | _isShowing = NO; 283 | _isBeingDismissed = NO; 284 | 285 | [self didFinishDismissing]; 286 | 287 | if (self.didFinishDismissingCompletion != nil) { 288 | self.didFinishDismissingCompletion(); 289 | } 290 | }; 291 | 292 | NSTimeInterval bounce1Duration = 0.13; 293 | NSTimeInterval bounce2Duration = (bounce1Duration * 2.0); 294 | 295 | // Animate content if needed 296 | if (animated) { 297 | switch (_dismissType) { 298 | case KLCPopupDismissTypeFadeOut: { 299 | [UIView animateWithDuration:0.15 300 | delay:0 301 | options:UIViewAnimationOptionCurveLinear 302 | animations:^{ 303 | _containerView.alpha = 0.0; 304 | } completion:completionBlock]; 305 | break; 306 | } 307 | 308 | case KLCPopupDismissTypeGrowOut: { 309 | [UIView animateWithDuration:0.15 310 | delay:0 311 | options:kAnimationOptionCurveIOS7 312 | animations:^{ 313 | _containerView.alpha = 0.0; 314 | _containerView.transform = CGAffineTransformMakeScale(1.1, 1.1); 315 | } completion:completionBlock]; 316 | break; 317 | } 318 | 319 | case KLCPopupDismissTypeShrinkOut: { 320 | [UIView animateWithDuration:0.15 321 | delay:0 322 | options:kAnimationOptionCurveIOS7 323 | animations:^{ 324 | _containerView.alpha = 0.0; 325 | _containerView.transform = CGAffineTransformMakeScale(0.8, 0.8); 326 | } completion:completionBlock]; 327 | break; 328 | } 329 | 330 | case KLCPopupDismissTypeSlideOutToTop: { 331 | [UIView animateWithDuration:0.30 332 | delay:0 333 | options:kAnimationOptionCurveIOS7 334 | animations:^{ 335 | CGRect finalFrame = _containerView.frame; 336 | finalFrame.origin.y = -CGRectGetHeight(finalFrame); 337 | _containerView.frame = finalFrame; 338 | } 339 | completion:completionBlock]; 340 | break; 341 | } 342 | 343 | case KLCPopupDismissTypeSlideOutToBottom: { 344 | [UIView animateWithDuration:0.30 345 | delay:0 346 | options:kAnimationOptionCurveIOS7 347 | animations:^{ 348 | CGRect finalFrame = _containerView.frame; 349 | finalFrame.origin.y = CGRectGetHeight(self.bounds); 350 | _containerView.frame = finalFrame; 351 | } 352 | completion:completionBlock]; 353 | break; 354 | } 355 | 356 | case KLCPopupDismissTypeSlideOutToLeft: { 357 | [UIView animateWithDuration:0.30 358 | delay:0 359 | options:kAnimationOptionCurveIOS7 360 | animations:^{ 361 | CGRect finalFrame = _containerView.frame; 362 | finalFrame.origin.x = -CGRectGetWidth(finalFrame); 363 | _containerView.frame = finalFrame; 364 | } 365 | completion:completionBlock]; 366 | break; 367 | } 368 | 369 | case KLCPopupDismissTypeSlideOutToRight: { 370 | [UIView animateWithDuration:0.30 371 | delay:0 372 | options:kAnimationOptionCurveIOS7 373 | animations:^{ 374 | CGRect finalFrame = _containerView.frame; 375 | finalFrame.origin.x = CGRectGetWidth(self.bounds); 376 | _containerView.frame = finalFrame; 377 | } 378 | completion:completionBlock]; 379 | 380 | break; 381 | } 382 | 383 | case KLCPopupDismissTypeBounceOut: { 384 | [UIView animateWithDuration:bounce1Duration 385 | delay:0 386 | options:UIViewAnimationOptionCurveEaseOut 387 | animations:^(void){ 388 | _containerView.transform = CGAffineTransformMakeScale(1.1, 1.1); 389 | } 390 | completion:^(BOOL finished){ 391 | 392 | [UIView animateWithDuration:bounce2Duration 393 | delay:0 394 | options:UIViewAnimationOptionCurveEaseIn 395 | animations:^(void){ 396 | _containerView.alpha = 0.0; 397 | _containerView.transform = CGAffineTransformMakeScale(0.1, 0.1); 398 | } 399 | completion:completionBlock]; 400 | }]; 401 | 402 | break; 403 | } 404 | 405 | case KLCPopupDismissTypeBounceOutToTop: { 406 | [UIView animateWithDuration:bounce1Duration 407 | delay:0 408 | options:UIViewAnimationOptionCurveEaseOut 409 | animations:^(void){ 410 | CGRect finalFrame = _containerView.frame; 411 | finalFrame.origin.y += 40.0; 412 | _containerView.frame = finalFrame; 413 | } 414 | completion:^(BOOL finished){ 415 | 416 | [UIView animateWithDuration:bounce2Duration 417 | delay:0 418 | options:UIViewAnimationOptionCurveEaseIn 419 | animations:^(void){ 420 | CGRect finalFrame = _containerView.frame; 421 | finalFrame.origin.y = -CGRectGetHeight(finalFrame); 422 | _containerView.frame = finalFrame; 423 | } 424 | completion:completionBlock]; 425 | }]; 426 | 427 | break; 428 | } 429 | 430 | case KLCPopupDismissTypeBounceOutToBottom: { 431 | [UIView animateWithDuration:bounce1Duration 432 | delay:0 433 | options:UIViewAnimationOptionCurveEaseOut 434 | animations:^(void){ 435 | CGRect finalFrame = _containerView.frame; 436 | finalFrame.origin.y -= 40.0; 437 | _containerView.frame = finalFrame; 438 | } 439 | completion:^(BOOL finished){ 440 | 441 | [UIView animateWithDuration:bounce2Duration 442 | delay:0 443 | options:UIViewAnimationOptionCurveEaseIn 444 | animations:^(void){ 445 | CGRect finalFrame = _containerView.frame; 446 | finalFrame.origin.y = CGRectGetHeight(self.bounds); 447 | _containerView.frame = finalFrame; 448 | } 449 | completion:completionBlock]; 450 | }]; 451 | 452 | break; 453 | } 454 | 455 | case KLCPopupDismissTypeBounceOutToLeft: { 456 | [UIView animateWithDuration:bounce1Duration 457 | delay:0 458 | options:UIViewAnimationOptionCurveEaseOut 459 | animations:^(void){ 460 | CGRect finalFrame = _containerView.frame; 461 | finalFrame.origin.x += 40.0; 462 | _containerView.frame = finalFrame; 463 | } 464 | completion:^(BOOL finished){ 465 | 466 | [UIView animateWithDuration:bounce2Duration 467 | delay:0 468 | options:UIViewAnimationOptionCurveEaseIn 469 | animations:^(void){ 470 | CGRect finalFrame = _containerView.frame; 471 | finalFrame.origin.x = -CGRectGetWidth(finalFrame); 472 | _containerView.frame = finalFrame; 473 | } 474 | completion:completionBlock]; 475 | }]; 476 | break; 477 | } 478 | 479 | case KLCPopupDismissTypeBounceOutToRight: { 480 | [UIView animateWithDuration:bounce1Duration 481 | delay:0 482 | options:UIViewAnimationOptionCurveEaseOut 483 | animations:^(void){ 484 | CGRect finalFrame = _containerView.frame; 485 | finalFrame.origin.x -= 40.0; 486 | _containerView.frame = finalFrame; 487 | } 488 | completion:^(BOOL finished){ 489 | 490 | [UIView animateWithDuration:bounce2Duration 491 | delay:0 492 | options:UIViewAnimationOptionCurveEaseIn 493 | animations:^(void){ 494 | CGRect finalFrame = _containerView.frame; 495 | finalFrame.origin.x = CGRectGetWidth(self.bounds); 496 | _containerView.frame = finalFrame; 497 | } 498 | completion:completionBlock]; 499 | }]; 500 | break; 501 | } 502 | 503 | default: { 504 | self.containerView.alpha = 0.0; 505 | completionBlock(YES); 506 | break; 507 | } 508 | } 509 | } else { 510 | self.containerView.alpha = 0.0; 511 | completionBlock(YES); 512 | } 513 | 514 | }); 515 | } 516 | } 517 | 518 | 519 | #pragma mark - Private 520 | 521 | - (void)showWithParameters:(NSDictionary*)parameters { 522 | 523 | // If popup can be shown 524 | if (!_isBeingShown && !_isShowing && !_isBeingDismissed) { 525 | _isBeingShown = YES; 526 | _isShowing = NO; 527 | _isBeingDismissed = NO; 528 | 529 | [self willStartShowing]; 530 | 531 | dispatch_async( dispatch_get_main_queue(), ^{ 532 | 533 | // Prepare by adding to the top window. 534 | if(!self.superview){ 535 | NSEnumerator *frontToBackWindows = [[[UIApplication sharedApplication] windows] reverseObjectEnumerator]; 536 | 537 | for (UIWindow *window in frontToBackWindows) { 538 | if (window.windowLevel == UIWindowLevelNormal) { 539 | [window addSubview:self]; 540 | 541 | break; 542 | } 543 | } 544 | } 545 | 546 | // Before we calculate layout for containerView, make sure we are transformed for current orientation. 547 | [self updateForInterfaceOrientation]; 548 | 549 | // Make sure we're not hidden 550 | self.hidden = NO; 551 | self.alpha = 1.0; 552 | 553 | // Setup background view 554 | _backgroundView.alpha = 0.0; 555 | if (_maskType == KLCPopupMaskTypeDimmed) { 556 | _backgroundView.backgroundColor = [UIColor colorWithRed:(0.0/255.0f) green:(0.0/255.0f) blue:(0.0/255.0f) alpha:self.dimmedMaskAlpha]; 557 | } else { 558 | _backgroundView.backgroundColor = [UIColor clearColor]; 559 | } 560 | 561 | // Animate background if needed 562 | void (^backgroundAnimationBlock)(void) = ^(void) { 563 | _backgroundView.alpha = 1.0; 564 | }; 565 | 566 | if (_showType != KLCPopupShowTypeNone) { 567 | // Make fade happen faster than motion. Use linear for fades. 568 | [UIView animateWithDuration:0.15 569 | delay:0 570 | options:UIViewAnimationOptionCurveLinear 571 | animations:backgroundAnimationBlock 572 | completion:NULL]; 573 | } else { 574 | backgroundAnimationBlock(); 575 | } 576 | 577 | // Determine duration. Default to 0 if none provided. 578 | NSTimeInterval duration; 579 | NSNumber* durationNumber = [parameters valueForKey:@"duration"]; 580 | if (durationNumber != nil) { 581 | duration = [durationNumber doubleValue]; 582 | } else { 583 | duration = 0.0; 584 | } 585 | 586 | // Setup completion block 587 | void (^completionBlock)(BOOL) = ^(BOOL finished) { 588 | _isBeingShown = NO; 589 | _isShowing = YES; 590 | _isBeingDismissed = NO; 591 | 592 | [self didFinishShowing]; 593 | 594 | if (self.didFinishShowingCompletion != nil) { 595 | self.didFinishShowingCompletion(); 596 | } 597 | 598 | // Set to hide after duration if greater than zero. 599 | if (duration > 0.0) { 600 | [self performSelector:@selector(dismiss) withObject:nil afterDelay:duration]; 601 | } 602 | }; 603 | 604 | // Add contentView to container 605 | if (self.contentView.superview != _containerView) { 606 | [_containerView addSubview:self.contentView]; 607 | } 608 | 609 | // Re-layout (this is needed if the contentView is using autoLayout) 610 | [self.contentView layoutIfNeeded]; 611 | 612 | // Size container to match contentView 613 | CGRect containerFrame = _containerView.frame; 614 | containerFrame.size = self.contentView.frame.size; 615 | _containerView.frame = containerFrame; 616 | // Position contentView to fill it 617 | CGRect contentViewFrame = self.contentView.frame; 618 | contentViewFrame.origin = CGPointZero; 619 | self.contentView.frame = contentViewFrame; 620 | 621 | // Reset _containerView's constraints in case contentView is uaing autolayout. 622 | UIView* contentView = _contentView; 623 | NSDictionary* views = NSDictionaryOfVariableBindings(contentView); 624 | 625 | [_containerView removeConstraints:_containerView.constraints]; 626 | [_containerView addConstraints: 627 | [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[contentView]|" 628 | options:0 629 | metrics:nil 630 | views:views]]; 631 | 632 | [_containerView addConstraints: 633 | [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|" 634 | options:0 635 | metrics:nil 636 | views:views]]; 637 | 638 | // Determine final position and necessary autoresizingMask for container. 639 | CGRect finalContainerFrame = containerFrame; 640 | UIViewAutoresizing containerAutoresizingMask = UIViewAutoresizingNone; 641 | 642 | // Use explicit center coordinates if provided. 643 | NSValue* centerValue = [parameters valueForKey:@"center"]; 644 | if (centerValue != nil) { 645 | 646 | CGPoint centerInView = [centerValue CGPointValue]; 647 | CGPoint centerInSelf; 648 | 649 | // Convert coordinates from provided view to self. Otherwise use as-is. 650 | UIView* fromView = [parameters valueForKey:@"view"]; 651 | if (fromView != nil) { 652 | centerInSelf = [self convertPoint:centerInView fromView:fromView]; 653 | } else { 654 | centerInSelf = centerInView; 655 | } 656 | 657 | finalContainerFrame.origin.x = (centerInSelf.x - CGRectGetWidth(finalContainerFrame)/2.0); 658 | finalContainerFrame.origin.y = (centerInSelf.y - CGRectGetHeight(finalContainerFrame)/2.0); 659 | containerAutoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin; 660 | } 661 | 662 | // Otherwise use relative layout. Default to center if none provided. 663 | else { 664 | 665 | NSValue* layoutValue = [parameters valueForKey:@"layout"]; 666 | KLCPopupLayout layout; 667 | if (layoutValue != nil) { 668 | layout = [layoutValue KLCPopupLayoutValue]; 669 | } else { 670 | layout = KLCPopupLayoutCenter; 671 | } 672 | 673 | switch (layout.horizontal) { 674 | 675 | case KLCPopupHorizontalLayoutLeft: { 676 | finalContainerFrame.origin.x = 0.0; 677 | containerAutoresizingMask = containerAutoresizingMask | UIViewAutoresizingFlexibleRightMargin; 678 | break; 679 | } 680 | 681 | case KLCPopupHorizontalLayoutLeftOfCenter: { 682 | finalContainerFrame.origin.x = floorf(CGRectGetWidth(self.bounds)/3.0 - CGRectGetWidth(containerFrame)/2.0); 683 | containerAutoresizingMask = containerAutoresizingMask | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; 684 | break; 685 | } 686 | 687 | case KLCPopupHorizontalLayoutCenter: { 688 | finalContainerFrame.origin.x = floorf((CGRectGetWidth(self.bounds) - CGRectGetWidth(containerFrame))/2.0); 689 | containerAutoresizingMask = containerAutoresizingMask | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; 690 | break; 691 | } 692 | 693 | case KLCPopupHorizontalLayoutRightOfCenter: { 694 | finalContainerFrame.origin.x = floorf(CGRectGetWidth(self.bounds)*2.0/3.0 - CGRectGetWidth(containerFrame)/2.0); 695 | containerAutoresizingMask = containerAutoresizingMask | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; 696 | break; 697 | } 698 | 699 | case KLCPopupHorizontalLayoutRight: { 700 | finalContainerFrame.origin.x = CGRectGetWidth(self.bounds) - CGRectGetWidth(containerFrame); 701 | containerAutoresizingMask = containerAutoresizingMask | UIViewAutoresizingFlexibleLeftMargin; 702 | break; 703 | } 704 | 705 | default: 706 | break; 707 | } 708 | 709 | // Vertical 710 | switch (layout.vertical) { 711 | 712 | case KLCPopupVerticalLayoutTop: { 713 | finalContainerFrame.origin.y = 0; 714 | containerAutoresizingMask = containerAutoresizingMask | UIViewAutoresizingFlexibleBottomMargin; 715 | break; 716 | } 717 | 718 | case KLCPopupVerticalLayoutAboveCenter: { 719 | finalContainerFrame.origin.y = floorf(CGRectGetHeight(self.bounds)/3.0 - CGRectGetHeight(containerFrame)/2.0); 720 | containerAutoresizingMask = containerAutoresizingMask | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; 721 | break; 722 | } 723 | 724 | case KLCPopupVerticalLayoutCenter: { 725 | finalContainerFrame.origin.y = floorf((CGRectGetHeight(self.bounds) - CGRectGetHeight(containerFrame))/2.0); 726 | containerAutoresizingMask = containerAutoresizingMask | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; 727 | break; 728 | } 729 | 730 | case KLCPopupVerticalLayoutBelowCenter: { 731 | finalContainerFrame.origin.y = floorf(CGRectGetHeight(self.bounds)*2.0/3.0 - CGRectGetHeight(containerFrame)/2.0); 732 | containerAutoresizingMask = containerAutoresizingMask | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; 733 | break; 734 | } 735 | 736 | case KLCPopupVerticalLayoutBottom: { 737 | finalContainerFrame.origin.y = CGRectGetHeight(self.bounds) - CGRectGetHeight(containerFrame); 738 | containerAutoresizingMask = containerAutoresizingMask | UIViewAutoresizingFlexibleTopMargin; 739 | break; 740 | } 741 | 742 | default: 743 | break; 744 | } 745 | } 746 | 747 | _containerView.autoresizingMask = containerAutoresizingMask; 748 | 749 | // Animate content if needed 750 | switch (_showType) { 751 | case KLCPopupShowTypeFadeIn: { 752 | 753 | _containerView.alpha = 0.0; 754 | _containerView.transform = CGAffineTransformIdentity; 755 | CGRect startFrame = finalContainerFrame; 756 | _containerView.frame = startFrame; 757 | 758 | [UIView animateWithDuration:0.15 759 | delay:0 760 | options:UIViewAnimationOptionCurveLinear 761 | animations:^{ 762 | _containerView.alpha = 1.0; 763 | } 764 | completion:completionBlock]; 765 | break; 766 | } 767 | 768 | case KLCPopupShowTypeGrowIn: { 769 | 770 | _containerView.alpha = 0.0; 771 | // set frame before transform here... 772 | CGRect startFrame = finalContainerFrame; 773 | _containerView.frame = startFrame; 774 | _containerView.transform = CGAffineTransformMakeScale(0.85, 0.85); 775 | 776 | [UIView animateWithDuration:0.15 777 | delay:0 778 | options:kAnimationOptionCurveIOS7 // note: this curve ignores durations 779 | animations:^{ 780 | _containerView.alpha = 1.0; 781 | // set transform before frame here... 782 | _containerView.transform = CGAffineTransformIdentity; 783 | _containerView.frame = finalContainerFrame; 784 | } 785 | completion:completionBlock]; 786 | 787 | break; 788 | } 789 | 790 | case KLCPopupShowTypeShrinkIn: { 791 | _containerView.alpha = 0.0; 792 | // set frame before transform here... 793 | CGRect startFrame = finalContainerFrame; 794 | _containerView.frame = startFrame; 795 | _containerView.transform = CGAffineTransformMakeScale(1.25, 1.25); 796 | 797 | [UIView animateWithDuration:0.15 798 | delay:0 799 | options:kAnimationOptionCurveIOS7 // note: this curve ignores durations 800 | animations:^{ 801 | _containerView.alpha = 1.0; 802 | // set transform before frame here... 803 | _containerView.transform = CGAffineTransformIdentity; 804 | _containerView.frame = finalContainerFrame; 805 | } 806 | completion:completionBlock]; 807 | break; 808 | } 809 | 810 | case KLCPopupShowTypeSlideInFromTop: { 811 | _containerView.alpha = 1.0; 812 | _containerView.transform = CGAffineTransformIdentity; 813 | CGRect startFrame = finalContainerFrame; 814 | startFrame.origin.y = -CGRectGetHeight(finalContainerFrame); 815 | _containerView.frame = startFrame; 816 | 817 | [UIView animateWithDuration:0.30 818 | delay:0 819 | options:kAnimationOptionCurveIOS7 // note: this curve ignores durations 820 | animations:^{ 821 | _containerView.frame = finalContainerFrame; 822 | } 823 | completion:completionBlock]; 824 | break; 825 | } 826 | 827 | case KLCPopupShowTypeSlideInFromBottom: { 828 | _containerView.alpha = 1.0; 829 | _containerView.transform = CGAffineTransformIdentity; 830 | CGRect startFrame = finalContainerFrame; 831 | startFrame.origin.y = CGRectGetHeight(self.bounds); 832 | _containerView.frame = startFrame; 833 | 834 | [UIView animateWithDuration:0.30 835 | delay:0 836 | options:kAnimationOptionCurveIOS7 // note: this curve ignores durations 837 | animations:^{ 838 | _containerView.frame = finalContainerFrame; 839 | } 840 | completion:completionBlock]; 841 | break; 842 | } 843 | 844 | case KLCPopupShowTypeSlideInFromLeft: { 845 | _containerView.alpha = 1.0; 846 | _containerView.transform = CGAffineTransformIdentity; 847 | CGRect startFrame = finalContainerFrame; 848 | startFrame.origin.x = -CGRectGetWidth(finalContainerFrame); 849 | _containerView.frame = startFrame; 850 | 851 | [UIView animateWithDuration:0.30 852 | delay:0 853 | options:kAnimationOptionCurveIOS7 // note: this curve ignores durations 854 | animations:^{ 855 | _containerView.frame = finalContainerFrame; 856 | } 857 | completion:completionBlock]; 858 | break; 859 | } 860 | 861 | case KLCPopupShowTypeSlideInFromRight: { 862 | _containerView.alpha = 1.0; 863 | _containerView.transform = CGAffineTransformIdentity; 864 | CGRect startFrame = finalContainerFrame; 865 | startFrame.origin.x = CGRectGetWidth(self.bounds); 866 | _containerView.frame = startFrame; 867 | 868 | [UIView animateWithDuration:0.30 869 | delay:0 870 | options:kAnimationOptionCurveIOS7 // note: this curve ignores durations 871 | animations:^{ 872 | _containerView.frame = finalContainerFrame; 873 | } 874 | completion:completionBlock]; 875 | 876 | break; 877 | } 878 | 879 | case KLCPopupShowTypeBounceIn: { 880 | _containerView.alpha = 0.0; 881 | // set frame before transform here... 882 | CGRect startFrame = finalContainerFrame; 883 | _containerView.frame = startFrame; 884 | _containerView.transform = CGAffineTransformMakeScale(0.1, 0.1); 885 | 886 | [UIView animateWithDuration:0.6 887 | delay:0.0 888 | usingSpringWithDamping:0.8 889 | initialSpringVelocity:15.0 890 | options:0 891 | animations:^{ 892 | _containerView.alpha = 1.0; 893 | _containerView.transform = CGAffineTransformIdentity; 894 | } 895 | completion:completionBlock]; 896 | 897 | break; 898 | } 899 | 900 | case KLCPopupShowTypeBounceInFromTop: { 901 | _containerView.alpha = 1.0; 902 | _containerView.transform = CGAffineTransformIdentity; 903 | CGRect startFrame = finalContainerFrame; 904 | startFrame.origin.y = -CGRectGetHeight(finalContainerFrame); 905 | _containerView.frame = startFrame; 906 | 907 | [UIView animateWithDuration:0.6 908 | delay:0.0 909 | usingSpringWithDamping:0.8 910 | initialSpringVelocity:10.0 911 | options:0 912 | animations:^{ 913 | _containerView.frame = finalContainerFrame; 914 | } 915 | completion:completionBlock]; 916 | break; 917 | } 918 | 919 | case KLCPopupShowTypeBounceInFromBottom: { 920 | _containerView.alpha = 1.0; 921 | _containerView.transform = CGAffineTransformIdentity; 922 | CGRect startFrame = finalContainerFrame; 923 | startFrame.origin.y = CGRectGetHeight(self.bounds); 924 | _containerView.frame = startFrame; 925 | 926 | [UIView animateWithDuration:0.6 927 | delay:0.0 928 | usingSpringWithDamping:0.8 929 | initialSpringVelocity:10.0 930 | options:0 931 | animations:^{ 932 | _containerView.frame = finalContainerFrame; 933 | } 934 | completion:completionBlock]; 935 | break; 936 | } 937 | 938 | case KLCPopupShowTypeBounceInFromLeft: { 939 | _containerView.alpha = 1.0; 940 | _containerView.transform = CGAffineTransformIdentity; 941 | CGRect startFrame = finalContainerFrame; 942 | startFrame.origin.x = -CGRectGetWidth(finalContainerFrame); 943 | _containerView.frame = startFrame; 944 | 945 | [UIView animateWithDuration:0.6 946 | delay:0.0 947 | usingSpringWithDamping:0.8 948 | initialSpringVelocity:10.0 949 | options:0 950 | animations:^{ 951 | _containerView.frame = finalContainerFrame; 952 | } 953 | completion:completionBlock]; 954 | break; 955 | } 956 | 957 | case KLCPopupShowTypeBounceInFromRight: { 958 | _containerView.alpha = 1.0; 959 | _containerView.transform = CGAffineTransformIdentity; 960 | CGRect startFrame = finalContainerFrame; 961 | startFrame.origin.x = CGRectGetWidth(self.bounds); 962 | _containerView.frame = startFrame; 963 | 964 | [UIView animateWithDuration:0.6 965 | delay:0.0 966 | usingSpringWithDamping:0.8 967 | initialSpringVelocity:10.0 968 | options:0 969 | animations:^{ 970 | _containerView.frame = finalContainerFrame; 971 | } 972 | completion:completionBlock]; 973 | break; 974 | } 975 | 976 | default: { 977 | self.containerView.alpha = 1.0; 978 | self.containerView.transform = CGAffineTransformIdentity; 979 | self.containerView.frame = finalContainerFrame; 980 | 981 | completionBlock(YES); 982 | 983 | break; 984 | } 985 | } 986 | 987 | }); 988 | } 989 | } 990 | 991 | 992 | - (void)dismiss { 993 | [self dismiss:YES]; 994 | } 995 | 996 | 997 | - (void)updateForInterfaceOrientation { 998 | 999 | // We must manually fix orientation prior to iOS 8 1000 | if (([[[UIDevice currentDevice] systemVersion] compare:@"8.0" options:NSNumericSearch] == NSOrderedAscending)) { 1001 | 1002 | UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; 1003 | CGFloat angle; 1004 | 1005 | switch (orientation) { 1006 | case UIInterfaceOrientationPortraitUpsideDown: 1007 | angle = M_PI; 1008 | break; 1009 | case UIInterfaceOrientationLandscapeLeft: 1010 | angle = -M_PI/2.0f;; 1011 | 1012 | break; 1013 | case UIInterfaceOrientationLandscapeRight: 1014 | angle = M_PI/2.0f; 1015 | 1016 | break; 1017 | default: // as UIInterfaceOrientationPortrait 1018 | angle = 0.0; 1019 | break; 1020 | } 1021 | 1022 | self.transform = CGAffineTransformMakeRotation(angle); 1023 | } 1024 | 1025 | self.frame = self.window.bounds; 1026 | } 1027 | 1028 | 1029 | #pragma mark - Notification handlers 1030 | 1031 | - (void)didChangeStatusBarOrientation:(NSNotification*)notification { 1032 | [self updateForInterfaceOrientation]; 1033 | } 1034 | 1035 | 1036 | #pragma mark - Subclassing 1037 | 1038 | - (void)willStartShowing { 1039 | 1040 | } 1041 | 1042 | 1043 | - (void)didFinishShowing { 1044 | 1045 | } 1046 | 1047 | 1048 | - (void)willStartDismissing { 1049 | 1050 | } 1051 | 1052 | 1053 | - (void)didFinishDismissing { 1054 | 1055 | } 1056 | 1057 | @end 1058 | 1059 | 1060 | 1061 | 1062 | #pragma mark - Categories 1063 | 1064 | @implementation UIView(KLCPopup) 1065 | 1066 | 1067 | - (void)forEachPopupDoBlock:(void (^)(KLCPopup* popup))block { 1068 | for (UIView *subview in self.subviews) 1069 | { 1070 | if ([subview isKindOfClass:[KLCPopup class]]) 1071 | { 1072 | block((KLCPopup *)subview); 1073 | } else { 1074 | [subview forEachPopupDoBlock:block]; 1075 | } 1076 | } 1077 | } 1078 | 1079 | 1080 | - (void)dismissPresentingPopup { 1081 | 1082 | // Iterate over superviews until you find a KLCPopup and dismiss it, then gtfo 1083 | UIView* view = self; 1084 | while (view != nil) { 1085 | if ([view isKindOfClass:[KLCPopup class]]) { 1086 | [(KLCPopup*)view dismiss:YES]; 1087 | break; 1088 | } 1089 | view = [view superview]; 1090 | } 1091 | } 1092 | 1093 | @end 1094 | 1095 | 1096 | 1097 | 1098 | @implementation NSValue (KLCPopupLayout) 1099 | 1100 | + (NSValue *)valueWithKLCPopupLayout:(KLCPopupLayout)layout 1101 | { 1102 | return [NSValue valueWithBytes:&layout objCType:@encode(KLCPopupLayout)]; 1103 | } 1104 | 1105 | - (KLCPopupLayout)KLCPopupLayoutValue 1106 | { 1107 | KLCPopupLayout layout; 1108 | 1109 | [self getValue:&layout]; 1110 | 1111 | return layout; 1112 | } 1113 | 1114 | @end 1115 | -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1F7E1B061965CF7D00D576D8 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F7E1B051965CF7D00D576D8 /* QuartzCore.framework */; }; 11 | 1FA76AB2195893080075A8E4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FA76AB1195893080075A8E4 /* Foundation.framework */; }; 12 | 1FA76AB4195893080075A8E4 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FA76AB3195893080075A8E4 /* CoreGraphics.framework */; }; 13 | 1FA76AB6195893080075A8E4 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FA76AB5195893080075A8E4 /* UIKit.framework */; }; 14 | 1FA76ABC195893080075A8E4 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1FA76ABA195893080075A8E4 /* InfoPlist.strings */; }; 15 | 1FA76ABE195893080075A8E4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FA76ABD195893080075A8E4 /* main.m */; }; 16 | 1FA76AC2195893080075A8E4 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FA76AC1195893080075A8E4 /* AppDelegate.m */; }; 17 | 1FA76ACB195893080075A8E4 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FA76ACA195893080075A8E4 /* ViewController.m */; }; 18 | 1FA76ACD195893080075A8E4 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1FA76ACC195893080075A8E4 /* Images.xcassets */; }; 19 | 1FA76AD4195893080075A8E4 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FA76AD3195893080075A8E4 /* XCTest.framework */; }; 20 | 1FA76AD5195893080075A8E4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FA76AB1195893080075A8E4 /* Foundation.framework */; }; 21 | 1FA76AD6195893080075A8E4 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FA76AB5195893080075A8E4 /* UIKit.framework */; }; 22 | 1FA76ADE195893080075A8E4 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1FA76ADC195893080075A8E4 /* InfoPlist.strings */; }; 23 | 1FA76AE0195893080075A8E4 /* KLCPopupExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FA76ADF195893080075A8E4 /* KLCPopupExampleTests.m */; }; 24 | 1FA76AF01958A2130075A8E4 /* KLCPopup.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FA76AEF1958A2130075A8E4 /* KLCPopup.m */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXContainerItemProxy section */ 28 | 1FA76AD7195893080075A8E4 /* PBXContainerItemProxy */ = { 29 | isa = PBXContainerItemProxy; 30 | containerPortal = 1FA76AA6195893080075A8E4 /* Project object */; 31 | proxyType = 1; 32 | remoteGlobalIDString = 1FA76AAD195893080075A8E4; 33 | remoteInfo = KLCPopupExample; 34 | }; 35 | /* End PBXContainerItemProxy section */ 36 | 37 | /* Begin PBXFileReference section */ 38 | 1F7E1B051965CF7D00D576D8 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 39 | 1FA76AAE195893080075A8E4 /* KLCPopupExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KLCPopupExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | 1FA76AB1195893080075A8E4 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 41 | 1FA76AB3195893080075A8E4 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 42 | 1FA76AB5195893080075A8E4 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 43 | 1FA76AB9195893080075A8E4 /* KLCPopupExample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "KLCPopupExample-Info.plist"; sourceTree = ""; }; 44 | 1FA76ABB195893080075A8E4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 45 | 1FA76ABD195893080075A8E4 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 46 | 1FA76ABF195893080075A8E4 /* KLCPopupExample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "KLCPopupExample-Prefix.pch"; sourceTree = ""; }; 47 | 1FA76AC0195893080075A8E4 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 48 | 1FA76AC1195893080075A8E4 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 49 | 1FA76AC9195893080075A8E4 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 50 | 1FA76ACA195893080075A8E4 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 51 | 1FA76ACC195893080075A8E4 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 52 | 1FA76AD2195893080075A8E4 /* KLCPopupExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KLCPopupExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | 1FA76AD3195893080075A8E4 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 54 | 1FA76ADB195893080075A8E4 /* KLCPopupExampleTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "KLCPopupExampleTests-Info.plist"; sourceTree = ""; }; 55 | 1FA76ADD195893080075A8E4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 56 | 1FA76ADF195893080075A8E4 /* KLCPopupExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KLCPopupExampleTests.m; sourceTree = ""; }; 57 | 1FA76AEE1958A2130075A8E4 /* KLCPopup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KLCPopup.h; sourceTree = ""; }; 58 | 1FA76AEF1958A2130075A8E4 /* KLCPopup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KLCPopup.m; sourceTree = ""; }; 59 | /* End PBXFileReference section */ 60 | 61 | /* Begin PBXFrameworksBuildPhase section */ 62 | 1FA76AAB195893080075A8E4 /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | 1F7E1B061965CF7D00D576D8 /* QuartzCore.framework in Frameworks */, 67 | 1FA76AB4195893080075A8E4 /* CoreGraphics.framework in Frameworks */, 68 | 1FA76AB6195893080075A8E4 /* UIKit.framework in Frameworks */, 69 | 1FA76AB2195893080075A8E4 /* Foundation.framework in Frameworks */, 70 | ); 71 | runOnlyForDeploymentPostprocessing = 0; 72 | }; 73 | 1FA76ACF195893080075A8E4 /* Frameworks */ = { 74 | isa = PBXFrameworksBuildPhase; 75 | buildActionMask = 2147483647; 76 | files = ( 77 | 1FA76AD4195893080075A8E4 /* XCTest.framework in Frameworks */, 78 | 1FA76AD6195893080075A8E4 /* UIKit.framework in Frameworks */, 79 | 1FA76AD5195893080075A8E4 /* Foundation.framework in Frameworks */, 80 | ); 81 | runOnlyForDeploymentPostprocessing = 0; 82 | }; 83 | /* End PBXFrameworksBuildPhase section */ 84 | 85 | /* Begin PBXGroup section */ 86 | 1FA76AA5195893080075A8E4 = { 87 | isa = PBXGroup; 88 | children = ( 89 | 1FA76AB7195893080075A8E4 /* KLCPopupExample */, 90 | 1FA76AD9195893080075A8E4 /* KLCPopupExampleTests */, 91 | 1FA76AED1958A2130075A8E4 /* KLCPopup */, 92 | 1FA76AB0195893080075A8E4 /* Frameworks */, 93 | 1FA76AAF195893080075A8E4 /* Products */, 94 | ); 95 | sourceTree = ""; 96 | }; 97 | 1FA76AAF195893080075A8E4 /* Products */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 1FA76AAE195893080075A8E4 /* KLCPopupExample.app */, 101 | 1FA76AD2195893080075A8E4 /* KLCPopupExampleTests.xctest */, 102 | ); 103 | name = Products; 104 | sourceTree = ""; 105 | }; 106 | 1FA76AB0195893080075A8E4 /* Frameworks */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 1F7E1B051965CF7D00D576D8 /* QuartzCore.framework */, 110 | 1FA76AB1195893080075A8E4 /* Foundation.framework */, 111 | 1FA76AB3195893080075A8E4 /* CoreGraphics.framework */, 112 | 1FA76AB5195893080075A8E4 /* UIKit.framework */, 113 | 1FA76AD3195893080075A8E4 /* XCTest.framework */, 114 | ); 115 | name = Frameworks; 116 | sourceTree = ""; 117 | }; 118 | 1FA76AB7195893080075A8E4 /* KLCPopupExample */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 1FA76AC0195893080075A8E4 /* AppDelegate.h */, 122 | 1FA76AC1195893080075A8E4 /* AppDelegate.m */, 123 | 1FA76AC9195893080075A8E4 /* ViewController.h */, 124 | 1FA76ACA195893080075A8E4 /* ViewController.m */, 125 | 1FA76ACC195893080075A8E4 /* Images.xcassets */, 126 | 1FA76AB8195893080075A8E4 /* Supporting Files */, 127 | ); 128 | path = KLCPopupExample; 129 | sourceTree = ""; 130 | }; 131 | 1FA76AB8195893080075A8E4 /* Supporting Files */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 1FA76AB9195893080075A8E4 /* KLCPopupExample-Info.plist */, 135 | 1FA76ABA195893080075A8E4 /* InfoPlist.strings */, 136 | 1FA76ABD195893080075A8E4 /* main.m */, 137 | 1FA76ABF195893080075A8E4 /* KLCPopupExample-Prefix.pch */, 138 | ); 139 | name = "Supporting Files"; 140 | sourceTree = ""; 141 | }; 142 | 1FA76AD9195893080075A8E4 /* KLCPopupExampleTests */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | 1FA76ADF195893080075A8E4 /* KLCPopupExampleTests.m */, 146 | 1FA76ADA195893080075A8E4 /* Supporting Files */, 147 | ); 148 | path = KLCPopupExampleTests; 149 | sourceTree = ""; 150 | }; 151 | 1FA76ADA195893080075A8E4 /* Supporting Files */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 1FA76ADB195893080075A8E4 /* KLCPopupExampleTests-Info.plist */, 155 | 1FA76ADC195893080075A8E4 /* InfoPlist.strings */, 156 | ); 157 | name = "Supporting Files"; 158 | sourceTree = ""; 159 | }; 160 | 1FA76AED1958A2130075A8E4 /* KLCPopup */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | 1FA76AEE1958A2130075A8E4 /* KLCPopup.h */, 164 | 1FA76AEF1958A2130075A8E4 /* KLCPopup.m */, 165 | ); 166 | name = KLCPopup; 167 | path = ../KLCPopup; 168 | sourceTree = ""; 169 | }; 170 | /* End PBXGroup section */ 171 | 172 | /* Begin PBXNativeTarget section */ 173 | 1FA76AAD195893080075A8E4 /* KLCPopupExample */ = { 174 | isa = PBXNativeTarget; 175 | buildConfigurationList = 1FA76AE3195893080075A8E4 /* Build configuration list for PBXNativeTarget "KLCPopupExample" */; 176 | buildPhases = ( 177 | 1FA76AAA195893080075A8E4 /* Sources */, 178 | 1FA76AAB195893080075A8E4 /* Frameworks */, 179 | 1FA76AAC195893080075A8E4 /* Resources */, 180 | ); 181 | buildRules = ( 182 | ); 183 | dependencies = ( 184 | ); 185 | name = KLCPopupExample; 186 | productName = KLCPopupExample; 187 | productReference = 1FA76AAE195893080075A8E4 /* KLCPopupExample.app */; 188 | productType = "com.apple.product-type.application"; 189 | }; 190 | 1FA76AD1195893080075A8E4 /* KLCPopupExampleTests */ = { 191 | isa = PBXNativeTarget; 192 | buildConfigurationList = 1FA76AE6195893080075A8E4 /* Build configuration list for PBXNativeTarget "KLCPopupExampleTests" */; 193 | buildPhases = ( 194 | 1FA76ACE195893080075A8E4 /* Sources */, 195 | 1FA76ACF195893080075A8E4 /* Frameworks */, 196 | 1FA76AD0195893080075A8E4 /* Resources */, 197 | ); 198 | buildRules = ( 199 | ); 200 | dependencies = ( 201 | 1FA76AD8195893080075A8E4 /* PBXTargetDependency */, 202 | ); 203 | name = KLCPopupExampleTests; 204 | productName = KLCPopupExampleTests; 205 | productReference = 1FA76AD2195893080075A8E4 /* KLCPopupExampleTests.xctest */; 206 | productType = "com.apple.product-type.bundle.unit-test"; 207 | }; 208 | /* End PBXNativeTarget section */ 209 | 210 | /* Begin PBXProject section */ 211 | 1FA76AA6195893080075A8E4 /* Project object */ = { 212 | isa = PBXProject; 213 | attributes = { 214 | CLASSPREFIX = KLC; 215 | LastUpgradeCheck = 0510; 216 | ORGANIZATIONNAME = Kullect; 217 | TargetAttributes = { 218 | 1FA76AD1195893080075A8E4 = { 219 | TestTargetID = 1FA76AAD195893080075A8E4; 220 | }; 221 | }; 222 | }; 223 | buildConfigurationList = 1FA76AA9195893080075A8E4 /* Build configuration list for PBXProject "KLCPopupExample" */; 224 | compatibilityVersion = "Xcode 3.2"; 225 | developmentRegion = English; 226 | hasScannedForEncodings = 0; 227 | knownRegions = ( 228 | en, 229 | Base, 230 | ); 231 | mainGroup = 1FA76AA5195893080075A8E4; 232 | productRefGroup = 1FA76AAF195893080075A8E4 /* Products */; 233 | projectDirPath = ""; 234 | projectRoot = ""; 235 | targets = ( 236 | 1FA76AAD195893080075A8E4 /* KLCPopupExample */, 237 | 1FA76AD1195893080075A8E4 /* KLCPopupExampleTests */, 238 | ); 239 | }; 240 | /* End PBXProject section */ 241 | 242 | /* Begin PBXResourcesBuildPhase section */ 243 | 1FA76AAC195893080075A8E4 /* Resources */ = { 244 | isa = PBXResourcesBuildPhase; 245 | buildActionMask = 2147483647; 246 | files = ( 247 | 1FA76ACD195893080075A8E4 /* Images.xcassets in Resources */, 248 | 1FA76ABC195893080075A8E4 /* InfoPlist.strings in Resources */, 249 | ); 250 | runOnlyForDeploymentPostprocessing = 0; 251 | }; 252 | 1FA76AD0195893080075A8E4 /* Resources */ = { 253 | isa = PBXResourcesBuildPhase; 254 | buildActionMask = 2147483647; 255 | files = ( 256 | 1FA76ADE195893080075A8E4 /* InfoPlist.strings in Resources */, 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | }; 260 | /* End PBXResourcesBuildPhase section */ 261 | 262 | /* Begin PBXSourcesBuildPhase section */ 263 | 1FA76AAA195893080075A8E4 /* Sources */ = { 264 | isa = PBXSourcesBuildPhase; 265 | buildActionMask = 2147483647; 266 | files = ( 267 | 1FA76ACB195893080075A8E4 /* ViewController.m in Sources */, 268 | 1FA76ABE195893080075A8E4 /* main.m in Sources */, 269 | 1FA76AC2195893080075A8E4 /* AppDelegate.m in Sources */, 270 | 1FA76AF01958A2130075A8E4 /* KLCPopup.m in Sources */, 271 | ); 272 | runOnlyForDeploymentPostprocessing = 0; 273 | }; 274 | 1FA76ACE195893080075A8E4 /* Sources */ = { 275 | isa = PBXSourcesBuildPhase; 276 | buildActionMask = 2147483647; 277 | files = ( 278 | 1FA76AE0195893080075A8E4 /* KLCPopupExampleTests.m in Sources */, 279 | ); 280 | runOnlyForDeploymentPostprocessing = 0; 281 | }; 282 | /* End PBXSourcesBuildPhase section */ 283 | 284 | /* Begin PBXTargetDependency section */ 285 | 1FA76AD8195893080075A8E4 /* PBXTargetDependency */ = { 286 | isa = PBXTargetDependency; 287 | target = 1FA76AAD195893080075A8E4 /* KLCPopupExample */; 288 | targetProxy = 1FA76AD7195893080075A8E4 /* PBXContainerItemProxy */; 289 | }; 290 | /* End PBXTargetDependency section */ 291 | 292 | /* Begin PBXVariantGroup section */ 293 | 1FA76ABA195893080075A8E4 /* InfoPlist.strings */ = { 294 | isa = PBXVariantGroup; 295 | children = ( 296 | 1FA76ABB195893080075A8E4 /* en */, 297 | ); 298 | name = InfoPlist.strings; 299 | sourceTree = ""; 300 | }; 301 | 1FA76ADC195893080075A8E4 /* InfoPlist.strings */ = { 302 | isa = PBXVariantGroup; 303 | children = ( 304 | 1FA76ADD195893080075A8E4 /* en */, 305 | ); 306 | name = InfoPlist.strings; 307 | sourceTree = ""; 308 | }; 309 | /* End PBXVariantGroup section */ 310 | 311 | /* Begin XCBuildConfiguration section */ 312 | 1FA76AE1195893080075A8E4 /* Debug */ = { 313 | isa = XCBuildConfiguration; 314 | buildSettings = { 315 | ALWAYS_SEARCH_USER_PATHS = NO; 316 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 317 | CLANG_CXX_LIBRARY = "libc++"; 318 | CLANG_ENABLE_MODULES = YES; 319 | CLANG_ENABLE_OBJC_ARC = YES; 320 | CLANG_WARN_BOOL_CONVERSION = YES; 321 | CLANG_WARN_CONSTANT_CONVERSION = YES; 322 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 323 | CLANG_WARN_EMPTY_BODY = YES; 324 | CLANG_WARN_ENUM_CONVERSION = YES; 325 | CLANG_WARN_INT_CONVERSION = YES; 326 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 327 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 328 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 329 | COPY_PHASE_STRIP = NO; 330 | GCC_C_LANGUAGE_STANDARD = gnu99; 331 | GCC_DYNAMIC_NO_PIC = NO; 332 | GCC_OPTIMIZATION_LEVEL = 0; 333 | GCC_PREPROCESSOR_DEFINITIONS = ( 334 | "DEBUG=1", 335 | "$(inherited)", 336 | ); 337 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 338 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 339 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 340 | GCC_WARN_UNDECLARED_SELECTOR = YES; 341 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 342 | GCC_WARN_UNUSED_FUNCTION = YES; 343 | GCC_WARN_UNUSED_VARIABLE = YES; 344 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 345 | ONLY_ACTIVE_ARCH = YES; 346 | SDKROOT = iphoneos; 347 | TARGETED_DEVICE_FAMILY = "1,2"; 348 | }; 349 | name = Debug; 350 | }; 351 | 1FA76AE2195893080075A8E4 /* Release */ = { 352 | isa = XCBuildConfiguration; 353 | buildSettings = { 354 | ALWAYS_SEARCH_USER_PATHS = NO; 355 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 356 | CLANG_CXX_LIBRARY = "libc++"; 357 | CLANG_ENABLE_MODULES = YES; 358 | CLANG_ENABLE_OBJC_ARC = YES; 359 | CLANG_WARN_BOOL_CONVERSION = YES; 360 | CLANG_WARN_CONSTANT_CONVERSION = YES; 361 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 362 | CLANG_WARN_EMPTY_BODY = YES; 363 | CLANG_WARN_ENUM_CONVERSION = YES; 364 | CLANG_WARN_INT_CONVERSION = YES; 365 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 366 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 367 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 368 | COPY_PHASE_STRIP = YES; 369 | ENABLE_NS_ASSERTIONS = NO; 370 | GCC_C_LANGUAGE_STANDARD = gnu99; 371 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 372 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 373 | GCC_WARN_UNDECLARED_SELECTOR = YES; 374 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 375 | GCC_WARN_UNUSED_FUNCTION = YES; 376 | GCC_WARN_UNUSED_VARIABLE = YES; 377 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 378 | SDKROOT = iphoneos; 379 | TARGETED_DEVICE_FAMILY = "1,2"; 380 | VALIDATE_PRODUCT = YES; 381 | }; 382 | name = Release; 383 | }; 384 | 1FA76AE4195893080075A8E4 /* Debug */ = { 385 | isa = XCBuildConfiguration; 386 | buildSettings = { 387 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 388 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 389 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 390 | GCC_PREFIX_HEADER = "KLCPopupExample/KLCPopupExample-Prefix.pch"; 391 | INFOPLIST_FILE = "KLCPopupExample/KLCPopupExample-Info.plist"; 392 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 393 | PRODUCT_NAME = "$(TARGET_NAME)"; 394 | WRAPPER_EXTENSION = app; 395 | }; 396 | name = Debug; 397 | }; 398 | 1FA76AE5195893080075A8E4 /* Release */ = { 399 | isa = XCBuildConfiguration; 400 | buildSettings = { 401 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 402 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 403 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 404 | GCC_PREFIX_HEADER = "KLCPopupExample/KLCPopupExample-Prefix.pch"; 405 | INFOPLIST_FILE = "KLCPopupExample/KLCPopupExample-Info.plist"; 406 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 407 | PRODUCT_NAME = "$(TARGET_NAME)"; 408 | WRAPPER_EXTENSION = app; 409 | }; 410 | name = Release; 411 | }; 412 | 1FA76AE7195893080075A8E4 /* Debug */ = { 413 | isa = XCBuildConfiguration; 414 | buildSettings = { 415 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/KLCPopupExample.app/KLCPopupExample"; 416 | FRAMEWORK_SEARCH_PATHS = ( 417 | "$(SDKROOT)/Developer/Library/Frameworks", 418 | "$(inherited)", 419 | "$(DEVELOPER_FRAMEWORKS_DIR)", 420 | ); 421 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 422 | GCC_PREFIX_HEADER = "KLCPopupExample/KLCPopupExample-Prefix.pch"; 423 | GCC_PREPROCESSOR_DEFINITIONS = ( 424 | "DEBUG=1", 425 | "$(inherited)", 426 | ); 427 | INFOPLIST_FILE = "KLCPopupExampleTests/KLCPopupExampleTests-Info.plist"; 428 | PRODUCT_NAME = "$(TARGET_NAME)"; 429 | TEST_HOST = "$(BUNDLE_LOADER)"; 430 | WRAPPER_EXTENSION = xctest; 431 | }; 432 | name = Debug; 433 | }; 434 | 1FA76AE8195893080075A8E4 /* Release */ = { 435 | isa = XCBuildConfiguration; 436 | buildSettings = { 437 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/KLCPopupExample.app/KLCPopupExample"; 438 | FRAMEWORK_SEARCH_PATHS = ( 439 | "$(SDKROOT)/Developer/Library/Frameworks", 440 | "$(inherited)", 441 | "$(DEVELOPER_FRAMEWORKS_DIR)", 442 | ); 443 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 444 | GCC_PREFIX_HEADER = "KLCPopupExample/KLCPopupExample-Prefix.pch"; 445 | INFOPLIST_FILE = "KLCPopupExampleTests/KLCPopupExampleTests-Info.plist"; 446 | PRODUCT_NAME = "$(TARGET_NAME)"; 447 | TEST_HOST = "$(BUNDLE_LOADER)"; 448 | WRAPPER_EXTENSION = xctest; 449 | }; 450 | name = Release; 451 | }; 452 | /* End XCBuildConfiguration section */ 453 | 454 | /* Begin XCConfigurationList section */ 455 | 1FA76AA9195893080075A8E4 /* Build configuration list for PBXProject "KLCPopupExample" */ = { 456 | isa = XCConfigurationList; 457 | buildConfigurations = ( 458 | 1FA76AE1195893080075A8E4 /* Debug */, 459 | 1FA76AE2195893080075A8E4 /* Release */, 460 | ); 461 | defaultConfigurationIsVisible = 0; 462 | defaultConfigurationName = Release; 463 | }; 464 | 1FA76AE3195893080075A8E4 /* Build configuration list for PBXNativeTarget "KLCPopupExample" */ = { 465 | isa = XCConfigurationList; 466 | buildConfigurations = ( 467 | 1FA76AE4195893080075A8E4 /* Debug */, 468 | 1FA76AE5195893080075A8E4 /* Release */, 469 | ); 470 | defaultConfigurationIsVisible = 0; 471 | defaultConfigurationName = Release; 472 | }; 473 | 1FA76AE6195893080075A8E4 /* Build configuration list for PBXNativeTarget "KLCPopupExampleTests" */ = { 474 | isa = XCConfigurationList; 475 | buildConfigurations = ( 476 | 1FA76AE7195893080075A8E4 /* Debug */, 477 | 1FA76AE8195893080075A8E4 /* Release */, 478 | ); 479 | defaultConfigurationIsVisible = 0; 480 | defaultConfigurationName = Release; 481 | }; 482 | /* End XCConfigurationList section */ 483 | }; 484 | rootObject = 1FA76AA6195893080075A8E4 /* Project object */; 485 | } 486 | -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // KLCPopupExample 4 | // 5 | // Copyright (c) 2014 Jeff Mascia (http://jeffmascia.com/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | #import 26 | 27 | @interface AppDelegate : UIResponder 28 | 29 | @property (strong, nonatomic) UIWindow *window; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // KLCPopupExample 4 | // 5 | // Copyright (c) 2014 Jeff Mascia (http://jeffmascia.com/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | #import "AppDelegate.h" 26 | #import "ViewController.h" 27 | 28 | @implementation AppDelegate 29 | 30 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 31 | { 32 | // Create app window 33 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 34 | 35 | ViewController* viewController = [[ViewController alloc] init]; 36 | UINavigationController* navigationController = [[UINavigationController alloc] initWithRootViewController:viewController]; 37 | [self.window setRootViewController:navigationController]; 38 | [self.window makeKeyAndVisible]; 39 | 40 | return YES; 41 | } 42 | 43 | - (void)applicationWillResignActive:(UIApplication *)application 44 | { 45 | // 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. 46 | // 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. 47 | } 48 | 49 | - (void)applicationDidEnterBackground:(UIApplication *)application 50 | { 51 | // 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. 52 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 53 | } 54 | 55 | - (void)applicationWillEnterForeground:(UIApplication *)application 56 | { 57 | // 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. 58 | } 59 | 60 | - (void)applicationDidBecomeActive:(UIApplication *)application 61 | { 62 | // 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. 63 | } 64 | 65 | - (void)applicationWillTerminate:(UIApplication *)application 66 | { 67 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "filename" : "icon-58-1.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "40x40", 11 | "idiom" : "iphone", 12 | "filename" : "icon-80.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "60x60", 17 | "idiom" : "iphone", 18 | "filename" : "icon-120.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "ipad", 24 | "filename" : "icon-29.png", 25 | "scale" : "1x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "ipad", 30 | "filename" : "icon-58.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "ipad", 36 | "filename" : "icon-40.png", 37 | "scale" : "1x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "ipad", 42 | "filename" : "icon-80-1.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "76x76", 47 | "idiom" : "ipad", 48 | "filename" : "icon-76.png", 49 | "scale" : "1x" 50 | }, 51 | { 52 | "size" : "76x76", 53 | "idiom" : "ipad", 54 | "filename" : "icon-152.png", 55 | "scale" : "2x" 56 | } 57 | ], 58 | "info" : { 59 | "version" : 1, 60 | "author" : "xcode" 61 | } 62 | } -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/Images.xcassets/AppIcon.appiconset/icon-120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmascia/KLCPopup/ba17c211cbfa62761fddb58201f9381126ca1fe7/KLCPopupExample/KLCPopupExample/Images.xcassets/AppIcon.appiconset/icon-120.png -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/Images.xcassets/AppIcon.appiconset/icon-152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmascia/KLCPopup/ba17c211cbfa62761fddb58201f9381126ca1fe7/KLCPopupExample/KLCPopupExample/Images.xcassets/AppIcon.appiconset/icon-152.png -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/Images.xcassets/AppIcon.appiconset/icon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmascia/KLCPopup/ba17c211cbfa62761fddb58201f9381126ca1fe7/KLCPopupExample/KLCPopupExample/Images.xcassets/AppIcon.appiconset/icon-29.png -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/Images.xcassets/AppIcon.appiconset/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmascia/KLCPopup/ba17c211cbfa62761fddb58201f9381126ca1fe7/KLCPopupExample/KLCPopupExample/Images.xcassets/AppIcon.appiconset/icon-40.png -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/Images.xcassets/AppIcon.appiconset/icon-58-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmascia/KLCPopup/ba17c211cbfa62761fddb58201f9381126ca1fe7/KLCPopupExample/KLCPopupExample/Images.xcassets/AppIcon.appiconset/icon-58-1.png -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/Images.xcassets/AppIcon.appiconset/icon-58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmascia/KLCPopup/ba17c211cbfa62761fddb58201f9381126ca1fe7/KLCPopupExample/KLCPopupExample/Images.xcassets/AppIcon.appiconset/icon-58.png -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/Images.xcassets/AppIcon.appiconset/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmascia/KLCPopup/ba17c211cbfa62761fddb58201f9381126ca1fe7/KLCPopupExample/KLCPopupExample/Images.xcassets/AppIcon.appiconset/icon-76.png -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/Images.xcassets/AppIcon.appiconset/icon-80-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmascia/KLCPopup/ba17c211cbfa62761fddb58201f9381126ca1fe7/KLCPopupExample/KLCPopupExample/Images.xcassets/AppIcon.appiconset/icon-80-1.png -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/Images.xcassets/AppIcon.appiconset/icon-80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmascia/KLCPopup/ba17c211cbfa62761fddb58201f9381126ca1fe7/KLCPopupExample/KLCPopupExample/Images.xcassets/AppIcon.appiconset/icon-80.png -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "filename" : "launch_iphone_2x.png", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "extent" : "full-screen", 13 | "idiom" : "iphone", 14 | "subtype" : "retina4", 15 | "filename" : "launch_iphone_r4.png", 16 | "minimum-system-version" : "7.0", 17 | "orientation" : "portrait", 18 | "scale" : "2x" 19 | }, 20 | { 21 | "orientation" : "portrait", 22 | "idiom" : "ipad", 23 | "extent" : "full-screen", 24 | "minimum-system-version" : "7.0", 25 | "filename" : "launch_ipad_portrait.png", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "orientation" : "landscape", 30 | "idiom" : "ipad", 31 | "extent" : "full-screen", 32 | "minimum-system-version" : "7.0", 33 | "filename" : "launch_ipad_landscape.png", 34 | "scale" : "1x" 35 | }, 36 | { 37 | "orientation" : "portrait", 38 | "idiom" : "ipad", 39 | "extent" : "full-screen", 40 | "minimum-system-version" : "7.0", 41 | "filename" : "launch_ipad_portrait@2x.png", 42 | "scale" : "2x" 43 | }, 44 | { 45 | "orientation" : "landscape", 46 | "idiom" : "ipad", 47 | "extent" : "full-screen", 48 | "minimum-system-version" : "7.0", 49 | "filename" : "launch_ipad_landscape@2x.png", 50 | "scale" : "2x" 51 | } 52 | ], 53 | "info" : { 54 | "version" : 1, 55 | "author" : "xcode" 56 | } 57 | } -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/Images.xcassets/LaunchImage.launchimage/launch_ipad_landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmascia/KLCPopup/ba17c211cbfa62761fddb58201f9381126ca1fe7/KLCPopupExample/KLCPopupExample/Images.xcassets/LaunchImage.launchimage/launch_ipad_landscape.png -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/Images.xcassets/LaunchImage.launchimage/launch_ipad_landscape@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmascia/KLCPopup/ba17c211cbfa62761fddb58201f9381126ca1fe7/KLCPopupExample/KLCPopupExample/Images.xcassets/LaunchImage.launchimage/launch_ipad_landscape@2x.png -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/Images.xcassets/LaunchImage.launchimage/launch_ipad_portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmascia/KLCPopup/ba17c211cbfa62761fddb58201f9381126ca1fe7/KLCPopupExample/KLCPopupExample/Images.xcassets/LaunchImage.launchimage/launch_ipad_portrait.png -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/Images.xcassets/LaunchImage.launchimage/launch_ipad_portrait@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmascia/KLCPopup/ba17c211cbfa62761fddb58201f9381126ca1fe7/KLCPopupExample/KLCPopupExample/Images.xcassets/LaunchImage.launchimage/launch_ipad_portrait@2x.png -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/Images.xcassets/LaunchImage.launchimage/launch_iphone_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmascia/KLCPopup/ba17c211cbfa62761fddb58201f9381126ca1fe7/KLCPopupExample/KLCPopupExample/Images.xcassets/LaunchImage.launchimage/launch_iphone_2x.png -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/Images.xcassets/LaunchImage.launchimage/launch_iphone_r4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmascia/KLCPopup/ba17c211cbfa62761fddb58201f9381126ca1fe7/KLCPopupExample/KLCPopupExample/Images.xcassets/LaunchImage.launchimage/launch_iphone_r4.png -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/KLCPopupExample-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | KLCPopup 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.kullect.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.1 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 2 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/KLCPopupExample-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // KLCPopupExample 4 | // 5 | // Copyright (c) 2014 Jeff Mascia (http://jeffmascia.com/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | #import 26 | 27 | @interface ViewController : UIViewController 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // KLCPopupExample 4 | // 5 | // Copyright (c) 2014 Jeff Mascia (http://jeffmascia.com/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | #import "ViewController.h" 26 | #import "KLCPopup.h" 27 | #import 28 | 29 | 30 | typedef NS_ENUM(NSInteger, FieldTag) { 31 | FieldTagHorizontalLayout = 1001, 32 | FieldTagVerticalLayout, 33 | FieldTagMaskType, 34 | FieldTagShowType, 35 | FieldTagDismissType, 36 | FieldTagBackgroundDismiss, 37 | FieldTagContentDismiss, 38 | FieldTagTimedDismiss, 39 | }; 40 | 41 | 42 | typedef NS_ENUM(NSInteger, CellType) { 43 | CellTypeNormal = 0, 44 | CellTypeSwitch, 45 | }; 46 | 47 | 48 | @interface ViewController () { 49 | 50 | NSArray* _fields; 51 | NSDictionary* _namesForFields; 52 | 53 | NSArray* _horizontalLayouts; 54 | NSArray* _verticalLayouts; 55 | NSArray* _maskTypes; 56 | NSArray* _showTypes; 57 | NSArray* _dismissTypes; 58 | 59 | NSDictionary* _namesForHorizontalLayouts; 60 | NSDictionary* _namesForVerticalLayouts; 61 | NSDictionary* _namesForMaskTypes; 62 | NSDictionary* _namesForShowTypes; 63 | NSDictionary* _namesForDismissTypes; 64 | 65 | NSInteger _selectedRowInHorizontalField; 66 | NSInteger _selectedRowInVerticalField; 67 | NSInteger _selectedRowInMaskField; 68 | NSInteger _selectedRowInShowField; 69 | NSInteger _selectedRowInDismissField; 70 | BOOL _shouldDismissOnBackgroundTouch; 71 | BOOL _shouldDismissOnContentTouch; 72 | BOOL _shouldDismissAfterDelay; 73 | } 74 | 75 | @property (nonatomic, strong) UITableView* tableView; 76 | @property (nonatomic, strong) UIPopoverController* popover; 77 | 78 | // Private 79 | - (void)updateFieldTableView:(UITableView*)tableView; 80 | - (NSInteger)valueForRow:(NSInteger)row inFieldWithTag:(NSInteger)tag; 81 | - (NSInteger)selectedRowForFieldWithTag:(NSInteger)tag; 82 | - (NSString*)nameForValue:(NSInteger)value inFieldWithTag:(NSInteger)tag; 83 | - (CellType)cellTypeForFieldWithTag:(NSInteger)tag; 84 | 85 | // Event handlers 86 | - (void)toggleValueDidChange:(id)sender; 87 | - (void)showButtonPressed:(id)sender; 88 | - (void)dismissButtonPressed:(id)sender; 89 | - (void)fieldCancelButtonPressed:(id)sender; 90 | 91 | @end 92 | 93 | 94 | @interface UIColor (KLCPopupExample) 95 | + (UIColor*)klcLightGreenColor; 96 | + (UIColor*)klcGreenColor; 97 | @end 98 | 99 | 100 | @interface UIView (KLCPopupExample) 101 | - (UITableViewCell*)parentCell; 102 | @end 103 | 104 | 105 | 106 | @implementation ViewController 107 | 108 | #pragma mark - UIViewController 109 | 110 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { 111 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 112 | if (self) { 113 | 114 | self.title = @"KLCPopup Example"; 115 | 116 | // MAIN LIST 117 | _fields = @[@(FieldTagHorizontalLayout), 118 | @(FieldTagVerticalLayout), 119 | @(FieldTagMaskType), 120 | @(FieldTagShowType), 121 | @(FieldTagDismissType), 122 | @(FieldTagBackgroundDismiss), 123 | @(FieldTagContentDismiss), 124 | @(FieldTagTimedDismiss)]; 125 | 126 | _namesForFields = @{@(FieldTagHorizontalLayout) : @"Horizontal layout", 127 | @(FieldTagVerticalLayout) : @"Vertical layout", 128 | @(FieldTagMaskType) : @"Background mask", 129 | @(FieldTagShowType) : @"Show type", 130 | @(FieldTagDismissType) : @"Dismiss type", 131 | @(FieldTagBackgroundDismiss) : @"Dismiss on background touch", 132 | @(FieldTagContentDismiss) : @"Dismiss on content touch", 133 | @(FieldTagTimedDismiss) : @"Dismiss after delay"}; 134 | 135 | // FIELD SUB-LISTS 136 | _horizontalLayouts = @[@(KLCPopupHorizontalLayoutLeft), 137 | @(KLCPopupHorizontalLayoutLeftOfCenter), 138 | @(KLCPopupHorizontalLayoutCenter), 139 | @(KLCPopupHorizontalLayoutRightOfCenter), 140 | @(KLCPopupHorizontalLayoutRight)]; 141 | 142 | _namesForHorizontalLayouts = @{@(KLCPopupHorizontalLayoutLeft) : @"Left", 143 | @(KLCPopupHorizontalLayoutLeftOfCenter) : @"Left of Center", 144 | @(KLCPopupHorizontalLayoutCenter) : @"Center", 145 | @(KLCPopupHorizontalLayoutRightOfCenter) : @"Right of Center", 146 | @(KLCPopupHorizontalLayoutRight) : @"Right"}; 147 | 148 | _verticalLayouts = @[@(KLCPopupVerticalLayoutTop), 149 | @(KLCPopupVerticalLayoutAboveCenter), 150 | @(KLCPopupVerticalLayoutCenter), 151 | @(KLCPopupVerticalLayoutBelowCenter), 152 | @(KLCPopupVerticalLayoutBottom)]; 153 | 154 | _namesForVerticalLayouts = @{@(KLCPopupVerticalLayoutTop) : @"Top", 155 | @(KLCPopupVerticalLayoutAboveCenter) : @"Above Center", 156 | @(KLCPopupVerticalLayoutCenter) : @"Center", 157 | @(KLCPopupVerticalLayoutBelowCenter) : @"Below Center", 158 | @(KLCPopupVerticalLayoutBottom) : @"Bottom"}; 159 | 160 | _maskTypes = @[@(KLCPopupMaskTypeNone), 161 | @(KLCPopupMaskTypeClear), 162 | @(KLCPopupMaskTypeDimmed)]; 163 | 164 | _namesForMaskTypes = @{@(KLCPopupMaskTypeNone) : @"None", 165 | @(KLCPopupMaskTypeClear) : @"Clear", 166 | @(KLCPopupMaskTypeDimmed) : @"Dimmed"}; 167 | 168 | _showTypes = @[@(KLCPopupShowTypeNone), 169 | @(KLCPopupShowTypeFadeIn), 170 | @(KLCPopupShowTypeGrowIn), 171 | @(KLCPopupShowTypeShrinkIn), 172 | @(KLCPopupShowTypeSlideInFromTop), 173 | @(KLCPopupShowTypeSlideInFromBottom), 174 | @(KLCPopupShowTypeSlideInFromLeft), 175 | @(KLCPopupShowTypeSlideInFromRight), 176 | @(KLCPopupShowTypeBounceIn), 177 | @(KLCPopupShowTypeBounceInFromTop), 178 | @(KLCPopupShowTypeBounceInFromBottom), 179 | @(KLCPopupShowTypeBounceInFromLeft), 180 | @(KLCPopupShowTypeBounceInFromRight)]; 181 | 182 | _namesForShowTypes = @{@(KLCPopupShowTypeNone) : @"None", 183 | @(KLCPopupShowTypeFadeIn) : @"Fade in", 184 | @(KLCPopupShowTypeGrowIn) : @"Grow in", 185 | @(KLCPopupShowTypeShrinkIn) : @"Shrink in", 186 | @(KLCPopupShowTypeSlideInFromTop) : @"Slide from Top", 187 | @(KLCPopupShowTypeSlideInFromBottom) : @"Slide from Bottom", 188 | @(KLCPopupShowTypeSlideInFromLeft) : @"Slide from Left", 189 | @(KLCPopupShowTypeSlideInFromRight) : @"Slide from Right", 190 | @(KLCPopupShowTypeBounceIn) : @"Bounce in", 191 | @(KLCPopupShowTypeBounceInFromTop) : @"Bounce from Top", 192 | @(KLCPopupShowTypeBounceInFromBottom) : @"Bounce from Bottom", 193 | @(KLCPopupShowTypeBounceInFromLeft) : @"Bounce from Left", 194 | @(KLCPopupShowTypeBounceInFromRight) : @"Bounce from Right"}; 195 | 196 | _dismissTypes = @[@(KLCPopupDismissTypeNone), 197 | @(KLCPopupDismissTypeFadeOut), 198 | @(KLCPopupDismissTypeGrowOut), 199 | @(KLCPopupDismissTypeShrinkOut), 200 | @(KLCPopupDismissTypeSlideOutToTop), 201 | @(KLCPopupDismissTypeSlideOutToBottom), 202 | @(KLCPopupDismissTypeSlideOutToLeft), 203 | @(KLCPopupDismissTypeSlideOutToRight), 204 | @(KLCPopupDismissTypeBounceOut), 205 | @(KLCPopupDismissTypeBounceOutToTop), 206 | @(KLCPopupDismissTypeBounceOutToBottom), 207 | @(KLCPopupDismissTypeBounceOutToLeft), 208 | @(KLCPopupDismissTypeBounceOutToRight)]; 209 | 210 | _namesForDismissTypes = @{@(KLCPopupDismissTypeNone) : @"None", 211 | @(KLCPopupDismissTypeFadeOut) : @"Fade out", 212 | @(KLCPopupDismissTypeGrowOut) : @"Grow out", 213 | @(KLCPopupDismissTypeShrinkOut) : @"Shrink out", 214 | @(KLCPopupDismissTypeSlideOutToTop) : @"Slide to Top", 215 | @(KLCPopupDismissTypeSlideOutToBottom) : @"Slide to Bottom", 216 | @(KLCPopupDismissTypeSlideOutToLeft) : @"Slide to Left", 217 | @(KLCPopupDismissTypeSlideOutToRight) : @"Slide to Right", 218 | @(KLCPopupDismissTypeBounceOut) : @"Bounce out", 219 | @(KLCPopupDismissTypeBounceOutToTop) : @"Bounce to Top", 220 | @(KLCPopupDismissTypeBounceOutToBottom) : @"Bounce to Bottom", 221 | @(KLCPopupDismissTypeBounceOutToLeft) : @"Bounce to Left", 222 | @(KLCPopupDismissTypeBounceOutToRight) : @"Bounce to Right"}; 223 | 224 | // DEFAULTS 225 | _selectedRowInHorizontalField = [_horizontalLayouts indexOfObject:@(KLCPopupHorizontalLayoutCenter)]; 226 | _selectedRowInVerticalField = [_verticalLayouts indexOfObject:@(KLCPopupVerticalLayoutCenter)]; 227 | _selectedRowInMaskField = [_maskTypes indexOfObject:@(KLCPopupMaskTypeDimmed)]; 228 | _selectedRowInShowField = [_showTypes indexOfObject:@(KLCPopupShowTypeBounceInFromTop)]; 229 | _selectedRowInDismissField = [_dismissTypes indexOfObject:@(KLCPopupDismissTypeBounceOutToBottom)]; 230 | _shouldDismissOnBackgroundTouch = YES; 231 | _shouldDismissOnContentTouch = NO; 232 | _shouldDismissAfterDelay = NO; 233 | } 234 | return self; 235 | } 236 | 237 | 238 | - (void)loadView { 239 | [super loadView]; 240 | 241 | // TABLEVIEW 242 | UITableView* tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; 243 | tableView.translatesAutoresizingMaskIntoConstraints = NO; 244 | tableView.delegate = self; 245 | tableView.dataSource = self; 246 | tableView.delaysContentTouches = NO; 247 | self.tableView = tableView; 248 | [self.view addSubview:tableView]; 249 | 250 | NSDictionary* views = NSDictionaryOfVariableBindings(tableView); 251 | NSDictionary* metrics = nil; 252 | [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[tableView]|" 253 | options:0 254 | metrics:metrics 255 | views:views]]; 256 | 257 | [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[tableView]|" 258 | options:0 259 | metrics:metrics 260 | views:views]]; 261 | 262 | // FOOTER 263 | UIView* footerView = [[UIView alloc] init]; 264 | 265 | UIButton* showButton = [UIButton buttonWithType:UIButtonTypeCustom]; 266 | showButton.translatesAutoresizingMaskIntoConstraints = NO; 267 | showButton.contentEdgeInsets = UIEdgeInsetsMake(14, 28, 14, 28); 268 | [showButton setTitle:@"Show it!" forState:UIControlStateNormal]; 269 | showButton.backgroundColor = [UIColor lightGrayColor]; 270 | [showButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 271 | [showButton setTitleColor:[[showButton titleColorForState:UIControlStateNormal] colorWithAlphaComponent:0.5] forState:UIControlStateHighlighted]; 272 | showButton.titleLabel.font = [UIFont boldSystemFontOfSize:20.0]; 273 | [showButton.layer setCornerRadius:8.0]; 274 | [showButton addTarget:self action:@selector(showButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; 275 | 276 | [footerView addSubview:showButton]; 277 | 278 | CGFloat topMargin = 12.0; 279 | CGFloat bottomMargin = 12.0; 280 | 281 | views = NSDictionaryOfVariableBindings(showButton); 282 | metrics = @{@"topMargin" : @(topMargin), 283 | @"bottomMargin" : @(bottomMargin)}; 284 | [footerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(topMargin)-[showButton]-(bottomMargin)-|" 285 | options:0 286 | metrics:metrics 287 | views:views]]; 288 | 289 | [footerView addConstraint:[NSLayoutConstraint constraintWithItem:showButton 290 | attribute:NSLayoutAttributeCenterX 291 | relatedBy:NSLayoutRelationEqual 292 | toItem:showButton.superview 293 | attribute:NSLayoutAttributeCenterX 294 | multiplier:1.0 295 | constant:0.0]]; 296 | 297 | CGRect footerFrame = CGRectZero; 298 | footerFrame.size = [showButton systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; 299 | footerFrame.size.height += topMargin + bottomMargin; 300 | footerView.frame = footerFrame; 301 | self.tableView.tableFooterView = footerView; 302 | } 303 | 304 | 305 | - (void)viewDidLoad 306 | { 307 | [super viewDidLoad]; 308 | 309 | self.automaticallyAdjustsScrollViewInsets = YES; 310 | self.view.backgroundColor = [UIColor whiteColor]; 311 | } 312 | 313 | 314 | #pragma mark - Event Handlers 315 | 316 | - (void)toggleValueDidChange:(id)sender { 317 | 318 | if ([sender isKindOfClass:[UISwitch class]]) { 319 | UISwitch* toggle = (UISwitch*)sender; 320 | 321 | NSIndexPath* indexPath = [self.tableView indexPathForCell:[toggle parentCell]]; 322 | id obj = [_fields objectAtIndex:indexPath.row]; 323 | if ([obj isKindOfClass:[NSNumber class]]) { 324 | 325 | NSInteger fieldTag = [(NSNumber*)obj integerValue]; 326 | if (fieldTag == FieldTagBackgroundDismiss) { 327 | _shouldDismissOnBackgroundTouch = toggle.on; 328 | 329 | } else if (fieldTag == FieldTagContentDismiss) { 330 | _shouldDismissOnContentTouch = toggle.on; 331 | 332 | } else if (fieldTag == FieldTagTimedDismiss) { 333 | _shouldDismissAfterDelay = toggle.on; 334 | } 335 | } 336 | } 337 | } 338 | 339 | 340 | - (void)showButtonPressed:(id)sender { 341 | 342 | // Generate content view to present 343 | UIView* contentView = [[UIView alloc] init]; 344 | contentView.translatesAutoresizingMaskIntoConstraints = NO; 345 | contentView.backgroundColor = [UIColor klcLightGreenColor]; 346 | contentView.layer.cornerRadius = 12.0; 347 | 348 | UILabel* dismissLabel = [[UILabel alloc] init]; 349 | dismissLabel.translatesAutoresizingMaskIntoConstraints = NO; 350 | dismissLabel.backgroundColor = [UIColor clearColor]; 351 | dismissLabel.textColor = [UIColor whiteColor]; 352 | dismissLabel.font = [UIFont boldSystemFontOfSize:72.0]; 353 | dismissLabel.text = @"Hi."; 354 | 355 | UIButton* dismissButton = [UIButton buttonWithType:UIButtonTypeCustom]; 356 | dismissButton.translatesAutoresizingMaskIntoConstraints = NO; 357 | dismissButton.contentEdgeInsets = UIEdgeInsetsMake(10, 20, 10, 20); 358 | dismissButton.backgroundColor = [UIColor klcGreenColor]; 359 | [dismissButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 360 | [dismissButton setTitleColor:[[dismissButton titleColorForState:UIControlStateNormal] colorWithAlphaComponent:0.5] forState:UIControlStateHighlighted]; 361 | dismissButton.titleLabel.font = [UIFont boldSystemFontOfSize:16.0]; 362 | [dismissButton setTitle:@"Bye" forState:UIControlStateNormal]; 363 | dismissButton.layer.cornerRadius = 6.0; 364 | [dismissButton addTarget:self action:@selector(dismissButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; 365 | 366 | [contentView addSubview:dismissLabel]; 367 | [contentView addSubview:dismissButton]; 368 | 369 | NSDictionary* views = NSDictionaryOfVariableBindings(contentView, dismissButton, dismissLabel); 370 | 371 | [contentView addConstraints: 372 | [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(16)-[dismissLabel]-(10)-[dismissButton]-(24)-|" 373 | options:NSLayoutFormatAlignAllCenterX 374 | metrics:nil 375 | views:views]]; 376 | 377 | [contentView addConstraints: 378 | [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(36)-[dismissLabel]-(36)-|" 379 | options:0 380 | metrics:nil 381 | views:views]]; 382 | 383 | // Show in popup 384 | KLCPopupLayout layout = KLCPopupLayoutMake((KLCPopupHorizontalLayout)[self valueForRow:_selectedRowInHorizontalField inFieldWithTag:FieldTagHorizontalLayout], 385 | (KLCPopupVerticalLayout)[self valueForRow:_selectedRowInVerticalField inFieldWithTag:FieldTagVerticalLayout]); 386 | 387 | KLCPopup* popup = [KLCPopup popupWithContentView:contentView 388 | showType:(KLCPopupShowType)[self valueForRow:_selectedRowInShowField inFieldWithTag:FieldTagShowType] 389 | dismissType:(KLCPopupDismissType)[self valueForRow:_selectedRowInDismissField inFieldWithTag:FieldTagDismissType] 390 | maskType:(KLCPopupMaskType)[self valueForRow:_selectedRowInMaskField inFieldWithTag:FieldTagMaskType] 391 | dismissOnBackgroundTouch:_shouldDismissOnBackgroundTouch 392 | dismissOnContentTouch:_shouldDismissOnContentTouch]; 393 | 394 | if (_shouldDismissAfterDelay) { 395 | [popup showWithLayout:layout duration:2.0]; 396 | } else { 397 | [popup showWithLayout:layout]; 398 | } 399 | } 400 | 401 | 402 | - (void)dismissButtonPressed:(id)sender { 403 | if ([sender isKindOfClass:[UIView class]]) { 404 | [(UIView*)sender dismissPresentingPopup]; 405 | } 406 | } 407 | 408 | 409 | - (void)fieldCancelButtonPressed:(id)sender { 410 | [self dismissViewControllerAnimated:YES completion:NULL]; 411 | } 412 | 413 | 414 | #pragma mark - Private 415 | 416 | - (void)updateFieldTableView:(UITableView*)tableView { 417 | 418 | if (tableView != nil) { 419 | 420 | NSInteger fieldTag = tableView.tag; 421 | NSInteger selectedRow = [self selectedRowForFieldWithTag:fieldTag]; 422 | 423 | for (NSIndexPath* indexPath in [tableView indexPathsForVisibleRows]) { 424 | 425 | UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath]; 426 | if (cell != nil) { 427 | 428 | if (indexPath.row == selectedRow) { 429 | cell.accessoryType = UITableViewCellAccessoryCheckmark; 430 | } else { 431 | cell.accessoryType = UITableViewCellAccessoryNone; 432 | } 433 | } 434 | } 435 | } 436 | } 437 | 438 | 439 | - (NSInteger)valueForRow:(NSInteger)row inFieldWithTag:(NSInteger)tag { 440 | 441 | NSArray* listForField = nil; 442 | if (tag == FieldTagHorizontalLayout) { 443 | listForField = _horizontalLayouts; 444 | 445 | } else if (tag == FieldTagVerticalLayout) { 446 | listForField = _verticalLayouts; 447 | 448 | } else if (tag == FieldTagMaskType) { 449 | listForField = _maskTypes; 450 | 451 | } else if (tag == FieldTagShowType) { 452 | listForField = _showTypes; 453 | 454 | } else if (tag == FieldTagDismissType) { 455 | listForField = _dismissTypes; 456 | } 457 | 458 | // If row is out of bounds, try using first row. 459 | if (row >= listForField.count) { 460 | row = 0; 461 | } 462 | 463 | if (row < listForField.count) { 464 | id obj = [listForField objectAtIndex:row]; 465 | if ([obj isKindOfClass:[NSNumber class]]) { 466 | return [(NSNumber*)obj integerValue]; 467 | } 468 | } 469 | 470 | return 0; 471 | } 472 | 473 | 474 | - (NSInteger)selectedRowForFieldWithTag:(NSInteger)tag { 475 | if (tag == FieldTagHorizontalLayout) { 476 | return _selectedRowInHorizontalField; 477 | 478 | } else if (tag == FieldTagVerticalLayout) { 479 | return _selectedRowInVerticalField; 480 | 481 | } else if (tag == FieldTagMaskType) { 482 | return _selectedRowInMaskField; 483 | 484 | } else if (tag == FieldTagShowType) { 485 | return _selectedRowInShowField; 486 | 487 | } else if (tag == FieldTagDismissType) { 488 | return _selectedRowInDismissField; 489 | } 490 | return NSNotFound; 491 | } 492 | 493 | 494 | - (NSString*)nameForValue:(NSInteger)value inFieldWithTag:(NSInteger)tag { 495 | 496 | NSDictionary* namesForField = nil; 497 | if (tag == FieldTagHorizontalLayout) { 498 | namesForField = _namesForHorizontalLayouts; 499 | 500 | } else if (tag == FieldTagVerticalLayout) { 501 | namesForField = _namesForVerticalLayouts; 502 | 503 | } else if (tag == FieldTagMaskType) { 504 | namesForField = _namesForMaskTypes; 505 | 506 | } else if (tag == FieldTagShowType) { 507 | namesForField = _namesForShowTypes; 508 | 509 | } else if (tag == FieldTagDismissType) { 510 | namesForField = _namesForDismissTypes; 511 | } 512 | 513 | if (namesForField != nil) { 514 | return [namesForField objectForKey:@(value)]; 515 | } 516 | return nil; 517 | } 518 | 519 | 520 | - (CellType)cellTypeForFieldWithTag:(NSInteger)tag { 521 | 522 | CellType cellType; 523 | switch (tag) { 524 | case FieldTagHorizontalLayout: 525 | cellType = CellTypeNormal; 526 | break; 527 | case FieldTagVerticalLayout: 528 | cellType = CellTypeNormal; 529 | break; 530 | case FieldTagMaskType: 531 | cellType = CellTypeNormal; 532 | break; 533 | case FieldTagShowType: 534 | cellType = CellTypeNormal; 535 | break; 536 | case FieldTagDismissType: 537 | cellType = CellTypeNormal; 538 | break; 539 | case FieldTagBackgroundDismiss: 540 | cellType = CellTypeSwitch; 541 | break; 542 | case FieldTagContentDismiss: 543 | cellType = CellTypeSwitch; 544 | break; 545 | case FieldTagTimedDismiss: 546 | cellType = CellTypeSwitch; 547 | break; 548 | default: 549 | cellType = CellTypeNormal; 550 | break; 551 | } 552 | return cellType; 553 | } 554 | 555 | 556 | #pragma mark - 557 | 558 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 559 | 560 | // MAIN TABLE 561 | if (tableView == self.tableView) { 562 | return _fields.count; 563 | } 564 | 565 | // FIELD TABLES 566 | else { 567 | 568 | if (tableView.tag == FieldTagHorizontalLayout) { 569 | return _horizontalLayouts.count; 570 | 571 | } else if (tableView.tag == FieldTagVerticalLayout) { 572 | return _verticalLayouts.count; 573 | 574 | } else if (tableView.tag == FieldTagMaskType) { 575 | return _maskTypes.count; 576 | 577 | } else if (tableView.tag == FieldTagShowType) { 578 | return _showTypes.count; 579 | 580 | } else if (tableView.tag == FieldTagDismissType) { 581 | return _dismissTypes.count; 582 | } 583 | } 584 | 585 | return 0; 586 | } 587 | 588 | 589 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 590 | 591 | // MAIN TABLE 592 | if (tableView == self.tableView) { 593 | 594 | id obj = [_fields objectAtIndex:indexPath.row]; 595 | if ([obj isKindOfClass:[NSNumber class]]) { 596 | FieldTag fieldTag = [(NSNumber*)obj integerValue]; 597 | 598 | UITableViewCell* cell = nil; 599 | CellType cellType = [self cellTypeForFieldWithTag:fieldTag]; 600 | 601 | NSString* identifier = @""; 602 | if (cellType == CellTypeNormal) { 603 | identifier = @"normal"; 604 | } else if (cellType == CellTypeSwitch) { 605 | identifier = @"switch"; 606 | } 607 | 608 | cell = [tableView dequeueReusableCellWithIdentifier:identifier]; 609 | 610 | if (nil == cell) { 611 | UITableViewCellStyle style = UITableViewCellStyleValue1; 612 | cell = [[UITableViewCell alloc] initWithStyle:style reuseIdentifier:identifier]; 613 | UIEdgeInsets newSeparatorInset = cell.separatorInset; 614 | newSeparatorInset.right = newSeparatorInset.left; 615 | cell.separatorInset = newSeparatorInset; 616 | 617 | if (cellType == CellTypeNormal) { 618 | cell.selectionStyle = UITableViewCellSelectionStyleGray; 619 | 620 | } else if (cellType == CellTypeSwitch) { 621 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 622 | UISwitch* toggle = [[UISwitch alloc] init]; 623 | toggle.onTintColor = [UIColor lightGrayColor]; 624 | [toggle addTarget:self action:@selector(toggleValueDidChange:) forControlEvents:UIControlEventValueChanged]; 625 | cell.accessoryView = toggle; 626 | } 627 | } 628 | 629 | cell.textLabel.text = [_namesForFields objectForKey:@(fieldTag)]; 630 | 631 | // populate Normal cell 632 | if (cellType == CellTypeNormal) { 633 | NSInteger selectedRowInField = [self selectedRowForFieldWithTag:fieldTag]; 634 | if (selectedRowInField != NSNotFound) { 635 | cell.detailTextLabel.text = [self nameForValue:[self valueForRow:selectedRowInField inFieldWithTag:fieldTag] inFieldWithTag:fieldTag]; 636 | } 637 | } 638 | // populate Switch cell 639 | else if (cellType == CellTypeSwitch) { 640 | if ([cell.accessoryView isKindOfClass:[UISwitch class]]) { 641 | BOOL on = NO; 642 | if (fieldTag == FieldTagBackgroundDismiss) { 643 | on = _shouldDismissOnBackgroundTouch; 644 | } else if (fieldTag == FieldTagContentDismiss) { 645 | on = _shouldDismissOnContentTouch; 646 | } else if (fieldTag == FieldTagTimedDismiss) { 647 | on = _shouldDismissAfterDelay; 648 | } 649 | [(UISwitch*)cell.accessoryView setOn:on]; 650 | } 651 | } 652 | 653 | return cell; 654 | } 655 | } 656 | 657 | // FIELD TABLES 658 | else { 659 | 660 | UITableViewCell* cell = nil; 661 | 662 | Class cellClass = [UITableViewCell class]; 663 | NSString* identifier = NSStringFromClass(cellClass); 664 | 665 | cell = [tableView dequeueReusableCellWithIdentifier:identifier]; 666 | 667 | if (nil == cell) { 668 | UITableViewCellStyle style = UITableViewCellStyleDefault; 669 | cell = [[cellClass alloc] initWithStyle:style reuseIdentifier:identifier]; 670 | UIEdgeInsets newSeparatorInset = cell.separatorInset; 671 | newSeparatorInset.right = newSeparatorInset.left; 672 | cell.separatorInset = newSeparatorInset; 673 | } 674 | 675 | NSInteger fieldTag = tableView.tag; 676 | 677 | cell.textLabel.text = [self nameForValue:[self valueForRow:indexPath.row inFieldWithTag:fieldTag] inFieldWithTag:fieldTag]; 678 | 679 | if (indexPath.row == [self selectedRowForFieldWithTag:fieldTag]) { 680 | cell.accessoryType = UITableViewCellAccessoryCheckmark; 681 | } else { 682 | cell.accessoryType = UITableViewCellAccessoryNone; 683 | } 684 | 685 | return cell; 686 | } 687 | 688 | return nil; 689 | } 690 | 691 | 692 | #pragma mark - 693 | 694 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 695 | 696 | // MAIN TABLE 697 | if (tableView == self.tableView) { 698 | 699 | id obj = [_fields objectAtIndex:indexPath.row]; 700 | if ([obj isKindOfClass:[NSNumber class]]) { 701 | NSInteger fieldTag = [(NSNumber*)obj integerValue]; 702 | 703 | if ([self cellTypeForFieldWithTag:fieldTag] == CellTypeNormal) { 704 | 705 | UIViewController* fieldController = [[UIViewController alloc] init]; 706 | 707 | UITableView* fieldTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; 708 | fieldTableView.delegate = self; 709 | fieldTableView.dataSource = self; 710 | fieldTableView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); 711 | fieldTableView.tag = fieldTag; 712 | fieldController.view = fieldTableView; 713 | 714 | // IPAD 715 | if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { 716 | 717 | // Present in a popover 718 | UIPopoverController* popover = [[UIPopoverController alloc] initWithContentViewController:fieldController]; 719 | popover.delegate = self; 720 | self.popover = popover; 721 | 722 | // Set KVO so we can adjust the popover's size to fit the table's content once it's populated. 723 | [fieldTableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil]; 724 | 725 | CGRect senderFrameInView = [self.tableView convertRect:[self.tableView rectForRowAtIndexPath:indexPath] toView:self.view]; 726 | [popover presentPopoverFromRect:senderFrameInView inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; 727 | } 728 | 729 | // IPHONE 730 | else { 731 | 732 | // Present in a modal 733 | UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath]; 734 | fieldController.title = cell.textLabel.text; 735 | UIBarButtonItem* cancelButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(fieldCancelButtonPressed:)]; 736 | fieldController.navigationItem.rightBarButtonItem = cancelButton; 737 | 738 | UINavigationController* navigationController = [[UINavigationController alloc] initWithRootViewController:fieldController]; 739 | navigationController.delegate = self; 740 | [self presentViewController:navigationController animated:YES completion:NULL]; 741 | } 742 | } 743 | } 744 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 745 | } 746 | 747 | // FIELD TABLES 748 | else { 749 | 750 | if (tableView.tag == FieldTagHorizontalLayout) { 751 | _selectedRowInHorizontalField = indexPath.row; 752 | 753 | } else if (tableView.tag == FieldTagVerticalLayout) { 754 | _selectedRowInVerticalField = indexPath.row; 755 | 756 | } else if (tableView.tag == FieldTagMaskType) { 757 | _selectedRowInMaskField = indexPath.row; 758 | 759 | } else if (tableView.tag == FieldTagShowType) { 760 | _selectedRowInShowField = indexPath.row; 761 | 762 | } else if (tableView.tag == FieldTagDismissType) { 763 | _selectedRowInDismissField = indexPath.row; 764 | } 765 | 766 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 767 | 768 | [self updateFieldTableView:tableView]; 769 | 770 | [self.tableView reloadData]; 771 | 772 | // IPAD 773 | if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { 774 | [self.popover dismissPopoverAnimated:YES]; 775 | } 776 | // IPHONE 777 | else { 778 | [self dismissViewControllerAnimated:YES completion:NULL]; 779 | } 780 | } 781 | } 782 | 783 | #pragma mark - 784 | 785 | - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated { 786 | 787 | // If this is a field table, make sure the selected row is scrolled into view when it appears. 788 | if ((navigationController == self.presentedViewController) && [viewController.view isKindOfClass:[UITableView class]]) { 789 | 790 | UITableView* fieldTableView = (UITableView*)viewController.view; 791 | 792 | NSInteger selectedRow = [self selectedRowForFieldWithTag:fieldTableView.tag]; 793 | if ([fieldTableView numberOfRowsInSection:0] > selectedRow) { 794 | [fieldTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:selectedRow inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:NO]; 795 | } 796 | } 797 | } 798 | 799 | 800 | #pragma mark - 801 | 802 | - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController { 803 | 804 | // Cleanup by removing KVO and reference to popover 805 | UIView* view = popoverController.contentViewController.view; 806 | if ([view isKindOfClass:[UITableView class]]) { 807 | [(UITableView*)view removeObserver:self forKeyPath:@"contentSize"]; 808 | } 809 | 810 | self.popover = nil; 811 | } 812 | 813 | 814 | #pragma mark - 815 | 816 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 817 | 818 | if ([keyPath isEqualToString:@"contentSize"]) { 819 | 820 | if ([object isKindOfClass:[UITableView class]]) { 821 | UITableView* tableView = (UITableView*)object; 822 | 823 | if (self.popover != nil) { 824 | [self.popover setPopoverContentSize:tableView.contentSize animated:NO]; 825 | } 826 | 827 | // Make sure the selected row is scrolled into view when it appears 828 | NSInteger fieldTag = tableView.tag; 829 | NSInteger selectedRow = [self selectedRowForFieldWithTag:fieldTag]; 830 | 831 | if ([tableView numberOfRowsInSection:0] > selectedRow) { 832 | [tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:selectedRow inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:NO]; 833 | } 834 | } 835 | } 836 | } 837 | 838 | 839 | @end 840 | 841 | 842 | #pragma mark - Categories 843 | 844 | @implementation UIColor (KLCPopupExample) 845 | 846 | + (UIColor*)klcLightGreenColor { 847 | return [UIColor colorWithRed:(184.0/255.0) green:(233.0/255.0) blue:(122.0/255.0) alpha:1.0]; 848 | } 849 | 850 | + (UIColor*)klcGreenColor { 851 | return [UIColor colorWithRed:(0.0/255.0) green:(204.0/255.0) blue:(134.0/255.0) alpha:1.0]; 852 | } 853 | 854 | @end 855 | 856 | 857 | 858 | @implementation UIView (KLCPopupExample) 859 | 860 | - (UITableViewCell*)parentCell { 861 | 862 | // Iterate over superviews until you find a UITableViewCell 863 | UIView* view = self; 864 | while (view != nil) { 865 | if ([view isKindOfClass:[UITableViewCell class]]) { 866 | return (UITableViewCell*)view; 867 | } else { 868 | view = [view superview]; 869 | } 870 | } 871 | return nil; 872 | } 873 | 874 | @end 875 | 876 | 877 | -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // KLCPopupExample 4 | // 5 | // Copyright (c) 2014 Jeff Mascia (http://jeffmascia.com/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | #import 26 | 27 | #import "AppDelegate.h" 28 | 29 | int main(int argc, char * argv[]) 30 | { 31 | @autoreleasepool { 32 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExampleTests/KLCPopupExampleTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.kullect.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExampleTests/KLCPopupExampleTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // KLCPopupExampleTests.m 3 | // KLCPopupExampleTests 4 | // 5 | // Copyright (c) 2014 Jeff Mascia (http://jeffmascia.com/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | #import 26 | 27 | @interface KLCPopupExampleTests : XCTestCase 28 | 29 | @end 30 | 31 | @implementation KLCPopupExampleTests 32 | 33 | - (void)setUp 34 | { 35 | [super setUp]; 36 | // Put setup code here. This method is called before the invocation of each test method in the class. 37 | } 38 | 39 | - (void)tearDown 40 | { 41 | // Put teardown code here. This method is called after the invocation of each test method in the class. 42 | [super tearDown]; 43 | } 44 | 45 | - (void)testExample 46 | { 47 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /KLCPopupExample/KLCPopupExampleTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2014 Kullect Inc. (http://kullect.com) 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | KLCPopup 2 | ======== 3 | 4 | KLCPopup is a simple and flexible iOS class for presenting any custom view as a popup. It includes a variety of options for controlling how your popup appears and behaves. 5 | 6 |

7 | 8 | ##Installation 9 | 10 | - Drag the `KLCPopup/KLCPopup` folder into your project. 11 | - `#import "KLCPopup.h"` where appropriate. 12 | 13 | ##Usage 14 | 15 | (see sample Xcode project in `/KLCPopupExample`) 16 | 17 | ### Creating a Popup 18 | 19 | Create a popup for displaying a UIView using default animations and behaviors (similar to a UIAlertView): 20 | 21 | + (KLCPopup*)popupWithContentView:(UIView*)contentView; 22 | 23 | Or create a popup with custom animations and behaviors. Customizations can also be accessed via properties on the popup instance: 24 | 25 | + (KLCPopup*)popupWithContentView:(UIView*)contentView 26 | showType:(KLCPopupShowType)showType 27 | dismissType:(KLCPopupDismissType)dismissType 28 | maskType:(KLCPopupMaskType)maskType 29 | dismissOnBackgroundTouch:(BOOL)shouldDismissOnBackgroundTouch 30 | dismissOnContentTouch:(BOOL)shouldDismissOnContentTouch; 31 | 32 | Note: You may pass `nil` for `contentView` when creating the popup, but **you must assign a `contentView` to the popup before showing it!** 33 | 34 | Also **you must give your `contentView` a size** before showing it (by setting its frame), or **it must size itself with AutoLayout**. 35 | 36 | 37 | ### Showing a Popup 38 | 39 | 40 | Show popup in middle of screen. 41 | 42 | - (void)show; 43 | 44 | There are two ways to control where your popup is displayed: 45 | 46 | 1. Relative layout presets (see `KLCPopup.h` for options). 47 | 48 | - (void)showWithLayout:(KLCPopupLayout)layout; 49 | 50 | 51 | 2. Explicit center point relative to a view's coordinate system. 52 | 53 | - (void)showAtCenter:(CGPoint)center inView:(UIView*)view; 54 | 55 | If you want your popup to dismiss automatically (like a toast in Android) you can set an explicit `duration`: 56 | 57 | - (void)showWithDuration:(NSTimeInterval)duration; 58 | 59 | ### Dismissing a Popup 60 | 61 | There are a few ways to dismiss a popup: 62 | 63 | If you have a reference to the popup instance, you can send this message to it. If `animated`, then it will use the animation specified in `dismissType`. Otherwise it will just disappear: 64 | 65 | - (void)dismiss:(BOOL)animated; 66 | 67 | If you lost your reference to a popup or you want to make sure no popups are showing, this class method dismisses any and all popups in your app: 68 | 69 | + (void)dismissAllPopups; 70 | 71 | Also you can call this category method from `UIView(KLCPopup)` on your contentView, or any of its subviews, to dismiss its parent popup: 72 | 73 | - (void)dismissPresentingPopup; // UIView category 74 | 75 | ### Customization 76 | 77 | 78 | Animation used to show your popup: 79 | 80 | @property (nonatomic, assign) KLCPopupShowType showType; 81 | 82 | Animation used to dismiss your popup: 83 | 84 | @property (nonatomic, assign) KLCPopupDismissType dismissType; 85 | 86 | Mask prevents touches to the background from passing through to views below: 87 | 88 | @property (nonatomic, assign) KLCPopupMaskType maskType; 89 | 90 | Popup will automatically dismiss if the background is touched: 91 | 92 | @property (nonatomic, assign) BOOL shouldDismissOnBackgroundTouch; 93 | 94 | Popup will automatically dismiss if the contentView is touched: 95 | 96 | @property (nonatomic, assign) BOOL shouldDismissOnContentTouch; 97 | 98 | Override alpha value for dimmed background mask: 99 | 100 | @property (nonatomic, assign) CGFloat dimmedMaskAlpha; 101 | 102 | 103 | ### Blocks 104 | 105 | Use these blocks to synchronize other actions with popup events: 106 | 107 | @property (nonatomic, copy) void (^didFinishShowingCompletion)(); 108 | 109 | @property (nonatomic, copy) void (^willStartDismissingCompletion)(); 110 | 111 | @property (nonatomic, copy) void (^didFinishDismissingCompletion)(); 112 | 113 | 114 | ### Example 115 | 116 | UIView* contentView = [[UIView alloc] init]; 117 | contentView.backgroundColor = [UIColor orangeColor]; 118 | contentView.frame = CGRectMake(0.0, 0.0, 100.0, 100.0); 119 | 120 | KLCPopup* popup = [KLCPopup popupWithContentView:contentView]; 121 | [popup show]; 122 | 123 | ## Notes 124 | 125 | ### Interface Orientation 126 | `KLCPopup` supports **Portrait** and **Landscape** by default. 127 | 128 | ### Deployment 129 | `KLCPopup` requires **iOS 7**. 130 | 131 | ### Devices 132 | `KLCPopup` supports **iPhone** and **iPad**. 133 | 134 | ### ARC 135 | `KLCPopup` requires ARC. 136 | 137 | ### TODO 138 | - Add support for keyboard show/hide. 139 | - Add support for drag-to-dismiss. 140 | - Add 'blur' option for background mask 141 | 142 | ##Credits 143 | KLCPopup was created by Jeff Mascia and the team at Kullect, where it's used in the [Shout Photo Messenger](http://tryshout.com) app. Aspects of KLCPopup were inspired by Sam Vermette's [SVProgressHUD](https://github.com/samvermette/SVProgressHUD). 144 | --------------------------------------------------------------------------------