You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@usergrid.apache.org by sn...@apache.org on 2016/02/16 14:15:41 UTC

[49/75] [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/README.md
----------------------------------------------------------------------
diff --git a/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/README.md b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/README.md
new file mode 100644
index 0000000..9a31355
--- /dev/null
+++ b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/README.md
@@ -0,0 +1,412 @@
+#SlackTextViewController
+
+[![Build Status](https://img.shields.io/travis/slackhq/SlackTextViewController.svg?style=flat-square)](https://travis-ci.org/slackhq/SlackTextViewController)
+[![Coverage Status](https://img.shields.io/coveralls/slackhq/SlackTextViewController/master.svg?style=flat-square)](https://coveralls.io/r/slackhq/SlackTextViewController)
+
+[![Pod Version](https://img.shields.io/cocoapods/v/SlackTextViewController.svg?style=flat-square)](http://cocoadocs.org/docsets/SlackTextViewController/1.8/)
+[![Carthage compatible](https://img.shields.io/badge/carthage-compatible-F5B369.svg?style=flat-square)](https://github.com/Carthage/Carthage)
+[![License](https://img.shields.io/badge/license-apache%202.0-blue.svg?style=flat-square)](http://opensource.org/licenses/Apache2.0)
+
+
+A drop-in UIViewController subclass with a growing text input view and other useful messaging features. Meant to be a replacement for UITableViewController & UICollectionViewController.
+
+![Demo Gif](Screenshots/slacktextviewcontroller_demo.gif)
+
+This library is used in Slack's iOS app. It was built to fit our needs, but is flexible enough to be reused by others wanting to build great messaging apps for iOS.
+
+## Feature List
+
+### Core
+- Works out of the box with [UITableView or UICollectionView or UIScrollView](https://github.com/slackhq/SlackTextViewController/tree/swift-example#subclassing)
+- [Growing Text View](https://github.com/slackhq/SlackTextViewController#growing-text-view), with line count limit support
+- Flexible UI built with Auto Layout
+- Customizable: provides left and right button, and toolbar outlets
+- Tap Gesture for dismissing the keyboard
+- [External keyboard](https://github.com/slackhq/SlackTextViewController#external-keyboard) commands support
+- Undo/Redo (with keyboard commands and UIMenuController)
+- Text Appending APIs
+
+### Additional
+- [Autocomplete Mode](https://github.com/slackhq/SlackTextViewController#autocompletion) by registering any prefix key (`@`, `#`, `/`)
+- [Edit Mode](https://github.com/slackhq/SlackTextViewController#edit-mode)
+- [Markdown Formatting](https://github.com/slackhq/SlackTextViewController#markdown-formatting)
+- [Typing Indicator](https://github.com/slackhq/SlackTextViewController#typing-indicator) display
+- [Shake Gesture](https://github.com/slackhq/SlackTextViewController#shake-gesture) for clearing text view
+- Multimedia Pasting (png, gif, mov, etc.)
+- [Inverted Mode](https://github.com/slackhq/SlackTextViewController#inverted-mode) for displaying cells upside-down (using CATransform) -- a necessary hack for some messaging apps. `YES` by default, so beware, your entire cells might be flipped!
+- Tap Gesture for dismissing the keyboard
+- [Panning Gesture](https://github.com/slackhq/SlackTextViewController#panning-gesture) for sliding down/up the keyboard
+- [Hideable TextInputbar](https://github.com/slackhq/SlackTextViewController#hideable-textinputbar)
+- [Dynamic Type](https://github.com/slackhq/SlackTextViewController#dynamic-type) for adjusting automatically the text input bar height based on the font size.
+- Bouncy Animations
+
+### Compatibility
+- Carthage & Cocoapods
+- Swift: [A sample project is available in a different branch] (https://github.com/slackhq/SlackTextViewController/tree/swift-example)
+- iOS 7, 8 & 9
+- iPhone & iPad
+- [Storyboard](https://github.com/slackhq/SlackTextViewController#storyboard)
+- UIPopOverController & UITabBarController
+- Container View Controller
+- Auto-Rotation
+- iPad Multitasking (iOS 9 only)
+- Localization
+
+## Installation
+
+###### With [Cocoa Pods](https://cocoapods.org/):
+```ruby
+pod 'SlackTextViewController'
+```
+
+###### With [Carthage](https://github.com/Carthage/Carthage):
+```
+github "slackhq/SlackTextViewController"
+```
+
+###### Manually:
+There are two ways to do this:
+- Copy and drag the `Source/` folder to your project.
+- or compile the project located in `Builder/SlackTextViewController.xcodeproj` to create a `SlackTextViewController.framework` package. You could also [link the library into your project](https://developer.apple.com/library/ios/recipes/xcode_help-project_editor/Articles/AddingaLibrarytoaTarget.html#//apple_ref/doc/uid/TP40010155-CH17-SW1).
+
+
+##How to use
+
+###Subclassing
+`SLKTextViewController` is meant to be subclassed, like you would normally do with UITableViewController or UICollectionViewController or UIScrollView. This pattern is a convenient way of extending UIViewController. SlackTextViewController manages a lot behind the scenes while still providing the ability to add custom behaviours. You may override methods, and decide to call super and  perform additional logic, or not to call super and override default logic.
+
+Start by creating a new subclass of `SLKTextViewController`.
+
+In the init overriding method, if you wish to use the `UITableView` version, call:
+```objc
+[super initWithTableViewStyle:UITableViewStylePlain]
+```
+
+or the `UICollectionView` version:
+```objc
+[super initWithCollectionViewLayout:[UICollectionViewFlowLayout new]]
+```
+
+or the `UIScrollView` version:
+```objc
+[super initWithScrollView:self.myStrongScrollView]
+```
+
+Protocols like `UITableViewDelegate` and `UITableViewDataSource` are already setup for you. You will be able to call whatever delegate and data source methods you need for customising your control.
+
+Calling `[super init]` will call `[super initWithTableViewStyle:UITableViewStylePlain]` by default.
+
+###Storyboard
+
+When using SlackTextViewController with storyboards, instead of overriding the traditional `initWithCoder:` you will need to override any of the two custom methods below. This approach helps preserving the exact same features from the programatic approach, but also limits the edition of the nib of your `SLKTextViewController` subclass since it doesn't layout subviews from the nib (subviews are still initialized and layed out programatically).
+
+if you wish to use the `UITableView` version, call:
+```objc
++ (UITableViewStyle)tableViewStyleForCoder:(NSCoder *)decoder
+{
+    return UITableViewStylePlain;
+}
+```
+
+or the `UICollectionView` version:
+```objc
++ (UICollectionViewLayout *)collectionViewLayoutForCoder:(NSCoder *)decoder
+{
+    return [UICollectionViewFlowLayout new];
+}
+```
+
+###Sample Project
+
+Check out the sample project,  everything is demo'd there.
+There are 2 main examples (different targets) for testing the programatic and storyboard approaches. Most of the features are implemented for you to quickly start using them.
+
+A CollectionView example, using Swift, is in progress on the `swift-example` branch. The idea with this project is to build a custom collection view layout allowing to display cells from the bottom (currently working but needs serious tweaks to make it perfect).
+Feel free to contribute!
+
+
+##Features
+
+
+###Growing Text View
+
+![Growing](Screenshots/screenshot_auto-expanding.png)
+
+The text view expands automatically when a new line is required, until it reaches its `maxNumberOfLines`value. You may change this property's value in the textView.
+
+By default, the number of lines is set to best fit each device dimensions:
+- iPhone 4      (<=480pts): 4 lines
+- iPhone 5/6    (>=568pts): 6 lines
+- iPad          (>=768pts): 8 lines
+
+On iPhone devices, in landscape orientation, the maximum number of lines is changed to fit the available space.
+
+
+###Inverted Mode
+
+Some layouts may require to show from bottom to top and new subviews are inserted from the bottom. To enable this, you must use the `inverted` flag property (default is YES). This will actually invert the entire ScrollView object. Make sure to apply the same transformation to every subview. In the case of UITableView, the best place for adjusting the transformation is in its data source methods like:
+
+````objc
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:chatCellIdentifier];
+    cell.transform = self.tableView.transform;
+}
+````
+
+
+###Autocompletion
+
+We use autocompletion for many things: names, channels, emoji, and more.
+
+![Autocompletion](Screenshots/screenshot_auto-completion.png)
+
+To set up autocompletion in your app, follow these simple steps:
+
+#### 1. Registration
+You must first register all the prefixes you'd like to support for autocompletion detection:
+````objc
+[self registerPrefixesForAutoCompletion:@[@"#"]];
+````
+
+#### 2. Processing
+Every time a new character is inserted in the text view, the nearest word to the caret will be processed and verified if it contains any of the registered prefixes.
+
+Once the prefix has been detected, `-didChangeAutoCompletionPrefix:andWord:` will be called. This is the perfect place to populate your data source and show/hide the autocompletion view. So you must override it in your subclass, to be able to perform additional tasks. Default returns NO.
+
+````objc
+- (void)didChangeAutoCompletionPrefix:(NSString *)prefix andWord:(NSString *)word
+{
+    self.searchResult = [[NSArray alloc] initWithArray:self.channels];
+    
+    if ([prefix isEqualToString:@"#"])
+    {
+        if (word.length > 0) {
+            self.searchResult = [self.searchResult filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"self BEGINSWITH[c] %@ AND self !=[c] %@", word, word]];
+        }
+    }
+
+    if (self.searchResult.count > 0) {
+        self.searchResult = [self.searchResult sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
+    }
+    
+    BOOL show = (self.searchResult.count > 0);
+    
+    [self showAutoCompletionView:show];
+}
+````
+
+The autocompletion view is a `UITableView` instance, so you will need to use `UITableViewDataSource` to populate its cells. You have complete freedom for customizing the cells.
+
+You don't need to call `-reloadData` yourself, since it will be invoked automatically right after calling the `-showAutoCompletionView` method.
+
+#### 3. Layout
+
+The maximum height of the autocompletion view is set to 140 pts by default. You can update this value anytime, so the view automatically adjusts based on the amount of displayed cells.
+
+````objc
+- (CGFloat)heightForAutoCompletionView
+{
+    CGFloat cellHeight = 34.0;
+    return cellHeight*self.searchResult.count;
+}
+````
+
+#### 4. Confirmation
+
+If the user selects any autocompletion view cell on `-tableView:didSelectRowAtIndexPath:`, you must call `-acceptAutoCompletionWithString:` to commit autocompletion. That method expects a string matching the selected item, that you would like to be inserted in the text view.
+
+`````objc
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
+{
+    if ([tableView isEqual:self.autoCompletionView]) {
+        
+        NSString *item = self.searchResult[indexPath.row];
+        
+        [self acceptAutoCompletionWithString:item];
+    }
+}
+````
+
+The autocompletion view will automatically be dismissed and the chosen string will be inserted in the text view, replacing the detected prefix and word.
+
+You can always call `-cancelAutoCompletion` to exit the autocompletion mode and refresh the UI.
+
+
+###Edit Mode
+
+![Edit Mode](Screenshots/screenshot_edit-mode.png)
+
+To enable edit mode, you simply need to call `[self editText:@"hello"];`, and the text input will switch to edit mode, removing both left and right buttons, extending the input bar a bit higher with "Accept" and "Cancel" buttons. Both of this buttons are accessible in the `SLKTextInputbar` instance for customisation.
+
+To capture the "Accept" or "Cancel" events, you must override the following methods.
+
+````objc
+- (void)didCommitTextEditing:(id)sender
+{
+    NSString *message = [self.textView.text copy];
+    
+    [self.messages removeObjectAtIndex:0];
+    [self.messages insertObject:message atIndex:0];
+    [self.tableView reloadData];
+    
+    [super didCommitTextEditing:sender];
+}
+
+- (void)didCancelTextEditing:(id)sender
+{
+    [super didCancelTextEditing:sender];
+}
+````
+
+Notice that you must call `super` at some point, so the text input exits the edit mode, re-adjusting the layout and clearing the text view.
+Use the `editing` property to know if the editing mode is on.
+
+
+###Markdown Formatting
+
+![Markdown Formatting](Screenshots/screenshot_markdown-formatting.png)
+
+You can register markdown formatting symbols so they can easily be used to wrap a text selection, with the help of the  native contextual menu, aka `UIMenuController`. This feature doesn't take care of the rendering of the markdown: it's sole purpose is to ease the formatting tools to the user.
+Optionally, you can enable `autoCompleteFormatting` so any pending markdown closure symbol can be added automatically after double tapping on the keyboard spacebar, just like the native gesture to add a sentence period. The sentence period is still being added as a fallback.
+
+![Markdown Formatting Animated](Screenshots/screenshot_markdown-formatting.gif)
+
+
+#### 1. Registration
+
+You must first register the formatting symbol and assign a title string to be used in the menu controller item.
+````objc
+[self.textView registerMarkdownFormattingSymbol:@"*" withTitle:@"Bold"];
+````
+
+#### 2. Customisation
+
+Futher more, you can customise some of the behavior for special formatting cases, using the `UITextViewDelegate` methods.
+In the following example, we don't present the Quote formatting in the contextual menu when the text selection isn't a paragraph.
+
+````objc
+- (BOOL)textView:(SLKTextView *)textView shouldOfferFormattingForSymbol:(NSString *)symbol
+{
+    if ([symbol isEqualToString:@">"]) {
+        
+        NSRange selection = textView.selectedRange;
+        
+        // The Quote formatting only applies new paragraphs
+        if (selection.location == 0 && selection.length > 0) {
+            return YES;
+        }
+        
+        // or older paragraphs too
+        NSString *prevString = [textView.text substringWithRange:NSMakeRange(selection.location-1, 1)];
+        
+        if ([[NSCharacterSet newlineCharacterSet] characterIsMember:[prevString characterAtIndex:0]]) {
+            return YES;
+        }
+
+        return NO;
+    }
+    
+    return [super textView:textView shouldOfferFormattingForSymbol:symbol];
+}
+````
+
+In this other method implementation, we don't want to allow auto-completion for the Quote formatting since it doesn't require a closure.
+````objc
+- (BOOL)textView:(SLKTextView *)textView shouldInsertSuffixForFormattingWithSymbol:(NSString *)symbol prefixRange:(NSRange)prefixRange
+{
+    if ([symbol isEqualToString:@">"]) {
+        return NO;
+    }
+    
+    return [super textView:textView shouldInsertSuffixForFormattingWithSymbol:symbol prefixRange:prefixRange];
+}
+````
+
+
+###Typing Indicator
+
+![Typing Indicator](Screenshots/screenshot_typing-indicator.png)
+
+Optionally, you can enable a simple typing indicator, which will be displayed right above the text input. It shows the name of the people that are typing, and if more than 2, it will display "Several are typing" message.
+
+To enable the typing indicator, just call `[self.typingIndicatorView insertUsername:@"John"];` and the view will automatically be animated on top of the text input. After a default interval of 6 seconds, if the same name hasn't been assigned once more, the view will be dismissed with animation.
+
+You can remove names from the list by calling `[self.typingIndicatorView removeUsername:@"John"];`
+
+You can also dismiss it by calling `[self.typingIndicatorView dismissIndicator];`
+
+
+###Panning Gesture
+
+Dismissing the keyboard with a panning gesture is enabled by default with the `keyboardPanningEnabled` property. You can always disable it if you'd like. You can extend the `verticalPanGesture` behaviors with the `UIGestureRecognizerDelegate` methods.
+
+
+###Hideable TextInputbar
+
+Sometimes you may need to hide the text input bar.
+Very similar to `UINavigationViewController`'s API, simply do:
+```objc
+[self setTextInputbarHidden:YES animated:YES];
+```
+
+
+###Shake Gesture
+
+![Shake Gesture](Screenshots/screenshot_shake-undo.png)
+
+A shake gesture to clear text is enabled by default with the `undoShakingEnabled` property.
+
+You can optionally override `-willRequestUndo`, to implement your UI to ask the users if he would like to clean the text view's text. If there is not text entered, the method will not be called.
+
+If you don't override `-willRequestUndo` and `undoShakingEnabled` is set to `YES`, a system UIAlertView will 
+
+
+###External Keyboard
+
+There a few basic key commands enabled by default:
+- cmd + z -> undo
+- shift + cmd + z -> redo
+- return key -> calls `-didPressRightButton:`, or `-didCommitTextEditing:` if in edit mode
+- shift/cmd + return key -> line break
+- escape key -> exits edit mode, or auto-completion mode, or dismisses the keyboard
+- up & down arrows -> vertical cursor movement
+
+To add additional key commands, simply override `-keyCommands` and append `super`'s array.
+
+`````objc
+- (NSArray *)keyCommands
+{
+    NSMutableArray *commands = [NSMutableArray arrayWithArray:[super keyCommands]];
+    
+    // Edit last message
+    [commands addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow
+                                           modifierFlags:0
+                                                   action:@selector(editLastMessage:)]];
+    
+    return commands;
+}
+````
+
+There are also a set of useful flags for keyboard special detections such as `isExternalKeyboardDetected`, `isKeyboardUndocked`, `typingSuggestionEnabled` and `isTrackpadEnabled` (iOS 9 only)
+
+
+###Dynamic Type
+
+Dynamic Type is enabled by default with the `dynamicTypeEnabled` property. You can always disable it if you'd like, but the text input bar would still adjust to best fit the font size of the text view.
+
+![Dynamic-Type](Screenshots/screenshot_dynamic-type.png)
+
+
+###XCode Templates
+
+![Template](Screenshots/screenshot_template.png)
+
+We have prepared a set of useful XCode templates so you can quickly start using SlackTextViewController.
+
+To install them, open up your terminal and type:
+```bash
+sh ./SlackTextViewController/File\ Templates/install.sh
+```
+
+These templates are also available in [Alcatraz](https://github.com/alcatraz/Alcatraz).

http://git-wip-us.apache.org/repos/asf/usergrid/blob/7442c881/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKInputAccessoryView.h
----------------------------------------------------------------------
diff --git a/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKInputAccessoryView.h b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKInputAccessoryView.h
new file mode 100644
index 0000000..6e1eb3e
--- /dev/null
+++ b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKInputAccessoryView.h
@@ -0,0 +1,24 @@
+//
+//   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 <UIKit/UIKit.h>
+
+@interface SLKInputAccessoryView : UIView
+
+/* The system keyboard view used as reference. */
+@property (nonatomic, weak, readonly) UIView *keyboardViewProxy;
+
+@end
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/usergrid/blob/7442c881/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKInputAccessoryView.m
----------------------------------------------------------------------
diff --git a/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKInputAccessoryView.m b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKInputAccessoryView.m
new file mode 100644
index 0000000..fd8c3c7
--- /dev/null
+++ b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKInputAccessoryView.m
@@ -0,0 +1,33 @@
+//
+//   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 "SLKInputAccessoryView.h"
+
+#import "SLKUIConstants.h"
+
+@implementation SLKInputAccessoryView
+
+
+#pragma mark - Super Overrides
+
+- (void)willMoveToSuperview:(UIView *)newSuperview
+{
+    if (!SLK_IS_IOS9_AND_HIGHER) {
+        _keyboardViewProxy = newSuperview;
+    }
+}
+
+@end
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/usergrid/blob/7442c881/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextInputbar.h
----------------------------------------------------------------------
diff --git a/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextInputbar.h b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextInputbar.h
new file mode 100644
index 0000000..68f8f20
--- /dev/null
+++ b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextInputbar.h
@@ -0,0 +1,153 @@
+//
+//   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 <UIKit/UIKit.h>
+
+@class SLKTextViewController;
+@class SLKTextView;
+@class SLKInputAccessoryView;
+
+typedef NS_ENUM(NSUInteger, SLKCounterStyle) {
+    SLKCounterStyleNone,
+    SLKCounterStyleSplit,
+    SLKCounterStyleCountdown,
+    SLKCounterStyleCountdownReversed
+};
+
+typedef NS_ENUM(NSUInteger, SLKCounterPosition) {
+    SLKCounterPositionTop,
+    SLKCounterPositionBottom
+};
+
+/** @name A custom tool bar encapsulating messaging controls. */
+@interface SLKTextInputbar : UIToolbar
+
+/** A weak reference to the core view controller. */
+@property (nonatomic, weak) SLKTextViewController *controller;
+
+/** The centered text input view.
+ The maximum number of lines is configured by default, to best fit each devices dimensions.
+ For iPhone 4       (<=480pts): 4 lines
+ For iPhone 5 & 6   (>=568pts): 6 lines
+ For iPad           (>=768pts): 8 lines
+ */
+@property (nonatomic, strong) SLKTextView *textView;
+
+/** The custom input accessory view, used as empty achor view to detect the keyboard frame. */
+@property (nonatomic, strong) SLKInputAccessoryView *inputAccessoryView;
+
+/** The left action button action. */
+@property (nonatomic, strong) UIButton *leftButton;
+
+/** The right action button action. */
+@property (nonatomic, strong) UIButton *rightButton;
+
+/** YES if the right button should be hidden animatedly in case the text view has no text in it. Default is YES. */
+@property (nonatomic, readwrite) BOOL autoHideRightButton;
+
+/** The inner padding to use when laying out content in the view. Default is {5, 8, 5, 8}. */
+@property (nonatomic, assign) UIEdgeInsets contentInset;
+
+/** The minimum height based on the intrinsic content size's. */
+@property (nonatomic, readonly) CGFloat minimumInputbarHeight;
+
+/** The most appropriate height calculated based on the amount of lines of text and other factors. */
+@property (nonatomic, readonly) CGFloat appropriateHeight;
+
+
+#pragma mark - Initialization
+///------------------------------------------------
+/// @name Initialization
+///------------------------------------------------
+
+/**
+ Initializes a text input bar with a class to be used for the text view
+ 
+ @param textViewClass The class to be used when creating the text view. May be nil. If provided, the class must be a subclass of SLKTextView
+ @return An initialized SLKTextInputbar object or nil if the object could not be created.
+ */
+- (instancetype)initWithTextViewClass:(Class)textViewClass;
+
+
+#pragma mark - Text Editing
+///------------------------------------------------
+/// @name Text Editing
+///------------------------------------------------
+
+/** The view displayed on top if the text input bar, containing the button outlets, when editing is enabled. */
+@property (nonatomic, strong) UIView *editorContentView;
+
+/** The title label displayed in the middle of the accessoryView. */
+@property (nonatomic, strong) UILabel *editorTitle;
+
+/** The 'cancel' button displayed left in the accessoryView. */
+@property (nonatomic, strong) UIButton *editorLeftButton;
+
+/** The 'accept' button displayed right in the accessoryView. */
+@property (nonatomic, strong) UIButton *editorRightButton;
+
+/** The accessory view's maximum height. Default is 38 pts. */
+@property (nonatomic, assign) CGFloat editorContentViewHeight;
+
+/** A Boolean value indicating whether the control is in edit mode. */
+@property (nonatomic, getter = isEditing) BOOL editing;
+
+/**
+ Verifies if the text can be edited.
+ 
+ @param text The text to be edited.
+ @return YES if the text is editable.
+ */
+- (BOOL)canEditText:(NSString *)text;
+
+/**
+ Begins editing the text, by updating the 'editing' flag and the view constraints.
+ */
+- (void)beginTextEditing;
+
+/**
+ End editing the text, by updating the 'editing' flag and the view constraints.
+ */
+- (void)endTextEdition;
+
+
+#pragma mark - Text Counting
+///------------------------------------------------
+/// @name Text Counting
+///------------------------------------------------
+
+/** The label used to display the character counts. */
+@property (nonatomic, readonly) UILabel *charCountLabel;
+
+/** The maximum character count allowed. If larger than 0, a character count label will be displayed on top of the right button. Default is 0, which means limitless.*/
+@property (nonatomic, readwrite) NSUInteger maxCharCount;
+
+/** The character counter formatting. Ignored if maxCharCount is 0. Default is None. */
+@property (nonatomic, assign) SLKCounterStyle counterStyle;
+
+/** The character counter layout style. Ignored if maxCharCount is 0. Default is SLKCounterPositionTop. */
+@property (nonatomic, assign) SLKCounterPosition counterPosition;
+
+/** YES if the maxmimum character count has been exceeded. */
+@property (nonatomic, readonly) BOOL limitExceeded;
+
+/** The normal color used for character counter label. Default is lightGrayColor. */
+@property (nonatomic, strong, readwrite) UIColor *charCountLabelNormalColor;
+
+/** The color used for character counter label when it has exceeded the limit. Default is redColor. */
+@property (nonatomic, strong, readwrite) UIColor *charCountLabelWarningColor;
+
+@end

http://git-wip-us.apache.org/repos/asf/usergrid/blob/7442c881/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextInputbar.m
----------------------------------------------------------------------
diff --git a/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextInputbar.m b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextInputbar.m
new file mode 100644
index 0000000..b43cdd5
--- /dev/null
+++ b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextInputbar.m
@@ -0,0 +1,779 @@
+//
+//   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 "SLKTextInputbar.h"
+#import "SLKTextViewController.h"
+#import "SLKTextView.h"
+#import "SLKInputAccessoryView.h"
+
+#import "SLKTextView+SLKAdditions.h"
+#import "UIView+SLKAdditions.h"
+
+#import "SLKUIConstants.h"
+
+NSString * const SLKTextInputbarDidMoveNotification =   @"SLKTextInputbarDidMoveNotification";
+
+@interface SLKTextInputbar ()
+
+@property (nonatomic, strong) NSLayoutConstraint *leftButtonWC;
+@property (nonatomic, strong) NSLayoutConstraint *leftButtonHC;
+@property (nonatomic, strong) NSLayoutConstraint *leftMarginWC;
+@property (nonatomic, strong) NSLayoutConstraint *bottomMarginWC;
+@property (nonatomic, strong) NSLayoutConstraint *rightButtonWC;
+@property (nonatomic, strong) NSLayoutConstraint *rightMarginWC;
+@property (nonatomic, strong) NSLayoutConstraint *rightButtonTopMarginC;
+@property (nonatomic, strong) NSLayoutConstraint *rightButtonBottomMarginC;
+@property (nonatomic, strong) NSLayoutConstraint *editorContentViewHC;
+@property (nonatomic, strong) NSArray *charCountLabelVCs;
+
+@property (nonatomic, strong) UILabel *charCountLabel;
+
+@property (nonatomic) CGPoint previousOrigin;
+
+@property (nonatomic, strong) Class textViewClass;
+
+@end
+
+@implementation SLKTextInputbar
+
+#pragma mark - Initialization
+
+- (instancetype)initWithTextViewClass:(Class)textViewClass
+{
+    if (self = [super init]) {
+        self.textViewClass = textViewClass;
+        [self slk_commonInit];
+    }
+    return self;
+}
+
+- (id)init
+{
+    if (self = [super init]) {
+        [self slk_commonInit];
+    }
+    return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder
+{
+    if (self = [super initWithCoder:coder]) {
+        [self slk_commonInit];
+    }
+    return self;
+}
+
+- (void)slk_commonInit
+{
+    self.charCountLabelNormalColor = [UIColor lightGrayColor];
+    self.charCountLabelWarningColor = [UIColor redColor];
+    
+    self.autoHideRightButton = YES;
+    self.editorContentViewHeight = 38.0;
+    self.contentInset = UIEdgeInsetsMake(5.0, 8.0, 5.0, 8.0);
+    
+    [self addSubview:self.editorContentView];
+    [self addSubview:self.leftButton];
+    [self addSubview:self.rightButton];
+    [self addSubview:self.textView];
+    [self addSubview:self.charCountLabel];
+    
+    [self slk_setupViewConstraints];
+    [self slk_updateConstraintConstants];
+    
+    self.counterStyle = SLKCounterStyleNone;
+    self.counterPosition = SLKCounterPositionTop;
+    
+    [self slk_registerNotifications];
+    
+    [self slk_registerTo:self.layer forSelector:@selector(position)];
+    [self slk_registerTo:self.leftButton.imageView forSelector:@selector(image)];
+    [self slk_registerTo:self.rightButton.titleLabel forSelector:@selector(font)];
+}
+
+
+#pragma mark - UIView Overrides
+
+- (void)layoutIfNeeded
+{
+    if (self.constraints.count == 0 || !self.window) {
+        return;
+    }
+    
+    [self slk_updateConstraintConstants];
+    [super layoutIfNeeded];
+}
+
+- (CGSize)intrinsicContentSize
+{
+    return CGSizeMake(UIViewNoIntrinsicMetric, [self minimumInputbarHeight]);
+}
+
++ (BOOL)requiresConstraintBasedLayout
+{
+    return YES;
+}
+
+
+#pragma mark - Getters
+
+- (SLKTextView *)textView
+{
+    if (!_textView) {
+        Class class = self.textViewClass ? : [SLKTextView class];
+        
+        _textView = [[class alloc] init];
+        _textView.translatesAutoresizingMaskIntoConstraints = NO;
+        _textView.font = [UIFont systemFontOfSize:15.0];
+        _textView.maxNumberOfLines = [self slk_defaultNumberOfLines];
+
+        _textView.typingSuggestionEnabled = YES;
+        _textView.autocapitalizationType = UITextAutocapitalizationTypeSentences;
+        _textView.keyboardType = UIKeyboardTypeTwitter;
+        _textView.returnKeyType = UIReturnKeyDefault;
+        _textView.enablesReturnKeyAutomatically = YES;
+        _textView.scrollIndicatorInsets = UIEdgeInsetsMake(0.0, -1.0, 0.0, 1.0);
+        _textView.textContainerInset = UIEdgeInsetsMake(8.0, 4.0, 8.0, 0.0);
+        _textView.layer.cornerRadius = 5.0;
+        _textView.layer.borderWidth = 0.5;
+        _textView.layer.borderColor =  [UIColor colorWithRed:200.0/255.0 green:200.0/255.0 blue:205.0/255.0 alpha:1.0].CGColor;
+    }
+    return _textView;
+}
+
+- (SLKInputAccessoryView *)inputAccessoryView
+{
+    if (!_inputAccessoryView)
+    {
+        _inputAccessoryView = [[SLKInputAccessoryView alloc] initWithFrame:CGRectZero];
+        _inputAccessoryView.backgroundColor = [UIColor clearColor];
+        _inputAccessoryView.userInteractionEnabled = NO;
+    }
+    
+    return _inputAccessoryView;
+}
+
+- (UIButton *)leftButton
+{
+    if (!_leftButton) {
+        _leftButton = [UIButton buttonWithType:UIButtonTypeSystem];
+        _leftButton.translatesAutoresizingMaskIntoConstraints = NO;
+        _leftButton.titleLabel.font = [UIFont systemFontOfSize:15.0];
+    }
+    return _leftButton;
+}
+
+- (UIButton *)rightButton
+{
+    if (!_rightButton) {
+        _rightButton = [UIButton buttonWithType:UIButtonTypeSystem];
+        _rightButton.translatesAutoresizingMaskIntoConstraints = NO;
+        _rightButton.titleLabel.font = [UIFont boldSystemFontOfSize:15.0];
+        _rightButton.enabled = NO;
+        
+        NSString *title = NSLocalizedString(@"Send", nil);
+        
+        [_rightButton setTitle:title forState:UIControlStateNormal];
+    }
+    return _rightButton;
+}
+
+- (UIView *)editorContentView
+{
+    if (!_editorContentView) {
+        _editorContentView = [UIView new];
+        _editorContentView.translatesAutoresizingMaskIntoConstraints = NO;
+        _editorContentView.backgroundColor = self.backgroundColor;
+        _editorContentView.clipsToBounds = YES;
+        _editorContentView.hidden = YES;
+        
+        [_editorContentView addSubview:self.editorTitle];
+        [_editorContentView addSubview:self.editorLeftButton];
+        [_editorContentView addSubview:self.editorRightButton];
+        
+        NSDictionary *views = @{@"label": self.editorTitle,
+                                @"leftButton": self.editorLeftButton,
+                                @"rightButton": self.editorRightButton,
+                                };
+        
+        NSDictionary *metrics = @{@"left" : @(self.contentInset.left),
+                                  @"right" : @(self.contentInset.right)
+                                  };
+        
+        [_editorContentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(left)-[leftButton(60)]-(left)-[label(>=0)]-(right)-[rightButton(60)]-(<=right)-|" options:0 metrics:metrics views:views]];
+        [_editorContentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[leftButton]|" options:0 metrics:metrics views:views]];
+        [_editorContentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[rightButton]|" options:0 metrics:metrics views:views]];
+        [_editorContentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[label]|" options:0 metrics:metrics views:views]];
+    }
+    return _editorContentView;
+}
+
+- (UILabel *)editorTitle
+{
+    if (!_editorTitle) {
+        _editorTitle = [UILabel new];
+        _editorTitle.translatesAutoresizingMaskIntoConstraints = NO;
+        _editorTitle.textAlignment = NSTextAlignmentCenter;
+        _editorTitle.backgroundColor = [UIColor clearColor];
+        _editorTitle.font = [UIFont boldSystemFontOfSize:15.0];
+        
+        NSString *title = NSLocalizedString(@"Editing Message", nil);
+        
+        _editorTitle.text = title;
+    }
+    return _editorTitle;
+}
+
+- (UIButton *)editorLeftButton
+{
+    if (!_editorLeftButton) {
+        _editorLeftButton = [UIButton buttonWithType:UIButtonTypeSystem];
+        _editorLeftButton.translatesAutoresizingMaskIntoConstraints = NO;
+        _editorLeftButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
+        _editorLeftButton.titleLabel.font = [UIFont systemFontOfSize:15.0];
+        
+        NSString *title = NSLocalizedString(@"Cancel", nil);
+        
+        [_editorLeftButton setTitle:title forState:UIControlStateNormal];
+    }
+    return _editorLeftButton;
+}
+
+- (UIButton *)editorRightButton
+{
+    if (!_editorRightButton) {
+        _editorRightButton = [UIButton buttonWithType:UIButtonTypeSystem];
+        _editorRightButton.translatesAutoresizingMaskIntoConstraints = NO;
+        _editorRightButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;
+        _editorRightButton.titleLabel.font = [UIFont boldSystemFontOfSize:15.0];
+        _editorRightButton.enabled = NO;
+        
+        NSString *title = NSLocalizedString(@"Save", nil);
+        
+        [_editorRightButton setTitle:title forState:UIControlStateNormal];
+    }
+    return _editorRightButton;
+}
+
+- (UILabel *)charCountLabel
+{
+    if (!_charCountLabel) {
+        _charCountLabel = [UILabel new];
+        _charCountLabel.translatesAutoresizingMaskIntoConstraints = NO;
+        _charCountLabel.backgroundColor = [UIColor clearColor];
+        _charCountLabel.textAlignment = NSTextAlignmentRight;
+        _charCountLabel.font = [UIFont systemFontOfSize:11.0];
+        
+        _charCountLabel.hidden = NO;
+    }
+    return _charCountLabel;
+}
+
+- (CGFloat)minimumInputbarHeight
+{
+    CGFloat minimumTextViewHeight = self.textView.intrinsicContentSize.height;
+    minimumTextViewHeight += self.contentInset.top + self.contentInset.bottom;
+    
+    return minimumTextViewHeight;
+}
+
+- (CGFloat)appropriateHeight
+{
+    CGFloat height = 0.0;
+    CGFloat minimumHeight = [self minimumInputbarHeight];
+    
+    if (self.textView.numberOfLines == 1) {
+        height = minimumHeight;
+    }
+    else if (self.textView.numberOfLines < self.textView.maxNumberOfLines) {
+        height = [self slk_inputBarHeightForLines:self.textView.numberOfLines];
+    }
+    else {
+        height = [self slk_inputBarHeightForLines:self.textView.maxNumberOfLines];
+    }
+    
+    if (height < minimumHeight) {
+        height = minimumHeight;
+    }
+    
+    if (self.isEditing) {
+        height += self.editorContentViewHeight;
+    }
+    
+    return roundf(height);
+}
+
+- (CGFloat)slk_inputBarHeightForLines:(NSUInteger)numberOfLines
+{
+    CGFloat height = self.textView.intrinsicContentSize.height;
+    height -= self.textView.font.lineHeight;
+    height += roundf(self.textView.font.lineHeight*numberOfLines);
+    height += self.contentInset.top + self.contentInset.bottom;
+    
+    return height;
+}
+
+- (BOOL)limitExceeded
+{
+    NSString *text = [self.textView.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+    
+    if (self.maxCharCount > 0 && text.length > self.maxCharCount) {
+        return YES;
+    }
+    return NO;
+}
+
+- (CGFloat)slk_appropriateRightButtonWidth
+{
+    if (self.autoHideRightButton) {
+        if (self.textView.text.length == 0) {
+            return 0.0;
+        }
+    }
+
+    NSString *title = [self.rightButton titleForState:UIControlStateNormal];
+
+    CGSize rightButtonSize;
+    
+    if ([title length] == 0 && self.rightButton.imageView.image) {
+        rightButtonSize = self.rightButton.imageView.image.size;
+    }
+    else {
+        rightButtonSize = [title sizeWithAttributes:@{NSFontAttributeName: self.rightButton.titleLabel.font}];
+    }
+
+    return rightButtonSize.width + self.contentInset.right;
+}
+
+- (CGFloat)slk_appropriateRightButtonMargin
+{
+    if (self.autoHideRightButton) {
+        if (self.textView.text.length == 0) {
+            return 0.0;
+        }
+    }
+    return self.contentInset.right;
+}
+
+- (NSUInteger)slk_defaultNumberOfLines
+{
+    if (SLK_IS_IPAD) {
+        return 8;
+    }
+    if (SLK_IS_IPHONE4) {
+        return 4;
+    }
+    else {
+        return 6;
+    }
+}
+
+
+#pragma mark - Setters
+
+- (void)setBackgroundColor:(UIColor *)color
+{
+    self.barTintColor = color;
+    self.editorContentView.backgroundColor = color;
+}
+
+- (void)setAutoHideRightButton:(BOOL)hide
+{
+    if (self.autoHideRightButton == hide) {
+        return;
+    }
+    
+    _autoHideRightButton = hide;
+    
+    self.rightButtonWC.constant = [self slk_appropriateRightButtonWidth];
+    self.rightMarginWC.constant = [self slk_appropriateRightButtonMargin];
+
+    [self layoutIfNeeded];
+}
+
+- (void)setContentInset:(UIEdgeInsets)insets
+{
+    if (UIEdgeInsetsEqualToEdgeInsets(self.contentInset, insets)) {
+        return;
+    }
+    
+    if (UIEdgeInsetsEqualToEdgeInsets(self.contentInset, UIEdgeInsetsZero)) {
+        _contentInset = insets;
+        return;
+    }
+    
+    _contentInset = insets;
+    
+    // Add new constraints
+    [self removeConstraints:self.constraints];
+    [self slk_setupViewConstraints];
+    
+    // Add constant values and refresh layout
+    [self slk_updateConstraintConstants];
+    [super layoutIfNeeded];
+}
+
+- (void)setEditing:(BOOL)editing
+{
+    if (self.isEditing == editing) {
+        return;
+    }
+    
+    _editing = editing;
+    _editorContentView.hidden = !editing;
+}
+
+- (void)setCounterPosition:(SLKCounterPosition)counterPosition
+{
+    if (self.counterPosition == counterPosition && self.charCountLabelVCs) {
+        return;
+    }
+    
+    // Clears the previous constraints
+    if (_charCountLabelVCs.count > 0) {
+        [self removeConstraints:_charCountLabelVCs];
+        _charCountLabelVCs = nil;
+    }
+    
+    _counterPosition = counterPosition;
+    
+    NSDictionary *views = @{@"rightButton": self.rightButton,
+                            @"charCountLabel": self.charCountLabel
+                            };
+    
+    NSDictionary *metrics = @{@"top" : @(self.contentInset.top),
+                              @"bottom" : @(-self.contentInset.bottom/2.0)
+                              };
+    
+    // Constraints are different depending of the counter's position type
+    if (counterPosition == SLKCounterPositionBottom) {
+        _charCountLabelVCs = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[charCountLabel]-(bottom)-[rightButton]" options:0 metrics:metrics views:views];
+    }
+    else {
+        _charCountLabelVCs = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(top@750)-[charCountLabel]-(>=0)-|" options:0 metrics:metrics views:views];
+    }
+    
+    [self addConstraints:self.charCountLabelVCs];
+}
+
+
+#pragma mark - Text Editing
+
+- (BOOL)canEditText:(NSString *)text
+{
+    if ((self.isEditing && [self.textView.text isEqualToString:text]) || self.controller.isTextInputbarHidden) {
+        return NO;
+    }
+    
+    return YES;
+}
+
+- (void)beginTextEditing
+{
+    if (self.isEditing || self.controller.isTextInputbarHidden) {
+        return;
+    }
+    
+    self.editing = YES;
+    
+    [self slk_updateConstraintConstants];
+    
+    if (!self.isFirstResponder) {
+        [self layoutIfNeeded];
+    }
+}
+
+- (void)endTextEdition
+{
+    if (!self.isEditing || self.controller.isTextInputbarHidden) {
+        return;
+    }
+    
+    self.editing = NO;
+    
+    [self slk_updateConstraintConstants];
+}
+
+
+#pragma mark - Character Counter
+
+- (void)slk_updateCounter
+{
+    NSString *text = [self.textView.text stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
+    NSString *counter = nil;
+    
+    if (self.counterStyle == SLKCounterStyleNone) {
+        counter = [NSString stringWithFormat:@"%lu", (unsigned long)text.length];
+    }
+    if (self.counterStyle == SLKCounterStyleSplit) {
+        counter = [NSString stringWithFormat:@"%lu/%lu", (unsigned long)text.length, (unsigned long)self.maxCharCount];
+    }
+    if (self.counterStyle == SLKCounterStyleCountdown) {
+        counter = [NSString stringWithFormat:@"%ld", (long)(text.length - self.maxCharCount)];
+    }
+    if (self.counterStyle == SLKCounterStyleCountdownReversed)
+    {
+        counter = [NSString stringWithFormat:@"%ld", (long)(self.maxCharCount - text.length)];
+    }
+    
+    self.charCountLabel.text = counter;
+    self.charCountLabel.textColor = [self limitExceeded] ? self.charCountLabelWarningColor : self.charCountLabelNormalColor;
+}
+
+
+#pragma mark - Notification Events
+
+- (void)slk_didChangeTextViewText:(NSNotification *)notification
+{
+    SLKTextView *textView = (SLKTextView *)notification.object;
+    
+    // Skips this it's not the expected textView.
+    if (![textView isEqual:self.textView]) {
+        return;
+    }
+    
+    // Updates the char counter label
+    if (self.maxCharCount > 0) {
+        [self slk_updateCounter];
+    }
+    
+    if (self.autoHideRightButton && !self.isEditing)
+    {
+        CGFloat rightButtonNewWidth = [self slk_appropriateRightButtonWidth];
+        
+        if (self.rightButtonWC.constant == rightButtonNewWidth) {
+            return;
+        }
+        
+        self.rightButtonWC.constant = rightButtonNewWidth;
+        self.rightMarginWC.constant = [self slk_appropriateRightButtonMargin];
+        
+        if (rightButtonNewWidth > 0) {
+            [self.rightButton sizeToFit];
+        }
+        
+        BOOL bounces = self.controller.bounces && [self.textView isFirstResponder];
+        
+        if (self.window) {
+            [self slk_animateLayoutIfNeededWithBounce:bounces
+                                              options:UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionAllowUserInteraction
+                                           animations:NULL];
+        }
+        else {
+            [self layoutIfNeeded];
+        }
+    }
+}
+
+- (void)slk_didChangeTextViewContentSize:(NSNotification *)notification
+{
+    if (self.maxCharCount > 0) {
+        BOOL shouldHide = (self.textView.numberOfLines == 1) || self.editing;
+        self.charCountLabel.hidden = shouldHide;
+    }
+}
+
+- (void)slk_didChangeContentSizeCategory:(NSNotification *)notification
+{
+    if (!self.textView.isDynamicTypeEnabled) {
+        return;
+    }
+    
+    [self layoutIfNeeded];
+}
+
+
+#pragma mark - View Auto-Layout
+
+- (void)slk_setupViewConstraints
+{
+    [self.rightButton sizeToFit];
+    
+    CGFloat rightVerMargin = (self.intrinsicContentSize.height - CGRectGetHeight(self.rightButton.frame)) / 2.0;
+    
+    NSDictionary *views = @{@"textView": self.textView,
+                            @"leftButton": self.leftButton,
+                            @"rightButton": self.rightButton,
+                            @"contentView": self.editorContentView,
+                            @"charCountLabel": self.charCountLabel
+                            };
+    
+    NSDictionary *metrics = @{@"top" : @(self.contentInset.top),
+                              @"bottom" : @(self.contentInset.bottom),
+                              @"left" : @(self.contentInset.left),
+                              @"right" : @(self.contentInset.right),
+                              @"rightVerMargin" : @(rightVerMargin),
+                              };
+    
+    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(left)-[leftButton(0)]-(<=left)-[textView]-(right)-[rightButton(0)]-(right)-|" options:0 metrics:metrics views:views]];
+    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=0)-[leftButton(0)]-(0@750)-|" options:0 metrics:metrics views:views]];
+    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=rightVerMargin)-[rightButton]-(<=rightVerMargin)-|" options:0 metrics:metrics views:views]];
+    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(left@250)-[charCountLabel(<=50@1000)]-(right@750)-|" options:0 metrics:metrics views:views]];
+    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[contentView(0)]-(<=top)-[textView(0@999)]-(bottom)-|" options:0 metrics:metrics views:views]];
+    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|" options:0 metrics:metrics views:views]];
+    
+    self.editorContentViewHC = [self slk_constraintForAttribute:NSLayoutAttributeHeight firstItem:self.editorContentView secondItem:nil];
+    
+    self.leftButtonWC = [self slk_constraintForAttribute:NSLayoutAttributeWidth firstItem:self.leftButton secondItem:nil];
+    self.leftButtonHC = [self slk_constraintForAttribute:NSLayoutAttributeHeight firstItem:self.leftButton secondItem:nil];
+    
+    self.leftMarginWC = [self slk_constraintsForAttribute:NSLayoutAttributeLeading][0];
+    self.bottomMarginWC = [self slk_constraintForAttribute:NSLayoutAttributeBottom firstItem:self secondItem:self.leftButton];
+    
+    self.rightButtonWC = [self slk_constraintForAttribute:NSLayoutAttributeWidth firstItem:self.rightButton secondItem:nil];
+    self.rightMarginWC = [self slk_constraintsForAttribute:NSLayoutAttributeTrailing][0];
+    
+    self.rightButtonTopMarginC = [self slk_constraintForAttribute:NSLayoutAttributeTop firstItem:self.rightButton secondItem:self];
+    self.rightButtonBottomMarginC = [self slk_constraintForAttribute:NSLayoutAttributeBottom firstItem:self secondItem:self.rightButton];
+}
+
+- (void)slk_updateConstraintConstants
+{
+    CGFloat zero = 0.0;
+    
+    if (self.isEditing)
+    {
+        self.editorContentViewHC.constant = self.editorContentViewHeight;
+        self.leftButtonWC.constant = zero;
+        self.leftButtonHC.constant = zero;
+        self.leftMarginWC.constant = zero;
+        self.bottomMarginWC.constant = zero;
+        self.rightButtonWC.constant = zero;
+        self.rightMarginWC.constant = zero;
+    }
+    else {
+        self.editorContentViewHC.constant = zero;
+        
+        CGSize leftButtonSize = [self.leftButton imageForState:self.leftButton.state].size;
+        
+        if (leftButtonSize.width > 0) {
+            self.leftButtonHC.constant = roundf(leftButtonSize.height);
+            self.bottomMarginWC.constant = roundf((self.intrinsicContentSize.height - leftButtonSize.height) / 2.0);
+        }
+        
+        self.leftButtonWC.constant = roundf(leftButtonSize.width);
+        self.leftMarginWC.constant = (leftButtonSize.width > 0) ? self.contentInset.left : zero;
+        
+        self.rightButtonWC.constant = [self slk_appropriateRightButtonWidth];
+        self.rightMarginWC.constant = [self slk_appropriateRightButtonMargin];
+        
+        [self.rightButton sizeToFit];
+        
+        CGFloat rightVerMargin = (self.intrinsicContentSize.height - CGRectGetHeight(self.rightButton.frame)) / 2.0;
+        
+        self.rightButtonTopMarginC.constant = rightVerMargin;
+        self.rightButtonBottomMarginC.constant = rightVerMargin;
+    }
+}
+
+
+#pragma mark - Observers
+
+- (void)slk_registerTo:(id)object forSelector:(SEL)selector
+{
+    if (object) {
+        [object addObserver:self forKeyPath:NSStringFromSelector(selector) options:NSKeyValueObservingOptionNew context:NULL];
+    }
+}
+
+- (void)slk_unregisterFrom:(id)object forSelector:(SEL)selector
+{
+    if (object) {
+        [object removeObserver:self forKeyPath:NSStringFromSelector(selector)];
+    }
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+{
+    if ([object isEqual:self.layer] && [keyPath isEqualToString:NSStringFromSelector(@selector(position))]) {
+        
+        if (!CGPointEqualToPoint(self.previousOrigin, self.frame.origin)) {
+            self.previousOrigin = self.frame.origin;
+            [[NSNotificationCenter defaultCenter] postNotificationName:SLKTextInputbarDidMoveNotification object:self userInfo:@{@"origin": [NSValue valueWithCGPoint:self.previousOrigin]}];
+        }
+    }
+    else if ([object isEqual:self.leftButton.imageView] && [keyPath isEqualToString:NSStringFromSelector(@selector(image))]) {
+        
+        UIImage *newImage = change[NSKeyValueChangeNewKey];
+        UIImage *oldImage = change[NSKeyValueChangeOldKey];
+        
+        if (![newImage isEqual:oldImage]) {
+            [self slk_updateConstraintConstants];
+        }
+    }
+    else if ([object isEqual:self.rightButton.titleLabel] && [keyPath isEqualToString:NSStringFromSelector(@selector(font))]) {
+        
+        [self slk_updateConstraintConstants];
+    }
+    else {
+        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+    }
+}
+
+
+#pragma mark - NSNotificationCenter register/unregister
+
+- (void)slk_registerNotifications
+{
+    [self slk_unregisterNotifications];
+    
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didChangeTextViewText:) name:UITextViewTextDidChangeNotification object:nil];
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didChangeTextViewContentSize:) name:SLKTextViewContentSizeDidChangeNotification object:nil];
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(slk_didChangeContentSizeCategory:) name:UIContentSizeCategoryDidChangeNotification object:nil];
+}
+
+- (void)slk_unregisterNotifications
+{
+    [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextViewTextDidChangeNotification object:nil];
+    [[NSNotificationCenter defaultCenter] removeObserver:self name:SLKTextViewContentSizeDidChangeNotification object:nil];
+    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIContentSizeCategoryDidChangeNotification object:nil];
+}
+
+
+#pragma mark - Lifeterm
+
+- (void)dealloc
+{
+    [self slk_unregisterNotifications];
+    
+    [self slk_unregisterFrom:self.layer forSelector:@selector(position)];
+    [self slk_unregisterFrom:self.leftButton.imageView forSelector:@selector(image)];
+    [self slk_unregisterFrom:self.rightButton.titleLabel forSelector:@selector(font)];
+    
+    _leftButton = nil;
+    _rightButton = nil;
+    
+    _inputAccessoryView = nil;
+    _textView.delegate = nil;
+    _textView = nil;
+    
+    _editorContentView = nil;
+    _editorTitle = nil;
+    _editorLeftButton = nil;
+    _editorRightButton = nil;
+    
+    _leftButtonWC = nil;
+    _leftButtonHC = nil;
+    _leftMarginWC = nil;
+    _bottomMarginWC = nil;
+    _rightButtonWC = nil;
+    _rightMarginWC = nil;
+    _rightButtonTopMarginC = nil;
+    _rightButtonBottomMarginC = nil;
+    _editorContentViewHC = nil;
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/usergrid/blob/7442c881/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextView+SLKAdditions.h
----------------------------------------------------------------------
diff --git a/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextView+SLKAdditions.h b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextView+SLKAdditions.h
new file mode 100644
index 0000000..00970d5
--- /dev/null
+++ b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextView+SLKAdditions.h
@@ -0,0 +1,98 @@
+//
+//   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"
+
+/** @name SLKTextView additional features used for SlackTextViewController. */
+@interface SLKTextView (SLKAdditions)
+
+/**
+ Clears the text.
+ 
+ @param clearUndo YES if clearing the text should also clear the undo manager (if enabled).
+ */
+- (void)slk_clearText:(BOOL)clearUndo;
+
+/**
+ Scrolls to the very end of the content size, animated.
+ 
+ @param animated YES if the scrolling should be animated.
+ */
+- (void)slk_scrollToBottomAnimated:(BOOL)animated;
+
+/**
+ Scrolls to the caret position, animated.
+ 
+ @param animated YES if the scrolling should be animated.
+ */
+- (void)slk_scrollToCaretPositonAnimated:(BOOL)animated;
+
+/**
+ Inserts a line break at the caret's position.
+ */
+- (void)slk_insertNewLineBreak;
+
+/**
+ Inserts a string at the caret's position.
+ 
+ @param text The string to be appended to the current text.
+ */
+- (void)slk_insertTextAtCaretRange:(NSString *)text;
+
+/**
+ Adds a string to a specific range.
+ 
+ @param text The string to be appended to the current text.
+ @param range The range where to insert text.
+ 
+ @return The range of the newly inserted text.
+ */
+- (NSRange)slk_insertText:(NSString *)text inRange:(NSRange)range;
+
+/**
+ Finds the word close to the caret's position, if any.
+ 
+ @param range Returns the range of the found word.
+ @returns The found word.
+ */
+- (NSString *)slk_wordAtCaretRange:(NSRangePointer)range;
+
+
+/**
+ Finds the word close to specific range.
+ 
+ @param range The range to be used for searching the word.
+ @param rangePointer Returns the range of the found word.
+ @returns The found word.
+ */
+- (NSString *)slk_wordAtRange:(NSRange)range rangeInText:(NSRangePointer)rangePointer;
+
+/**
+ Registers the current text for future undo actions.
+ 
+ @param description A simple description associated with the Undo or Redo command.
+ */
+- (void)slk_prepareForUndo:(NSString *)description;
+
+/**
+ Returns a constant font size difference reflecting the current accessibility settings.
+ 
+ @param category A content size category constant string.
+ @returns A float constant font size difference.
+ */
++ (CGFloat)pointSizeDifferenceForCategory:(NSString *)category;
+
+@end

http://git-wip-us.apache.org/repos/asf/usergrid/blob/7442c881/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextView+SLKAdditions.m
----------------------------------------------------------------------
diff --git a/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextView+SLKAdditions.m b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextView+SLKAdditions.m
new file mode 100644
index 0000000..aa9632f
--- /dev/null
+++ b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextView+SLKAdditions.m
@@ -0,0 +1,189 @@
+//
+//   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+SLKAdditions.h"
+
+@implementation SLKTextView (SLKAdditions)
+
+- (void)slk_clearText:(BOOL)clearUndo
+{
+    // Important to call self implementation, as SLKTextView overrides setText: to add additional features.
+    [self setText:nil];
+    
+    if (self.undoManagerEnabled && clearUndo) {
+        [self.undoManager removeAllActions];
+    }
+}
+
+- (void)slk_scrollToCaretPositonAnimated:(BOOL)animated
+{
+    if (animated) {
+        [self scrollRangeToVisible:self.selectedRange];
+    }
+    else {
+        [UIView performWithoutAnimation:^{
+            [self scrollRangeToVisible:self.selectedRange];
+        }];
+    }
+}
+
+- (void)slk_scrollToBottomAnimated:(BOOL)animated
+{
+    CGRect rect = [self caretRectForPosition:self.selectedTextRange.end];
+    rect.size.height += self.textContainerInset.bottom;
+    
+    if (animated) {
+        [self scrollRectToVisible:rect animated:animated];
+    }
+    else {
+        [UIView performWithoutAnimation:^{
+            [self scrollRectToVisible:rect animated:NO];
+        }];
+    }
+}
+
+- (void)slk_insertNewLineBreak
+{
+    [self slk_insertTextAtCaretRange:@"\n"];
+    
+    // if the text view cannot expand anymore, scrolling to bottom are not animated to fix a UITextView issue scrolling twice.
+    BOOL animated = !self.isExpanding;
+    
+    //Detected break. Should scroll to bottom if needed.
+    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.0125 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+        [self slk_scrollToBottomAnimated:animated];
+    });
+}
+
+- (void)slk_insertTextAtCaretRange:(NSString *)text
+{
+    NSRange range = [self slk_insertText:text inRange:self.selectedRange];
+    self.selectedRange = NSMakeRange(range.location, 0);
+}
+
+- (NSRange)slk_insertText:(NSString *)text inRange:(NSRange)range
+{
+    // Skip if the text is empty
+    if (text.length == 0) {
+        return NSMakeRange(0, 0);
+    }
+    
+    // Registers for undo management
+    [self slk_prepareForUndo:@"Text appending"];
+    
+    // Append the new string at the caret position
+    if (range.length == 0)
+    {
+        NSString *leftString = [self.text substringToIndex:range.location];
+        NSString *rightString = [self.text substringFromIndex: range.location];
+        
+        self.text = [NSString stringWithFormat:@"%@%@%@", leftString, text, rightString];
+        
+        range.location += text.length;
+
+        return range;
+    }
+    // Some text is selected, so we replace it with the new text
+    else if (range.location != NSNotFound && range.length > 0)
+    {
+        self.text = [self.text stringByReplacingCharactersInRange:range withString:text];
+
+        range.location += text.length;
+        
+        return range;
+    }
+    
+    // No text has been inserted, but still return the caret range
+    return self.selectedRange;
+}
+
+- (NSString *)slk_wordAtCaretRange:(NSRangePointer)range
+{
+    return [self slk_wordAtRange:self.selectedRange rangeInText:range];
+}
+
+- (NSString *)slk_wordAtRange:(NSRange)range rangeInText:(NSRangePointer)rangePointer
+{
+    NSString *text = self.text;
+    NSInteger location = range.location;
+    
+    // Aborts in case minimum requieres are not fufilled
+    if (text.length == 0 || location < 0 || (range.location+range.length) > text.length) {
+        *rangePointer = NSMakeRange(0, 0);
+        return nil;
+    }
+    
+    NSString *leftPortion = [text substringToIndex:location];
+    NSArray *leftComponents = [leftPortion componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+    NSString *leftWordPart = [leftComponents lastObject];
+    
+    NSString *rightPortion = [text substringFromIndex:location];
+    NSArray *rightComponents = [rightPortion componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+    NSString *rightPart = [rightComponents firstObject];
+    
+    if (location > 0) {
+        NSString *characterBeforeCursor = [text substringWithRange:NSMakeRange(location-1, 1)];
+        
+        if ([characterBeforeCursor isEqualToString:@" "]) {
+            // At the start of a word, just use the word behind the cursor for the current word
+            *rangePointer = NSMakeRange(location, rightPart.length);
+            
+            return rightPart;
+        }
+    }
+    
+    // In the middle of a word, so combine the part of the word before the cursor, and after the cursor to get the current word
+    *rangePointer = NSMakeRange(location-leftWordPart.length, leftWordPart.length+rightPart.length);
+    NSString *word = [leftWordPart stringByAppendingString:rightPart];
+    
+    // If a break is detected, return the last component of the string
+    if ([word rangeOfString:@"\n"].location != NSNotFound) {
+        *rangePointer = [text rangeOfString:word];
+        word = [[word componentsSeparatedByString:@"\n"] lastObject];
+    }
+    
+    return word;
+}
+
+- (void)slk_prepareForUndo:(NSString *)description
+{
+    if (!self.undoManagerEnabled) {
+        return;
+    }
+    
+    SLKTextView *prepareInvocation = [self.undoManager prepareWithInvocationTarget:self];
+    [prepareInvocation setText:self.text];
+    [self.undoManager setActionName:description];
+}
+
++ (CGFloat)pointSizeDifferenceForCategory:(NSString *)category
+{
+    if ([category isEqualToString:UIContentSizeCategoryExtraSmall])                         return -3.0;
+    if ([category isEqualToString:UIContentSizeCategorySmall])                              return -2.0;
+    if ([category isEqualToString:UIContentSizeCategoryMedium])                             return -1.0;
+    if ([category isEqualToString:UIContentSizeCategoryLarge])                              return 0.0;
+    if ([category isEqualToString:UIContentSizeCategoryExtraLarge])                         return 2.0;
+    if ([category isEqualToString:UIContentSizeCategoryExtraExtraLarge])                    return 4.0;
+    if ([category isEqualToString:UIContentSizeCategoryExtraExtraExtraLarge])               return 6.0;
+    if ([category isEqualToString:UIContentSizeCategoryAccessibilityMedium])                return 8.0;
+    if ([category isEqualToString:UIContentSizeCategoryAccessibilityLarge])                 return 10.0;
+    if ([category isEqualToString:UIContentSizeCategoryAccessibilityExtraLarge])            return 11.0;
+    if ([category isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraLarge])       return 12.0;
+    if ([category isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraExtraLarge])  return 13.0;
+    return 0;
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/usergrid/blob/7442c881/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextView.h
----------------------------------------------------------------------
diff --git a/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextView.h b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextView.h
new file mode 100644
index 0000000..61d5b10
--- /dev/null
+++ b/sdks/swift/Samples/ActivityFeed/Pods/SlackTextViewController/Source/SLKTextView.h
@@ -0,0 +1,148 @@
+//
+//   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 <UIKit/UIKit.h>
+
+UIKIT_EXTERN NSString * const SLKTextViewTextWillChangeNotification;
+UIKIT_EXTERN NSString * const SLKTextViewContentSizeDidChangeNotification;
+UIKIT_EXTERN NSString * const SLKTextViewSelectedRangeDidChangeNotification;
+UIKIT_EXTERN NSString * const SLKTextViewDidPasteItemNotification;
+UIKIT_EXTERN NSString * const SLKTextViewDidShakeNotification;
+
+UIKIT_EXTERN NSString * const SLKTextViewPastedItemContentType;
+UIKIT_EXTERN NSString * const SLKTextViewPastedItemMediaType;
+UIKIT_EXTERN NSString * const SLKTextViewPastedItemData;
+
+typedef NS_OPTIONS(NSUInteger, SLKPastableMediaType) {
+    SLKPastableMediaTypeNone        = 0,
+    SLKPastableMediaTypePNG         = 1 << 0,
+    SLKPastableMediaTypeJPEG        = 1 << 1,
+    SLKPastableMediaTypeTIFF        = 1 << 2,
+    SLKPastableMediaTypeGIF         = 1 << 3,
+    SLKPastableMediaTypeMOV         = 1 << 4,
+    SLKPastableMediaTypePassbook    = 1 << 5,
+    SLKPastableMediaTypeImages      = SLKPastableMediaTypePNG|SLKPastableMediaTypeJPEG|SLKPastableMediaTypeTIFF|SLKPastableMediaTypeGIF,
+    SLKPastableMediaTypeVideos      = SLKPastableMediaTypeMOV,
+    SLKPastableMediaTypeAll         = SLKPastableMediaTypeImages|SLKPastableMediaTypeMOV
+};
+
+@protocol SLKTextViewDelegate;
+
+/** @name A custom text input view. */
+@interface SLKTextView : UITextView
+
+@property (nonatomic, weak) id<SLKTextViewDelegate,UITextViewDelegate>delegate;
+
+/** The placeholder text string. Default is nil. */
+@property (nonatomic, copy) NSString *placeholder;
+
+/** The placeholder color. Default is lightGrayColor. */
+@property (nonatomic, copy) UIColor *placeholderColor;
+
+/** The maximum number of lines before enabling scrolling. Default is 0 wich means limitless.
+ If dynamic type is enabled, the maximum number of lines will be calculated proportionally to the user preferred font size. */
+@property (nonatomic, readwrite) NSUInteger maxNumberOfLines;
+
+/** The current displayed number of lines. */
+@property (nonatomic, readonly) NSUInteger numberOfLines;
+
+/** The supported media types allowed to be pasted in the text view, such as images or videos. Default is None. */
+@property (nonatomic) SLKPastableMediaType pastableMediaTypes;
+
+/** YES if the text view is and can still expand it self, depending if the maximum number of lines are reached. */
+@property (nonatomic, readonly) BOOL isExpanding;
+
+/** YES if quickly refreshed the textview without the intension to dismiss the keyboard. @view -disableQuicktypeBar: for more details. */
+@property (nonatomic, readwrite) BOOL didNotResignFirstResponder;
+
+/** YES if the magnifying glass is visible.
+ This feature is deprecated since there are no legit alternatives to detect the magnifying glass.
+ Open Radar: http://openradar.appspot.com/radar?id=5021485877952512
+ */
+@property (nonatomic, getter=isLoupeVisible) BOOL loupeVisible DEPRECATED_ATTRIBUTE;
+
+/** YES if the keyboard track pad has been recognized. iOS 9 only. */
+@property (nonatomic, readonly, getter=isTrackpadEnabled) BOOL trackpadEnabled;
+
+/** YES if autocorrection and spell checking are enabled. On iOS8, this property also controls the predictive QuickType bar from being visible. Default is YES. */
+@property (nonatomic, getter=isTypingSuggestionEnabled) BOOL typingSuggestionEnabled;
+
+/** YES if the text view supports undoing, either using UIMenuController, or with ctrl+z when using an external keyboard. Default is YES. */
+@property (nonatomic, readwrite) BOOL undoManagerEnabled;
+
+/** YES if the font size should dynamically adapt based on the font sizing option preferred by the user. Default is YES. */
+@property (nonatomic, getter=isDynamicTypeEnabled) BOOL dynamicTypeEnabled;
+
+/**
+ Some text view properties don't update when it's already firstResponder (auto-correction, spelling-check, etc.)
+ To be able to update the text view while still being first responder, requieres to switch quickly from -resignFirstResponder to -becomeFirstResponder.
+ When doing so, the flag 'didNotResignFirstResponder' is momentarly set to YES before it goes back to -isFirstResponder, to be able to prevent some tasks to be excuted because of UIKeyboard notifications.
+ 
+ You can also use this method to confirm an auto-correction programatically, before the text view resigns first responder.
+ */
+- (void)refreshFirstResponder;
+- (void)refreshInputViews;
+
+/**
+ Notifies the text view that the user pressed any arrow key. This is used to move the cursor up and down while having multiple lines.
+ */
+- (void)didPressAnyArrowKey:(id)sender;
+
+
+#pragma mark - Markdown Formatting
+
+/** YES if the a markdown closure symbol should be added automatically after double spacebar tap, just like the native gesture to add a sentence period. Default is YES.
+ This will always be NO if there isn't any registered formatting symbols.
+ */
+@property (nonatomic) BOOL autoCompleteFormatting;
+
+/** An array of the registered formatting symbols. */
+@property (nonatomic, readonly) NSArray *registeredSymbols;
+
+/**
+ Registers any string markdown symbol for formatting tooltip, presented after selecting some text.
+ The symbol must be valid string (i.e: '*', '~', '_', and so on). This also checks if no repeated symbols are inserted, and respects the ordering for the tooltip.
+ 
+ @param symbol A markdown symbol to be prefixed and sufixed to a text selection.
+ @param title The tooltip item title for this formatting.
+ */
+- (void)registerMarkdownFormattingSymbol:(NSString *)symbol withTitle:(NSString *)title;
+
+@end
+
+
+@protocol SLKTextViewDelegate <UITextViewDelegate>
+@optional
+
+/**
+ Asks the delegate whether the specified formatting symbol should be displayed in the tooltip.
+ This is useful to remove some tooltip options when they no longer apply in some context.
+ For example, Blockquotes formatting requires the symbol to be prefixed at the begining of a paragraph.
+ 
+ @param textView The text view containing the changes.
+ @param symbol The formatting symbol to be verified.
+ @return YES if the formatting symbol should be displayed in the tooltip. Default is YES.
+ */
+- (BOOL)textView:(SLKTextView *)textView shouldOfferFormattingForSymbol:(NSString *)symbol;
+
+/**
+ Asks the delegate whether the specified formatting symbol should be suffixed, to close the formatting wrap.
+
+ @para  The prefix range
+ */
+- (BOOL)textView:(SLKTextView *)textView shouldInsertSuffixForFormattingWithSymbol:(NSString *)symbol prefixRange:(NSRange)prefixRange;
+
+@end