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