mirror of
https://github.com/zhigang1992/GitHawk.git
synced 2026-05-13 18:00:52 +08:00
* Move SwipeCellKit & SlackTextViewController to Local Pods directory * Move playgrounds to own folder * Create podspec for MMMarkdown and move to Local Pods
813 lines
27 KiB
Objective-C
Executable File
813 lines
27 KiB
Objective-C
Executable File
//
|
|
// SlackTextViewController
|
|
// https://github.com/slackhq/SlackTextViewController
|
|
//
|
|
// Copyright 2014-2016 Slack Technologies, Inc.
|
|
// Licence: MIT-Licence
|
|
//
|
|
|
|
#import "SLKTextInputbar.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 *textViewBottomMarginC;
|
|
@property (nonatomic, strong) NSLayoutConstraint *contentViewHC;
|
|
@property (nonatomic, strong) NSLayoutConstraint *leftButtonWC;
|
|
@property (nonatomic, strong) NSLayoutConstraint *leftButtonHC;
|
|
@property (nonatomic, strong) NSLayoutConstraint *leftMarginWC;
|
|
@property (nonatomic, strong) NSLayoutConstraint *leftButtonBottomMarginC;
|
|
@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;
|
|
|
|
@property (nonatomic, getter=isHidden) BOOL hidden; // Required override
|
|
|
|
@end
|
|
|
|
@implementation SLKTextInputbar
|
|
@synthesize textView = _textView;
|
|
@synthesize contentView = _contentView;
|
|
@synthesize inputAccessoryView = _inputAccessoryView;
|
|
@synthesize hidden = _hidden;
|
|
|
|
#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 addSubview:self.contentView];
|
|
|
|
[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];
|
|
}
|
|
|
|
// Compensate for layout bug in iOS 11 Beta 2
|
|
// https://github.com/Recfive/SlackTextViewController/commit/5faf3952fb1623508120c7f44291bfb0259f25af
|
|
- (void)layoutSubviews
|
|
{
|
|
[super layoutSubviews];
|
|
|
|
[self bringSubviewToFront:self.textView];
|
|
[self bringSubviewToFront:self.contentView];
|
|
[self bringSubviewToFront:self.rightButton];
|
|
[self bringSubviewToFront:self.leftButton];
|
|
}
|
|
|
|
- (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.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 = 1 / [UIScreen mainScreen].scale;
|
|
_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;
|
|
}
|
|
|
|
- (UIView *)contentView
|
|
{
|
|
if (!_contentView) {
|
|
_contentView = [UIView new];
|
|
_contentView.translatesAutoresizingMaskIntoConstraints = NO;
|
|
_contentView.backgroundColor = [UIColor clearColor];
|
|
_contentView.clipsToBounds = YES;
|
|
}
|
|
return _contentView;
|
|
}
|
|
|
|
- (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;
|
|
}
|
|
|
|
- (BOOL)isHidden
|
|
{
|
|
return _hidden;
|
|
}
|
|
|
|
- (CGFloat)minimumInputbarHeight
|
|
{
|
|
CGFloat minimumHeight = self.textView.intrinsicContentSize.height;
|
|
minimumHeight += self.contentInset.top;
|
|
minimumHeight += self.slk_bottomMargin;
|
|
|
|
return minimumHeight;
|
|
}
|
|
|
|
- (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);
|
|
}
|
|
|
|
- (BOOL)limitExceeded
|
|
{
|
|
NSString *text = [self.textView.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
|
|
|
if (self.maxCharCount > 0 && text.length > self.maxCharCount) {
|
|
return YES;
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
- (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;
|
|
height += self.slk_bottomMargin;
|
|
|
|
return height;
|
|
}
|
|
|
|
- (CGFloat)slk_bottomMargin
|
|
{
|
|
CGFloat margin = self.contentInset.bottom;
|
|
margin += self.slk_contentViewHeight;
|
|
|
|
return margin;
|
|
}
|
|
|
|
- (CGFloat)slk_contentViewHeight
|
|
{
|
|
if (!self.editing) {
|
|
return CGRectGetHeight(self.contentView.frame);
|
|
}
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
- (CGFloat)slk_appropriateRightButtonWidth
|
|
{
|
|
if (self.autoHideRightButton) {
|
|
if (self.textView.text.length == 0) {
|
|
return 0.0;
|
|
}
|
|
}
|
|
|
|
return [self.rightButton intrinsicContentSize].width;
|
|
}
|
|
|
|
- (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;
|
|
}
|
|
else 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;
|
|
|
|
self.contentViewHC.active = editing;
|
|
|
|
[super setNeedsLayout];
|
|
[super layoutIfNeeded];
|
|
}
|
|
|
|
- (void)setHidden:(BOOL)hidden
|
|
{
|
|
// We don't call super here, since we want to avoid to visually hide the view.
|
|
// The hidden render state is handled by the view controller.
|
|
|
|
_hidden = hidden;
|
|
|
|
if (!self.isEditing) {
|
|
self.contentViewHC.active = hidden;
|
|
|
|
[super setNeedsLayout];
|
|
[super layoutIfNeeded];
|
|
}
|
|
}
|
|
|
|
- (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.slk_bottomMargin/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.isHidden) {
|
|
return NO;
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (void)beginTextEditing
|
|
{
|
|
if (self.isEditing || self.isHidden) {
|
|
return;
|
|
}
|
|
|
|
self.editing = YES;
|
|
|
|
[self slk_updateConstraintConstants];
|
|
|
|
if (!self.isFirstResponder) {
|
|
[self layoutIfNeeded];
|
|
}
|
|
}
|
|
|
|
- (void)endTextEdition
|
|
{
|
|
if (!self.isEditing || self.isHidden) {
|
|
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];
|
|
|
|
// Only updates if the width did change
|
|
if (self.rightButtonWC.constant == rightButtonNewWidth) {
|
|
return;
|
|
}
|
|
|
|
self.rightButtonWC.constant = rightButtonNewWidth;
|
|
self.rightMarginWC.constant = [self slk_appropriateRightButtonMargin];
|
|
[self.rightButton layoutIfNeeded]; // Avoids the right button to stretch when animating the constraint changes
|
|
|
|
BOOL bounces = self.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
|
|
{
|
|
NSDictionary *views = @{@"textView": self.textView,
|
|
@"leftButton": self.leftButton,
|
|
@"rightButton": self.rightButton,
|
|
@"editorContentView": self.editorContentView,
|
|
@"charCountLabel": self.charCountLabel,
|
|
@"contentView": self.contentView,
|
|
};
|
|
|
|
NSDictionary *metrics = @{@"top" : @(self.contentInset.top),
|
|
@"left" : @(self.contentInset.left),
|
|
@"right" : @(self.contentInset.right),
|
|
};
|
|
|
|
[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:|-(>=0)-[rightButton]-(<=0)-|" 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-[editorContentView(0)]-(<=top)-[textView(0@999)]-(0)-|" options:0 metrics:metrics views:views]];
|
|
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[editorContentView]|" options:0 metrics:metrics views:views]];
|
|
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|" options:0 metrics:metrics views:views]];
|
|
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[contentView(0)]|" options:0 metrics:metrics views:views]];
|
|
|
|
self.textViewBottomMarginC = [self slk_constraintForAttribute:NSLayoutAttributeBottom firstItem:self secondItem:self.textView];
|
|
self.editorContentViewHC = [self slk_constraintForAttribute:NSLayoutAttributeHeight firstItem:self.editorContentView secondItem:nil];
|
|
self.contentViewHC = [self slk_constraintForAttribute:NSLayoutAttributeHeight firstItem:self.contentView secondItem:nil];;
|
|
self.contentViewHC.active = NO; // Disabled by default, so the height is calculated with the height of its subviews
|
|
|
|
self.leftButtonWC = [self slk_constraintForAttribute:NSLayoutAttributeWidth firstItem:self.leftButton secondItem:nil];
|
|
self.leftButtonHC = [self slk_constraintForAttribute:NSLayoutAttributeHeight firstItem:self.leftButton secondItem:nil];
|
|
self.leftButtonBottomMarginC = [self slk_constraintForAttribute:NSLayoutAttributeBottom firstItem:self secondItem:self.leftButton];
|
|
|
|
self.leftMarginWC = [[self slk_constraintsForAttribute:NSLayoutAttributeLeading] firstObject];
|
|
|
|
self.rightButtonWC = [self slk_constraintForAttribute:NSLayoutAttributeWidth firstItem:self.rightButton secondItem:nil];
|
|
self.rightMarginWC = [[self slk_constraintsForAttribute:NSLayoutAttributeTrailing] firstObject];
|
|
|
|
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;
|
|
|
|
self.textViewBottomMarginC.constant = self.slk_bottomMargin;
|
|
|
|
if (self.isEditing)
|
|
{
|
|
self.editorContentViewHC.constant = self.editorContentViewHeight;
|
|
|
|
self.leftButtonWC.constant = zero;
|
|
self.leftButtonHC.constant = zero;
|
|
self.leftMarginWC.constant = zero;
|
|
self.leftButtonBottomMarginC.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.leftButtonBottomMarginC.constant = roundf((self.intrinsicContentSize.height - leftButtonSize.height) / 2.0) + self.slk_contentViewHeight / 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];
|
|
|
|
CGFloat rightVerMargin = (self.intrinsicContentSize.height - self.slk_contentViewHeight - self.rightButton.intrinsicContentSize.height) / 2.0;
|
|
CGFloat rightVerBottomMargin = rightVerMargin + self.slk_contentViewHeight;
|
|
|
|
self.rightButtonTopMarginC.constant = rightVerMargin;
|
|
self.rightButtonBottomMarginC.constant = rightVerBottomMargin;
|
|
}
|
|
}
|
|
|
|
|
|
#pragma mark - Observers
|
|
|
|
- (void)slk_registerTo:(id)object forSelector:(SEL)selector
|
|
{
|
|
if (object) {
|
|
[object addObserver:self forKeyPath:NSStringFromSelector(selector) options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld 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 registration
|
|
|
|
- (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)];
|
|
}
|
|
|
|
@end
|