Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reposition KGModal when Keyboard is present. Fixes issue #19 #26

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions ExampleApp/KGAppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -57,30 +58,27 @@ - (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;
UILabel *infoLabel = [[UILabel 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];
"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 whiteColor];
[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];

// [[KGModal sharedInstance] setCloseButtonLocation:KGModalCloseButtonLocationRight];
[[KGModal sharedInstance] showWithContentView:contentView andAnimated:YES];
}

Expand All @@ -105,7 +103,9 @@ - (void)changeCloseButtonType:(id)sender{
KGModal *modal = [KGModal sharedInstance];
KGModalCloseButtonType type = modal.closeButtonType;

if(type == KGModalCloseButtonTypeLeft){
[modal endEditing:NO];

if (type == KGModalCloseButtonTypeLeft) {
modal.closeButtonType = KGModalCloseButtonTypeRight;
[button setTitle:@"Close Button Right" forState:UIControlStateNormal];
}else if(type == KGModalCloseButtonTypeRight){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,38 @@
<key>IDESourceControlProjectFavoriteDictionaryKey</key>
<false/>
<key>IDESourceControlProjectIdentifier</key>
<<<<<<< HEAD
<string>7FB544D5-CF66-4E55-88A3-3D4CB5D50505</string>
=======
<string>5C978CE1-221A-4169-AF81-B64C6BBF112C</string>
>>>>>>> a3c6591e8ca7472544632dde12013794036ebf2e
<key>IDESourceControlProjectName</key>
<string>KGModalExample</string>
<key>IDESourceControlProjectOriginsDictionary</key>
<dict>
<<<<<<< HEAD
<key>2826E242-8CFF-4E68-8504-3384724F0FBF</key>
<string>ssh://github.com/ashikahmad/KGModal.git</string>
=======
<key>FB6C026C-C1C4-455C-BA11-CFF8C0912A70</key>
<string>https://github.com/kgn/KGModal.git</string>
>>>>>>> a3c6591e8ca7472544632dde12013794036ebf2e
</dict>
<key>IDESourceControlProjectPath</key>
<string>ExampleApp/KGModalExample.xcodeproj/project.xcworkspace</string>
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
<dict>
<<<<<<< HEAD
<key>2826E242-8CFF-4E68-8504-3384724F0FBF</key>
<string>../../..</string>
</dict>
<key>IDESourceControlProjectURL</key>
<string>ssh://github.com/ashikahmad/KGModal.git</string>
<key>IDESourceControlProjectVersion</key>
<integer>110</integer>
<key>IDESourceControlProjectWCCIdentifier</key>
<string>2826E242-8CFF-4E68-8504-3384724F0FBF</string>
=======
<key>FB6C026C-C1C4-455C-BA11-CFF8C0912A70</key>
<string>../../..</string>
</dict>
Expand All @@ -26,13 +46,18 @@
<integer>110</integer>
<key>IDESourceControlProjectWCCIdentifier</key>
<string>FB6C026C-C1C4-455C-BA11-CFF8C0912A70</string>
>>>>>>> a3c6591e8ca7472544632dde12013794036ebf2e
<key>IDESourceControlProjectWCConfigurations</key>
<array>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<<<<<<< HEAD
<string>2826E242-8CFF-4E68-8504-3384724F0FBF</string>
=======
<string>FB6C026C-C1C4-455C-BA11-CFF8C0912A70</string>
>>>>>>> a3c6591e8ca7472544632dde12013794036ebf2e
<key>IDESourceControlWCCName</key>
<string>KGModal</string>
</dict>
Expand Down
5 changes: 5 additions & 0 deletions KGModal.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,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;
Expand Down Expand Up @@ -79,4 +81,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
133 changes: 121 additions & 12 deletions KGModal.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,20 @@
#import "KGModal.h"
#import <QuartzCore/QuartzCore.h>

#pragma mark - Utils for Keyboard response

#define swap(a, b) do{typeof(a) odd13var=a; a=b; b=odd13var;}while(0)

@interface UIView (KGFirstResponder)
- (UIView *)kgFindFirstResponder;
@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";

Expand Down Expand Up @@ -80,14 +90,93 @@ -(void)setCloseButtonType:(KGModalCloseButtonType)closeButtonType {

CGRect closeFrame = self.closeButton.frame;
if(closeButtonType == KGModalCloseButtonTypeRight){
closeFrame.origin.x = round(CGRectGetWidth(self.containerView.frame)-kDefaultCloseButtonPadding-CGRectGetWidth(closeFrame)/2);
closeFrame.origin.x = round(CGRectGetWidth(self.containerView.frame)-kDefaultPadding-CGRectGetWidth(closeFrame)/2);
}else{
closeFrame.origin.x = 0;
}
self.closeButton.frame = closeFrame;
}
}

#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;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above calculation for right-side close button frame is done at line [141 | 222] by recalling setter. So not needed here anymore.
And furthermore, it is better to have this calculated from one place so we can update them easily (if needed) without breaking anything.

// 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 kgFindFirstResponder];
// 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];
}
Expand All @@ -110,8 +199,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));
Expand All @@ -120,19 +208,12 @@ - (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;

KGModalCloseButton *closeButton = [[KGModalCloseButton alloc] init];

if(self.closeButtonType == KGModalCloseButtonTypeRight){
CGRect closeFrame = closeButton.frame;
closeFrame.origin.x = CGRectGetWidth(containerView.bounds)-CGRectGetWidth(closeFrame);
closeButton.frame = closeFrame;
}

[closeButton addTarget:self action:@selector(closeAction:) forControlEvents:UIControlEventTouchUpInside];
[containerView addSubview:closeButton];
self.closeButton = closeButton;
Expand All @@ -143,6 +224,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(), ^{
Expand Down Expand Up @@ -243,6 +327,10 @@ - (void)setModalBackgroundColor:(UIColor *)modalBackgroundColor{
}
}

-(void)endEditing:(BOOL)force {
[self.containerView endEditing:force];
}

- (void)dealloc{
[self cleanup];
}
Expand Down Expand Up @@ -427,3 +515,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