You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@usergrid.apache.org by sn...@apache.org on 2016/02/16 14:15:59 UTC
[67/75] usergrid git commit: Major Updates. See commit details.
http://git-wip-us.apache.org/repos/asf/usergrid/blob/c638c774/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextViewController.m
----------------------------------------------------------------------
diff --git a/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextViewController.m b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextViewController.m
deleted file mode 100644
index 5febfcb..0000000
--- a/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextViewController.m
+++ /dev/null
@@ -1,2392 +0,0 @@
-//
-// Copyright 2014 Slack Technologies, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#import "SLKTextViewController.h"
-#import "SLKInputAccessoryView.h"
-
-#import "UIResponder+SLKAdditions.h"
-
-/** Feature flagged while waiting to implement a more reliable technique. */
-#define SLKBottomPanningEnabled 0
-
-#define kSLKAlertViewClearTextTag [NSStringFromClass([SLKTextViewController class]) hash]
-
-NSString * const SLKKeyboardWillShowNotification = @"SLKKeyboardWillShowNotification";
-NSString * const SLKKeyboardDidShowNotification = @"SLKKeyboardDidShowNotification";
-NSString * const SLKKeyboardWillHideNotification = @"SLKKeyboardWillHideNotification";
-NSString * const SLKKeyboardDidHideNotification = @"SLKKeyboardDidHideNotification";
-
-CGFloat const SLKAutoCompletionViewDefaultHeight = 140.0;
-
-@interface SLKTextViewController ()
-{
- CGPoint _scrollViewOffsetBeforeDragging;
- CGFloat _keyboardHeightBeforeDragging;
-}
-
-// The shared scrollView pointer, either a tableView or collectionView
-@property (nonatomic, weak) UIScrollView *scrollViewProxy;
-
-// A hairline displayed on top of the auto-completion view, to better separate the content from the control.
-@property (nonatomic, strong) UIView *autoCompletionHairline;
-
-// Auto-Layout height constraints used for updating their constants
-@property (nonatomic, strong) NSLayoutConstraint *scrollViewHC;
-@property (nonatomic, strong) NSLayoutConstraint *textInputbarHC;
-@property (nonatomic, strong) NSLayoutConstraint *typingIndicatorViewHC;
-@property (nonatomic, strong) NSLayoutConstraint *autoCompletionViewHC;
-@property (nonatomic, strong) NSLayoutConstraint *keyboardHC;
-
-// YES if the user is moving the keyboard with a gesture
-@property (nonatomic, assign, getter = isMovingKeyboard) BOOL movingKeyboard;
-
-// The current keyboard status (hidden, showing, etc.)
-@property (nonatomic) SLKKeyboardStatus keyboardStatus;
-
-// YES if the view controller did appear and everything is finished configurating. This allows blocking some layout animations among other things.
-@property (nonatomic, getter=isViewVisible) BOOL viewVisible;
-
-// YES if the view controller's view's size is changing by its parent (i.e. when its window rotates or is resized)
-@property (nonatomic, getter = isTransitioning) BOOL transitioning;
-
-// Optional classes to be used instead of the default ones.
-@property (nonatomic, strong) Class textViewClass;
-@property (nonatomic, strong) Class typingIndicatorViewClass;
-
-@end
-
-@implementation SLKTextViewController
-@synthesize tableView = _tableView;
-@synthesize collectionView = _collectionView;
-@synthesize scrollView = _scrollView;
-@synthesize typingIndicatorProxyView = _typingIndicatorProxyView;
-@synthesize textInputbar = _textInputbar;
-@synthesize autoCompletionView = _autoCompletionView;
-@synthesize autoCompleting = _autoCompleting;
-@synthesize scrollViewProxy = _scrollViewProxy;
-@synthesize presentedInPopover = _presentedInPopover;
-
-#pragma mark - Initializer
-
-- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
-{
- return [self initWithTableViewStyle:UITableViewStylePlain];
-}
-
-- (instancetype)init
-{
- return [self initWithTableViewStyle:UITableViewStylePlain];
-}
-
-- (instancetype)initWithTableViewStyle:(UITableViewStyle)style
-{
- NSAssert([self class] != [SLKTextViewController class], @"Oops! You must subclass SLKTextViewController.");
-
- if (self = [super initWithNibName:nil bundle:nil])
- {
- self.scrollViewProxy = [self tableViewWithStyle:style];
- [self slk_commonInit];
- }
- return self;
-}
-
-- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout
-{
- NSAssert([self class] != [SLKTextViewController class], @"Oops! You must subclass SLKTextViewController.");
-
- if (self = [super initWithNibName:nil bundle:nil])
- {
- self.scrollViewProxy = [self collectionViewWithLayout:layout];
- [self slk_commonInit];
- }
- return self;
-}
-
-- (instancetype)initWithScrollView:(UIScrollView *)scrollView
-{
- NSAssert([self class] != [SLKTextViewController class], @"Oops! You must subclass SLKTextViewController.");
-
- if (self = [super initWithNibName:nil bundle:nil])
- {
- _scrollView = scrollView;
- _scrollView.translatesAutoresizingMaskIntoConstraints = NO; // Makes sure the scrollView plays nice with auto-layout
-
- self.scrollViewProxy = _scrollView;
- [self slk_commonInit];
- }
- return self;
-}
-
-- (instancetype)initWithCoder:(NSCoder *)decoder
-{
- NSAssert([self class] != [SLKTextViewController class], @"Oops! You must subclass SLKTextViewController.");
-
- if (self = [super initWithCoder:decoder])
- {
- UITableViewStyle tableViewStyle = [[self class] tableViewStyleForCoder:decoder];
- UICollectionViewLayout *collectionViewLayout = [[self class] collectionViewLayoutForCoder:decoder];
-
- if ([collectionViewLayout isKindOfClass:[UICollectionViewLayout class]]) {
- self.scrollViewProxy = [self collectionViewWithLayout:collectionViewLayout];
- }
- else {
- self.scrollViewProxy = [self tableViewWithStyle:tableViewStyle];
- }
-
- [self slk_commonInit];
- }
- return self;
-}
-
-- (void)slk_commonInit
-{
- [self slk_registerNotifications];
-
- self.bounces = YES;
- self.inverted = YES;
- self.shakeToClearEnabled = NO;
- self.keyboardPanningEnabled = YES;
- self.shouldClearTextAtRightButtonPress = YES;
- self.shouldScrollToBottomAfterKeyboardShows = NO;
-
- self.automaticallyAdjustsScrollViewInsets = YES;
- self.extendedLayoutIncludesOpaqueBars = YES;
-}
-
-
-#pragma mark - View lifecycle
-
-- (void)loadView
-{
- [super loadView];
-}
-
-- (void)viewDidLoad
-{
- [super viewDidLoad];
-
- [self.view addSubview:self.scrollViewProxy];
- [self.view addSubview:self.autoCompletionView];
- [self.view addSubview:self.typingIndicatorProxyView];
- [self.view addSubview:self.textInputbar];
-
- [self slk_setupViewConstraints];
-}
-
-- (void)viewWillAppear:(BOOL)animated
-{
- [super viewWillAppear:animated];
-
- // Invalidates this flag when the view appears
- self.textView.didNotResignFirstResponder = NO;
-
- // Forces laying out the recently added subviews and update their constraints
- [self.view layoutIfNeeded];
-
- [UIView performWithoutAnimation:^{
- // Reloads any cached text
- [self slk_reloadTextView];
- }];
-}
-
-- (void)viewDidAppear:(BOOL)animated
-{
- [super viewDidAppear:animated];
-
- [self.scrollViewProxy flashScrollIndicators];
-
- self.viewVisible = YES;
-}
-
-- (void)viewWillDisappear:(BOOL)animated
-{
- [super viewWillDisappear:animated];
-
- // Stops the keyboard from being dismissed during the navigation controller's "swipe-to-pop"
- self.textView.didNotResignFirstResponder = self.isMovingFromParentViewController;
-
- self.viewVisible = NO;
-
- // Caches the text before it's too late!
- [self slk_cacheTextView];
-}
-
-- (void)viewDidDisappear:(BOOL)animated
-{
- [super viewDidDisappear:animated];
-}
-
-- (void)viewWillLayoutSubviews
-{
- [super viewWillLayoutSubviews];
-
- [self slk_adjustContentConfigurationIfNeeded];
-}
-
-- (void)viewDidLayoutSubviews
-{
- [super viewDidLayoutSubviews];
-}
-
-
-#pragma mark - Getters
-
-+ (UITableViewStyle)tableViewStyleForCoder:(NSCoder *)decoder
-{
- return UITableViewStylePlain;
-}
-
-+ (UICollectionViewLayout *)collectionViewLayoutForCoder:(NSCoder *)decoder
-{
- return nil;
-}
-
-- (UITableView *)tableViewWithStyle:(UITableViewStyle)style
-{
- if (!_tableView) {
- _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:style];
- _tableView.translatesAutoresizingMaskIntoConstraints = NO;
- _tableView.scrollsToTop = YES;
- _tableView.dataSource = self;
- _tableView.delegate = self;
- _tableView.clipsToBounds = NO;
- }
- return _tableView;
-}
-
-- (UICollectionView *)collectionViewWithLayout:(UICollectionViewLayout *)layout
-{
- if (!_collectionView) {
- _collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
- _collectionView.translatesAutoresizingMaskIntoConstraints = NO;
- _collectionView.scrollsToTop = YES;
- _collectionView.dataSource = self;
- _collectionView.delegate = self;
- }
- return _collectionView;
-}
-
-- (UITableView *)autoCompletionView
-{
- if (!_autoCompletionView) {
- _autoCompletionView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
- _autoCompletionView.translatesAutoresizingMaskIntoConstraints = NO;
- _autoCompletionView.backgroundColor = [UIColor colorWithWhite:0.97 alpha:1.0];
- _autoCompletionView.scrollsToTop = NO;
- _autoCompletionView.dataSource = self;
- _autoCompletionView.delegate = self;
-
-#ifdef __IPHONE_9_0
- if ([_autoCompletionView respondsToSelector:@selector(cellLayoutMarginsFollowReadableWidth)]) {
- _autoCompletionView.cellLayoutMarginsFollowReadableWidth = NO;
- }
-#endif
-
- CGRect rect = CGRectZero;
- rect.size = CGSizeMake(CGRectGetWidth(self.view.frame), 0.5);
-
- _autoCompletionHairline = [[UIView alloc] initWithFrame:rect];
- _autoCompletionHairline.autoresizingMask = UIViewAutoresizingFlexibleWidth;
- _autoCompletionHairline.backgroundColor = _autoCompletionView.separatorColor;
- [_autoCompletionView addSubview:_autoCompletionHairline];
- }
- return _autoCompletionView;
-}
-
-- (SLKTextInputbar *)textInputbar
-{
- if (!_textInputbar) {
- _textInputbar = [[SLKTextInputbar alloc] initWithTextViewClass:self.textViewClass];
- _textInputbar.translatesAutoresizingMaskIntoConstraints = NO;
- _textInputbar.controller = self;
-
- [_textInputbar.leftButton addTarget:self action:@selector(didPressLeftButton:) forControlEvents:UIControlEventTouchUpInside];
- [_textInputbar.rightButton addTarget:self action:@selector(didPressRightButton:) forControlEvents:UIControlEventTouchUpInside];
- [_textInputbar.editorLeftButton addTarget:self action:@selector(didCancelTextEditing:) forControlEvents:UIControlEventTouchUpInside];
- [_textInputbar.editorRightButton addTarget:self action:@selector(didCommitTextEditing:) forControlEvents:UIControlEventTouchUpInside];
-
- _textInputbar.textView.delegate = self;
-
- _verticalPanGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(slk_didPanTextInputBar:)];
- _verticalPanGesture.delegate = self;
-
- [_textInputbar addGestureRecognizer:self.verticalPanGesture];
- }
- return _textInputbar;
-}
-
-- (UIView <SLKTypingIndicatorProtocol> *)typingIndicatorProxyView
-{
- if (!_typingIndicatorProxyView) {
- Class class = self.typingIndicatorViewClass ? : [SLKTypingIndicatorView class];
-
- _typingIndicatorProxyView = [[class alloc] init];
- _typingIndicatorProxyView.translatesAutoresizingMaskIntoConstraints = NO;
- _typingIndicatorProxyView.hidden = YES;
-
- [_typingIndicatorProxyView addObserver:self forKeyPath:@"visible" options:NSKeyValueObservingOptionNew context:nil];
- }
- return _typingIndicatorProxyView;
-}
-
-- (SLKTypingIndicatorView *)typingIndicatorView
-{
- if ([_typingIndicatorProxyView isKindOfClass:[SLKTypingIndicatorView class]]) {
- return (SLKTypingIndicatorView *)self.typingIndicatorProxyView;
- }
- return nil;
-}
-
-- (BOOL)isPresentedInPopover
-{
- return _presentedInPopover && SLK_IS_IPAD;
-}
-
-- (SLKTextView *)textView
-{
- return self.textInputbar.textView;
-}
-
-- (UIButton *)leftButton
-{
- return self.textInputbar.leftButton;
-}
-
-- (UIButton *)rightButton
-{
- return self.textInputbar.rightButton;
-}
-
-- (UIModalPresentationStyle)modalPresentationStyle
-{
- if (self.navigationController) {
- return self.navigationController.modalPresentationStyle;
- }
- return [super modalPresentationStyle];
-}
-
-- (CGFloat)slk_appropriateKeyboardHeightFromNotification:(NSNotification *)notification
-{
- // Let's first detect keyboard special states such as external keyboard, undocked or split layouts.
- [self slk_detectKeyboardStatesInNotification:notification];
-
- if ([self ignoreTextInputbarAdjustment]) {
- return [self slk_appropriateBottomMargin];
- }
-
- CGRect keyboardRect = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
-
- return [self slk_appropriateKeyboardHeightFromRect:keyboardRect];
-}
-
-- (CGFloat)slk_appropriateKeyboardHeightFromRect:(CGRect)rect
-{
- CGRect keyboardRect = [self.view convertRect:rect fromView:nil];
-
- CGFloat viewHeight = CGRectGetHeight(self.view.bounds);
- CGFloat keyboardMinY = CGRectGetMinY(keyboardRect);
-
- CGFloat keyboardHeight = MAX(0.0, viewHeight - keyboardMinY);
- CGFloat bottomMargin = [self slk_appropriateBottomMargin];
-
- // When the keyboard height is zero, we can assume there is no keyboard visible
- // In that case, let's see if there are any other views outside of the view hiearchy
- // requiring to adjust the text input bottom margin
- if (keyboardHeight < bottomMargin) {
- keyboardHeight = bottomMargin;
- }
-
- return keyboardHeight;
-}
-
-- (CGFloat)slk_appropriateBottomMargin
-{
- // A bottom margin is required only if the view is extended out of it bounds
- if ((self.edgesForExtendedLayout & UIRectEdgeBottom) > 0) {
- if (self.tabBarController) {
- return CGRectGetHeight(self.tabBarController.tabBar.frame);
- }
- }
-
- return 0.0;
-}
-
-- (CGFloat)slk_appropriateScrollViewHeight
-{
- CGFloat scrollViewHeight = CGRectGetHeight(self.view.bounds);
-
- scrollViewHeight -= self.keyboardHC.constant;
- scrollViewHeight -= self.textInputbarHC.constant;
- scrollViewHeight -= self.autoCompletionViewHC.constant;
- scrollViewHeight -= self.typingIndicatorViewHC.constant;
-
- if (scrollViewHeight < 0) return 0;
- else return scrollViewHeight;
-}
-
-- (CGFloat)slk_topBarsHeight
-{
- // No need to adjust if the edge isn't available
- if ((self.edgesForExtendedLayout & UIRectEdgeTop) == 0) {
- return 0.0;
- }
-
- CGFloat topBarsHeight = CGRectGetHeight(self.navigationController.navigationBar.frame);
-
- if ((SLK_IS_IPHONE && SLK_IS_LANDSCAPE && SLK_IS_IOS8_AND_HIGHER) ||
- (SLK_IS_IPAD && self.modalPresentationStyle == UIModalPresentationFormSheet) ||
- self.isPresentedInPopover) {
- return topBarsHeight;
- }
-
- topBarsHeight += CGRectGetHeight([UIApplication sharedApplication].statusBarFrame);
-
- return topBarsHeight;
-}
-
-- (NSString *)slk_appropriateKeyboardNotificationName:(NSNotification *)notification
-{
- NSString *name = notification.name;
-
- if ([name isEqualToString:UIKeyboardWillShowNotification]) {
- return SLKKeyboardWillShowNotification;
- }
- if ([name isEqualToString:UIKeyboardWillHideNotification]) {
- return SLKKeyboardWillHideNotification;
- }
- if ([name isEqualToString:UIKeyboardDidShowNotification]) {
- return SLKKeyboardDidShowNotification;
- }
- if ([name isEqualToString:UIKeyboardDidHideNotification]) {
- return SLKKeyboardDidHideNotification;
- }
- return nil;
-}
-
-- (SLKKeyboardStatus)slk_keyboardStatusForNotification:(NSNotification *)notification
-{
- NSString *name = notification.name;
-
- if ([name isEqualToString:UIKeyboardWillShowNotification]) {
- return SLKKeyboardStatusWillShow;
- }
- if ([name isEqualToString:UIKeyboardDidShowNotification]) {
- return SLKKeyboardStatusDidShow;
- }
- if ([name isEqualToString:UIKeyboardWillHideNotification]) {
- return SLKKeyboardStatusWillHide;
- }
- if ([name isEqualToString:UIKeyboardDidHideNotification]) {
- return SLKKeyboardStatusDidHide;
- }
- return -1;
-}
-
-- (BOOL)slk_isIllogicalKeyboardStatus:(SLKKeyboardStatus)newStatus
-{
- if ((self.keyboardStatus == SLKKeyboardStatusDidHide && newStatus == SLKKeyboardStatusWillShow) ||
- (self.keyboardStatus == SLKKeyboardStatusWillShow && newStatus == SLKKeyboardStatusDidShow) ||
- (self.keyboardStatus == SLKKeyboardStatusDidShow && newStatus == SLKKeyboardStatusWillHide) ||
- (self.keyboardStatus == SLKKeyboardStatusWillHide && newStatus == SLKKeyboardStatusDidHide)) {
- return NO;
- }
- return YES;
-}
-
-
-#pragma mark - Setters
-
-- (void)setEdgesForExtendedLayout:(UIRectEdge)rectEdge
-{
- if (self.edgesForExtendedLayout == rectEdge) {
- return;
- }
-
- [super setEdgesForExtendedLayout:rectEdge];
-
- [self slk_updateViewConstraints];
-}
-
-- (void)setScrollViewProxy:(UIScrollView *)scrollView
-{
- if ([_scrollViewProxy isEqual:scrollView]) {
- return;
- }
-
- _singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(slk_didTapScrollView:)];
- _singleTapGesture.delegate = self;
- [_singleTapGesture requireGestureRecognizerToFail:scrollView.panGestureRecognizer];
-
- [scrollView addGestureRecognizer:self.singleTapGesture];
-
- [scrollView.panGestureRecognizer addTarget:self action:@selector(slk_didPanTextInputBar:)];
-
- _scrollViewProxy = scrollView;
-}
-
-- (void)setAutoCompleting:(BOOL)autoCompleting
-{
- if (_autoCompleting == autoCompleting) {
- return;
- }
-
- _autoCompleting = autoCompleting;
-
- self.scrollViewProxy.scrollEnabled = !autoCompleting;
-}
-
-- (void)setInverted:(BOOL)inverted
-{
- if (_inverted == inverted) {
- return;
- }
-
- _inverted = inverted;
-
- self.scrollViewProxy.transform = inverted ? CGAffineTransformMake(1, 0, 0, -1, 0, 0) : CGAffineTransformIdentity;
-}
-
-- (BOOL)slk_updateKeyboardStatus:(SLKKeyboardStatus)status
-{
- // Skips if trying to update the same status
- if (_keyboardStatus == status) {
- return NO;
- }
-
- // Skips illogical conditions
- if ([self slk_isIllogicalKeyboardStatus:status]) {
- return NO;
- }
-
- _keyboardStatus = status;
-
- [self didChangeKeyboardStatus:status];
-
- return YES;
-}
-
-
-#pragma mark - Public & Subclassable Methods
-
-- (void)presentKeyboard:(BOOL)animated
-{
- // Skips if already first responder
- if ([self.textView isFirstResponder]) {
- return;
- }
-
- if (!animated) {
- [UIView performWithoutAnimation:^{
- [self.textView becomeFirstResponder];
- }];
- }
- else {
- [self.textView becomeFirstResponder];
- }
-}
-
-- (void)dismissKeyboard:(BOOL)animated
-{
- // Dismisses the keyboard from any first responder in the window.
- if (![self.textView isFirstResponder] && self.keyboardHC.constant > 0) {
- [self.view.window endEditing:NO];
- }
-
- if (!animated) {
- [UIView performWithoutAnimation:^{
- [self.textView resignFirstResponder];
- }];
- }
- else {
- [self.textView resignFirstResponder];
- }
-}
-
-- (BOOL)forceTextInputbarAdjustmentForResponder:(UIResponder *)responder
-{
- return NO;
-}
-
-- (BOOL)ignoreTextInputbarAdjustment
-{
- if (self.isExternalKeyboardDetected || self.isKeyboardUndocked) {
- return YES;
- }
-
- return NO;
-}
-
-- (void)didChangeKeyboardStatus:(SLKKeyboardStatus)status
-{
- // No implementation here. Meant to be overriden in subclass.
-}
-
-- (void)textWillUpdate
-{
- // No implementation here. Meant to be overriden in subclass.
-}
-
-- (void)textDidUpdate:(BOOL)animated
-{
- if (self.textInputbarHidden) {
- return;
- }
-
- CGFloat inputbarHeight = self.textInputbar.appropriateHeight;
-
- self.textInputbar.rightButton.enabled = [self canPressRightButton];
- self.textInputbar.editorRightButton.enabled = [self canPressRightButton];
-
- if (inputbarHeight != self.textInputbarHC.constant)
- {
- self.textInputbarHC.constant = inputbarHeight;
- self.scrollViewHC.constant = [self slk_appropriateScrollViewHeight];
-
- if (animated) {
-
- BOOL bounces = self.bounces && [self.textView isFirstResponder];
-
- [self.view slk_animateLayoutIfNeededWithBounce:bounces
- options:UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionLayoutSubviews|UIViewAnimationOptionBeginFromCurrentState
- animations:^{
- if (self.textInputbar.isEditing) {
- [self.textView slk_scrollToCaretPositonAnimated:NO];
- }
- }];
- }
- else {
- [self.view layoutIfNeeded];
- }
- }
-
- // Toggles auto-correction if requiered
- [self slk_enableTypingSuggestionIfNeeded];
-}
-
-- (void)textSelectionDidChange
-{
- // The text view must be first responder
- if (![self.textView isFirstResponder]) {
- return;
- }
-
- // Skips there is a real text selection
- if (self.textView.isTrackpadEnabled) {
- return;
- }
-
- if (self.textView.selectedRange.length > 0) {
- if (self.isAutoCompleting) {
- [self cancelAutoCompletion];
- }
- return;
- }
-
- // Process the text at every caret movement
- [self slk_processTextForAutoCompletion];
-}
-
-- (BOOL)canPressRightButton
-{
- NSString *text = [self.textView.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
-
- if (text.length > 0 && ![self.textInputbar limitExceeded]) {
- return YES;
- }
-
- return NO;
-}
-
-- (void)didPressLeftButton:(id)sender
-{
- // No implementation here. Meant to be overriden in subclass.
-}
-
-- (void)didPressRightButton:(id)sender
-{
- if (self.shouldClearTextAtRightButtonPress) {
- // Clears the text and the undo manager
- [self.textView slk_clearText:YES];
- }
-
- // Clears cache
- [self clearCachedText];
-}
-
-- (void)editText:(NSString *)text
-{
- if (![self.textInputbar canEditText:text]) {
- return;
- }
-
- // Caches the current text, in case the user cancels the edition
- [self slk_cacheTextToDisk:self.textView.text];
-
- [self.textInputbar beginTextEditing];
-
- // Setting the text after calling -beginTextEditing is safer
- [self.textView setText:text];
-
- [self.textView slk_scrollToCaretPositonAnimated:YES];
-
- // Brings up the keyboard if needed
- [self presentKeyboard:YES];
-}
-
-- (void)didCommitTextEditing:(id)sender
-{
- if (!self.textInputbar.isEditing) {
- return;
- }
-
- [self.textInputbar endTextEdition];
-
- // Clears the text and but not the undo manager
- [self.textView slk_clearText:NO];
-}
-
-- (void)didCancelTextEditing:(id)sender
-{
- if (!self.textInputbar.isEditing) {
- return;
- }
-
- [self.textInputbar endTextEdition];
-
- // Clears the text and but not the undo manager
- [self.textView slk_clearText:NO];
-
- // Restores any previous cached text before entering in editing mode
- [self slk_reloadTextView];
-}
-
-- (BOOL)canShowTypingIndicator
-{
- // Don't show if the text is being edited or auto-completed.
- if (self.textInputbar.isEditing || self.isAutoCompleting) {
- return NO;
- }
-
- // Don't show if the content offset is not at top (when inverted) or at bottom (when not inverted)
- if ((self.isInverted && ![self.scrollViewProxy slk_isAtTop]) || (!self.isInverted && ![self.scrollViewProxy slk_isAtBottom])) {
- return NO;
- }
-
- return YES;
-}
-
-- (CGFloat)heightForAutoCompletionView
-{
- return 0.0;
-}
-
-- (CGFloat)maximumHeightForAutoCompletionView
-{
- CGFloat maxiumumHeight = SLKAutoCompletionViewDefaultHeight;
-
- if (self.isAutoCompleting) {
- CGFloat scrollViewHeight = self.scrollViewHC.constant;
- scrollViewHeight -= [self slk_topBarsHeight];
-
- if (scrollViewHeight < maxiumumHeight) {
- maxiumumHeight = scrollViewHeight;
- }
- }
-
- return maxiumumHeight;
-}
-
-- (void)didPasteMediaContent:(NSDictionary *)userInfo
-{
- // No implementation here. Meant to be overriden in subclass.
-}
-
-- (void)willRequestUndo
-{
- NSString *title = NSLocalizedString(@"Undo Typing", nil);
- NSString *acceptTitle = NSLocalizedString(@"Undo", nil);
- NSString *cancelTitle = NSLocalizedString(@"Cancel", nil);
-
-#ifdef __IPHONE_8_0
- UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleAlert];
-
- [alertController addAction:[UIAlertAction actionWithTitle:acceptTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
- // Clears the text but doesn't clear the undo manager
- if (self.shakeToClearEnabled) {
- [self.textView slk_clearText:NO];
- }
- }]];
-
- [alertController addAction:[UIAlertAction actionWithTitle:cancelTitle style:UIAlertActionStyleCancel handler:NULL]];
-
- [self presentViewController:alertController animated:YES completion:nil];
-#else
- UIAlertView *alert = [UIAlertView new];
- [alert setTitle:title];
- [alert addButtonWithTitle:acceptTitle];
- [alert addButtonWithTitle:cancelTitle];
- [alert setCancelButtonIndex:1];
- [alert setTag:kSLKAlertViewClearTextTag];
- [alert setDelegate:self];
- [alert show];
-#endif
-}
-
-- (void)setTextInputbarHidden:(BOOL)hidden
-{
- [self setTextInputbarHidden:hidden animated:NO];
-}
-
-- (void)setTextInputbarHidden:(BOOL)hidden animated:(BOOL)animated
-{
- if (self.isTextInputbarHidden == hidden) {
- return;
- }
-
- _textInputbarHidden = hidden;
-
- __weak typeof(self) weakSelf = self;
-
- void (^animations)() = ^void(){
-
- weakSelf.textInputbarHC.constant = hidden ? 0 : weakSelf.textInputbar.appropriateHeight;
-
- [weakSelf.view layoutIfNeeded];
- };
-
- void (^completion)(BOOL finished) = ^void(BOOL finished){
- if (hidden) {
- [self dismissKeyboard:YES];
- }
- };
-
- if (animated) {
- [UIView animateWithDuration:0.25 animations:animations completion:completion];
- }
- else {
- animations();
- completion(NO);
- }
-}
-
-
-#pragma mark - Private Methods
-
-- (void)slk_didPanTextInputBar:(UIPanGestureRecognizer *)gesture
-{
- // Textinput dragging isn't supported when
- if (!self.view.window || !self.keyboardPanningEnabled ||
- [self ignoreTextInputbarAdjustment] || self.isPresentedInPopover) {
- return;
- }
-
- dispatch_async(dispatch_get_main_queue(), ^{
- [self slk_handlePanGestureRecognizer:gesture];
- });
-}
-
-- (void)slk_handlePanGestureRecognizer:(UIPanGestureRecognizer *)gesture
-{
- // Local variables
- static CGPoint startPoint;
- static CGRect originalFrame;
- static BOOL dragging = NO;
- static BOOL presenting = NO;
-
- __block UIView *keyboardView = [self.textInputbar.inputAccessoryView keyboardViewProxy];
-
- // When no keyboard view has been detecting, let's skip any handling.
- if (!keyboardView) {
- return;
- }
-
- // Dynamic variables
- CGPoint gestureLocation = [gesture locationInView:self.view];
- CGPoint gestureVelocity = [gesture velocityInView:self.view];
-
- CGFloat keyboardMaxY = CGRectGetHeight(SLKKeyWindowBounds());
- CGFloat keyboardMinY = keyboardMaxY - CGRectGetHeight(keyboardView.frame);
-
-
- // Skips this if it's not the expected textView.
- // Checking the keyboard height constant helps to disable the view constraints update on iPad when the keyboard is undocked.
- // Checking the keyboard status allows to keep the inputAccessoryView valid when still reacing the bottom of the screen.
- if (![self.textView isFirstResponder] || (self.keyboardHC.constant == 0 && self.keyboardStatus == SLKKeyboardStatusDidHide)) {
-#if SLKBottomPanningEnabled
- if ([gesture.view isEqual:self.scrollViewProxy]) {
- if (gestureVelocity.y > 0) {
- return;
- }
- else if ((self.isInverted && ![self.scrollViewProxy slk_isAtTop]) || (!self.isInverted && ![self.scrollViewProxy slk_isAtBottom])) {
- return;
- }
- }
-
- presenting = YES;
-#else
- if ([gesture.view isEqual:self.textInputbar] && gestureVelocity.y < 0) {
- [self presentKeyboard:YES];
- }
- return;
-#endif
- }
-
- switch (gesture.state) {
- case UIGestureRecognizerStateBegan: {
-
- startPoint = CGPointZero;
- dragging = NO;
-
- if (presenting) {
- // Let's first present the keyboard without animation
- [self presentKeyboard:NO];
-
- // So we can capture the keyboard's view
- keyboardView = [self.textInputbar.inputAccessoryView keyboardViewProxy];
-
- originalFrame = keyboardView.frame;
- originalFrame.origin.y = CGRectGetMaxY(self.view.frame);
-
- // And move the keyboard to the bottom edge
- // TODO: Fix an occasional layout glitch when the keyboard appears for the first time.
- keyboardView.frame = originalFrame;
- }
-
- break;
- }
- case UIGestureRecognizerStateChanged: {
-
- if (CGRectContainsPoint(self.textInputbar.frame, gestureLocation) || dragging || presenting){
-
- if (CGPointEqualToPoint(startPoint, CGPointZero)) {
- startPoint = gestureLocation;
- dragging = YES;
-
- if (!presenting) {
- originalFrame = keyboardView.frame;
- }
- }
-
- self.movingKeyboard = YES;
-
- CGPoint transition = CGPointMake(gestureLocation.x - startPoint.x, gestureLocation.y - startPoint.y);
-
- CGRect keyboardFrame = originalFrame;
-
- if (presenting) {
- keyboardFrame.origin.y += transition.y;
- }
- else {
- keyboardFrame.origin.y += MAX(transition.y, 0.0);
- }
-
- // Makes sure they keyboard is always anchored to the bottom
- if (CGRectGetMinY(keyboardFrame) < keyboardMinY) {
- keyboardFrame.origin.y = keyboardMinY;
- }
-
- keyboardView.frame = keyboardFrame;
-
-
- self.keyboardHC.constant = [self slk_appropriateKeyboardHeightFromRect:keyboardFrame];
- self.scrollViewHC.constant = [self slk_appropriateScrollViewHeight];
-
- // layoutIfNeeded must be called before any further scrollView internal adjustments (content offset and size)
- [self.view layoutIfNeeded];
-
- // Overrides the scrollView's contentOffset to allow following the same position when dragging the keyboard
- CGPoint offset = _scrollViewOffsetBeforeDragging;
-
- if (self.isInverted) {
- if (!self.scrollViewProxy.isDecelerating && self.scrollViewProxy.isTracking) {
- self.scrollViewProxy.contentOffset = _scrollViewOffsetBeforeDragging;
- }
- }
- else {
- CGFloat keyboardHeightDelta = _keyboardHeightBeforeDragging-self.keyboardHC.constant;
- offset.y -= keyboardHeightDelta;
-
- self.scrollViewProxy.contentOffset = offset;
- }
- }
-
- break;
- }
- case UIGestureRecognizerStatePossible:
- case UIGestureRecognizerStateCancelled:
- case UIGestureRecognizerStateEnded:
- case UIGestureRecognizerStateFailed: {
-
- if (!dragging) {
- break;
- }
-
- CGPoint transition = CGPointMake(0.0, fabs(gestureLocation.y - startPoint.y));
-
- CGRect keyboardFrame = originalFrame;
-
- if (presenting) {
- keyboardFrame.origin.y = keyboardMinY;
- }
-
- // The velocity can be changed to hide or show the keyboard based on the gesture
- CGFloat minVelocity = 20.0;
- CGFloat minDistance = CGRectGetHeight(keyboardFrame)/2.0;
-
- BOOL hide = (gestureVelocity.y > minVelocity) || (presenting && transition.y < minDistance) || (!presenting && transition.y > minDistance);
-
- if (hide) keyboardFrame.origin.y = keyboardMaxY;
-
- self.keyboardHC.constant = [self slk_appropriateKeyboardHeightFromRect:keyboardFrame];
- self.scrollViewHC.constant = [self slk_appropriateScrollViewHeight];
-
- [UIView animateWithDuration:0.25
- delay:0.0
- options:UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionBeginFromCurrentState
- animations:^{
- [self.view layoutIfNeeded];
- keyboardView.frame = keyboardFrame;
- }
- completion:^(BOOL finished) {
- if (hide) {
- [self dismissKeyboard:NO];
- }
-
- // Tear down
- startPoint = CGPointZero;
- originalFrame = CGRectZero;
- dragging = NO;
- presenting = NO;
-
- self.movingKeyboard = NO;
- }];
-
- break;
- }
-
- default:
- break;
- }
-}
-
-- (void)slk_didTapScrollView:(UIGestureRecognizer *)gesture
-{
- if (!self.isPresentedInPopover && ![self ignoreTextInputbarAdjustment]) {
- [self dismissKeyboard:YES];
- }
-}
-
-- (void)slk_didPanTextView:(UIGestureRecognizer *)gesture
-{
- [self presentKeyboard:YES];
-}
-
-- (void)slk_performRightAction
-{
- NSArray *actions = [self.rightButton actionsForTarget:self forControlEvent:UIControlEventTouchUpInside];
-
- if (actions.count > 0 && [self canPressRightButton]) {
- [self.rightButton sendActionsForControlEvents:UIControlEventTouchUpInside];
- }
-}
-
-- (void)slk_postKeyboarStatusNotification:(NSNotification *)notification
-{
- if ([self ignoreTextInputbarAdjustment] || self.isTransitioning) {
- return;
- }
-
- NSMutableDictionary *userInfo = [notification.userInfo mutableCopy];
-
- CGRect beginFrame = [notification.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
- CGRect endFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
-
- // Fixes iOS7 oddness with inverted values on landscape orientation
- if (!SLK_IS_IOS8_AND_HIGHER && SLK_IS_LANDSCAPE) {
- beginFrame = SLKRectInvert(beginFrame);
- endFrame = SLKRectInvert(endFrame);
- }
-
- CGFloat keyboardHeight = CGRectGetHeight(endFrame);
-
- beginFrame.size.height = keyboardHeight;
- endFrame.size.height = keyboardHeight;
-
- [userInfo setObject:[NSValue valueWithCGRect:beginFrame] forKey:UIKeyboardFrameBeginUserInfoKey];
- [userInfo setObject:[NSValue valueWithCGRect:endFrame] forKey:UIKeyboardFrameEndUserInfoKey];
-
- NSString *name = [self slk_appropriateKeyboardNotificationName:notification];
- [[NSNotificationCenter defaultCenter] postNotificationName:name object:self.textView userInfo:userInfo];
-}
-
-- (void)slk_enableTypingSuggestionIfNeeded
-{
- if (![self.textView isFirstResponder]) {
- return;
- }
-
- BOOL enable = !self.isAutoCompleting;
-
- // Toggling autocorrect on Japanese keyboards breaks autocompletion by replacing the autocompletion prefix by an empty string.
- // So for now, let's not disable autocorrection for Japanese.
- if ([self.textView.textInputMode.primaryLanguage isEqualToString:@"ja-JP"]) {
- return;
- }
-
- // During text autocompletion, the iOS 8 QuickType bar is hidden and auto-correction and spell checking are disabled.
- [self.textView setTypingSuggestionEnabled:enable];
-}
-
-- (void)slk_dismissTextInputbarIfNeeded
-{
- if (self.keyboardHC.constant == 0) {
- return;
- }
-
- self.keyboardHC.constant = 0.0;
- self.scrollViewHC.constant = [self slk_appropriateScrollViewHeight];
-
- [self slk_hideAutoCompletionViewIfNeeded];
-
- [self.view layoutIfNeeded];
-}
-
-- (void)slk_detectKeyboardStatesInNotification:(NSNotification *)notification
-{
- // Tear down
- _externalKeyboardDetected = NO;
- _keyboardUndocked = NO;
-
- if (self.isMovingKeyboard) {
- return;
- }
-
- // Based on http://stackoverflow.com/a/5760910/287403
- // We can determine if the external keyboard is showing by adding the origin.y of the target finish rect (end when showing, begin when hiding) to the inputAccessoryHeight.
- // If it's greater(or equal) the window height, it's an external keyboard.
- CGRect beginRect = [notification.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
- CGRect endRect = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
-
- // Grab the base view for conversions as we don't want window coordinates in < iOS 8
- // iOS 8 fixes the whole coordinate system issue for us, but iOS 7 doesn't rotate the app window coordinate space.
- UIView *baseView = self.view.window.rootViewController.view;
-
- CGRect screenBounds = [UIScreen mainScreen].bounds;
-
- // Convert the main screen bounds into the correct coordinate space but ignore the origin.
- CGRect viewBounds = [self.view convertRect:SLKKeyWindowBounds() fromView:nil];
- viewBounds = CGRectMake(0, 0, viewBounds.size.width, viewBounds.size.height);
-
- // We want these rects in the correct coordinate space as well.
- CGRect convertBegin = [baseView convertRect:beginRect fromView:nil];
- CGRect convertEnd = [baseView convertRect:endRect fromView:nil];
-
- if ([notification.name isEqualToString:UIKeyboardWillShowNotification]) {
- if (convertEnd.origin.y >= viewBounds.size.height) {
- _externalKeyboardDetected = YES;
- }
- }
- else if ([notification.name isEqualToString:UIKeyboardWillHideNotification]) {
- // The additional logic check here (== to width) accounts for a glitch (iOS 8 only?) where the window has rotated it's coordinates
- // but the beginRect doesn't yet reflect that. It should never cause a false positive.
- if (convertBegin.origin.y >= viewBounds.size.height ||
- convertBegin.origin.y == viewBounds.size.width) {
- _externalKeyboardDetected = YES;
- }
- }
-
- if (SLK_IS_IPAD && CGRectGetMaxY(convertEnd) < CGRectGetMaxY(screenBounds)) {
-
- // The keyboard is undocked or split (iPad Only)
- _keyboardUndocked = YES;
-
- // An external keyboard cannot be detected anymore
- _externalKeyboardDetected = NO;
- }
-}
-
-- (void)slk_adjustContentConfigurationIfNeeded
-{
- UIEdgeInsets contentInset = self.scrollViewProxy.contentInset;
-
- // When inverted, we need to substract the top bars height (generally status bar + navigation bar's) to align the top of the
- // scrollView correctly to its top edge.
- if (self.inverted) {
- contentInset.bottom = [self slk_topBarsHeight];
- contentInset.top = contentInset.bottom > 0.0 ? 0.0 : contentInset.top;
- }
- else {
- contentInset.bottom = 0.0;
- }
-
- self.scrollViewProxy.contentInset = contentInset;
- self.scrollViewProxy.scrollIndicatorInsets = contentInset;
-}
-
-- (void)slk_prepareForInterfaceTransitionWithDuration:(NSTimeInterval)duration
-{
- self.transitioning = YES;
-
- [self.view layoutIfNeeded];
-
- if ([self.textView isFirstResponder]) {
- [self.textView slk_scrollToCaretPositonAnimated:NO];
- }
- else {
- [self.textView slk_scrollToBottomAnimated:NO];
- }
-
- // Disables the flag after the rotation animation is finished
- // Hacky but works.
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- self.transitioning = NO;
- });
-}
-
-
-#pragma mark - Keyboard Events
-
-- (void)didPressReturnKey:(id)sender
-{
- if (self.textInputbar.isEditing) {
- [self didCommitTextEditing:sender];
- }
- else {
- [self slk_performRightAction];
- }
-}
-
-- (void)didPressEscapeKey:(id)sender
-{
- if (self.isAutoCompleting) {
- [self cancelAutoCompletion];
- }
- else if (self.textInputbar.isEditing) {
- [self didCancelTextEditing:sender];
- }
-
- if ([self ignoreTextInputbarAdjustment] || ([self.textView isFirstResponder] && self.keyboardHC.constant == 0)) {
- return;
- }
-
- [self dismissKeyboard:YES];
-}
-
-- (void)didPressArrowKey:(id)sender
-{
- [self.textView didPressAnyArrowKey:sender];
-}
-
-
-#pragma mark - Notification Events
-
-- (void)slk_willShowOrHideKeyboard:(NSNotification *)notification
-{
- // Skips if the view isn't visible.
- if (!self.view.window) {
- return;
- }
-
- // Skips if it is presented inside of a popover.
- if (self.isPresentedInPopover) {
- return;
- }
-
- // Skips if textview did refresh only.
- if (self.textView.didNotResignFirstResponder) {
- return;
- }
-
- SLKKeyboardStatus status = [self slk_keyboardStatusForNotification:notification];
-
- // Skips if it's the current status
- if (self.keyboardStatus == status) {
- return;
- }
-
- // Updates and notifies about the keyboard status update
- if ([self slk_updateKeyboardStatus:status]) {
- // Posts custom keyboard notification, if logical conditions apply
- [self slk_postKeyboarStatusNotification:notification];
- }
-
- // Skips this it's not the expected textView and shouldn't force adjustment of the text input bar.
- // This will also dismiss the text input bar if it's visible, and exit auto-completion mode if enabled.
- if (![self.textView isFirstResponder]) {
- // Detect the current first responder. If there is no first responder, we should just ignore these notifications.
- UIResponder *currentResponder = [UIResponder slk_currentFirstResponder];
-
- if (!currentResponder) {
- return;
- }
- else if (![self forceTextInputbarAdjustmentForResponder:currentResponder]) {
- return [self slk_dismissTextInputbarIfNeeded];
- }
- }
-
- // Programatically stops scrolling before updating the view constraints (to avoid scrolling glitch).
- if (status == SLKKeyboardStatusWillShow) {
- [self.scrollViewProxy slk_stopScrolling];
- }
-
- // Hides the auto-completion view if the keyboard is being dismissed.
- if (![self.textView isFirstResponder] || status == SLKKeyboardStatusWillHide) {
- [self slk_hideAutoCompletionViewIfNeeded];
- }
-
- // Updates the height constraints' constants
- self.keyboardHC.constant = [self slk_appropriateKeyboardHeightFromNotification:notification];
- self.scrollViewHC.constant = [self slk_appropriateScrollViewHeight];
-
-
- NSInteger curve = [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
- NSTimeInterval duration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
-
- CGRect beginFrame = [notification.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
- CGRect endFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
-
- void (^animations)() = ^void() {
- // Scrolls to bottom only if the keyboard is about to show.
- if (self.shouldScrollToBottomAfterKeyboardShows && self.keyboardStatus == SLKKeyboardStatusWillShow) {
- if (self.isInverted) {
- [self.scrollViewProxy slk_scrollToTopAnimated:YES];
- }
- else {
- [self.scrollViewProxy slk_scrollToBottomAnimated:YES];
- }
- }
- };
-
- // Begin and end frames are the same when the keyboard is shown during navigation controller's push animation.
- // The animation happens in window coordinates (slides from right to left) but doesn't in the view controller's view coordinates.
- if (!CGRectEqualToRect(beginFrame, endFrame))
- {
- // Only for this animation, we set bo to bounce since we want to give the impression that the text input is glued to the keyboard.
- [self.view slk_animateLayoutIfNeededWithDuration:duration
- bounce:NO
- options:(curve<<16)|UIViewAnimationOptionLayoutSubviews|UIViewAnimationOptionBeginFromCurrentState
- animations:animations
- completion:NULL];
- }
- else {
- animations();
- }
-}
-
-- (void)slk_didShowOrHideKeyboard:(NSNotification *)notification
-{
- // Skips if the view isn't visible
- if (!self.view.window) {
- return;
- }
-
- // Skips if it is presented inside of a popover
- if (self.isPresentedInPopover) {
- return;
- }
-
- // Skips if textview did refresh only
- if (self.textView.didNotResignFirstResponder) {
- return;
- }
-
- SLKKeyboardStatus status = [self slk_keyboardStatusForNotification:notification];
-
- // Skips if it's the current status
- if (self.keyboardStatus == status) {
- return;
- }
-
- // Updates and notifies about the keyboard status update
- if ([self slk_updateKeyboardStatus:status]) {
- // Posts custom keyboard notification, if logical conditions apply
- [self slk_postKeyboarStatusNotification:notification];
- }
-
- // After showing keyboard, check if the current cursor position could diplay autocompletion
- if ([self.textView isFirstResponder] && status == SLKKeyboardStatusDidShow && !self.isAutoCompleting) {
-
- // Wait till the end of the current run loop
- dispatch_async(dispatch_get_main_queue(), ^{
- [self slk_processTextForAutoCompletion];
- });
- }
-
- // Very important to invalidate this flag after the keyboard is dismissed or presented, to start with a clean state next time.
- self.movingKeyboard = NO;
-}
-
-- (void)slk_didPostSLKKeyboardNotification:(NSNotification *)notification
-{
- if (![notification.object isEqual:self.textView]) {
- return;
- }
-
- // Used for debug only
- NSLog(@"%@ %s: %@", NSStringFromClass([self class]), __FUNCTION__, notification);
-}
-
-- (void)slk_willChangeTextViewText:(NSNotification *)notification
-{
- // Skips this it's not the expected textView.
- if (![notification.object isEqual:self.textView] || !self.textView.window) {
- return;
- }
-
- [self textWillUpdate];
-}
-
-- (void)slk_didChangeTextViewText:(NSNotification *)notification
-{
- // Skips this it's not the expected textView.
- if (![notification.object isEqual:self.textView] || !self.textView.window) {
- return;
- }
-
- // Animated only if the view already appeared.
- [self textDidUpdate:self.isViewVisible];
-
- // Process the text at every change, when the view is visible
- if (self.isViewVisible) {
- [self slk_processTextForAutoCompletion];
- }
-}
-
-- (void)slk_didChangeTextViewContentSize:(NSNotification *)notification
-{
- // Skips this it's not the expected textView.
- if (![notification.object isEqual:self.textView] || !self.textView.window) {
- return;
- }
-
- // Animated only if the view already appeared.
- [self textDidUpdate:self.isViewVisible];
-}
-
-- (void)slk_didChangeTextViewSelectedRange:(NSNotification *)notification
-{
- // Skips this it's not the expected textView.
- if (![notification.object isEqual:self.textView] || !self.textView.window) {
- return;
- }
-
- [self textSelectionDidChange];
-}
-
-- (void)slk_didChangeTextViewPasteboard:(NSNotification *)notification
-{
- // Skips this if it's not the expected textView.
- if (![self.textView isFirstResponder]) {
- return;
- }
-
- // Notifies only if the pasted item is nested in a dictionary.
- if ([notification.userInfo isKindOfClass:[NSDictionary class]]) {
- [self didPasteMediaContent:notification.userInfo];
- }
-}
-
-- (void)slk_didShakeTextView:(NSNotification *)notification
-{
- // Skips this if it's not the expected textView.
- if (![self.textView isFirstResponder]) {
- return;
- }
-
- // Notifies of the shake gesture if undo mode is on and the text view is not empty
- if (self.shakeToClearEnabled && self.textView.text.length > 0) {
- [self willRequestUndo];
- }
-}
-
-- (void)slk_willShowOrHideTypeIndicatorView:(UIView <SLKTypingIndicatorProtocol> *)typingIndicatorView
-{
- // Skips if the typing indicator should not show. Ignores the checking if it's trying to hide.
- if (![self canShowTypingIndicator] && typingIndicatorView.isVisible) {
- return;
- }
-
- CGFloat systemLayoutSizeHeight = [typingIndicatorView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
- CGFloat height = typingIndicatorView.isVisible ? systemLayoutSizeHeight : 0.0;
-
- self.typingIndicatorViewHC.constant = height;
- self.scrollViewHC.constant -= height;
-
- if (typingIndicatorView.isVisible) {
- typingIndicatorView.hidden = NO;
- }
-
- [self.view slk_animateLayoutIfNeededWithBounce:self.bounces
- options:UIViewAnimationOptionCurveEaseInOut
- animations:NULL
- completion:^(BOOL finished) {
- if (!typingIndicatorView.isVisible) {
- typingIndicatorView.hidden = YES;
- }
- }];
-}
-
-- (void)slk_willTerminateApplication:(NSNotification *)notification
-{
- // Caches the text before it's too late!
- if (self.isViewVisible) {
- [self slk_cacheTextView];
- }
-}
-
-
-#pragma mark - KVO Events
-
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
-{
- if ([object conformsToProtocol:@protocol(SLKTypingIndicatorProtocol)] && [keyPath isEqualToString:@"visible"]) {
- [self slk_willShowOrHideTypeIndicatorView:object];
- }
- else {
- [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
- }
-}
-
-
-#pragma mark - Auto-Completion Text Processing
-
-- (void)registerPrefixesForAutoCompletion:(NSArray *)prefixes
-{
- NSMutableArray *array = [NSMutableArray arrayWithArray:self.registeredPrefixes];
-
- for (NSString *prefix in prefixes) {
- // Skips if the prefix is not a valid string
- if (![prefix isKindOfClass:[NSString class]] || prefix.length == 0) {
- continue;
- }
-
- // Adds the prefix if not contained already
- if (![array containsObject:prefix]) {
- [array addObject:prefix];
- }
- }
-
- if (_registeredPrefixes) {
- _registeredPrefixes = nil;
- }
-
- _registeredPrefixes = [[NSArray alloc] initWithArray:array];
-}
-
-- (void)didChangeAutoCompletionPrefix:(NSString *)prefix andWord:(NSString *)word
-{
- // No implementation here. Meant to be overriden in subclass.
-}
-
-- (BOOL)canShowAutoCompletion
-{
- // Let's keep this around for a bit, for backwards compatibility.
- return NO;
-}
-
-- (void)showAutoCompletionView:(BOOL)show
-{
- // Reloads the tableview before showing/hiding
- if (show) {
- [self.autoCompletionView reloadData];
- }
-
- self.autoCompleting = show;
-
- // Toggles auto-correction if requiered
- [self slk_enableTypingSuggestionIfNeeded];
-
- CGFloat viewHeight = show ? [self heightForAutoCompletionView] : 0.0;
-
- if (self.autoCompletionViewHC.constant == viewHeight) {
- return;
- }
-
- // If the auto-completion view height is bigger than the maximum height allows, it is reduce to that size. Default 140 pts.
- CGFloat maximumHeight = [self maximumHeightForAutoCompletionView];
-
- if (viewHeight > maximumHeight) {
- viewHeight = maximumHeight;
- }
-
- CGFloat contentViewHeight = self.scrollViewHC.constant + self.autoCompletionViewHC.constant;
-
- // On iPhone, the auto-completion view can't extend beyond the content view height
- if (SLK_IS_IPHONE && viewHeight > contentViewHeight) {
- viewHeight = contentViewHeight;
- }
-
- self.autoCompletionViewHC.constant = viewHeight;
-
- [self.view slk_animateLayoutIfNeededWithBounce:self.bounces
- options:UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionLayoutSubviews|UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionAllowUserInteraction
- animations:NULL];
-}
-
-- (void)acceptAutoCompletionWithString:(NSString *)string
-{
- [self acceptAutoCompletionWithString:string keepPrefix:YES];
-}
-
-- (void)acceptAutoCompletionWithString:(NSString *)string keepPrefix:(BOOL)keepPrefix
-{
- if (string.length == 0) {
- return;
- }
-
- SLKTextView *textView = self.textView;
-
- NSUInteger location = self.foundPrefixRange.location;
- if (keepPrefix) {
- location += self.foundPrefixRange.length;
- }
-
- NSUInteger length = self.foundWord.length;
- if (!keepPrefix) {
- length += self.foundPrefixRange.length;
- }
-
- NSRange range = NSMakeRange(location, length);
- NSRange insertionRange = [textView slk_insertText:string inRange:range];
-
- textView.selectedRange = NSMakeRange(insertionRange.location, 0);
-
- [self cancelAutoCompletion];
-
- [textView slk_scrollToCaretPositonAnimated:NO];
-}
-
-- (void)cancelAutoCompletion
-{
- [self slk_invalidateAutoCompletion];
- [self slk_hideAutoCompletionViewIfNeeded];
-}
-
-- (void)slk_processTextForAutoCompletion
-{
- if (self.isTransitioning) {
- return;
- }
-
- // Avoids text processing for auto-completion if the registered prefix list is empty.
- if (self.registeredPrefixes.count == 0) {
- return;
- }
-
- NSString *text = self.textView.text;
-
- // Skip, when there is no text to process
- if (text.length == 0) {
- return [self cancelAutoCompletion];
- }
-
- NSRange range;
- NSString *word = [self.textView slk_wordAtCaretRange:&range];
-
- [self slk_invalidateAutoCompletion];
-
- if (word.length > 0) {
-
- for (NSString *prefix in self.registeredPrefixes) {
- if ([word hasPrefix:prefix]) {
- // Captures the detected symbol prefix
- _foundPrefix = prefix;
-
- // Used later for replacing the detected range with a new string alias returned in -acceptAutoCompletionWithString:
- _foundPrefixRange = NSMakeRange(range.location, prefix.length);
- }
- }
- }
-
- [self slk_handleProcessedWord:word range:range];
-}
-
-- (void)slk_handleProcessedWord:(NSString *)word range:(NSRange)range
-{
- // Cancel auto-completion if the cursor is placed before the prefix
- if (self.textView.selectedRange.location <= self.foundPrefixRange.location) {
- return [self cancelAutoCompletion];
- }
-
- if (self.foundPrefix.length > 0) {
- if (range.length == 0 || range.length != word.length) {
- return [self cancelAutoCompletion];
- }
-
- if (word.length > 0) {
- // Removes the found prefix
- _foundWord = [word substringFromIndex:self.foundPrefix.length];
-
- // If the prefix is still contained in the word, cancels
- if ([self.foundWord rangeOfString:self.foundPrefix].location != NSNotFound) {
- return [self cancelAutoCompletion];
- }
- }
- else {
- return [self cancelAutoCompletion];
- }
- }
- else {
- return [self cancelAutoCompletion];
- }
-
- [self didChangeAutoCompletionPrefix:self.foundPrefix andWord:self.foundWord];
-}
-
-- (void)slk_invalidateAutoCompletion
-{
- _foundPrefix = nil;
- _foundWord = nil;
- _foundPrefixRange = NSMakeRange(0, 0);
-
- [self.autoCompletionView setContentOffset:CGPointZero];
-}
-
-- (void)slk_hideAutoCompletionViewIfNeeded
-{
- if (self.isAutoCompleting) {
- [self showAutoCompletionView:NO];
- }
-}
-
-
-#pragma mark - Text Caching
-
-- (NSString *)keyForTextCaching
-{
- // No implementation here. Meant to be overriden in subclass.
- return nil;
-}
-
-- (NSString *)slk_keyForPersistency
-{
- NSString *key = [self keyForTextCaching];
- if (key == nil) {
- return nil;
- }
- return [NSString stringWithFormat:@"%@.%@", SLKTextViewControllerDomain, key];
-}
-
-- (void)slk_reloadTextView
-{
- NSString *key = [self slk_keyForPersistency];
- if (key == nil) {
- return;
- }
- NSString *cachedText = [[NSUserDefaults standardUserDefaults] objectForKey:key];
-
- if (self.textView.text.length == 0 || cachedText.length > 0) {
- self.textView.text = cachedText;
- }
-}
-
-- (void)slk_cacheTextView
-{
- [self slk_cacheTextToDisk:self.textView.text];
-}
-
-- (void)clearCachedText
-{
- [self slk_cacheTextToDisk:nil];
-}
-
-- (void)slk_cacheTextToDisk:(NSString *)text
-{
- NSString *key = [self slk_keyForPersistency];
-
- if (!key || key.length == 0) {
- return;
- }
-
- NSString *cachedText = [[NSUserDefaults standardUserDefaults] objectForKey:key];
-
- // Caches text only if its a valid string and not already cached
- if (text.length > 0 && ![text isEqualToString:cachedText]) {
- [[NSUserDefaults standardUserDefaults] setObject:text forKey:key];
- }
- // Clears cache only if it exists
- else if (text.length == 0 && cachedText.length > 0) {
- [[NSUserDefaults standardUserDefaults] removeObjectForKey:key];
- }
- else {
- // Skips so it doesn't hit 'synchronize' unnecessarily
- return;
- }
-
- [[NSUserDefaults standardUserDefaults] synchronize];
-}
-
-+ (void)clearAllCachedText
-{
- NSMutableArray *cachedKeys = [NSMutableArray new];
-
- for (NSString *key in [[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys]) {
- if ([key rangeOfString:SLKTextViewControllerDomain].location != NSNotFound) {
- [cachedKeys addObject:key];
- }
- }
-
- if (cachedKeys.count == 0) {
- return;
- }
-
- for (NSString *cachedKey in cachedKeys) {
- [[NSUserDefaults standardUserDefaults] removeObjectForKey:cachedKey];
- }
-
- [[NSUserDefaults standardUserDefaults] synchronize];
-}
-
-
-#pragma mark - Customization
-
-- (void)registerClassForTextView:(Class)aClass
-{
- if (aClass == nil) {
- return;
- }
-
- NSAssert([aClass isSubclassOfClass:[SLKTextView class]], @"The registered class is invalid, it must be a subclass of SLKTextView.");
- self.textViewClass = aClass;
-}
-
-- (void)registerClassForTypingIndicatorView:(Class)aClass
-{
- if (aClass == nil) {
- return;
- }
-
- NSAssert([aClass isSubclassOfClass:[UIView class]], @"The registered class is invalid, it must be a subclass of UIView.");
- self.typingIndicatorViewClass = aClass;
-}
-
-
-#pragma mark - UITextViewDelegate Methods
-
-- (BOOL)textView:(SLKTextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
-{
- if (![textView isKindOfClass:[SLKTextView class]]) {
- return YES;
- }
-
- BOOL newWordInserted = ([text rangeOfCharacterFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].location != NSNotFound);
-
- // It should not change if auto-completion is active and trying to replace with an auto-correction suggested text.
- if (self.isAutoCompleting && text.length > 1) {
- return NO;
- }
-
- // Records text for undo for every new word
- if (newWordInserted) {
- [textView slk_prepareForUndo:@"Word Change"];
- }
-
- // Detects double spacebar tapping, to replace the default "." insert with a formatting symbol, if needed.
- if (textView.autoCompleteFormatting && range.location > 0 && [text length] > 0 &&
- [[NSCharacterSet whitespaceCharacterSet] characterIsMember:[text characterAtIndex:0]] &&
- [[NSCharacterSet whitespaceCharacterSet] characterIsMember:[textView.text characterAtIndex:range.location - 1]]) {
-
- BOOL shouldChange = YES;
-
- NSRange wordRange = range;
- wordRange.location -= 2; // minus the white space added with the double space bar tapping
-
- NSArray *symbols = textView.registeredSymbols;
-
- NSMutableCharacterSet *invalidCharacters = [NSMutableCharacterSet new];
- [invalidCharacters formUnionWithCharacterSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
- [invalidCharacters formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
- [invalidCharacters removeCharactersInString:[symbols componentsJoinedByString:@""]];
-
- for (NSString *symbol in symbols) {
-
- // Detects the closest registered symbol to the caret, from right to left
- NSRange searchRange = NSMakeRange(0, wordRange.location);
- NSRange prefixRange = [textView.text rangeOfString:symbol options:NSBackwardsSearch range:searchRange];
-
- if (prefixRange.location == NSNotFound) {
- continue;
- }
-
- NSRange nextCharRange = NSMakeRange(prefixRange.location+1, 1);
- NSString *charAfterSymbol = [textView.text substringWithRange:nextCharRange];
-
- if (prefixRange.location != NSNotFound && ![invalidCharacters characterIsMember:[charAfterSymbol characterAtIndex:0]]) {
-
- if ([self textView:textView shouldInsertSuffixForFormattingWithSymbol:symbol prefixRange:prefixRange]) {
-
- NSRange suffixRange;
- [textView slk_wordAtRange:wordRange rangeInText:&suffixRange];
-
- // Skip if the detected word already has a suffix
- if ([[textView.text substringWithRange:suffixRange] hasSuffix:symbol]) {
- continue;
- }
-
- suffixRange.location += suffixRange.length;
- suffixRange.length = 0;
-
- NSString *lastCharacter = [textView.text substringWithRange:NSMakeRange(suffixRange.location, 1)];
-
- // Checks if the last character was a line break, so we append the symbol in the next line too
- if ([[NSCharacterSet newlineCharacterSet] characterIsMember:[lastCharacter characterAtIndex:0]]) {
- suffixRange.location += 1;
- }
-
- [textView slk_insertText:symbol inRange:suffixRange];
- shouldChange = NO;
-
- break; // exit
- }
- }
- }
-
- return shouldChange;
- }
- else if ([text isEqualToString:@"\n"]) {
- //Detected break. Should insert new line break programatically instead.
- [textView slk_insertNewLineBreak];
-
- return NO;
- }
- else {
- NSDictionary *userInfo = @{@"text": text, @"range": [NSValue valueWithRange:range]};
- [[NSNotificationCenter defaultCenter] postNotificationName:SLKTextViewTextWillChangeNotification object:self.textView userInfo:userInfo];
-
- return YES;
- }
-}
-
-- (void)textViewDidChange:(SLKTextView *)textView
-{
- // Keep to avoid unnecessary crashes. Was meant to be overriden in subclass while calling super.
-}
-
-- (void)textViewDidChangeSelection:(SLKTextView *)textView
-{
- // Keep to avoid unnecessary crashes. Was meant to be overriden in subclass while calling super.
-}
-
-- (BOOL)textViewShouldBeginEditing:(SLKTextView *)textView
-{
- return YES;
-}
-
-- (BOOL)textViewShouldEndEditing:(SLKTextView *)textView
-{
- return YES;
-}
-
-- (void)textViewDidBeginEditing:(SLKTextView *)textView
-{
- // No implementation here. Meant to be overriden in subclass.
-}
-
-- (void)textViewDidEndEditing:(SLKTextView *)textView
-{
- // No implementation here. Meant to be overriden in subclass.
-}
-
-
-#pragma mark - SLKTextViewDelegate Methods
-
-- (BOOL)textView:(SLKTextView *)textView shouldOfferFormattingForSymbol:(NSString *)symbol
-{
- return YES;
-}
-
-- (BOOL)textView:(SLKTextView *)textView shouldInsertSuffixForFormattingWithSymbol:(NSString *)symbol prefixRange:(NSRange)prefixRange
-{
- if (prefixRange.location > 0) {
- NSRange previousCharRange = NSMakeRange(prefixRange.location-1, 1);
- NSString *previousCharacter = [self.textView.text substringWithRange:previousCharRange];
-
- // Only insert a suffix if the character before the prefix was a whitespace or a line break
- if ([previousCharacter rangeOfCharacterFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].location != NSNotFound) {
- return YES;
- }
- else {
- return NO;
- }
- }
-
- return YES;
-}
-
-
-#pragma mark - UITableViewDataSource Methods
-
-- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
-{
- return 0;
-}
-
-- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
-{
- return nil;
-}
-
-
-#pragma mark - UICollectionViewDataSource Methods
-
-- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section;
-{
- return 0;
-}
-
-- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
-{
- return nil;
-}
-
-
-#pragma mark - UIScrollViewDelegate Methods
-
-- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView
-{
- if (!self.scrollViewProxy.scrollsToTop || self.keyboardStatus == SLKKeyboardStatusWillShow) {
- return NO;
- }
-
- if (self.isInverted) {
- [self.scrollViewProxy slk_scrollToBottomAnimated:YES];
- return NO;
- }
- else {
- return YES;
- }
-}
-
-- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
-{
- self.movingKeyboard = NO;
-}
-
-- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
-{
- self.movingKeyboard = NO;
-}
-
-- (void)scrollViewDidScroll:(UIScrollView *)scrollView
-{
- if ([scrollView isEqual:self.autoCompletionView]) {
- CGRect frame = self.autoCompletionHairline.frame;
- frame.origin.y = scrollView.contentOffset.y;
- self.autoCompletionHairline.frame = frame;
- }
- else {
- if (!self.isMovingKeyboard) {
- _scrollViewOffsetBeforeDragging = scrollView.contentOffset;
- _keyboardHeightBeforeDragging = self.keyboardHC.constant;
- }
- }
-}
-
-
-#pragma mark - UIGestureRecognizerDelegate Methods
-
-- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gesture
-{
- if ([gesture isEqual:self.singleTapGesture]) {
- return [self.textView isFirstResponder] && ![self ignoreTextInputbarAdjustment];
- }
- else if ([gesture isEqual:self.verticalPanGesture]) {
- return self.keyboardPanningEnabled && ![self ignoreTextInputbarAdjustment];
- }
-
- return NO;
-}
-
-
-#pragma mark - UIAlertViewDelegate Methods
-
-- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
-{
- if (alertView.tag != kSLKAlertViewClearTextTag || buttonIndex == [alertView cancelButtonIndex] ) {
- return;
- }
-
- // Clears the text but doesn't clear the undo manager
- if (self.shakeToClearEnabled) {
- [self.textView slk_clearText:NO];
- }
-}
-
-
-#pragma mark - View Auto-Layout
-
-- (void)slk_setupViewConstraints
-{
- NSDictionary *views = @{@"scrollView": self.scrollViewProxy,
- @"autoCompletionView": self.autoCompletionView,
- @"typingIndicatorView": self.typingIndicatorProxyView,
- @"textInputbar": self.textInputbar,
- };
-
- [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView(0@750)][autoCompletionView(0@750)][typingIndicatorView(0)]-0@999-[textInputbar(0)]-0-|" options:0 metrics:nil views:views]];
- [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics:nil views:views]];
- [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[autoCompletionView]|" options:0 metrics:nil views:views]];
- [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[typingIndicatorView]|" options:0 metrics:nil views:views]];
- [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[textInputbar]|" options:0 metrics:nil views:views]];
-
- self.scrollViewHC = [self.view slk_constraintForAttribute:NSLayoutAttributeHeight firstItem:self.scrollViewProxy secondItem:nil];
- self.autoCompletionViewHC = [self.view slk_constraintForAttribute:NSLayoutAttributeHeight firstItem:self.autoCompletionView secondItem:nil];
- self.typingIndicatorViewHC = [self.view slk_constraintForAttribute:NSLayoutAttributeHeight firstItem:self.typingIndicatorProxyView secondItem:nil];
- self.textInputbarHC = [self.view slk_constraintForAttribute:NSLayoutAttributeHeight firstItem:self.textInputbar secondItem:nil];
- self.keyboardHC = [self.view slk_constraintForAttribute:NSLayoutAttributeBottom firstItem:self.view secondItem:self.textInputbar];
-
- [self slk_updateViewConstraints];
-}
-
-- (void)slk_updateViewConstraints
-{
- self.textInputbarHC.constant = self.textInputbar.minimumInputbarHeight;
- self.scrollViewHC.constant = [self slk_appropriateScrollViewHeight];
- self.keyboardHC.constant = [self slk_appropriateKeyboardHeightFromRect:CGRectNull];
-
- if (self.textInputbar.isEditing) {
- self.textInputbarHC.constant += self.textInputbar.editorContentViewHeight;
- }
-
- [super updateViewConstraints];
-}
-
-
-#pragma mark - External Keyboard Support
-
-- (NSArray *)keyCommands
-{
- NSMutableArray *keyboardCommands = [NSMutableArray new];
-
- [keyboardCommands addObject:[self slk_returnKeyCommand]];
- [keyboardCommands addObject:[self slk_escKeyCommand]];
- [keyboardCommands addObject:[self slk_arrowKeyCommand:UIKeyInputUpArrow]];
- [keyboardCommands addObject:[self slk_arrowKeyCommand:UIKeyInputDownArrow]];
-
- return keyboardCommands;
-}
-
-- (UIKeyCommand *)slk_returnKeyCommand
-{
- UIKeyCommand *command = [UIKeyCommand keyCommandWithInput:@"\r" modifierFlags:0 action:@selector(didPressReturnKey:)];
-
-#ifdef __IPHONE_9_0
- if ([UIKeyCommand respondsToSelector:@selector(keyCommandWithInput:modifierFlags:action:discoverabilityTitle:)] ) {
- // Only available since iOS 9
- if (self.textInputbar.isEditing) {
- command.discoverabilityTitle = [self.textInputbar.editorRightButton titleForState:UIControlStateNormal] ? : NSLocalizedString(@"Commit Editing", nil);
- }
- else if (self.textView.text.length > 0) {
- command.discoverabilityTitle = [self.rightButton titleForState:UIControlStateNormal] ? : NSLocalizedString(@"Send", nil);
- }
- }
-#endif
-
- return command;
-}
-
-- (UIKeyCommand *)slk_escKeyCommand
-{
- UIKeyCommand *command = [UIKeyCommand keyCommandWithInput:UIKeyInputEscape modifierFlags:0 action:@selector(didPressEscapeKey:)];
-
-#ifdef __IPHONE_9_0
- if ([UIKeyCommand respondsToSelector:@selector(keyCommandWithInput:modifierFlags:action:discoverabilityTitle:)] ) {
- // Only available since iOS 9
- if (self.isAutoCompleting) {
- command.discoverabilityTitle = NSLocalizedString(@"Exit Auto-Completion", nil);
- }
- else if (self.textInputbar.isEditing) {
- command.discoverabilityTitle = [self.textInputbar.editorRightButton titleForState:UIControlStateNormal] ? : NSLocalizedString(@"Exit Editing", nil);
- }
- else if (!self.isExternalKeyboardDetected && self.keyboardHC.constant != 0) {
- command.discoverabilityTitle = NSLocalizedString(@"Hide Keyboard", nil);
- }
- }
-#endif
-
- return command;
-}
-
-- (UIKeyCommand *)slk_arrowKeyCommand:(NSString *)inputUpArrow
-{
- UIKeyCommand *command = [UIKeyCommand keyCommandWithInput:inputUpArrow modifierFlags:0 action:@selector(didPressArrowKey:)];
-
-#ifdef __IPHONE_9_0
- // Only available since iOS 9
- if ([UIKeyCommand respondsToSelector:@selector(keyCommandWithInput:modifierFlags:action:discoverabilityTitle:)] ) {
- if ([inputUpArrow isEqualToString:UIKeyInputUpArrow]) {
- command.discoverabilityTitle = NSLocalizedString(@"Move Up", nil);
- }
- if ([inputUpArrow isEqualToString:UIKeyInputDownArrow]) {
- command.discoverabilityTitle = NSLocalizedString(@"Move Down", nil);
- }
- }
-#endif
-
- return command;
-}
-
-
-#pragma mark - NSNotificationCenter register/unregister
-
-- (void)slk_registerNotifications
-{
- [self slk_unregisterNotifications];
-
- // Keyboard notifications
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_willShowOrHideKeyboard:) name:UIKeyboardWillShowNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_willShowOrHideKeyboard:) name:UIKeyboardWillHideNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didShowOrHideKeyboard:) name:UIKeyboardDidShowNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didShowOrHideKeyboard:) name:UIKeyboardDidHideNotification object:nil];
-
-#if SLK_KEYBOARD_NOTIFICATION_DEBUG
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didPostSLKKeyboardNotification:) name:SLKKeyboardWillShowNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didPostSLKKeyboardNotification:) name:SLKKeyboardDidShowNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didPostSLKKeyboardNotification:) name:SLKKeyboardWillHideNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didPostSLKKeyboardNotification:) name:SLKKeyboardDidHideNotification object:nil];
-#endif
-
- // TextView notifications
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_willChangeTextViewText:) name:SLKTextViewTextWillChangeNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didChangeTextViewText:) name:UITextViewTextDidChangeNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didChangeTextViewContentSize:) name:SLKTextViewContentSizeDidChangeNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didChangeTextViewSelectedRange:) name:SLKTextViewSelectedRangeDidChangeNotification object:nil];
-
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didChangeTextViewPasteboard:) name:SLKTextViewDidPasteItemNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didShakeTextView:) name:SLKTextViewDidShakeNotification object:nil];
-
- // Application notifications
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_willTerminateApplication:) name:UIApplicationWillTerminateNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_willTerminateApplication:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
-}
-
-- (void)slk_unregisterNotifications
-{
- // Keyboard notifications
- [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidShowNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
-
-#if SLK_KEYBOARD_NOTIFICATION_DEBUG
- [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKKeyboardWillShowNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKKeyboardDidShowNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKKeyboardWillHideNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKKeyboardDidHideNotification object:nil];
-#endif
-
- // TextView notifications
- [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextViewTextDidBeginEditingNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextViewTextDidEndEditingNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKTextViewTextWillChangeNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextViewTextDidChangeNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKTextViewContentSizeDidChangeNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKTextViewSelectedRangeDidChangeNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKTextViewDidPasteItemNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKTextViewDidShakeNotification object:nil];
-
- // Application notifications
- [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillTerminateNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
-}
-
-
-#pragma mark - View Auto-Rotation
-
-#ifdef __IPHONE_8_0
-- (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator
-{
- [super willTransitionToTraitCollection:newCollection withTransitionCoordinator:coordinator];
-}
-
-- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
-{
- [self slk_prepareForInterfaceTransitionWithDuration:coordinator.transitionDuration];
-
- [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
-}
-#else
-- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
-{
- if ([self respondsToSelector:@selector(viewWillTransitionToSize:withTransitionCoordinator:)]) {
- return;
- }
-
- [self slk_prepareForInterfaceTransitionWithDuration:duration];
-}
-#endif
-
-#ifdef __IPHONE_9_0
-- (UIInterfaceOrientationMask)supportedInterfaceOrientations
-#else
-- (NSUInteger)supportedInterfaceOrientations
-#endif
-{
- return UIInterfaceOrientationMaskAll;
-}
-
-- (BOOL)shouldAutorotate
-{
- return YES;
-}
-
-
-#pragma mark - View lifeterm
-
-- (void)didReceiveMemoryWarning
-{
- [super didReceiveMemoryWarning];
-}
-
-- (void)dealloc
-{
- _tableView.delegate = nil;
- _tableView.dataSource = nil;
- _tableView = nil;
-
- _collectionView.delegate = nil;
- _collectionView.dataSource = nil;
- _collectionView = nil;
-
- _scrollView = nil;
-
- _autoCompletionView.delegate = nil;
- _autoCompletionView.dataSource = nil;
- _autoCompletionView = nil;
-
- _textInputbar.textView.delegate = nil;
- _textInputbar = nil;
- _textViewClass = nil;
-
- [_typingIndicatorProxyView removeObserver:self forKeyPath:@"visible"];
- _typingIndicatorProxyView = nil;
- _typingIndicatorViewClass = nil;
-
- _registeredPrefixes = nil;
- _singleTapGesture.delegate = nil;
- _singleTapGesture = nil;
- _verticalPanGesture.delegate = nil;
- _verticalPanGesture = nil;
- _scrollViewHC = nil;
- _textInputbarHC = nil;
- _typingIndicatorViewHC = nil;
- _autoCompletionViewHC = nil;
- _keyboardHC = nil;
-
- [self slk_unregisterNotifications];
-}
-
-@end
http://git-wip-us.apache.org/repos/asf/usergrid/blob/c638c774/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTypingIndicatorProtocol.h
----------------------------------------------------------------------
diff --git a/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTypingIndicatorProtocol.h b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTypingIndicatorProtocol.h
deleted file mode 100644
index 7b2deae..0000000
--- a/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTypingIndicatorProtocol.h
+++ /dev/null
@@ -1,37 +0,0 @@
-//
-// Copyright 2014 Slack Technologies, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#import <Foundation/Foundation.h>
-
-/** Generic protocol needed when customizing your own typing indicator view. */
-@protocol SLKTypingIndicatorProtocol <NSObject>
-@required
-
-/**
- Returns YES if the indicator is visible.
- SLKTextViewController depends on this property internally, by observing its value changes to update the typing indicator view's constraints automatically.
- You can simply @synthesize this property to make it KVO compliant, or override its setter method and wrap its implementation with -willChangeValueForKey: and -didChangeValueForKey: methods, for more complex KVO compliance.
- */
-@property (nonatomic, getter = isVisible) BOOL visible;
-
-@optional
-
-/**
- Dismisses the indicator view.
- */
-- (void)dismissIndicator;
-
-@end
\ No newline at end of file