diff --git a/Classes/Base.lproj/WizardViewController.xib b/Classes/Base.lproj/WizardViewController.xib index bc04a016c..7a88a452a 100644 --- a/Classes/Base.lproj/WizardViewController.xib +++ b/Classes/Base.lproj/WizardViewController.xib @@ -1,8 +1,8 @@ - + - - + + @@ -20,7 +20,7 @@ - + @@ -97,6 +97,8 @@ + + @@ -109,4 +111,9 @@ - \ No newline at end of file + + + + + + diff --git a/Classes/Base.lproj/WizardViewController~ipad.xib b/Classes/Base.lproj/WizardViewController~ipad.xib index ad94a9549..eee0b856d 100644 --- a/Classes/Base.lproj/WizardViewController~ipad.xib +++ b/Classes/Base.lproj/WizardViewController~ipad.xib @@ -1,8 +1,8 @@ - + - - + + @@ -21,7 +21,7 @@ - + @@ -98,12 +98,14 @@ + + - + @@ -180,6 +182,8 @@ + + @@ -200,4 +204,9 @@ + + + + + diff --git a/Classes/Base.lproj/WizardViews.xib b/Classes/Base.lproj/WizardViews.xib index 19d9acf68..82ac47106 100644 --- a/Classes/Base.lproj/WizardViews.xib +++ b/Classes/Base.lproj/WizardViews.xib @@ -1,8 +1,8 @@ - + - + @@ -22,7 +22,6 @@ - @@ -244,22 +243,6 @@ - diff --git a/Classes/InAppProductsManager.m b/Classes/InAppProductsManager.m index b3657a29b..9ce1f73c9 100644 --- a/Classes/InAppProductsManager.m +++ b/Classes/InAppProductsManager.m @@ -44,9 +44,8 @@ - (instancetype)init { if ((self = [super init]) != nil) { - _enabled = - (([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) && ([SKPaymentQueue canMakePayments]) && - ([[LinphoneManager instance] lpConfigBoolForKey:@"enabled" forSection:@"in_app_purchase"])); + _enabled = (([SKPaymentQueue canMakePayments]) && + ([[LinphoneManager instance] lpConfigBoolForKey:@"enabled" forSection:@"in_app_purchase"])); _initialized = false; _available = false; _accountActivationInProgress = false; diff --git a/Classes/Utils/TPKeyboardAvoiding/LICENSE.txt b/Classes/Utils/TPKeyboardAvoiding/LICENSE.txt new file mode 100755 index 000000000..b2071e3e3 --- /dev/null +++ b/Classes/Utils/TPKeyboardAvoiding/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2013 Michael Tyson + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. \ No newline at end of file diff --git a/Classes/Utils/TPKeyboardAvoiding/TPKeyboardAvoidingCollectionView.h b/Classes/Utils/TPKeyboardAvoiding/TPKeyboardAvoidingCollectionView.h new file mode 100755 index 000000000..b870e97fd --- /dev/null +++ b/Classes/Utils/TPKeyboardAvoiding/TPKeyboardAvoidingCollectionView.h @@ -0,0 +1,14 @@ +// +// TPKeyboardAvoidingCollectionView.h +// +// Created by Michael Tyson on 30/09/2013. +// Copyright 2013 A Tasty Pixel & The CocoaBots. All rights reserved. +// + +#import +#import "UIScrollView+TPKeyboardAvoidingAdditions.h" + +@interface TPKeyboardAvoidingCollectionView : UICollectionView +- (BOOL)focusNextTextField; +- (void)scrollToActiveTextField; +@end diff --git a/Classes/Utils/TPKeyboardAvoiding/TPKeyboardAvoidingCollectionView.m b/Classes/Utils/TPKeyboardAvoiding/TPKeyboardAvoidingCollectionView.m new file mode 100755 index 000000000..caad404e9 --- /dev/null +++ b/Classes/Utils/TPKeyboardAvoiding/TPKeyboardAvoidingCollectionView.m @@ -0,0 +1,96 @@ +// +// TPKeyboardAvoidingCollectionView.m +// +// Created by Michael Tyson on 30/09/2013. +// Copyright 2013 A Tasty Pixel & The CocoaBots. All rights reserved. +// + +#import "TPKeyboardAvoidingCollectionView.h" + +@interface TPKeyboardAvoidingCollectionView () +@end + +@implementation TPKeyboardAvoidingCollectionView + +#pragma mark - Setup/Teardown + +- (void)setup { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(TPKeyboardAvoiding_keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(TPKeyboardAvoiding_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; +} + +-(id)initWithFrame:(CGRect)frame { + if ( !(self = [super initWithFrame:frame]) ) return nil; + [self setup]; + return self; +} + +- (id)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout { + if ( !(self = [super initWithFrame:frame collectionViewLayout:layout]) ) return nil; + [self setup]; + return self; +} + +-(void)awakeFromNib { + [self setup]; +} + +-(void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +#if !__has_feature(objc_arc) + [super dealloc]; +#endif +} + +-(void)setFrame:(CGRect)frame { + [super setFrame:frame]; + [self TPKeyboardAvoiding_updateContentInset]; +} + +-(void)setContentSize:(CGSize)contentSize { + if (CGSizeEqualToSize(contentSize, self.contentSize)) { + // Prevent triggering contentSize when it's already the same that + // cause weird infinte scrolling and locking bug + return; + } + [super setContentSize:contentSize]; + [self TPKeyboardAvoiding_updateContentInset]; +} + +- (BOOL)focusNextTextField { + return [self TPKeyboardAvoiding_focusNextTextField]; + +} +- (void)scrollToActiveTextField { + return [self TPKeyboardAvoiding_scrollToActiveTextField]; +} + +#pragma mark - Responders, events + +- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + [[self TPKeyboardAvoiding_findFirstResponderBeneathView:self] resignFirstResponder]; + [super touchesEnded:touches withEvent:event]; +} + +-(BOOL)textFieldShouldReturn:(UITextField *)textField { + if ( ![self focusNextTextField] ) { + [textField resignFirstResponder]; + } + return YES; +} + +-(void)textFieldDidBeginEditing:(UITextField *)textField { + [self scrollToActiveTextField]; +} + +-(void)textViewDidBeginEditing:(UITextView *)textView { + [self scrollToActiveTextField]; +} + +-(void)layoutSubviews { + [super layoutSubviews]; + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) object:self]; + [self performSelector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) withObject:self afterDelay:0.1]; +} + +@end diff --git a/Classes/Utils/TPKeyboardAvoiding/TPKeyboardAvoidingScrollView.h b/Classes/Utils/TPKeyboardAvoiding/TPKeyboardAvoidingScrollView.h new file mode 100755 index 000000000..4d38de93c --- /dev/null +++ b/Classes/Utils/TPKeyboardAvoiding/TPKeyboardAvoidingScrollView.h @@ -0,0 +1,15 @@ +// +// TPKeyboardAvoidingScrollView.h +// +// Created by Michael Tyson on 30/09/2013. +// Copyright 2013 A Tasty Pixel. All rights reserved. +// + +#import +#import "UIScrollView+TPKeyboardAvoidingAdditions.h" + +@interface TPKeyboardAvoidingScrollView : UIScrollView +- (void)contentSizeToFit; +- (BOOL)focusNextTextField; +- (void)scrollToActiveTextField; +@end diff --git a/Classes/Utils/TPKeyboardAvoiding/TPKeyboardAvoidingScrollView.m b/Classes/Utils/TPKeyboardAvoiding/TPKeyboardAvoidingScrollView.m new file mode 100755 index 000000000..2959ec03b --- /dev/null +++ b/Classes/Utils/TPKeyboardAvoiding/TPKeyboardAvoidingScrollView.m @@ -0,0 +1,89 @@ +// +// TPKeyboardAvoidingScrollView.m +// +// Created by Michael Tyson on 30/09/2013. +// Copyright 2013 A Tasty Pixel. All rights reserved. +// + +#import "TPKeyboardAvoidingScrollView.h" + +@interface TPKeyboardAvoidingScrollView () +@end + +@implementation TPKeyboardAvoidingScrollView + +#pragma mark - Setup/Teardown + +- (void)setup { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(TPKeyboardAvoiding_keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(TPKeyboardAvoiding_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; +} + +-(id)initWithFrame:(CGRect)frame { + if ( !(self = [super initWithFrame:frame]) ) return nil; + [self setup]; + return self; +} + +-(void)awakeFromNib { + [self setup]; +} + +-(void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +#if !__has_feature(objc_arc) + [super dealloc]; +#endif +} + +-(void)setFrame:(CGRect)frame { + [super setFrame:frame]; + [self TPKeyboardAvoiding_updateContentInset]; +} + +-(void)setContentSize:(CGSize)contentSize { + [super setContentSize:contentSize]; + [self TPKeyboardAvoiding_updateFromContentSizeChange]; +} + +- (void)contentSizeToFit { + self.contentSize = [self TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames]; +} + +- (BOOL)focusNextTextField { + return [self TPKeyboardAvoiding_focusNextTextField]; + +} +- (void)scrollToActiveTextField { + return [self TPKeyboardAvoiding_scrollToActiveTextField]; +} + +#pragma mark - Responders, events + +- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + [[self TPKeyboardAvoiding_findFirstResponderBeneathView:self] resignFirstResponder]; + [super touchesEnded:touches withEvent:event]; +} + +-(BOOL)textFieldShouldReturn:(UITextField *)textField { + if ( ![self focusNextTextField] ) { + [textField resignFirstResponder]; + } + return YES; +} + +-(void)textFieldDidBeginEditing:(UITextField *)textField { + [self scrollToActiveTextField]; +} + +-(void)textViewDidBeginEditing:(UITextView *)textView { + [self scrollToActiveTextField]; +} + +-(void)layoutSubviews { + [super layoutSubviews]; + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) object:self]; + [self performSelector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) withObject:self afterDelay:0.1]; +} + +@end diff --git a/Classes/Utils/TPKeyboardAvoiding/TPKeyboardAvoidingTableView.h b/Classes/Utils/TPKeyboardAvoiding/TPKeyboardAvoidingTableView.h new file mode 100755 index 000000000..83cde3204 --- /dev/null +++ b/Classes/Utils/TPKeyboardAvoiding/TPKeyboardAvoidingTableView.h @@ -0,0 +1,14 @@ +// +// TPKeyboardAvoidingTableView.h +// +// Created by Michael Tyson on 30/09/2013. +// Copyright 2013 A Tasty Pixel. All rights reserved. +// + +#import +#import "UIScrollView+TPKeyboardAvoidingAdditions.h" + +@interface TPKeyboardAvoidingTableView : UITableView +- (BOOL)focusNextTextField; +- (void)scrollToActiveTextField; +@end diff --git a/Classes/Utils/TPKeyboardAvoiding/TPKeyboardAvoidingTableView.m b/Classes/Utils/TPKeyboardAvoiding/TPKeyboardAvoidingTableView.m new file mode 100755 index 000000000..e1b57efa1 --- /dev/null +++ b/Classes/Utils/TPKeyboardAvoiding/TPKeyboardAvoidingTableView.m @@ -0,0 +1,91 @@ +// +// TPKeyboardAvoidingTableView.m +// +// Created by Michael Tyson on 30/09/2013. +// Copyright 2013 A Tasty Pixel. All rights reserved. +// + +#import "TPKeyboardAvoidingTableView.h" + +@interface TPKeyboardAvoidingTableView () +@end + +@implementation TPKeyboardAvoidingTableView + +#pragma mark - Setup/Teardown + +- (void)setup { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(TPKeyboardAvoiding_keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(TPKeyboardAvoiding_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; +} + +-(id)initWithFrame:(CGRect)frame { + if ( !(self = [super initWithFrame:frame]) ) return nil; + [self setup]; + return self; +} + +-(id)initWithFrame:(CGRect)frame style:(UITableViewStyle)withStyle { + if ( !(self = [super initWithFrame:frame style:withStyle]) ) return nil; + [self setup]; + return self; +} + +-(void)awakeFromNib { + [self setup]; +} + +-(void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +#if !__has_feature(objc_arc) + [super dealloc]; +#endif +} + +-(void)setFrame:(CGRect)frame { + [super setFrame:frame]; + [self TPKeyboardAvoiding_updateContentInset]; +} + +-(void)setContentSize:(CGSize)contentSize { + [super setContentSize:contentSize]; + [self TPKeyboardAvoiding_updateContentInset]; +} + +- (BOOL)focusNextTextField { + return [self TPKeyboardAvoiding_focusNextTextField]; + +} +- (void)scrollToActiveTextField { + return [self TPKeyboardAvoiding_scrollToActiveTextField]; +} + +#pragma mark - Responders, events + +- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + [[self TPKeyboardAvoiding_findFirstResponderBeneathView:self] resignFirstResponder]; + [super touchesEnded:touches withEvent:event]; +} + +-(BOOL)textFieldShouldReturn:(UITextField *)textField { + if ( ![self focusNextTextField] ) { + [textField resignFirstResponder]; + } + return YES; +} + +-(void)textFieldDidBeginEditing:(UITextField *)textField { + [self scrollToActiveTextField]; +} + +-(void)textViewDidBeginEditing:(UITextView *)textView { + [self scrollToActiveTextField]; +} + +-(void)layoutSubviews { + [super layoutSubviews]; + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) object:self]; + [self performSelector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) withObject:self afterDelay:0.1]; +} + +@end diff --git a/Classes/Utils/TPKeyboardAvoiding/UIScrollView+TPKeyboardAvoidingAdditions.h b/Classes/Utils/TPKeyboardAvoiding/UIScrollView+TPKeyboardAvoidingAdditions.h new file mode 100755 index 000000000..86c151830 --- /dev/null +++ b/Classes/Utils/TPKeyboardAvoiding/UIScrollView+TPKeyboardAvoidingAdditions.h @@ -0,0 +1,22 @@ +// +// UIScrollView+TPKeyboardAvoidingAdditions.h +// TPKeyboardAvoidingSample +// +// Created by Michael Tyson on 30/09/2013. +// Copyright 2013 A Tasty Pixel. All rights reserved. +// + +#import + +@interface UIScrollView (TPKeyboardAvoidingAdditions) +- (BOOL)TPKeyboardAvoiding_focusNextTextField; +- (void)TPKeyboardAvoiding_scrollToActiveTextField; + +- (void)TPKeyboardAvoiding_keyboardWillShow:(NSNotification*)notification; +- (void)TPKeyboardAvoiding_keyboardWillHide:(NSNotification*)notification; +- (void)TPKeyboardAvoiding_updateContentInset; +- (void)TPKeyboardAvoiding_updateFromContentSizeChange; +- (void)TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:(UIView*)view; +- (UIView*)TPKeyboardAvoiding_findFirstResponderBeneathView:(UIView*)view; +-(CGSize)TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames; +@end diff --git a/Classes/Utils/TPKeyboardAvoiding/UIScrollView+TPKeyboardAvoidingAdditions.m b/Classes/Utils/TPKeyboardAvoiding/UIScrollView+TPKeyboardAvoidingAdditions.m new file mode 100755 index 000000000..cd70affe9 --- /dev/null +++ b/Classes/Utils/TPKeyboardAvoiding/UIScrollView+TPKeyboardAvoidingAdditions.m @@ -0,0 +1,285 @@ +// +// UIScrollView+TPKeyboardAvoidingAdditions.m +// TPKeyboardAvoidingSample +// +// Created by Michael Tyson on 30/09/2013. +// Copyright 2013 A Tasty Pixel. All rights reserved. +// + +#import "UIScrollView+TPKeyboardAvoidingAdditions.h" +#import "TPKeyboardAvoidingScrollView.h" +#import + +static const CGFloat kCalculatedContentPadding = 10; +static const CGFloat kMinimumScrollOffsetPadding = 20; + +static const int kStateKey; + +#define _UIKeyboardFrameEndUserInfoKey (&UIKeyboardFrameEndUserInfoKey != NULL ? UIKeyboardFrameEndUserInfoKey : @"UIKeyboardBoundsUserInfoKey") + +@interface TPKeyboardAvoidingState : NSObject +@property (nonatomic, assign) UIEdgeInsets priorInset; +@property (nonatomic, assign) UIEdgeInsets priorScrollIndicatorInsets; +@property (nonatomic, assign) BOOL keyboardVisible; +@property (nonatomic, assign) CGRect keyboardRect; +@property (nonatomic, assign) CGSize priorContentSize; +@end + +@implementation UIScrollView (TPKeyboardAvoidingAdditions) + +- (TPKeyboardAvoidingState*)keyboardAvoidingState { + TPKeyboardAvoidingState *state = objc_getAssociatedObject(self, &kStateKey); + if ( !state ) { + state = [[TPKeyboardAvoidingState alloc] init]; + objc_setAssociatedObject(self, &kStateKey, state, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +#if !__has_feature(objc_arc) + [state release]; +#endif + } + return state; +} + +- (void)TPKeyboardAvoiding_keyboardWillShow:(NSNotification*)notification { + TPKeyboardAvoidingState *state = self.keyboardAvoidingState; + + if ( state.keyboardVisible ) { + return; + } + + UIView *firstResponder = [self TPKeyboardAvoiding_findFirstResponderBeneathView:self]; + + state.keyboardRect = [self convertRect:[[[notification userInfo] objectForKey:_UIKeyboardFrameEndUserInfoKey] CGRectValue] fromView:nil]; + state.keyboardVisible = YES; + state.priorInset = self.contentInset; + state.priorScrollIndicatorInsets = self.scrollIndicatorInsets; + + if ( [self isKindOfClass:[TPKeyboardAvoidingScrollView class]] ) { + state.priorContentSize = self.contentSize; + + if ( CGSizeEqualToSize(self.contentSize, CGSizeZero) ) { + // Set the content size, if it's not set. Do not set content size explicitly if auto-layout + // is being used to manage subviews + self.contentSize = [self TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames]; + } + } + + // Shrink view's inset by the keyboard's height, and scroll to show the text field/view being edited + [UIView beginAnimations:nil context:NULL]; + [UIView setAnimationCurve:[[[notification userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]]; + [UIView setAnimationDuration:[[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue]]; + + self.contentInset = [self TPKeyboardAvoiding_contentInsetForKeyboard]; + + if ( firstResponder ) { + CGFloat viewableHeight = self.bounds.size.height - self.contentInset.top - self.contentInset.bottom; + [self setContentOffset:CGPointMake(self.contentOffset.x, + [self TPKeyboardAvoiding_idealOffsetForView:firstResponder + withViewingAreaHeight:viewableHeight]) + animated:NO]; + } + + self.scrollIndicatorInsets = self.contentInset; + + [UIView commitAnimations]; +} + +- (void)TPKeyboardAvoiding_keyboardWillHide:(NSNotification*)notification { + TPKeyboardAvoidingState *state = self.keyboardAvoidingState; + + if ( !state.keyboardVisible ) { + return; + } + + state.keyboardRect = CGRectZero; + state.keyboardVisible = NO; + + // Restore dimensions to prior size + [UIView beginAnimations:nil context:NULL]; + [UIView setAnimationCurve:[[[notification userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]]; + [UIView setAnimationDuration:[[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue]]; + + if ( [self isKindOfClass:[TPKeyboardAvoidingScrollView class]] ) { + self.contentSize = state.priorContentSize; + } + + self.contentInset = state.priorInset; + self.scrollIndicatorInsets = state.priorScrollIndicatorInsets; + [UIView commitAnimations]; +} + +- (void)TPKeyboardAvoiding_updateContentInset { + TPKeyboardAvoidingState *state = self.keyboardAvoidingState; + if ( state.keyboardVisible ) { + self.contentInset = [self TPKeyboardAvoiding_contentInsetForKeyboard]; + } +} + +- (void)TPKeyboardAvoiding_updateFromContentSizeChange { + TPKeyboardAvoidingState *state = self.keyboardAvoidingState; + if ( state.keyboardVisible ) { + state.priorContentSize = self.contentSize; + self.contentInset = [self TPKeyboardAvoiding_contentInsetForKeyboard]; + } +} + +#pragma mark - Utilities + +- (BOOL)TPKeyboardAvoiding_focusNextTextField { + UIView *firstResponder = [self TPKeyboardAvoiding_findFirstResponderBeneathView:self]; + if ( !firstResponder ) { + return NO; + } + + CGFloat minY = CGFLOAT_MAX; + UIView *view = nil; + [self TPKeyboardAvoiding_findTextFieldAfterTextField:firstResponder beneathView:self minY:&minY foundView:&view]; + + if ( view ) { + [view performSelector:@selector(becomeFirstResponder) withObject:nil afterDelay:0.0]; + return YES; + } + + return NO; +} + +-(void)TPKeyboardAvoiding_scrollToActiveTextField { + TPKeyboardAvoidingState *state = self.keyboardAvoidingState; + + if ( !state.keyboardVisible ) return; + + CGFloat visibleSpace = self.bounds.size.height - self.contentInset.top - self.contentInset.bottom; + + CGPoint idealOffset = CGPointMake(0, [self TPKeyboardAvoiding_idealOffsetForView:[self TPKeyboardAvoiding_findFirstResponderBeneathView:self] + withViewingAreaHeight:visibleSpace]); + + // Ordinarily we'd use -setContentOffset:animated:YES here, but it does not appear to + // scroll to the desired content offset. So we wrap in our own animation block. + [UIView animateWithDuration:0.25 animations:^{ + [self setContentOffset:idealOffset animated:NO]; + }]; +} + +#pragma mark - Helpers + +- (UIView*)TPKeyboardAvoiding_findFirstResponderBeneathView:(UIView*)view { + // Search recursively for first responder + for ( UIView *childView in view.subviews ) { + if ( [childView respondsToSelector:@selector(isFirstResponder)] && [childView isFirstResponder] ) return childView; + UIView *result = [self TPKeyboardAvoiding_findFirstResponderBeneathView:childView]; + if ( result ) return result; + } + return nil; +} + +- (void)TPKeyboardAvoiding_findTextFieldAfterTextField:(UIView*)priorTextField beneathView:(UIView*)view minY:(CGFloat*)minY foundView:(UIView**)foundView { + // Search recursively for text field or text view below priorTextField + CGFloat priorFieldOffset = CGRectGetMinY([self convertRect:priorTextField.frame fromView:priorTextField.superview]); + for ( UIView *childView in view.subviews ) { + if ( childView.hidden ) continue; + if ( ([childView isKindOfClass:[UITextField class]] || [childView isKindOfClass:[UITextView class]]) && childView.isUserInteractionEnabled) { + CGRect frame = [self convertRect:childView.frame fromView:view]; + if ( childView != priorTextField + && CGRectGetMinY(frame) >= priorFieldOffset + && CGRectGetMinY(frame) < *minY && + !(frame.origin.y == priorTextField.frame.origin.y + && frame.origin.x < priorTextField.frame.origin.x) ) { + *minY = CGRectGetMinY(frame); + *foundView = childView; + } + } else { + [self TPKeyboardAvoiding_findTextFieldAfterTextField:priorTextField beneathView:childView minY:minY foundView:foundView]; + } + } +} + +- (void)TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:(UIView*)view { + for ( UIView *childView in view.subviews ) { + if ( ([childView isKindOfClass:[UITextField class]] || [childView isKindOfClass:[UITextView class]]) ) { + [self TPKeyboardAvoiding_initializeView:childView]; + } else { + [self TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:childView]; + } + } +} + +-(CGSize)TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames { + + BOOL wasShowingVerticalScrollIndicator = self.showsVerticalScrollIndicator; + BOOL wasShowingHorizontalScrollIndicator = self.showsHorizontalScrollIndicator; + + self.showsVerticalScrollIndicator = NO; + self.showsHorizontalScrollIndicator = NO; + + CGRect rect = CGRectZero; + for ( UIView *view in self.subviews ) { + rect = CGRectUnion(rect, view.frame); + } + rect.size.height += kCalculatedContentPadding; + + self.showsVerticalScrollIndicator = wasShowingVerticalScrollIndicator; + self.showsHorizontalScrollIndicator = wasShowingHorizontalScrollIndicator; + + return rect.size; +} + + +- (UIEdgeInsets)TPKeyboardAvoiding_contentInsetForKeyboard { + TPKeyboardAvoidingState *state = self.keyboardAvoidingState; + UIEdgeInsets newInset = self.contentInset; + CGRect keyboardRect = state.keyboardRect; + newInset.bottom = keyboardRect.size.height - (CGRectGetMaxY(keyboardRect) - CGRectGetMaxY(self.bounds)); + return newInset; +} + +-(CGFloat)TPKeyboardAvoiding_idealOffsetForView:(UIView *)view withViewingAreaHeight:(CGFloat)viewAreaHeight { + CGSize contentSize = self.contentSize; + CGFloat offset = 0.0; + + CGRect subviewRect = [view convertRect:view.bounds toView:self]; + + // Attempt to center the subview in the visible space, but if that means there will be less than kMinimumScrollOffsetPadding + // pixels above the view, then substitute kMinimumScrollOffsetPadding + CGFloat padding = (viewAreaHeight - subviewRect.size.height) / 2; + if ( padding < kMinimumScrollOffsetPadding ) { + padding = kMinimumScrollOffsetPadding; + } + + // Ideal offset places the subview rectangle origin "padding" points from the top of the scrollview. + // If there is a top contentInset, also compensate for this so that subviewRect will not be placed under + // things like navigation bars. + offset = subviewRect.origin.y - padding - self.contentInset.top; + + // Constrain the new contentOffset so we can't scroll past the bottom. Note that we don't take the bottom + // inset into account, as this is manipulated to make space for the keyboard. + if ( offset > (contentSize.height - viewAreaHeight) ) { + offset = contentSize.height - viewAreaHeight; + } + + // Constrain the new contentOffset so we can't scroll past the top, taking contentInsets into account + if ( offset < -self.contentInset.top ) { + offset = -self.contentInset.top; + } + + return offset; +} + +- (void)TPKeyboardAvoiding_initializeView:(UIView*)view { + if ( [view isKindOfClass:[UITextField class]] && ((UITextField*)view).returnKeyType == UIReturnKeyDefault && (![(id)view delegate] || [(UIScrollView*)view delegate] == (id)self) ) { + [(UIScrollView*)view setDelegate:(id)self]; + UIView *otherView = nil; + CGFloat minY = CGFLOAT_MAX; + [self TPKeyboardAvoiding_findTextFieldAfterTextField:view beneathView:self minY:&minY foundView:&otherView]; + + if ( otherView ) { + ((UITextField*)view).returnKeyType = UIReturnKeyNext; + } else { + ((UITextField*)view).returnKeyType = UIReturnKeyDone; + } + } +} + +@end + + +@implementation TPKeyboardAvoidingState +@end diff --git a/Classes/WizardViewController.h b/Classes/WizardViewController.h index 35a97b423..87aa1a152 100644 --- a/Classes/WizardViewController.h +++ b/Classes/WizardViewController.h @@ -22,6 +22,7 @@ #import "UICompositeViewController.h" #import "UILinphoneTextField.h" #import "LinphoneUI/UILinphoneButton.h" +#import "TPKeyboardAvoidingScrollView.h" @interface WizardViewController : TPMultiLayoutViewController