-
Notifications
You must be signed in to change notification settings - Fork 150
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Moved code from KeyboardStateScroller project
- Loading branch information
1 parent
873cbfe
commit deee184
Showing
33 changed files
with
1,547 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# KeyboardStateScroller CHANGELOG | ||
|
||
## 0.1.0 | ||
|
||
Initial release. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// | ||
// IHKeyboardAvoiding.h | ||
// IHKeyboardAvoiding | ||
// | ||
// Created by Fraser Scott-Morrison on 29/03/13. | ||
// Copyright (c) 2013 Idle Hands Apps. All rights reserved. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
enum KeyboardAvoidingMode { | ||
KeyboardAvoidingModeMaximum = 0, | ||
KeyboardAvoidingModeMinimum = 1, | ||
KeyboardAvoidingModeMinimumDelayed = 2 | ||
} | ||
typedef KeyboardAvoidingMode; | ||
|
||
@interface IHKeyboardAvoiding : NSObject | ||
|
||
+ (void)setKeyboardAvoidingMode:(KeyboardAvoidingMode)keyboardAvoidingMode; | ||
|
||
+ (void)setAvoidingView:(UIView *)avoidingView withTarget:(UIView *)targetView; | ||
+ (void)addTarget:(UIView *)targetView; | ||
+ (void)removeTarget:(UIView *)targetView; | ||
+ (void)removeAll; | ||
|
||
// utility method to find out if the keyboard is visible. Works for docked, undocked and split keyboards | ||
+ (BOOL)isKeyboardVisible; | ||
|
||
// If the visible keyboard plus the buffer intersect with the targetView, then the avoiding View will be moved. Default buffer is 0 points | ||
+ (void)setBuffer:(int)buffer; | ||
|
||
// padding to put between the keyboard and avoiding view. Default padding is 0 points | ||
+ (void)setPadding:(int)padding; | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,257 @@ | ||
// | ||
// IHKeyboardAvoiding.m | ||
// IHKeyboardAvoiding | ||
// | ||
// Created by Fraser Scott-Morrison on 29/03/13. | ||
// Copyright (c) 2013 Idle Hands Apps. All rights reserved. | ||
// | ||
|
||
#import "IHKeyboardAvoiding.h" | ||
|
||
@implementation IHKeyboardAvoiding | ||
|
||
static NSMutableArray *_targetViews; | ||
static UIView *_avoidingView; | ||
static NSMutableArray *_updatedConstraints; | ||
static NSMutableArray *_updatedConstraintConstants; | ||
|
||
static BOOL _isKeyboardVisible; | ||
static BOOL _avoidingViewUsesAutoLayout; | ||
static int _buffer = 0; | ||
static int _padding = 0; | ||
static float _defaultAnimationDuration = 0.3; // If keyboard is not animating, animate the avoiding view anyway | ||
static KeyboardAvoidingMode _keyboardAvoidingMode = KeyboardAvoidingModeMinimum; | ||
static float _minimumAnimationDuration; | ||
|
||
+ (void)didChange:(NSNotification *)notification | ||
{ | ||
BOOL isKeyBoardShowing = NO; // isKeyBoardShowing and is it merged and docked. | ||
BOOL isPortrait = UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation]); | ||
|
||
// get the keyboard & window frames | ||
CGRect keyboardFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; | ||
CGRect windowFrame = [UIApplication sharedApplication].keyWindow.frame; | ||
|
||
// if split keyboard is being dragged, then skip notification | ||
if (keyboardFrame.size.height == 0) { | ||
CGRect keyboardBeginFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue]; | ||
|
||
if (isPortrait) { | ||
if (keyboardBeginFrame.origin.y + keyboardBeginFrame.size.height == windowFrame.size.height) | ||
return; | ||
} else { | ||
if (keyboardBeginFrame.origin.x + keyboardBeginFrame.size.width == windowFrame.size.width) | ||
return; | ||
} | ||
} | ||
|
||
// calculate if we are to move up the avoiding view | ||
if (isPortrait) { | ||
if (keyboardFrame.origin.y == 0 || (keyboardFrame.origin.y + keyboardFrame.size.height == windowFrame.size.height)) { | ||
isKeyBoardShowing = YES; | ||
} | ||
} else { | ||
if (keyboardFrame.origin.x == 0 || (keyboardFrame.origin.x + keyboardFrame.size.width == windowFrame.size.width)) { | ||
isKeyBoardShowing = YES; | ||
} | ||
} | ||
|
||
// get animation duration | ||
float animationDuration = [[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue]; | ||
if (animationDuration == 0) { | ||
animationDuration = _defaultAnimationDuration; | ||
} | ||
|
||
if (isKeyBoardShowing) { | ||
for (int i = 0; i < _targetViews.count; i++) { | ||
UIView *targetView = [_targetViews objectAtIndex:i]; | ||
//showing and docked | ||
if (targetView) { | ||
float diff = 0; | ||
CGPoint originInWindow = [targetView.superview convertPoint:targetView.frame.origin toView:nil]; | ||
switch ([[UIApplication sharedApplication] statusBarOrientation]) { | ||
case UIInterfaceOrientationPortrait: | ||
diff = keyboardFrame.origin.y; | ||
diff -= (originInWindow.y + targetView.frame.size.height); | ||
break; | ||
case UIInterfaceOrientationPortraitUpsideDown: | ||
diff = windowFrame.size.height - keyboardFrame.size.height; | ||
originInWindow.y = windowFrame.size.height - originInWindow.y; | ||
diff -= (originInWindow.y + targetView.frame.size.height); | ||
break; | ||
case UIInterfaceOrientationLandscapeLeft: | ||
diff = keyboardFrame.origin.x; | ||
diff -= (originInWindow.x + targetView.frame.size.height); | ||
break; | ||
case UIInterfaceOrientationLandscapeRight: | ||
diff = windowFrame.size.width - keyboardFrame.size.width; | ||
originInWindow.x = windowFrame.size.width - originInWindow.x; | ||
diff -= (originInWindow.x + targetView.frame.size.height); | ||
default: | ||
break; | ||
} | ||
|
||
|
||
if (diff < _buffer) { | ||
|
||
float displacement = ( isPortrait ? -keyboardFrame.size.height : -keyboardFrame.size.width); | ||
float delay = 0; | ||
|
||
switch (_keyboardAvoidingMode) { | ||
case KeyboardAvoidingModeMaximum: | ||
{ | ||
_minimumAnimationDuration = animationDuration; | ||
break; | ||
} | ||
case KeyboardAvoidingModeMinimumDelayed: | ||
{ | ||
float minimumDisplacement = fmaxf(displacement, diff); | ||
_minimumAnimationDuration = animationDuration * (minimumDisplacement / displacement); | ||
displacement = minimumDisplacement - _padding; | ||
delay = (animationDuration - _minimumAnimationDuration); | ||
animationDuration = _minimumAnimationDuration; | ||
break; | ||
} | ||
case KeyboardAvoidingModeMinimum: | ||
default: | ||
{ | ||
float minimumDisplacement = fmaxf(displacement, diff); | ||
displacement = minimumDisplacement - _padding; | ||
break; | ||
} | ||
} | ||
|
||
if (_avoidingViewUsesAutoLayout) { // if view uses constrains | ||
for (NSLayoutConstraint *constraint in _avoidingView.superview.constraints) { | ||
if (constraint.secondItem == _avoidingView && (constraint.secondAttribute == NSLayoutAttributeCenterY || constraint.secondAttribute == NSLayoutAttributeTop || constraint.secondAttribute == NSLayoutAttributeBottom)) { | ||
[_updatedConstraints addObject:constraint]; | ||
[_updatedConstraintConstants addObject:[NSNumber numberWithFloat:constraint.constant]]; | ||
constraint.constant -= displacement; | ||
break; | ||
} | ||
} | ||
[_avoidingView.superview setNeedsUpdateConstraints]; | ||
} | ||
|
||
[UIView animateWithDuration:animationDuration | ||
delay:delay | ||
options:UIViewAnimationOptionCurveLinear | ||
animations:^{ | ||
if (_avoidingViewUsesAutoLayout) { | ||
[_avoidingView.superview layoutIfNeeded]; // to animate constraint changes | ||
} | ||
else { | ||
_avoidingView.transform = CGAffineTransformMakeTranslation(0, displacement); | ||
} | ||
} | ||
completion:nil]; | ||
|
||
} | ||
} | ||
} | ||
|
||
} | ||
else if (_isKeyboardVisible) { | ||
// hiding, undocking or splitting | ||
|
||
switch (_keyboardAvoidingMode) { | ||
case KeyboardAvoidingModeMaximum: | ||
{ | ||
|
||
break; | ||
} | ||
case KeyboardAvoidingModeMinimumDelayed: | ||
{ | ||
animationDuration = _minimumAnimationDuration; | ||
break; | ||
} | ||
case KeyboardAvoidingModeMinimum: | ||
default: | ||
{ | ||
break; | ||
} | ||
} | ||
|
||
// restore state | ||
if (_avoidingViewUsesAutoLayout) { // if view uses constrains | ||
for (int i = 0; i < _updatedConstraints.count; i++) { | ||
NSLayoutConstraint *updatedConstraint = [_updatedConstraints objectAtIndex:i]; | ||
float updatedConstraintConstant = [[_updatedConstraintConstants objectAtIndex:i] floatValue]; | ||
updatedConstraint.constant = updatedConstraintConstant; | ||
|
||
} | ||
[_avoidingView setNeedsUpdateConstraints]; // to animate constraint changes | ||
} | ||
|
||
[UIView animateWithDuration:animationDuration + 0.075 | ||
delay:0 | ||
options:[[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] integerValue] | ||
animations:^{ | ||
if (_avoidingViewUsesAutoLayout) { | ||
[_avoidingView.superview layoutIfNeeded]; | ||
} | ||
else { | ||
_avoidingView.transform = CGAffineTransformIdentity; | ||
} | ||
} completion:^(BOOL finished){ | ||
[_updatedConstraints removeAllObjects]; | ||
[_updatedConstraintConstants removeAllObjects]; | ||
}]; | ||
} | ||
|
||
_isKeyboardVisible = CGRectContainsRect(windowFrame, keyboardFrame); | ||
} | ||
|
||
+ (void)setAvoidingView:(UIView *)avoidingView withTarget:(UIView *)targetView; | ||
{ | ||
[self init]; | ||
|
||
[_targetViews removeAllObjects]; | ||
[_targetViews addObject:targetView]; | ||
_avoidingView = avoidingView; | ||
_avoidingViewUsesAutoLayout = _avoidingView.superview.constraints.count > 0; | ||
} | ||
|
||
+ (void)addTarget:(UIView *)targetView; | ||
{ | ||
[_targetViews addObject:targetView]; | ||
} | ||
|
||
+ (void)removeTarget:(UIView *)targetView; | ||
{ | ||
[_targetViews removeObject:targetView]; | ||
} | ||
|
||
+ (void)removeAll { | ||
[_targetViews removeAllObjects]; | ||
_avoidingView = nil; | ||
} | ||
|
||
+ (BOOL)isKeyboardVisible { | ||
return _isKeyboardVisible; | ||
} | ||
|
||
+ (void)setBuffer:(int)buffer { | ||
_buffer = buffer; | ||
} | ||
|
||
+ (void)setPadding:(int)padding { | ||
_padding = padding; | ||
} | ||
|
||
+ (void)setKeyboardAvoidingMode:(KeyboardAvoidingMode)keyboardAvoidingMode { | ||
_keyboardAvoidingMode = keyboardAvoidingMode; | ||
} | ||
|
||
+ (void)init { | ||
static dispatch_once_t onceToken; | ||
dispatch_once(&onceToken, ^{ | ||
// make sure we only add this once | ||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didChange:) name:UIKeyboardWillChangeFrameNotification object:nil]; | ||
_targetViews = [[NSMutableArray alloc] init]; | ||
_updatedConstraints = [[NSMutableArray alloc] init]; | ||
_updatedConstraintConstants = [[NSMutableArray alloc] init]; | ||
}); | ||
} | ||
|
||
@end |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// | ||
// IHAppDelegate.h | ||
// IHKeyboardAvoiding | ||
// | ||
// Created by Fraser Scott-Morrison on 18/04/13. | ||
// Copyright (c) 2013 Idle Hands Apps. All rights reserved. | ||
// | ||
|
||
#import <UIKit/UIKit.h> | ||
|
||
@class IHViewController; | ||
|
||
@interface IHAppDelegate : UIResponder <UIApplicationDelegate> | ||
|
||
@property (strong, nonatomic) UIWindow *window; | ||
|
||
@property (strong, nonatomic) IHViewController *viewController; | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
// | ||
// IHAppDelegate.m | ||
// IHKeyboardAvoiding | ||
// | ||
// Created by Fraser Scott-Morrison on 18/04/13. | ||
// Copyright (c) 2013 Idle Hands Apps. All rights reserved. | ||
// | ||
|
||
#import "IHAppDelegate.h" | ||
|
||
#import "IHViewController.h" | ||
|
||
@implementation IHAppDelegate | ||
|
||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions | ||
{ | ||
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; | ||
// Override point for customization after application launch. | ||
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { | ||
self.viewController = [[IHViewController alloc] initWithNibName:@"IHViewController_iPhone" bundle:nil]; | ||
} else { | ||
self.viewController = [[IHViewController alloc] initWithNibName:@"IHViewController_iPad" bundle:nil]; | ||
} | ||
self.window.rootViewController = self.viewController; | ||
[self.window makeKeyAndVisible]; | ||
return YES; | ||
} | ||
|
||
- (void)applicationWillResignActive:(UIApplication *)application | ||
{ | ||
// 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. | ||
// 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. | ||
} | ||
|
||
- (void)applicationDidEnterBackground:(UIApplication *)application | ||
{ | ||
// 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. | ||
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. | ||
} | ||
|
||
- (void)applicationWillEnterForeground:(UIApplication *)application | ||
{ | ||
// 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. | ||
} | ||
|
||
- (void)applicationDidBecomeActive:(UIApplication *)application | ||
{ | ||
// 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. | ||
} | ||
|
||
- (void)applicationWillTerminate:(UIApplication *)application | ||
{ | ||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. | ||
} | ||
|
||
@end |
Oops, something went wrong.