From 9a3fe151737253850e2e0e11c7a421509c8c27c9 Mon Sep 17 00:00:00 2001 From: Ashik Ahmad Date: Thu, 12 Dec 2013 01:20:18 +0600 Subject: [PATCH 1/5] made responsive to keyboard --- ExampleApp/KGAppDelegate.m | 11 ++-- KGModal.h | 5 ++ KGModal.m | 123 +++++++++++++++++++++++++++++++++++-- 3 files changed, 130 insertions(+), 9 deletions(-) diff --git a/ExampleApp/KGAppDelegate.m b/ExampleApp/KGAppDelegate.m index 4ab54dd..4f77a65 100644 --- a/ExampleApp/KGAppDelegate.m +++ b/ExampleApp/KGAppDelegate.m @@ -28,6 +28,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( [showButton addTarget:self action:@selector(showAction:) forControlEvents:UIControlEventTouchUpInside]; [self.window.rootViewController.view addSubview:showButton]; + [KGModal sharedInstance].responsiveToKeyboard = YES; [KGModal sharedInstance].closeButtonType = KGModalCloseButtonTypeRight; [self.window makeKeyAndVisible]; @@ -54,16 +55,16 @@ - (void)showAction:(id)sender{ CGRect infoLabelRect = CGRectInset(contentView.bounds, 5, 5); infoLabelRect.origin.y = CGRectGetMaxY(welcomeLabelRect)+5; infoLabelRect.size.height -= CGRectGetMinY(infoLabelRect) + 50; - UILabel *infoLabel = [[UILabel alloc] initWithFrame:infoLabelRect]; + UITextView *infoLabel = [[UITextView alloc] initWithFrame:infoLabelRect]; infoLabel.text = @"KGModal is an easy drop in control that allows you to display any view " "in a modal popup. The modal will automatically scale to fit the content view " "and center it on screen with nice animations!"; - infoLabel.numberOfLines = 6; +// infoLabel.numberOfLines = 6; infoLabel.textColor = [UIColor whiteColor]; infoLabel.textAlignment = NSTextAlignmentCenter; infoLabel.backgroundColor = [UIColor clearColor]; - infoLabel.shadowColor = [UIColor blackColor]; - infoLabel.shadowOffset = CGSizeMake(0, 1); +// infoLabel.shadowColor = [UIColor blackColor]; +// infoLabel.shadowOffset = CGSizeMake(0, 1); [contentView addSubview:infoLabel]; CGFloat btnY = CGRectGetMaxY(infoLabelRect)+5; @@ -82,6 +83,8 @@ -(void) changeCloseButtonType:(id) sender { KGModal*modal = [KGModal sharedInstance]; KGModalCloseButtonType type = modal.closeButtonType; + [modal endEditing:NO]; + if (KGModalCloseButtonTypeLeft == type) { modal.closeButtonType = KGModalCloseButtonTypeRight; [btn setTitle:@"Close Button Right" forState:UIControlStateNormal]; diff --git a/KGModal.h b/KGModal.h index 5af3a5c..d74b562 100644 --- a/KGModal.h +++ b/KGModal.h @@ -21,6 +21,8 @@ typedef NS_ENUM(NSUInteger, KGModalCloseButtonType) { @interface KGModal : NSObject +@property (nonatomic) BOOL responsiveToKeyboard; + // Determines if the modal should dismiss if the user taps outside of the modal view // Defaults to YES @property (nonatomic) BOOL tapOutsideToDismiss; @@ -75,4 +77,7 @@ typedef NS_ENUM(NSUInteger, KGModalCloseButtonType) { // run the completion after the modal is hidden - (void)hideAnimated:(BOOL)animated withCompletionBlock:(void(^)())completion; +// Send endEditing message to containing view +-(void) endEditing:(BOOL) force; + @end diff --git a/KGModal.m b/KGModal.m index 3528b5c..636c163 100644 --- a/KGModal.m +++ b/KGModal.m @@ -9,10 +9,38 @@ #import "KGModal.h" #import +#pragma mark - Utils for Keyboard response + +#define swap(a, b) do{typeof(a) odd13var=a; a=b; b=odd13var;}while(0) + +@interface UIView (FirstResponder) +- (UIView *)findFirstResponder; +@end + +@implementation UIView (FirstResponder) + +- (UIView *)findFirstResponder +{ + if (self.isFirstResponder) + return self; + + for (UIView *subView in self.subviews) { + UIView *firstResponder = [subView findFirstResponder]; + + if (firstResponder != nil) { + return firstResponder; + } + } + return nil; +} +@end + +#pragma mark - + CGFloat const kFadeInAnimationDuration = 0.3; CGFloat const kTransformPart1AnimationDuration = 0.2; CGFloat const kTransformPart2AnimationDuration = 0.1; -CGFloat const kDefaultCloseButtonPadding = 17.0; +CGFloat const kDefaultPadding = 17.0; NSString *const KGModalGradientViewTapped = @"KGModalGradientViewTapped"; @@ -75,7 +103,7 @@ -(void)setCloseButtonType:(KGModalCloseButtonType)closeButtonType { CGRect closeFrame = self.closeButton.frame; if (closeButtonType == KGModalCloseButtonTypeRight) { - closeFrame.origin.x = self.containerView.frame.size.width-kDefaultCloseButtonPadding-closeFrame.size.width/2; + closeFrame.origin.x = self.containerView.frame.size.width-kDefaultPadding-closeFrame.size.width/2; } else { closeFrame.origin.x = 0; } @@ -83,6 +111,85 @@ -(void)setCloseButtonType:(KGModalCloseButtonType)closeButtonType { } } +#pragma mark - Methods for Keyboard response + +-(void)setResponsiveToKeyboard:(BOOL)responsiveToKeyboard { + _responsiveToKeyboard = responsiveToKeyboard; + + // Clear anything before + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidShowNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; + + // Add if needed + if(responsiveToKeyboard){ + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(keyboardWasShown:) + name:UIKeyboardDidShowNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(keyboardWillBeHidden:) + name:UIKeyboardWillHideNotification + object:nil]; + } +} + +-(void) _adjustPositionForKeyboard:(CGSize) keyboardSize inTime:(double) duration { + CGRect containerViewRect = self.containerView.frame; + CGRect freeScreen = self.window.bounds; + + if (UIDeviceOrientationIsLandscape(self.window.rootViewController.interfaceOrientation)) { + swap(freeScreen.size.width, freeScreen.size.height); + } + + // TODO: could be more accurate + // Simple go through. Should work on 99% cases, but may have exceptions anyways. + freeScreen.size.height -= keyboardSize.height; + CGFloat newY = (freeScreen.size.height - containerViewRect.size.height)/2 + freeScreen.origin.y; + + // A threshhold to check if animation really needed + if (ABS(newY - containerViewRect.origin.y) < 2) { + containerViewRect.origin.y = newY; + self.containerView.frame = containerViewRect; + } else { + containerViewRect.origin.y = newY; + UIView *container = self.containerView; + [UIView animateWithDuration:duration + animations:^{ + container.frame = containerViewRect; + }]; + } +} + +-(void) keyboardWasShown:(NSNotification *) aNotification { + UIView *selectedView = [self.containerView findFirstResponder]; + // Do only this modal contains the firstResponder + if (selectedView) { + CGSize keyboardSize = [[[aNotification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; + if (UIDeviceOrientationIsLandscape(self.window.rootViewController.interfaceOrientation)) + swap(keyboardSize.width, keyboardSize.height); + + if ([selectedView respondsToSelector:@selector(inputAccessoryView)]) { + UIView *accessoryView = [selectedView inputAccessoryView]; + if (accessoryView) { + keyboardSize = CGSizeMake(keyboardSize.width, + keyboardSize.height + accessoryView.frame.size.height); + } + } + + double animDuration = [[[aNotification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]; + + [self _adjustPositionForKeyboard:keyboardSize inTime:animDuration]; + } +} + +-(void) keyboardWillBeHidden:(NSNotification *) aNotification { + double animDuration = [[[aNotification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]; + + [self _adjustPositionForKeyboard:CGSizeZero inTime:animDuration]; +} + +#pragma mark - + - (void)showWithContentView:(UIView *)contentView{ [self showWithContentView:contentView andAnimated:YES]; } @@ -105,8 +212,7 @@ - (void)showWithContentView:(UIView *)contentView andAnimated:(BOOL)animated { self.window.rootViewController = viewController; self.viewController = viewController; - CGFloat padding = 17; - CGRect containerViewRect = CGRectInset(contentView.bounds, -padding, -padding); + CGRect containerViewRect = CGRectInset(contentView.bounds, -kDefaultPadding, -kDefaultPadding); containerViewRect.origin.x = containerViewRect.origin.y = 0; containerViewRect.origin.x = round(CGRectGetMidX(self.window.bounds)-CGRectGetMidX(containerViewRect)); containerViewRect.origin.y = round(CGRectGetMidY(self.window.bounds)-CGRectGetMidY(containerViewRect)); @@ -115,7 +221,7 @@ - (void)showWithContentView:(UIView *)contentView andAnimated:(BOOL)animated { containerView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin| UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleBottomMargin; containerView.layer.rasterizationScale = [[UIScreen mainScreen] scale]; - contentView.frame = (CGRect){padding, padding, contentView.bounds.size}; + contentView.frame = (CGRect){kDefaultPadding, kDefaultPadding, contentView.bounds.size}; [containerView addSubview:contentView]; [viewController.view addSubview:containerView]; self.containerView = containerView; @@ -131,6 +237,9 @@ - (void)showWithContentView:(UIView *)contentView andAnimated:(BOOL)animated { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(tapCloseAction:) name:KGModalGradientViewTapped object:nil]; + // Force adding keyboard observers if needed + [self setResponsiveToKeyboard:self.responsiveToKeyboard]; + // The window has to be un-hidden on the main thread // This will cause the window to display dispatch_async(dispatch_get_main_queue(), ^{ @@ -226,6 +335,10 @@ - (void)setModalBackgroundColor:(UIColor *)modalBackgroundColor{ } } +-(void)endEditing:(BOOL)force { + [self.containerView endEditing:force]; +} + - (void)dealloc{ [self cleanup]; } From b4abc3c3b1e886b3fa0b20bb8a97e2cf9a9411db Mon Sep 17 00:00:00 2001 From: Ashik Ahmad Date: Wed, 18 Dec 2013 01:57:46 +0600 Subject: [PATCH 2/5] Cleaning up --- ExampleApp/KGAppDelegate.m | 25 ++++++++++----------- KGModal.m | 45 ++++++++++++++++++++------------------ 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/ExampleApp/KGAppDelegate.m b/ExampleApp/KGAppDelegate.m index 4f77a65..9c3d51d 100644 --- a/ExampleApp/KGAppDelegate.m +++ b/ExampleApp/KGAppDelegate.m @@ -52,25 +52,22 @@ - (void)showAction:(id)sender{ welcomeLabel.shadowOffset = CGSizeMake(0, 1); [contentView addSubview:welcomeLabel]; - CGRect infoLabelRect = CGRectInset(contentView.bounds, 5, 5); - infoLabelRect.origin.y = CGRectGetMaxY(welcomeLabelRect)+5; - infoLabelRect.size.height -= CGRectGetMinY(infoLabelRect) + 50; - UITextView *infoLabel = [[UITextView alloc] initWithFrame:infoLabelRect]; - infoLabel.text = @"KGModal is an easy drop in control that allows you to display any view " + CGRect infoTextRect = CGRectInset(contentView.bounds, 5, 5); + infoTextRect.origin.y = CGRectGetMaxY(welcomeLabelRect)+5; + infoTextRect.size.height -= CGRectGetMinY(infoTextRect) + 50; + UITextView *infoText = [[UITextView alloc] initWithFrame:infoTextRect]; + infoText.text = @"KGModal is an easy drop in control that allows you to display any view " "in a modal popup. The modal will automatically scale to fit the content view " "and center it on screen with nice animations!"; -// infoLabel.numberOfLines = 6; - infoLabel.textColor = [UIColor whiteColor]; - infoLabel.textAlignment = NSTextAlignmentCenter; - infoLabel.backgroundColor = [UIColor clearColor]; -// infoLabel.shadowColor = [UIColor blackColor]; -// infoLabel.shadowOffset = CGSizeMake(0, 1); - [contentView addSubview:infoLabel]; + infoText.textColor = [UIColor whiteColor]; + infoText.textAlignment = NSTextAlignmentCenter; + infoText.backgroundColor = [UIColor clearColor]; + [contentView addSubview:infoText]; - CGFloat btnY = CGRectGetMaxY(infoLabelRect)+5; + CGFloat btnY = CGRectGetMaxY(infoTextRect)+5; CGFloat btnH = CGRectGetMaxY(contentView.frame)-5 - btnY; UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; - btn.frame = CGRectMake(infoLabelRect.origin.x, btnY, infoLabelRect.size.width, btnH); + btn.frame = CGRectMake(infoTextRect.origin.x, btnY, infoTextRect.size.width, btnH); [btn setTitle:@"Close Button Right" forState:UIControlStateNormal]; [btn addTarget:self action:@selector(changeCloseButtonType:) forControlEvents:UIControlEventTouchUpInside]; [contentView addSubview:btn]; diff --git a/KGModal.m b/KGModal.m index 636c163..5738f84 100644 --- a/KGModal.m +++ b/KGModal.m @@ -13,26 +13,8 @@ #define swap(a, b) do{typeof(a) odd13var=a; a=b; b=odd13var;}while(0) -@interface UIView (FirstResponder) -- (UIView *)findFirstResponder; -@end - -@implementation UIView (FirstResponder) - -- (UIView *)findFirstResponder -{ - if (self.isFirstResponder) - return self; - - for (UIView *subView in self.subviews) { - UIView *firstResponder = [subView findFirstResponder]; - - if (firstResponder != nil) { - return firstResponder; - } - } - return nil; -} +@interface UIView (KGFirstResponder) +- (UIView *)kgFindFirstResponder; @end #pragma mark - @@ -161,7 +143,7 @@ -(void) _adjustPositionForKeyboard:(CGSize) keyboardSize inTime:(double) duratio } -(void) keyboardWasShown:(NSNotification *) aNotification { - UIView *selectedView = [self.containerView findFirstResponder]; + UIView *selectedView = [self.containerView kgFindFirstResponder]; // Do only this modal contains the firstResponder if (selectedView) { CGSize keyboardSize = [[[aNotification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; @@ -519,3 +501,24 @@ - (UIImage *)closeButtonImage{ } @end + +#pragma mark - + +@implementation UIView (KGFirstResponder) + +- (UIView *)kgFindFirstResponder +{ + if (self.isFirstResponder) + return self; + + for (UIView *subView in self.subviews) { + UIView *firstResponder = [subView kgFindFirstResponder]; + + if (firstResponder != nil) { + return firstResponder; + } + } + return nil; +} +@end + From e90f7e2e31e0bf33721058ccde93ced59c8c4d65 Mon Sep 17 00:00:00 2001 From: Ashik Ahmad Date: Wed, 18 Dec 2013 02:55:42 +0600 Subject: [PATCH 3/5] made exaple straigh-forward --- ExampleApp/KGAppDelegate.m | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ExampleApp/KGAppDelegate.m b/ExampleApp/KGAppDelegate.m index 9c3d51d..80ea1f0 100644 --- a/ExampleApp/KGAppDelegate.m +++ b/ExampleApp/KGAppDelegate.m @@ -58,10 +58,11 @@ - (void)showAction:(id)sender{ UITextView *infoText = [[UITextView alloc] initWithFrame:infoTextRect]; infoText.text = @"KGModal is an easy drop in control that allows you to display any view " "in a modal popup. The modal will automatically scale to fit the content view " - "and center it on screen with nice animations!"; - infoText.textColor = [UIColor whiteColor]; + "and center it on screen with nice animations!\n" + "(Tap here and then tap button below)"; + infoText.textColor = [UIColor blackColor]; infoText.textAlignment = NSTextAlignmentCenter; - infoText.backgroundColor = [UIColor clearColor]; + infoText.backgroundColor = [UIColor whiteColor]; [contentView addSubview:infoText]; CGFloat btnY = CGRectGetMaxY(infoTextRect)+5; From 368a232948cf7f7761992e0e269bfc999d3f83ea Mon Sep 17 00:00:00 2001 From: Ashik Ahmad Date: Sat, 28 Dec 2013 16:48:37 +0600 Subject: [PATCH 4/5] added xccheckout file to avoid merge conflict --- .../xcshareddata/KGModalExample.xccheckout | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 ExampleApp/KGModalExample.xcodeproj/project.xcworkspace/xcshareddata/KGModalExample.xccheckout diff --git a/ExampleApp/KGModalExample.xcodeproj/project.xcworkspace/xcshareddata/KGModalExample.xccheckout b/ExampleApp/KGModalExample.xcodeproj/project.xcworkspace/xcshareddata/KGModalExample.xccheckout new file mode 100644 index 0000000..3b9dc0f --- /dev/null +++ b/ExampleApp/KGModalExample.xcodeproj/project.xcworkspace/xcshareddata/KGModalExample.xccheckout @@ -0,0 +1,41 @@ + + + + + IDESourceControlProjectFavoriteDictionaryKey + + IDESourceControlProjectIdentifier + 7FB544D5-CF66-4E55-88A3-3D4CB5D50505 + IDESourceControlProjectName + KGModalExample + IDESourceControlProjectOriginsDictionary + + 2826E242-8CFF-4E68-8504-3384724F0FBF + ssh://github.com/ashikahmad/KGModal.git + + IDESourceControlProjectPath + ExampleApp/KGModalExample.xcodeproj/project.xcworkspace + IDESourceControlProjectRelativeInstallPathDictionary + + 2826E242-8CFF-4E68-8504-3384724F0FBF + ../../.. + + IDESourceControlProjectURL + ssh://github.com/ashikahmad/KGModal.git + IDESourceControlProjectVersion + 110 + IDESourceControlProjectWCCIdentifier + 2826E242-8CFF-4E68-8504-3384724F0FBF + IDESourceControlProjectWCConfigurations + + + IDESourceControlRepositoryExtensionIdentifierKey + public.vcs.git + IDESourceControlWCCIdentifierKey + 2826E242-8CFF-4E68-8504-3384724F0FBF + IDESourceControlWCCName + KGModal + + + + From 1eeb9a19e67a0acc0a07f8eb425a39e629c9b7da Mon Sep 17 00:00:00 2001 From: Ashik Ahmad Date: Sat, 28 Dec 2013 18:24:38 +0600 Subject: [PATCH 5/5] removed xccheckout file from git and added it to .gitignore --- .gitignore | 1 + .../xcshareddata/KGModalExample.xccheckout | 66 ------------------- 2 files changed, 1 insertion(+), 66 deletions(-) delete mode 100644 ExampleApp/KGModalExample.xcodeproj/project.xcworkspace/xcshareddata/KGModalExample.xccheckout diff --git a/.gitignore b/.gitignore index bb1e388..b1ab962 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ user.*.espressostorage Sparkle/*.app *.zip PXListView +*.xccheckout diff --git a/ExampleApp/KGModalExample.xcodeproj/project.xcworkspace/xcshareddata/KGModalExample.xccheckout b/ExampleApp/KGModalExample.xcodeproj/project.xcworkspace/xcshareddata/KGModalExample.xccheckout deleted file mode 100644 index 4c267ef..0000000 --- a/ExampleApp/KGModalExample.xcodeproj/project.xcworkspace/xcshareddata/KGModalExample.xccheckout +++ /dev/null @@ -1,66 +0,0 @@ - - - - - IDESourceControlProjectFavoriteDictionaryKey - - IDESourceControlProjectIdentifier -<<<<<<< HEAD - 7FB544D5-CF66-4E55-88A3-3D4CB5D50505 -======= - 5C978CE1-221A-4169-AF81-B64C6BBF112C ->>>>>>> a3c6591e8ca7472544632dde12013794036ebf2e - IDESourceControlProjectName - KGModalExample - IDESourceControlProjectOriginsDictionary - -<<<<<<< HEAD - 2826E242-8CFF-4E68-8504-3384724F0FBF - ssh://github.com/ashikahmad/KGModal.git -======= - FB6C026C-C1C4-455C-BA11-CFF8C0912A70 - https://github.com/kgn/KGModal.git ->>>>>>> a3c6591e8ca7472544632dde12013794036ebf2e - - IDESourceControlProjectPath - ExampleApp/KGModalExample.xcodeproj/project.xcworkspace - IDESourceControlProjectRelativeInstallPathDictionary - -<<<<<<< HEAD - 2826E242-8CFF-4E68-8504-3384724F0FBF - ../../.. - - IDESourceControlProjectURL - ssh://github.com/ashikahmad/KGModal.git - IDESourceControlProjectVersion - 110 - IDESourceControlProjectWCCIdentifier - 2826E242-8CFF-4E68-8504-3384724F0FBF -======= - FB6C026C-C1C4-455C-BA11-CFF8C0912A70 - ../../.. - - IDESourceControlProjectURL - https://github.com/kgn/KGModal.git - IDESourceControlProjectVersion - 110 - IDESourceControlProjectWCCIdentifier - FB6C026C-C1C4-455C-BA11-CFF8C0912A70 ->>>>>>> a3c6591e8ca7472544632dde12013794036ebf2e - IDESourceControlProjectWCConfigurations - - - IDESourceControlRepositoryExtensionIdentifierKey - public.vcs.git - IDESourceControlWCCIdentifierKey -<<<<<<< HEAD - 2826E242-8CFF-4E68-8504-3384724F0FBF -======= - FB6C026C-C1C4-455C-BA11-CFF8C0912A70 ->>>>>>> a3c6591e8ca7472544632dde12013794036ebf2e - IDESourceControlWCCName - KGModal - - - -