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/18 16:52:56 UTC

[48/89] [partial] usergrid git commit: Initial commit of the Swift SDK.

http://git-wip-us.apache.org/repos/asf/usergrid/blob/7442c881/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextView.m
----------------------------------------------------------------------
diff --git a/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextView.m b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextView.m
new file mode 100644
index 0000000..a1433b7
--- /dev/null
+++ b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextView.m
@@ -0,0 +1,1117 @@
+//
+//   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 "SLKTextView.h"
+
+#import "SLKTextView+SLKAdditions.h"
+
+#import "SLKUIConstants.h"
+
+NSString * const SLKTextViewTextWillChangeNotification =            @"SLKTextViewTextWillChangeNotification";
+NSString * const SLKTextViewContentSizeDidChangeNotification =      @"SLKTextViewContentSizeDidChangeNotification";
+NSString * const SLKTextViewSelectedRangeDidChangeNotification =    @"SLKTextViewSelectedRangeDidChangeNotification";
+NSString * const SLKTextViewDidPasteItemNotification =              @"SLKTextViewDidPasteItemNotification";
+NSString * const SLKTextViewDidShakeNotification =                  @"SLKTextViewDidShakeNotification";
+
+NSString * const SLKTextViewPastedItemContentType =                 @"SLKTextViewPastedItemContentType";
+NSString * const SLKTextViewPastedItemMediaType =                   @"SLKTextViewPastedItemMediaType";
+NSString * const SLKTextViewPastedItemData =                        @"SLKTextViewPastedItemData";
+
+static NSString *const SLKTextViewGenericFormattingSelectorPrefix = @"slk_format_";
+
+@interface SLKTextView ()
+
+// The label used as placeholder
+@property (nonatomic, strong) UILabel *placeholderLabel;
+
+// The initial font point size, used for dynamic type calculations
+@property (nonatomic) CGFloat initialFontSize;
+
+// The keyboard commands available for external keyboards
+@property (nonatomic, strong) NSArray *keyboardCommands;
+
+// Used for moving the caret up/down
+@property (nonatomic) UITextLayoutDirection verticalMoveDirection;
+@property (nonatomic) CGRect verticalMoveStartCaretRect;
+@property (nonatomic) CGRect verticalMoveLastCaretRect;
+
+// Used for detecting if the scroll indicator was previously flashed
+@property (nonatomic) BOOL didFlashScrollIndicators;
+
+@property (nonatomic, strong) NSMutableArray *registeredFormattingTitles;
+@property (nonatomic, strong) NSMutableArray *registeredFormattingSymbols;
+@property (nonatomic, getter=isFormatting) BOOL formatting;
+
+@end
+
+@implementation SLKTextView
+@synthesize delegate = _delegate;
+
+#pragma mark - Initialization
+
+- (instancetype)initWithFrame:(CGRect)frame textContainer:(NSTextContainer *)textContainer
+{
+    if (self = [super initWithFrame:frame textContainer:textContainer]) {
+        [self slk_commonInit];
+    }
+    return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder
+{
+    if (self = [super initWithCoder:coder]) {
+        [self slk_commonInit];
+    }
+    return self;
+}
+
+- (void)slk_commonInit
+{
+    _pastableMediaTypes = SLKPastableMediaTypeNone;
+    _dynamicTypeEnabled = YES;
+
+    self.undoManagerEnabled = YES;
+    self.autoCompleteFormatting = YES;
+    
+    self.editable = YES;
+    self.selectable = YES;
+    self.scrollEnabled = YES;
+    self.scrollsToTop = NO;
+    self.directionalLockEnabled = YES;
+    self.dataDetectorTypes = UIDataDetectorTypeNone;
+    
+    [self slk_registerNotifications];
+    
+    [self addObserver:self forKeyPath:NSStringFromSelector(@selector(contentSize)) options:NSKeyValueObservingOptionNew context:NULL];
+}
+
+
+#pragma mark - UIView Overrides
+
+- (CGSize)intrinsicContentSize
+{
+    CGFloat height = self.font.lineHeight;
+    height += self.textContainerInset.top + self.textContainerInset.bottom;
+    
+    return CGSizeMake(UIViewNoIntrinsicMetric, height);
+}
+
++ (BOOL)requiresConstraintBasedLayout
+{
+    return YES;
+}
+
+- (void)layoutIfNeeded
+{
+    if (!self.window) {
+        return;
+    }
+    
+    [super layoutIfNeeded];
+}
+
+- (void)layoutSubviews
+{
+    [super layoutSubviews];
+    
+    self.placeholderLabel.hidden = [self slk_shouldHidePlaceholder];
+    
+    if (!self.placeholderLabel.hidden) {
+        
+        [UIView performWithoutAnimation:^{
+            self.placeholderLabel.frame = [self slk_placeholderRectThatFits:self.bounds];
+            [self sendSubviewToBack:self.placeholderLabel];
+        }];
+    }
+}
+
+
+#pragma mark - Getters
+
+- (UILabel *)placeholderLabel
+{
+    if (!_placeholderLabel) {
+        _placeholderLabel = [UILabel new];
+        _placeholderLabel.clipsToBounds = NO;
+        _placeholderLabel.autoresizesSubviews = NO;
+        _placeholderLabel.numberOfLines = 1;
+        _placeholderLabel.font = self.font;
+        _placeholderLabel.backgroundColor = [UIColor clearColor];
+        _placeholderLabel.textColor = [UIColor lightGrayColor];
+        _placeholderLabel.hidden = YES;
+        
+        [self addSubview:_placeholderLabel];
+    }
+    return _placeholderLabel;
+}
+
+- (NSString *)placeholder
+{
+    return self.placeholderLabel.text;
+}
+
+- (UIColor *)placeholderColor
+{
+    return self.placeholderLabel.textColor;
+}
+
+- (NSUInteger)numberOfLines
+{
+    CGSize contentSize = self.contentSize;
+    
+    CGFloat contentHeight = contentSize.height;
+    contentHeight -= self.textContainerInset.top + self.textContainerInset.bottom;
+    
+    NSUInteger lines = fabs(contentHeight/self.font.lineHeight);
+    
+    // This helps preventing the content's height to be larger that the bounds' height
+    // Avoiding this way to have unnecessary scrolling in the text view when there is only 1 line of content
+    if (lines == 1 && contentSize.height > self.bounds.size.height) {
+        contentSize.height = self.bounds.size.height;
+        self.contentSize = contentSize;
+    }
+    
+    // Let's fallback to the minimum line count
+    if (lines == 0) {
+        lines = 1;
+    }
+    
+    return lines;
+}
+
+- (NSUInteger)maxNumberOfLines
+{
+    NSUInteger numberOfLines = _maxNumberOfLines;
+    
+    if (SLK_IS_LANDSCAPE) {
+        if ((SLK_IS_IPHONE4 || SLK_IS_IPHONE5)) {
+            numberOfLines = 2.0; // 2 lines max on smaller iPhones
+        }
+        else if (SLK_IS_IPHONE) {
+            numberOfLines /= 2.0; // Half size on larger iPhone
+        }
+    }
+    
+    if (self.isDynamicTypeEnabled) {
+        NSString *contentSizeCategory = [[UIApplication sharedApplication] preferredContentSizeCategory];
+        CGFloat pointSizeDifference = [SLKTextView pointSizeDifferenceForCategory:contentSizeCategory];
+        
+        CGFloat factor = pointSizeDifference/self.initialFontSize;
+        
+        if (fabs(factor) > 0.75) {
+            factor = 0.75;
+        }
+        
+        numberOfLines -= floorf(numberOfLines * factor); // Calculates a dynamic number of lines depending of the user preferred font size
+    }
+    
+    return numberOfLines;
+}
+
+- (BOOL)isTypingSuggestionEnabled
+{
+    return (self.autocorrectionType == UITextAutocorrectionTypeNo) ? NO : YES;
+}
+
+- (BOOL)autoCompleteFormatting
+{
+    if (_registeredFormattingSymbols.count == 0) {
+        return NO;
+    }
+    return _autoCompleteFormatting;
+}
+
+// Returns only a supported pasted item
+- (id)slk_pastedItem
+{
+    NSString *contentType = [self slk_pasteboardContentType];
+    NSData *data = [[UIPasteboard generalPasteboard] dataForPasteboardType:contentType];
+    
+    if (data && [data isKindOfClass:[NSData class]])
+    {
+        SLKPastableMediaType mediaType = SLKPastableMediaTypeFromNSString(contentType);
+        
+        NSDictionary *userInfo = @{SLKTextViewPastedItemContentType: contentType,
+                                   SLKTextViewPastedItemMediaType: @(mediaType),
+                                   SLKTextViewPastedItemData: data};
+        return userInfo;
+    }
+    if ([[UIPasteboard generalPasteboard] URL]) {
+        return [[[UIPasteboard generalPasteboard] URL] absoluteString];
+    }
+    if ([[UIPasteboard generalPasteboard] string]) {
+        return [[UIPasteboard generalPasteboard] string];
+    }
+    
+    return nil;
+}
+
+// Checks if any supported media found in the general pasteboard
+- (BOOL)slk_isPasteboardItemSupported
+{
+    if ([self slk_pasteboardContentType].length > 0) {
+        return YES;
+    }
+    return NO;
+}
+
+- (NSString *)slk_pasteboardContentType
+{
+    NSArray *pasteboardTypes = [[UIPasteboard generalPasteboard] pasteboardTypes];
+    NSMutableArray *subpredicates = [NSMutableArray new];
+    
+    for (NSString *type in [self slk_supportedMediaTypes]) {
+        [subpredicates addObject:[NSPredicate predicateWithFormat:@"SELF == %@", type]];
+    }
+    
+    return [[pasteboardTypes filteredArrayUsingPredicate:[NSCompoundPredicate orPredicateWithSubpredicates:subpredicates]] firstObject];
+}
+
+- (NSArray *)slk_supportedMediaTypes
+{
+    if (self.pastableMediaTypes == SLKPastableMediaTypeNone) {
+        return nil;
+    }
+    
+    NSMutableArray *types = [NSMutableArray new];
+    
+    if (self.pastableMediaTypes & SLKPastableMediaTypePNG) {
+        [types addObject:NSStringFromSLKPastableMediaType(SLKPastableMediaTypePNG)];
+    }
+    if (self.pastableMediaTypes & SLKPastableMediaTypeJPEG) {
+        [types addObject:NSStringFromSLKPastableMediaType(SLKPastableMediaTypeJPEG)];
+    }
+    if (self.pastableMediaTypes & SLKPastableMediaTypeTIFF) {
+        [types addObject:NSStringFromSLKPastableMediaType(SLKPastableMediaTypeTIFF)];
+    }
+    if (self.pastableMediaTypes & SLKPastableMediaTypeGIF) {
+        [types addObject:NSStringFromSLKPastableMediaType(SLKPastableMediaTypeGIF)];
+    }
+    if (self.pastableMediaTypes & SLKPastableMediaTypeMOV) {
+        [types addObject:NSStringFromSLKPastableMediaType(SLKPastableMediaTypeMOV)];
+    }
+    if (self.pastableMediaTypes & SLKPastableMediaTypePassbook) {
+        [types addObject:NSStringFromSLKPastableMediaType(SLKPastableMediaTypePassbook)];
+    }
+    
+    if (self.pastableMediaTypes & SLKPastableMediaTypeImages) {
+        [types addObject:NSStringFromSLKPastableMediaType(SLKPastableMediaTypeImages)];
+    }
+    
+    return types;
+}
+
+NSString *NSStringFromSLKPastableMediaType(SLKPastableMediaType type)
+{
+    if (type == SLKPastableMediaTypePNG) {
+        return @"public.png";
+    }
+    if (type == SLKPastableMediaTypeJPEG) {
+        return @"public.jpeg";
+    }
+    if (type == SLKPastableMediaTypeTIFF) {
+        return @"public.tiff";
+    }
+    if (type == SLKPastableMediaTypeGIF) {
+        return @"com.compuserve.gif";
+    }
+    if (type == SLKPastableMediaTypeMOV) {
+        return @"com.apple.quicktime";
+    }
+    if (type == SLKPastableMediaTypePassbook) {
+        return @"com.apple.pkpass";
+    }
+    if (type == SLKPastableMediaTypeImages) {
+        return @"com.apple.uikit.image";
+    }
+    
+    return nil;
+}
+
+SLKPastableMediaType SLKPastableMediaTypeFromNSString(NSString *string)
+{
+    if ([string isEqualToString:NSStringFromSLKPastableMediaType(SLKPastableMediaTypePNG)]) {
+        return SLKPastableMediaTypePNG;
+    }
+    if ([string isEqualToString:NSStringFromSLKPastableMediaType(SLKPastableMediaTypeJPEG)]) {
+        return SLKPastableMediaTypeJPEG;
+    }
+    if ([string isEqualToString:NSStringFromSLKPastableMediaType(SLKPastableMediaTypeTIFF)]) {
+        return SLKPastableMediaTypeTIFF;
+    }
+    if ([string isEqualToString:NSStringFromSLKPastableMediaType(SLKPastableMediaTypeGIF)]) {
+        return SLKPastableMediaTypeGIF;
+    }
+    if ([string isEqualToString:NSStringFromSLKPastableMediaType(SLKPastableMediaTypeMOV)]) {
+        return SLKPastableMediaTypeMOV;
+    }
+    if ([string isEqualToString:NSStringFromSLKPastableMediaType(SLKPastableMediaTypePassbook)]) {
+        return SLKPastableMediaTypePassbook;
+    }
+    if ([string isEqualToString:NSStringFromSLKPastableMediaType(SLKPastableMediaTypeImages)]) {
+        return SLKPastableMediaTypeImages;
+    }
+    return SLKPastableMediaTypeNone;
+}
+
+- (BOOL)isExpanding
+{
+    if (self.numberOfLines >= self.maxNumberOfLines) {
+        return YES;
+    }
+    return NO;
+}
+
+- (BOOL)slk_shouldHidePlaceholder
+{
+    if (self.placeholder.length == 0 || self.text.length > 0) {
+        return YES;
+    }
+    return NO;
+}
+
+- (CGRect)slk_placeholderRectThatFits:(CGRect)bounds
+{
+    CGFloat padding = self.textContainer.lineFragmentPadding;
+    
+    CGRect rect = CGRectZero;
+    rect.size.height = [self.placeholderLabel sizeThatFits:bounds.size].height;
+    rect.size.width = self.textContainer.size.width - padding*2.0;
+    rect.origin = UIEdgeInsetsInsetRect(bounds, self.textContainerInset).origin;
+    rect.origin.x += padding;
+    
+    return rect;
+}
+
+
+#pragma mark - Setters
+
+- (void)setPlaceholder:(NSString *)placeholder
+{
+    self.placeholderLabel.text = placeholder;
+    self.accessibilityLabel = placeholder;
+    
+    [self setNeedsLayout];
+}
+
+- (void)setPlaceholderColor:(UIColor *)color
+{
+    self.placeholderLabel.textColor = color;
+}
+
+- (void)setUndoManagerEnabled:(BOOL)enabled
+{
+    if (self.undoManagerEnabled == enabled) {
+        return;
+    }
+    
+    self.undoManager.levelsOfUndo = 10;
+    [self.undoManager removeAllActions];
+    [self.undoManager setActionIsDiscardable:YES];
+    
+    _undoManagerEnabled = enabled;
+}
+
+- (void)setTypingSuggestionEnabled:(BOOL)enabled
+{
+    if (self.isTypingSuggestionEnabled == enabled) {
+        return;
+    }
+    
+    self.autocorrectionType = enabled ? UITextAutocorrectionTypeDefault : UITextAutocorrectionTypeNo;
+    self.spellCheckingType = enabled ? UITextSpellCheckingTypeDefault : UITextSpellCheckingTypeNo;
+    
+    [self refreshFirstResponder];
+}
+
+
+#pragma mark - UITextView Overrides
+
+- (void)setSelectedRange:(NSRange)selectedRange
+{
+    [super setSelectedRange:selectedRange];
+}
+
+- (void)setSelectedTextRange:(UITextRange *)selectedTextRange
+{
+    [super setSelectedTextRange:selectedTextRange];
+    
+    [[NSNotificationCenter defaultCenter] postNotificationName:SLKTextViewSelectedRangeDidChangeNotification object:self userInfo:nil];
+}
+
+- (void)setText:(NSString *)text
+{
+    // Registers for undo management
+    [self slk_prepareForUndo:@"Text Set"];
+    
+    [super setText:text];
+    
+    [[NSNotificationCenter defaultCenter] postNotificationName:UITextViewTextDidChangeNotification object:self];
+}
+
+- (void)setAttributedText:(NSAttributedString *)attributedText
+{
+    // Registers for undo management
+    [self slk_prepareForUndo:@"Attributed Text Set"];
+    
+    [super setAttributedText:attributedText];
+    
+    [[NSNotificationCenter defaultCenter] postNotificationName:UITextViewTextDidChangeNotification object:self];
+}
+
+- (void)setFont:(UIFont *)font
+{
+    NSString *contentSizeCategory = [[UIApplication sharedApplication] preferredContentSizeCategory];
+    
+    [self setFontName:font.fontName pointSize:font.pointSize withContentSizeCategory:contentSizeCategory];
+    
+    self.initialFontSize = font.pointSize;
+}
+
+- (void)setFontName:(NSString *)fontName pointSize:(CGFloat)pointSize withContentSizeCategory:(NSString *)contentSizeCategory
+{
+    if (self.isDynamicTypeEnabled) {
+        pointSize += [SLKTextView pointSizeDifferenceForCategory:contentSizeCategory];
+    }
+    
+    UIFont *dynamicFont = [UIFont fontWithName:fontName size:pointSize];
+    
+    [super setFont:dynamicFont];
+    
+    // Updates the placeholder font too
+    self.placeholderLabel.font = dynamicFont;
+}
+
+- (void)setDynamicTypeEnabled:(BOOL)dynamicTypeEnabled
+{
+    if (self.isDynamicTypeEnabled == dynamicTypeEnabled) {
+        return;
+    }
+    
+    _dynamicTypeEnabled = dynamicTypeEnabled;
+    
+    NSString *contentSizeCategory = [[UIApplication sharedApplication] preferredContentSizeCategory];
+
+    [self setFontName:self.font.fontName pointSize:self.initialFontSize withContentSizeCategory:contentSizeCategory];
+}
+
+- (void)setTextAlignment:(NSTextAlignment)textAlignment
+{
+    [super setTextAlignment:textAlignment];
+    
+    // Updates the placeholder text alignment too
+    self.placeholderLabel.textAlignment = textAlignment;
+}
+
+
+#pragma mark - UITextInput Overrides
+
+- (void)beginFloatingCursorAtPoint:(CGPoint)point
+{
+    [super beginFloatingCursorAtPoint:point];
+    
+    _trackpadEnabled = YES;
+}
+
+- (void)updateFloatingCursorAtPoint:(CGPoint)point
+{
+    [super updateFloatingCursorAtPoint:point];
+}
+
+- (void)endFloatingCursor
+{
+    [super endFloatingCursor];
+
+    _trackpadEnabled = NO;
+    
+    // We still need to notify a selection change in the textview after the trackpad is disabled
+    if (self.delegate && [self.delegate respondsToSelector:@selector(textViewDidChangeSelection:)]) {
+        [self.delegate textViewDidChangeSelection:self];
+    }
+    
+    [[NSNotificationCenter defaultCenter] postNotificationName:SLKTextViewSelectedRangeDidChangeNotification object:self userInfo:nil];
+}
+
+
+#pragma mark - UIResponder Overrides
+
+- (BOOL)canBecomeFirstResponder
+{
+    [self slk_addCustomMenuControllerItems];
+    
+    return [super canBecomeFirstResponder];
+}
+
+- (BOOL)becomeFirstResponder
+{
+    return [super becomeFirstResponder];
+}
+
+- (BOOL)canResignFirstResponder
+{
+    // Removes undo/redo items
+    if (self.undoManagerEnabled) {
+        [self.undoManager removeAllActions];
+    }
+    
+    return [super canResignFirstResponder];
+}
+
+- (BOOL)resignFirstResponder
+{
+    return [super resignFirstResponder];
+}
+
+- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
+{
+    if (self.isFormatting) {
+        NSString *title = [self slk_formattingTitleFromSelector:action];
+        NSString *symbol = [self slk_formattingSymbolWithTitle:title];
+        
+        if (symbol.length > 0) {
+            if (self.delegate && [self.delegate respondsToSelector:@selector(textView:shouldOfferFormattingForSymbol:)]) {
+                return [self.delegate textView:self shouldOfferFormattingForSymbol:symbol];
+            }
+            else {
+                return YES;
+            }
+        }
+        
+        return NO;
+    }
+
+    if (action == @selector(delete:)) {
+        return NO;
+    }
+    
+    if (action == NSSelectorFromString(@"_share:") || action == NSSelectorFromString(@"_define:") || action == NSSelectorFromString(@"_promptForReplace:")) {
+        return NO;
+    }
+    
+    if (action == @selector(slk_presentFormattingMenu:)) {
+        return self.selectedRange.length > 0 ? YES : NO;
+    }
+    
+    if (action == @selector(paste:) && [self slk_isPasteboardItemSupported]) {
+        return YES;
+    }
+    
+    if (action == @selector(paste:) && [self slk_isPasteboardItemSupported]) {
+        return YES;
+    }
+    
+    if (self.undoManagerEnabled) {
+        if (action == @selector(slk_undo:)) {
+            if (self.undoManager.undoActionIsDiscardable) {
+                return NO;
+            }
+            return [self.undoManager canUndo];
+        }
+        if (action == @selector(slk_redo:)) {
+            if (self.undoManager.redoActionIsDiscardable) {
+                return NO;
+            }
+            return [self.undoManager canRedo];
+        }
+    }
+    
+    return [super canPerformAction:action withSender:sender];
+}
+
+- (void)paste:(id)sender
+{
+    id pastedItem = [self slk_pastedItem];
+    
+    if ([pastedItem isKindOfClass:[NSDictionary class]]) {
+        [[NSNotificationCenter defaultCenter] postNotificationName:SLKTextViewDidPasteItemNotification object:nil userInfo:pastedItem];
+    }
+    else if ([pastedItem isKindOfClass:[NSString class]]) {
+        // Respect the delegate yo!
+        if (self.delegate && [self.delegate respondsToSelector:@selector(textView:shouldChangeTextInRange:replacementText:)]) {
+            if (![self.delegate textView:self shouldChangeTextInRange:self.selectedRange replacementText:pastedItem]) {
+                return;
+            }
+        }
+        
+        // Inserting the text fixes a UITextView bug whitch automatically scrolls to the bottom
+        // and beyond scroll content size sometimes when the text is too long
+        [self slk_insertTextAtCaretRange:pastedItem];
+    }
+}
+
+
+#pragma mark - NSObject Overrides
+
+- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
+{
+    if ([super methodSignatureForSelector:sel]) {
+        return [super methodSignatureForSelector:sel];
+    }
+    return [super methodSignatureForSelector:@selector(slk_format:)];
+}
+
+- (void)forwardInvocation:(NSInvocation *)invocation
+{
+    NSString *title = [self slk_formattingTitleFromSelector:[invocation selector]];
+    
+    if (title.length > 0) {
+        [self slk_format:title];
+    }
+    else {
+        [super forwardInvocation:invocation];
+    }
+}
+
+
+#pragma mark - Custom Actions
+
+- (void)slk_flashScrollIndicatorsIfNeeded
+{
+    if (self.numberOfLines == self.maxNumberOfLines+1) {
+        if (!_didFlashScrollIndicators) {
+            _didFlashScrollIndicators = YES;
+            [super flashScrollIndicators];
+        }
+    }
+    else if (_didFlashScrollIndicators) {
+        _didFlashScrollIndicators = NO;
+    }
+}
+
+- (void)refreshFirstResponder
+{
+    if (!self.isFirstResponder) {
+        return;
+    }
+    
+    _didNotResignFirstResponder = YES;
+    [self resignFirstResponder];
+    
+    _didNotResignFirstResponder = NO;
+    [self becomeFirstResponder];
+}
+
+- (void)refreshInputViews
+{
+    _didNotResignFirstResponder = YES;
+    
+    [super reloadInputViews];
+    
+    _didNotResignFirstResponder = NO;
+}
+
+- (void)slk_addCustomMenuControllerItems
+{
+    UIMenuItem *undo = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"Undo", nil) action:@selector(slk_undo:)];
+    UIMenuItem *redo = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"Redo", nil) action:@selector(slk_redo:)];
+    UIMenuItem *format = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"Format", nil) action:@selector(slk_presentFormattingMenu:)];
+    
+    [[UIMenuController sharedMenuController] setMenuItems:@[undo, redo, format]];
+}
+
+- (void)slk_undo:(id)sender
+{
+    [self.undoManager undo];
+}
+
+- (void)slk_redo:(id)sender
+{
+    [self.undoManager redo];
+}
+
+- (void)slk_presentFormattingMenu:(id)sender
+{
+    NSMutableArray *items = [NSMutableArray arrayWithCapacity:self.registeredFormattingTitles.count];
+    
+    for (NSString *name in self.registeredFormattingTitles) {
+        
+        NSString *sel = [NSString stringWithFormat:@"%@%@", SLKTextViewGenericFormattingSelectorPrefix, name];
+        
+        UIMenuItem *item = [[UIMenuItem alloc] initWithTitle:name action:NSSelectorFromString(sel)];
+        [items addObject:item];
+    }
+    
+    self.formatting = YES;
+    
+    UIMenuController *menu = [UIMenuController sharedMenuController];
+    [menu setMenuItems:items];
+    
+    NSLayoutManager *manager = self.layoutManager;
+    CGRect targetRect = [manager boundingRectForGlyphRange:self.selectedRange inTextContainer:self.textContainer];
+    
+    [menu setTargetRect:targetRect inView:self];
+    
+    [menu setMenuVisible:YES animated:YES];
+}
+
+- (NSString *)slk_formattingTitleFromSelector:(SEL)selector
+{
+    NSString *selectorString = NSStringFromSelector(selector);
+    NSRange match = [selectorString rangeOfString:SLKTextViewGenericFormattingSelectorPrefix];
+    
+    if (match.location != NSNotFound) {
+        return [selectorString substringFromIndex:SLKTextViewGenericFormattingSelectorPrefix.length];
+    }
+    
+    return nil;
+}
+
+- (NSString *)slk_formattingSymbolWithTitle:(NSString *)title
+{
+    NSUInteger idx = [self.registeredFormattingTitles indexOfObject:title];
+    
+    if (idx <= self.registeredFormattingSymbols.count -1) {
+        return self.registeredFormattingSymbols[idx];
+    }
+    
+    return nil;
+}
+
+- (void)slk_format:(NSString *)titles
+{
+    NSString *symbol = [self slk_formattingSymbolWithTitle:titles];
+    
+    if (symbol.length > 0) {
+        NSRange selection = self.selectedRange;
+        
+        NSRange range = [self slk_insertText:symbol inRange:NSMakeRange(selection.location, 0)];
+        range.location += selection.length;
+        range.length = 0;
+        
+        // The default behavior is to add a closure
+        BOOL addClosure = YES;
+        
+        if (self.delegate && [self.delegate respondsToSelector:@selector(textView:shouldInsertSuffixForFormattingWithSymbol:prefixRange:)]) {
+            addClosure = [self.delegate textView:self shouldInsertSuffixForFormattingWithSymbol:symbol prefixRange:selection];
+        }
+        
+        if (addClosure) {
+            self.selectedRange = [self slk_insertText:symbol inRange:range];
+        }
+    }
+}
+
+
+#pragma mark - Markdown Formatting
+
+- (void)registerMarkdownFormattingSymbol:(NSString *)symbol withTitle:(NSString *)title
+{
+    if (!symbol || !title) {
+        return;
+    }
+    
+    if (!_registeredFormattingTitles) {
+        _registeredFormattingTitles = [NSMutableArray new];
+        _registeredFormattingSymbols = [NSMutableArray new];
+    }
+    
+    // Adds the symbol if not contained already
+    if (![self.registeredSymbols containsObject:symbol]) {
+        [self.registeredFormattingTitles addObject:title];
+        [self.registeredFormattingSymbols addObject:symbol];
+    }
+}
+
+- (NSArray *)registeredSymbols
+{
+    return self.registeredFormattingSymbols;
+}
+
+
+#pragma mark - Notification Events
+
+- (void)slk_didBeginEditing:(NSNotification *)notification
+{
+    if (![notification.object isEqual:self]) {
+        return;
+    }
+    
+    // Do something
+}
+
+- (void)slk_didChangeText:(NSNotification *)notification
+{
+    if (![notification.object isEqual:self]) {
+        return;
+    }
+    
+    if (self.placeholderLabel.hidden != [self slk_shouldHidePlaceholder]) {
+        [self setNeedsLayout];
+    }
+    
+    [self slk_flashScrollIndicatorsIfNeeded];
+}
+
+- (void)slk_didEndEditing:(NSNotification *)notification
+{
+    if (![notification.object isEqual:self]) {
+        return;
+    }
+    
+    // Do something
+}
+
+- (void)slk_didChangeTextInputMode:(NSNotification *)notification
+{
+    // Do something
+}
+
+- (void)slk_didChangeContentSizeCategory:(NSNotification *)notification
+{
+    if (!self.isDynamicTypeEnabled) {
+        return;
+    }
+    
+    NSString *contentSizeCategory = notification.userInfo[UIContentSizeCategoryNewValueKey];
+    
+    [self setFontName:self.font.fontName pointSize:self.initialFontSize withContentSizeCategory:contentSizeCategory];
+    
+    NSString *text = [self.text copy];
+    
+    // Reloads the content size of the text view
+    [self setText:@" "];
+    [self setText:text];
+}
+
+- (void)slk_willShowMenuController:(NSNotification *)notification
+{
+    
+}
+
+- (void)slk_didHideMenuController:(NSNotification *)notification
+{
+    self.formatting = NO;
+    
+    [self slk_addCustomMenuControllerItems];
+}
+
+
+#pragma mark - KVO Listener
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+{
+    if ([object isEqual:self] && [keyPath isEqualToString:NSStringFromSelector(@selector(contentSize))]) {
+        [[NSNotificationCenter defaultCenter] postNotificationName:SLKTextViewContentSizeDidChangeNotification object:self userInfo:nil];
+    }
+    else {
+        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+    }
+}
+
+
+#pragma mark - Motion Events
+
+- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
+{
+    if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake) {
+        [[NSNotificationCenter defaultCenter] postNotificationName:SLKTextViewDidShakeNotification object:self];
+    }
+}
+
+
+#pragma mark - External Keyboard Support
+
+- (NSArray *)keyCommands
+{
+    if (_keyboardCommands) {
+        return _keyboardCommands;
+    }
+    
+    _keyboardCommands = @[
+         // Return
+         [UIKeyCommand keyCommandWithInput:@"\r" modifierFlags:UIKeyModifierShift action:@selector(slk_didPressLineBreakKeys:)],
+         [UIKeyCommand keyCommandWithInput:@"\r" modifierFlags:UIKeyModifierAlternate action:@selector(slk_didPressLineBreakKeys:)],
+         [UIKeyCommand keyCommandWithInput:@"\r" modifierFlags:UIKeyModifierControl action:@selector(slk_didPressLineBreakKeys:)],
+         
+         // Undo/Redo
+         [UIKeyCommand keyCommandWithInput:@"z" modifierFlags:UIKeyModifierCommand action:@selector(slk_didPressCommandZKeys:)],
+         [UIKeyCommand keyCommandWithInput:@"z" modifierFlags:UIKeyModifierShift|UIKeyModifierCommand action:@selector(slk_didPressCommandZKeys:)],
+         ];
+    
+    return _keyboardCommands;
+}
+
+
+#pragma mark Line Break
+
+- (void)slk_didPressLineBreakKeys:(id)sender
+{
+    [self slk_insertNewLineBreak];
+}
+
+
+#pragma mark Undo/Redo Text
+
+- (void)slk_didPressCommandZKeys:(id)sender
+{
+    if (!self.undoManagerEnabled) {
+        return;
+    }
+    
+    UIKeyCommand *keyCommand = (UIKeyCommand *)sender;
+    
+    if ((keyCommand.modifierFlags & UIKeyModifierShift) > 0) {
+        
+        if ([self.undoManager canRedo]) {
+            [self.undoManager redo];
+        }
+    }
+    else {
+        if ([self.undoManager canUndo]) {
+            [self.undoManager undo];
+        }
+    }
+}
+
+#pragma mark Up/Down Cursor Movement
+
+- (void)didPressAnyArrowKey:(id)sender
+{
+    if (self.text.length == 0 || self.numberOfLines < 2) {
+        return;
+    }
+    
+    UIKeyCommand *keyCommand = (UIKeyCommand *)sender;
+    
+    if ([keyCommand.input isEqualToString:UIKeyInputUpArrow]) {
+        [self slk_moveCursorTodirection:UITextLayoutDirectionUp];
+    }
+    else if ([keyCommand.input isEqualToString:UIKeyInputDownArrow]) {
+        [self slk_moveCursorTodirection:UITextLayoutDirectionDown];
+    }
+}
+
+- (void)slk_moveCursorTodirection:(UITextLayoutDirection)direction
+{
+    UITextPosition *start = (direction == UITextLayoutDirectionUp) ? self.selectedTextRange.start : self.selectedTextRange.end;
+    
+    if ([self slk_isNewVerticalMovementForPosition:start inDirection:direction]) {
+        self.verticalMoveDirection = direction;
+        self.verticalMoveStartCaretRect = [self caretRectForPosition:start];
+    }
+    
+    if (start) {
+        UITextPosition *end = [self slk_closestPositionToPosition:start inDirection:direction];
+        
+        if (end) {
+            self.verticalMoveLastCaretRect = [self caretRectForPosition:end];
+            self.selectedTextRange = [self textRangeFromPosition:end toPosition:end];
+            
+            [self slk_scrollToCaretPositonAnimated:NO];
+        }
+    }
+}
+
+// Based on code from Ruben Cabaco
+// https://gist.github.com/rcabaco/6765778
+
+- (UITextPosition *)slk_closestPositionToPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction
+{
+    // Only up/down are implemented. No real need for left/right since that is native to UITextInput.
+    NSParameterAssert(direction == UITextLayoutDirectionUp || direction == UITextLayoutDirectionDown);
+    
+    // Translate the vertical direction to a horizontal direction.
+    UITextLayoutDirection lookupDirection = (direction == UITextLayoutDirectionUp) ? UITextLayoutDirectionLeft : UITextLayoutDirectionRight;
+    
+    // Walk one character at a time in `lookupDirection` until the next line is reached.
+    UITextPosition *checkPosition = position;
+    UITextPosition *closestPosition = position;
+    CGRect startingCaretRect = [self caretRectForPosition:position];
+    CGRect nextLineCaretRect;
+    BOOL isInNextLine = NO;
+    
+    while (YES) {
+        UITextPosition *nextPosition = [self positionFromPosition:checkPosition inDirection:lookupDirection offset:1];
+        
+        // End of line.
+        if (!nextPosition || [self comparePosition:checkPosition toPosition:nextPosition] == NSOrderedSame) {
+            break;
+        }
+        
+        checkPosition = nextPosition;
+        CGRect checkRect = [self caretRectForPosition:checkPosition];
+        if (CGRectGetMidY(startingCaretRect) != CGRectGetMidY(checkRect)) {
+            // While on the next line stop just above/below the starting position.
+            if (lookupDirection == UITextLayoutDirectionLeft && CGRectGetMidX(checkRect) <= CGRectGetMidX(self.verticalMoveStartCaretRect)) {
+                closestPosition = checkPosition;
+                break;
+            }
+            if (lookupDirection == UITextLayoutDirectionRight && CGRectGetMidX(checkRect) >= CGRectGetMidX(self.verticalMoveStartCaretRect)) {
+                closestPosition = checkPosition;
+                break;
+            }
+            // But don't skip lines.
+            if (isInNextLine && CGRectGetMidY(checkRect) != CGRectGetMidY(nextLineCaretRect)) {
+                break;
+            }
+            
+            isInNextLine = YES;
+            nextLineCaretRect = checkRect;
+            closestPosition = checkPosition;
+        }
+    }
+    return closestPosition;
+}
+
+- (BOOL)slk_isNewVerticalMovementForPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction
+{
+    CGRect caretRect = [self caretRectForPosition:position];
+    BOOL noPreviousStartPosition = CGRectEqualToRect(self.verticalMoveStartCaretRect, CGRectZero);
+    BOOL caretMovedSinceLastPosition = !CGRectEqualToRect(caretRect, self.verticalMoveLastCaretRect);
+    BOOL directionChanged = self.verticalMoveDirection != direction;
+    
+    BOOL newMovement = noPreviousStartPosition || caretMovedSinceLastPosition || directionChanged;
+    return newMovement;
+}
+
+
+#pragma mark - NSNotificationCenter register/unregister
+
+- (void)slk_registerNotifications
+{
+    [self slk_unregisterNotifications];
+    
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didBeginEditing:) name:UITextViewTextDidBeginEditingNotification object:nil];
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didChangeText:) name:UITextViewTextDidChangeNotification object:nil];
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didEndEditing:) name:UITextViewTextDidEndEditingNotification object:nil];
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didChangeTextInputMode:) name:UITextInputCurrentInputModeDidChangeNotification object:nil];
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didChangeContentSizeCategory:) name:UIContentSizeCategoryDidChangeNotification object:nil];
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_willShowMenuController:) name:UIMenuControllerWillShowMenuNotification object:nil];
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didHideMenuController:) name:UIMenuControllerDidHideMenuNotification object:nil];
+}
+
+- (void)slk_unregisterNotifications
+{
+    [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextViewTextDidBeginEditingNotification object:nil];
+    [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextViewTextDidChangeNotification object:nil];
+    [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextViewTextDidEndEditingNotification object:nil];
+    [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextInputCurrentInputModeDidChangeNotification object:nil];
+    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIContentSizeCategoryDidChangeNotification object:nil];
+}
+
+
+#pragma mark - Lifeterm
+
+- (void)dealloc
+{
+    [self slk_unregisterNotifications];
+    
+    [self removeObserver:self forKeyPath:NSStringFromSelector(@selector(contentSize))];
+    
+    _placeholderLabel = nil;
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/usergrid/blob/7442c881/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextViewController.h
----------------------------------------------------------------------
diff --git a/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextViewController.h b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextViewController.h
new file mode 100644
index 0000000..1e429d3
--- /dev/null
+++ b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextViewController.h
@@ -0,0 +1,584 @@
+//
+//   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>
+#import <UIKit/UIKit.h>
+
+#import "SLKTextInputbar.h"
+#import "SLKTextView.h"
+#import "SLKTypingIndicatorView.h"
+#import "SLKTypingIndicatorProtocol.h"
+
+#import "SLKTextView+SLKAdditions.h"
+#import "UIScrollView+SLKAdditions.h"
+#import "UIView+SLKAdditions.h"
+
+#import "SLKUIConstants.h"
+
+/**
+ UIKeyboard notification replacement, posting reliably only when showing/hiding the keyboard (not when resizing keyboard, or with inputAccessoryView reloads, etc).
+ Only triggered when using SLKTextViewController's text view.
+ */
+UIKIT_EXTERN NSString *const SLKKeyboardWillShowNotification;
+UIKIT_EXTERN NSString *const SLKKeyboardDidShowNotification;
+UIKIT_EXTERN NSString *const SLKKeyboardWillHideNotification;
+UIKIT_EXTERN NSString *const SLKKeyboardDidHideNotification;
+
+/**
+ This feature doesn't work on iOS 9 due to no legit alternatives to detect the keyboard view.
+ Open Radar: http://openradar.appspot.com/radar?id=5021485877952512
+ */
+UIKIT_EXTERN NSString *const SLKTextInputbarDidMoveNotification;
+
+typedef NS_ENUM(NSUInteger, SLKKeyboardStatus) {
+    SLKKeyboardStatusDidHide,
+    SLKKeyboardStatusWillShow,
+    SLKKeyboardStatusDidShow,
+    SLKKeyboardStatusWillHide
+};
+
+/** @name A drop-in UIViewController subclass with a growing text input view and other useful messaging features. */
+NS_CLASS_AVAILABLE_IOS(7_0) @interface SLKTextViewController : UIViewController <SLKTextViewDelegate, UITableViewDelegate, UITableViewDataSource,
+                                                                                UICollectionViewDelegate, UICollectionViewDataSource,
+                                                                                UIGestureRecognizerDelegate, UIAlertViewDelegate>
+
+/** The main table view managed by the controller object. Created by default initializing with -init or initWithNibName:bundle: */
+@property (nonatomic, readonly) UITableView *tableView;
+
+/** The main collection view managed by the controller object. Not nil if the controller is initialised with -initWithCollectionViewLayout: */
+@property (nonatomic, readonly) UICollectionView *collectionView;
+
+/** The main scroll view managed by the controller object. Not nil if the controller is initialised with -initWithScrollView: */
+@property (nonatomic, readonly) UIScrollView *scrollView;
+
+/** The bottom toolbar containing a text view and buttons. */
+@property (nonatomic, readonly) SLKTextInputbar *textInputbar;
+
+/** The default typing indicator used to display user names horizontally. */
+@property (nonatomic, readonly) SLKTypingIndicatorView *typingIndicatorView;
+
+/**
+ The custom typing indicator view. Default is kind of SLKTypingIndicatorView.
+ To customize the typing indicator view, you will need to call -registerClassForTypingIndicatorView: nside of any initialization method.
+ To interact with it directly, you will need to cast the return value of -typingIndicatorProxyView to the appropriate type.
+ */
+@property (nonatomic, readonly) UIView <SLKTypingIndicatorProtocol> *typingIndicatorProxyView;
+
+/** A single tap gesture used to dismiss the keyboard. SLKTextViewController is its delegate. */
+@property (nonatomic, readonly) UIGestureRecognizer *singleTapGesture;
+
+/** A vertical pan gesture used for bringing the keyboard from the bottom. SLKTextViewController is its delegate. */
+@property (nonatomic, readonly) UIPanGestureRecognizer *verticalPanGesture;
+
+/** YES if control's animation should have bouncy effects. Default is YES. */
+@property (nonatomic, assign) BOOL bounces;
+
+/** YES if text view's content can be cleaned with a shake gesture. Default is NO. */
+@property (nonatomic, assign) BOOL shakeToClearEnabled;
+
+/**
+ YES if keyboard can be dismissed gradually with a vertical panning gesture. Default is YES.
+ 
+ This feature doesn't work on iOS 9 due to no legit alternatives to detect the keyboard view.
+ Open Radar: http://openradar.appspot.com/radar?id=5021485877952512
+ */
+@property (nonatomic, assign, getter = isKeyboardPanningEnabled) BOOL keyboardPanningEnabled;
+
+/** YES if an external keyboard has been detected (this value updates only when the text view becomes first responder). */
+@property (nonatomic, readonly, getter=isExternalKeyboardDetected) BOOL externalKeyboardDetected;
+
+/** YES if the keyboard has been detected as undocked or split (iPad Only). */
+@property (nonatomic, readonly, getter=isKeyboardUndocked) BOOL keyboardUndocked;
+
+/** YES if after right button press, the text view is cleared out. Default is YES. */
+@property (nonatomic, assign) BOOL shouldClearTextAtRightButtonPress;
+
+/** YES if the scrollView should scroll to bottom when the keyboard is shown. Default is NO.*/
+@property (nonatomic, assign) BOOL shouldScrollToBottomAfterKeyboardShows;
+
+/**
+ YES if the main table view is inverted. Default is YES.
+ This allows the table view to start from the bottom like any typical messaging interface.
+ If inverted, you must assign the same transform property to your cells to match the orientation (ie: cell.transform = tableView.transform;)
+ Inverting the table view will enable some great features such as content offset corrections automatically when resizing the text input and/or showing autocompletion.
+ */
+@property (nonatomic, assign, getter = isInverted) BOOL inverted;
+
+/** YES if the view controller is presented inside of a popover controller. If YES, the keyboard won't move the text input bar and tapping on the tableView/collectionView will not cause the keyboard to be dismissed. This property is compatible only with iPad. */
+@property (nonatomic, assign, getter = isPresentedInPopover) BOOL presentedInPopover;
+
+/** Convenience accessors (accessed through the text input bar) */
+@property (nonatomic, readonly) SLKTextView *textView;
+@property (nonatomic, readonly) UIButton *leftButton;
+@property (nonatomic, readonly) UIButton *rightButton;
+
+
+#pragma mark - Initialization
+///------------------------------------------------
+/// @name Initialization
+///------------------------------------------------
+
+/**
+ Initializes a text view controller to manage a table view of a given style.
+ If you use the standard -init method, a table view with plain style will be created.
+ 
+ @param style A constant that specifies the style of main table view that the controller object is to manage (UITableViewStylePlain or UITableViewStyleGrouped).
+ @return An initialized SLKTextViewController object or nil if the object could not be created.
+ */
+- (instancetype)initWithTableViewStyle:(UITableViewStyle)style SLK_DESIGNATED_INITIALIZER;
+
+/**
+ Initializes a collection view controller and configures the collection view with the provided layout.
+ If you use the standard -init method, a table view with plain style will be created.
+ 
+ @param layout The layout object to associate with the collection view. The layout controls how the collection view presents its cells and supplementary views.
+ @return An initialized SLKTextViewController object or nil if the object could not be created.
+ */
+- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout SLK_DESIGNATED_INITIALIZER;
+
+/**
+ Initializes a text view controller to manage an arbitraty scroll view. The caller is responsible for configuration of the scroll view, including wiring the delegate.
+ 
+ @param a UISCrollView to be used as the main content area.
+ @return An initialized SLKTextViewController object or nil if the object could not be created.
+ */
+- (instancetype)initWithScrollView:(UIScrollView *)scrollView SLK_DESIGNATED_INITIALIZER;
+
+/**
+ Initializes either a table or collection view controller.
+ You must override either +tableViewStyleForCoder: or +collectionViewLayoutForCoder: to define witch view to be layed out.
+ 
+ @param decoder An unarchiver object.
+ @return An initialized SLKTextViewController object or nil if the object could not be created.
+ */
+- (instancetype)initWithCoder:(NSCoder *)decoder SLK_DESIGNATED_INITIALIZER;
+
+/**
+ Returns the tableView style to be configured when using Interface Builder. Default is UITableViewStylePlain.
+ You must override this method if you want to configure a tableView.
+ 
+ @param decoder An unarchiver object.
+ @return The tableView style to be used in the new instantiated tableView.
+ */
++ (UITableViewStyle)tableViewStyleForCoder:(NSCoder *)decoder;
+
+/**
+ Returns the tableView style to be configured when using Interface Builder. Default is nil.
+ You must override this method if you want to configure a collectionView.
+ 
+ @param decoder An unarchiver object.
+ @return The collectionView style to be used in the new instantiated collectionView.
+ */
++ (UICollectionViewLayout *)collectionViewLayoutForCoder:(NSCoder *)decoder;
+
+
+#pragma mark - Keyboard Handling
+///------------------------------------------------
+/// @name Keyboard Handling
+///------------------------------------------------
+
+/**
+ Presents the keyboard, if not already, animated.
+ You can override this method to perform additional tasks associated with presenting the keyboard.
+ You SHOULD call super to inherit some conditionals.
+
+ @param animated YES if the keyboard should show using an animation.
+ */
+- (void)presentKeyboard:(BOOL)animated;
+
+/**
+ Dimisses the keyboard, if not already, animated.
+ You can override this method to perform additional tasks associated with dismissing the keyboard.
+ You SHOULD call super to inherit some conditionals.
+ 
+ @param animated YES if the keyboard should be dismissed using an animation.
+ */
+- (void)dismissKeyboard:(BOOL)animated;
+
+/**
+ Verifies if the text input bar should still move up/down even if it is not first responder. Default is NO.
+ You can override this method to perform additional tasks associated with presenting the view.
+ You don't need call super since this method doesn't do anything.
+ 
+ @param responder The current first responder object.
+ @return YES so the text input bar still move up/down.
+ */
+- (BOOL)forceTextInputbarAdjustmentForResponder:(UIResponder *)responder;
+
+/**
+ Verifies if the text input bar should still move up/down when it is first responder. Default is NO.
+ This is very useful when presenting the view controller in a custom modal presentation, when there keyboard events are being handled externally to reframe the presented view.
+ You SHOULD call super to inherit some conditionals.
+ */
+- (BOOL)ignoreTextInputbarAdjustment NS_REQUIRES_SUPER;
+
+/**
+ Notifies the view controller that the keyboard changed status.
+ You can override this method to perform additional tasks associated with presenting the view.
+ You don't need call super since this method doesn't do anything.
+ 
+ @param status The new keyboard status.
+ */
+- (void)didChangeKeyboardStatus:(SLKKeyboardStatus)status;
+
+
+#pragma mark - Interaction Notifications
+///------------------------------------------------
+/// @name Interaction Notifications
+///------------------------------------------------
+
+/**
+ Notifies the view controller that the text will update.
+ You can override this method to perform additional tasks associated with text changes.
+ You MUST call super at some point in your implementation.
+ */
+- (void)textWillUpdate NS_REQUIRES_SUPER;
+
+/**
+ Notifies the view controller that the text did update.
+ You can override this method to perform additional tasks associated with text changes.
+ You MUST call super at some point in your implementation.
+ 
+ @param If YES, the text input bar will be resized using an animation.
+ */
+- (void)textDidUpdate:(BOOL)animated NS_REQUIRES_SUPER;
+
+/**
+ Notifies the view controller that the text selection did change.
+ Use this method a replacement of UITextViewDelegate's -textViewDidChangeSelection: which is not reliable enough when using third-party keyboards (they don't forward events properly sometimes).
+ 
+ You can override this method to perform additional tasks associated with text changes.
+ You MUST call super at some point in your implementation.
+ */
+- (void)textSelectionDidChange NS_REQUIRES_SUPER;
+
+/**
+ Notifies the view controller when the left button's action has been triggered, manually.
+ You can override this method to perform additional tasks associated with the left button.
+ You don't need call super since this method doesn't do anything.
+ 
+ @param sender The object calling this method.
+ */
+- (void)didPressLeftButton:(id)sender;
+
+/**
+ Notifies the view controller when the right button's action has been triggered, manually or by using the keyboard return key.
+ You can override this method to perform additional tasks associated with the right button.
+ You MUST call super at some point in your implementation.
+ 
+ @param sender The object calling this method.
+ */
+- (void)didPressRightButton:(id)sender NS_REQUIRES_SUPER;
+
+/**
+ Verifies if the right button can be pressed. If NO, the button is disabled.
+ You can override this method to perform additional tasks. You SHOULD call super to inherit some conditionals.
+ 
+ @return YES if the right button can be pressed.
+ */
+- (BOOL)canPressRightButton;
+
+/**
+ Notifies the view controller when the user has pasted a supported media content (images and/or videos).
+ You can override this method to perform additional tasks associated with image/video pasting. You don't need to call super since this method doesn't do anything.
+ Only supported pastable medias configured in SLKTextView will be forwarded (take a look at SLKPastableMediaType).
+ 
+ @para userInfo The payload containing the media data, content and media types.
+ */
+- (void)didPasteMediaContent:(NSDictionary *)userInfo;
+
+/**
+ Verifies that the typing indicator view should be shown. Default is YES, if meeting some requierements.
+ You can override this method to perform additional tasks.
+ You SHOULD call super to inherit some conditionals.
+ 
+ @return YES if the typing indicator view should be presented.
+ */
+- (BOOL)canShowTypingIndicator;
+
+/**
+ Notifies the view controller when the user has shaked the device for undoing text typing.
+ You can override this method to perform additional tasks associated with the shake gesture.
+ Calling super will prompt a system alert view with undo option. This will not be called if 'undoShakingEnabled' is set to NO and/or if the text view's content is empty.
+ */
+- (void)willRequestUndo;
+
+/**
+ Notifies the view controller when the user has pressed the Return key (↵) with an external keyboard.
+ You can override this method to perform additional tasks.
+ You MUST call super at some point in your implementation.
+ */
+- (void)didPressReturnKey:(id)sender NS_REQUIRES_SUPER;
+
+/**
+ Notifies the view controller when the user has pressed the Escape key (Esc) with an external keyboard.
+ You can override this method to perform additional tasks.
+ You MUST call super at some point in your implementation.
+ */
+- (void)didPressEscapeKey:(id)sender NS_REQUIRES_SUPER;
+
+/**
+ Notifies the view controller when the user has pressed the arrow key with an external keyboard.
+ You can override this method to perform additional tasks.
+ You MUST call super at some point in your implementation.
+ */
+- (void)didPressArrowKey:(id)sender NS_REQUIRES_SUPER;
+
+
+#pragma mark - Text Input Bar Adjustment
+///------------------------------------------------
+/// @name Text Input Bar Adjustment
+///------------------------------------------------
+
+/** YES if the text inputbar is hidden. Default is NO. */
+@property (nonatomic, getter=isTextInputbarHidden) BOOL textInputbarHidden;
+
+/**
+ Changes the visibility of the text input bar.
+ Calling this method with the animated parameter set to NO is equivalent to setting the value of the toolbarHidden property directly.
+ 
+ @param hidden Specify YES to hide the toolbar or NO to show it.
+ @param animated Specify YES if you want the toolbar to be animated on or off the screen.
+ */
+- (void)setTextInputbarHidden:(BOOL)hidden animated:(BOOL)animated;
+
+
+#pragma mark - Text Edition
+///------------------------------------------------
+/// @name Text Edition
+///------------------------------------------------
+
+/** YES if the text editing mode is active. */
+@property (nonatomic, readonly, getter = isEditing) BOOL editing;
+
+/**
+ Re-uses the text layout for edition, displaying an accessory view on top of the text input bar with options (cancel & save).
+ You can override this method to perform additional tasks
+ You MUST call super at some point in your implementation.
+ 
+ @param text The string text to edit.
+ */
+- (void)editText:(NSString *)text NS_REQUIRES_SUPER;
+
+/**
+ Notifies the view controller when the editing bar's right button's action has been triggered, manually or by using the external keyboard's Return key.
+ You can override this method to perform additional tasks associated with accepting changes.
+ You MUST call super at some point in your implementation.
+ 
+ @param sender The object calling this method.
+ */
+- (void)didCommitTextEditing:(id)sender NS_REQUIRES_SUPER;
+
+/**
+ Notifies the view controller when the editing bar's right button's action has been triggered, manually or by using the external keyboard's Esc key.
+ You can override this method to perform additional tasks associated with accepting changes.
+ You MUST call super at some point in your implementation.
+ 
+ @param sender The object calling this method.
+ */
+- (void)didCancelTextEditing:(id)sender NS_REQUIRES_SUPER;
+
+
+#pragma mark - Text Auto-Completion
+///------------------------------------------------
+/// @name Text Auto-Completion
+///------------------------------------------------
+
+/** The table view used to display autocompletion results. */
+@property (nonatomic, readonly) UITableView *autoCompletionView;
+
+/** YES if the autocompletion mode is active. */
+@property (nonatomic, readonly, getter = isAutoCompleting) BOOL autoCompleting;
+
+/** The recently found prefix symbol used as prefix for autocompletion mode. */
+@property (nonatomic, readonly, copy) NSString *foundPrefix;
+
+/** The range of the found prefix in the text view content. */
+@property (nonatomic, readonly) NSRange foundPrefixRange;
+
+/** The recently found word at the text view's caret position. */
+@property (nonatomic, readonly, copy) NSString *foundWord;
+
+/** An array containing all the registered prefix strings for autocompletion. */
+@property (nonatomic, readonly, copy) NSArray *registeredPrefixes;
+
+/**
+ Registers any string prefix for autocompletion detection, useful for user mentions and/or hashtags autocompletion.
+ The prefix must be valid string (i.e: '@', '#', '\', and so on). This also checks if no repeated prefix are inserted.
+ Prefixes can be of any length.
+ 
+ @param prefixes An array of prefix strings.
+ */
+- (void)registerPrefixesForAutoCompletion:(NSArray *)prefixes;
+
+/**
+ Notifies the view controller either the autocompletion prefix or word have changed.
+ Use this method to modify your data source or fetch data asynchronously from an HTTP resource.
+ Once your data source is ready, make sure to call -showAutoCompletionView: to display the view accordingly.
+ You don't need call super since this method doesn't do anything.
+
+ @param prefix The detected prefix.
+ @param word The derected word.
+ */
+- (void)didChangeAutoCompletionPrefix:(NSString *)prefix andWord:(NSString *)word;
+
+/**
+ Use this method to programatically show/hide the autocompletion view.
+ Right before the view is shown, -reloadData is called. So avoid calling it manually.
+ 
+ @param show YES if the autocompletion view should be shown.
+ */
+- (void)showAutoCompletionView:(BOOL)show;
+
+/**
+ Verifies that the autocompletion view should be shown. Default is NO.
+ To enabled autocompletion, you MUST override this method to perform additional tasks, before the autocompletion view is shown (i.e. populating the data source).
+ 
+ @return YES if the autocompletion view should be shown.
+ */
+- (BOOL)canShowAutoCompletion DEPRECATED_MSG_ATTRIBUTE("Override -didChangeAutoCompletionPrefix:andWord: instead");
+
+/**
+ Returns a custom height for the autocompletion view. Default is 0.0.
+ You can override this method to return a custom height.
+ 
+ @return The autocompletion view's height.
+ */
+- (CGFloat)heightForAutoCompletionView;
+
+/**
+ Returns the maximum height for the autocompletion view. Default is 140 pts.
+ You can override this method to return a custom max height.
+ 
+ @return The autocompletion view's max height.
+ */
+- (CGFloat)maximumHeightForAutoCompletionView;
+
+/**
+ Cancels and hides the autocompletion view, animated.
+ */
+- (void)cancelAutoCompletion;
+
+/**
+ Accepts the autocompletion, replacing the detected word with a new string, keeping the prefix.
+ This method is a convinience of -acceptAutoCompletionWithString:keepPrefix:
+ 
+ @param string The string to be used for replacing autocompletion placeholders.
+ */
+- (void)acceptAutoCompletionWithString:(NSString *)string;
+
+/**
+ Accepts the autocompletion, replacing the detected word with a new string, and optionally replacing the prefix too.
+ 
+ @param string The string to be used for replacing autocompletion placeholders.
+ @param keepPrefix YES if the prefix shouldn't be overidden.
+ */
+- (void)acceptAutoCompletionWithString:(NSString *)string keepPrefix:(BOOL)keepPrefix;
+
+
+#pragma mark - Text Caching
+///------------------------------------------------
+/// @name Text Caching
+///------------------------------------------------
+
+/**
+ Returns the key to be associated with a given text to be cached. Default is nil.
+ To enable text caching, you must override this method to return valid key.
+ The text view will be populated automatically when the view controller is configured.
+ You don't need to call super since this method doesn't do anything.
+ 
+ @return The string key for which to enable text caching.
+ */
+- (NSString *)keyForTextCaching;
+
+/**
+ Removes the current's vien controller cached text.
+ To enable this, you must return a valid key string in -keyForTextCaching.
+ */
+- (void)clearCachedText;
+
+/**
+ Removes all the cached text from disk.
+ */
++ (void)clearAllCachedText;
+
+
+#pragma mark - Customization
+///------------------------------------------------
+/// @name Customization
+///------------------------------------------------
+
+/**
+ Registers a class for customizing the behavior and appearance of the text view.
+ You need to call this method inside of any initialization method.
+ 
+ @param aClass A SLKTextView subclass.
+ */
+- (void)registerClassForTextView:(Class)aClass;
+
+/**
+ Registers a class for customizing the behavior and appearance of the typing indicator view.
+ You need to call this method inside of any initialization method.
+ Make sure to conform to SLKTypingIndicatorProtocol and implement the required methods.
+ 
+ @param aClass A UIView subclass conforming to the SLKTypingIndicatorProtocol.
+ */
+- (void)registerClassForTypingIndicatorView:(Class)aClass;
+
+
+#pragma mark - Delegate Methods Requiring Super
+///------------------------------------------------
+/// @name Delegate Methods Requiring Super
+///------------------------------------------------
+
+/** UITextViewDelegate */
+- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text NS_REQUIRES_SUPER;
+
+/** SLKTextViewDelegate */
+- (BOOL)textView:(SLKTextView *)textView shouldInsertSuffixForFormattingWithSymbol:(NSString *)symbol prefixRange:(NSRange)prefixRange NS_REQUIRES_SUPER;
+
+/** UIScrollViewDelegate */
+- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView NS_REQUIRES_SUPER;
+- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate NS_REQUIRES_SUPER;
+- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView NS_REQUIRES_SUPER;
+- (void)scrollViewDidScroll:(UIScrollView *)scrollView NS_REQUIRES_SUPER;
+
+/** UIGestureRecognizerDelegate */
+- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer NS_REQUIRES_SUPER;
+
+/** UIAlertViewDelegate */
+#ifndef __IPHONE_8_0
+- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex NS_REQUIRES_SUPER;
+#endif
+
+#pragma mark - Life Cycle Methods Requiring Super
+///------------------------------------------------
+/// @name Life Cycle Methods Requiring Super
+///------------------------------------------------
+
+/**
+ Configures view hierarchy and layout constraints. If you override these methods, make sure to call super.
+ */
+- (void)loadView NS_REQUIRES_SUPER;
+- (void)viewDidLoad NS_REQUIRES_SUPER;
+- (void)viewWillAppear:(BOOL)animated NS_REQUIRES_SUPER;
+- (void)viewDidAppear:(BOOL)animated NS_REQUIRES_SUPER;
+- (void)viewWillDisappear:(BOOL)animated NS_REQUIRES_SUPER;
+- (void)viewDidDisappear:(BOOL)animated NS_REQUIRES_SUPER;
+- (void)viewWillLayoutSubviews NS_REQUIRES_SUPER;
+- (void)viewDidLayoutSubviews NS_REQUIRES_SUPER;
+
+@end