Skip to content

Commit

Permalink
Adds support for an optional view to host outlets under the text view (
Browse files Browse the repository at this point in the history
…slackhq#562)

* Adds temporarily bottom margin to the text input bar. Might add a content view instead, for better control.

* Adds support for an optional view to host outlets under the text view, adjusting its height based on its subviews. Non-visible by default. Subviews' layout should be configured using auto-layout as well.

* Updates constraint of the content view when hidding the text input as well
  • Loading branch information
Ignacio Romero Zurbuchen authored Jan 10, 2017
1 parent 7890a84 commit ac17d97
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 90 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
Expand Down Expand Up @@ -36,6 +46,16 @@
"filename" : "[email protected]",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
Expand Down
16 changes: 16 additions & 0 deletions Examples/Messenger-Shared/MessageViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#import <LoremIpsum/LoremIpsum.h>

#define DEBUG_CUSTOM_TYPING_INDICATOR 0
#define DEBUG_CUSTOM_BOTTOM_VIEW 0

@interface MessageViewController ()

Expand Down Expand Up @@ -104,6 +105,20 @@ - (void)viewDidLoad
[self.textInputbar.editorLeftButton setTintColor:[UIColor colorWithRed:0.0/255.0 green:122.0/255.0 blue:255.0/255.0 alpha:1.0]];
[self.textInputbar.editorRightButton setTintColor:[UIColor colorWithRed:0.0/255.0 green:122.0/255.0 blue:255.0/255.0 alpha:1.0]];

#if DEBUG_CUSTOM_BOTTOM_VIEW
// Example of view that can be added to the bottom of the text view

UIView *bannerView = [UIView new];
bannerView.translatesAutoresizingMaskIntoConstraints = NO;
bannerView.backgroundColor = [UIColor blueColor];

NSDictionary *views = NSDictionaryOfVariableBindings(bannerView);

[self.textInputbar.contentView addSubview:bannerView];
[self.textInputbar.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[bannerView]|" options:0 metrics:nil views:views]];
[self.textInputbar.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[bannerView(40)]|" options:0 metrics:nil views:views]];
#endif

#if !DEBUG_CUSTOM_TYPING_INDICATOR
self.typingIndicatorView.canResignByTouch = YES;
#endif
Expand Down Expand Up @@ -199,6 +214,7 @@ - (void)hideOrShowTextInputbar:(id)sender
UIBarButtonItem *buttonItem = (UIBarButtonItem *)sender;

[self setTextInputbarHidden:hide animated:YES];

[buttonItem setImage:image];
}

Expand Down
7 changes: 5 additions & 2 deletions Source/SLKTextInputbar.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,13 @@ NS_ASSUME_NONNULL_BEGIN
For iPhone 5 & 6 (>=568pts): 6 lines
For iPad (>=768pts): 8 lines
*/
@property (nonatomic, strong) SLKTextView *textView;
@property (nonatomic, readonly, strong) SLKTextView *textView;

/** Optional view to host outlets under the text view, adjusting its height based on its subviews. Non-visible by default. Subviews' layout should be configured using auto-layout as well. */
@property (nonatomic, readonly, strong) UIView *contentView;

/** The custom input accessory view, used as empty achor view to detect the keyboard frame. */
@property (nonatomic, strong) SLKInputAccessoryView *inputAccessoryView;
@property (nonatomic, readonly, strong) SLKInputAccessoryView *inputAccessoryView;

/** The left action button action. */
@property (nonatomic, strong) UIButton *leftButton;
Expand Down
126 changes: 81 additions & 45 deletions Source/SLKTextInputbar.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@

@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 *bottomMarginWC;
@property (nonatomic, strong) NSLayoutConstraint *leftButtonBottomMarginC;
@property (nonatomic, strong) NSLayoutConstraint *rightButtonWC;
@property (nonatomic, strong) NSLayoutConstraint *rightMarginWC;
@property (nonatomic, strong) NSLayoutConstraint *rightButtonTopMarginC;
Expand All @@ -41,6 +43,9 @@ @interface SLKTextInputbar ()
@end

@implementation SLKTextInputbar
@synthesize textView = _textView;
@synthesize contentView = _contentView;
@synthesize inputAccessoryView = _inputAccessoryView;
@synthesize hidden = _hidden;

#pragma mark - Initialization
Expand Down Expand Up @@ -84,6 +89,7 @@ - (void)slk_commonInit
[self addSubview:self.rightButton];
[self addSubview:self.textView];
[self addSubview:self.charCountLabel];
[self addSubview:self.contentView];

[self slk_setupViewConstraints];
[self slk_updateConstraintConstants];
Expand Down Expand Up @@ -146,6 +152,17 @@ - (SLKTextView *)textView
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) {
Expand Down Expand Up @@ -280,10 +297,11 @@ - (BOOL)isHidden

- (CGFloat)minimumInputbarHeight
{
CGFloat minimumTextViewHeight = self.textView.intrinsicContentSize.height;
minimumTextViewHeight += self.contentInset.top + self.contentInset.bottom;
CGFloat minimumHeight = self.textView.intrinsicContentSize.height;
minimumHeight += self.contentInset.top;
minimumHeight += self.slk_bottomMargin;

return minimumTextViewHeight;
return minimumHeight;
}

- (CGFloat)appropriateHeight
Expand Down Expand Up @@ -312,24 +330,42 @@ - (CGFloat)appropriateHeight
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 + self.contentInset.bottom;
height += self.contentInset.top;
height += self.slk_bottomMargin;

return height;
}

- (BOOL)limitExceeded
- (CGFloat)slk_bottomMargin
{
NSString *text = [self.textView.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
CGFloat margin = self.contentInset.bottom;
margin += self.slk_contentViewHeight;

if (self.maxCharCount > 0 && text.length > self.maxCharCount) {
return YES;
return margin;
}

- (CGFloat)slk_contentViewHeight
{
if (!self.editing) {
return CGRectGetHeight(self.contentView.frame);
}
return NO;

return 0.0;
}

- (CGFloat)slk_appropriateRightButtonWidth
Expand Down Expand Up @@ -409,6 +445,7 @@ - (void)setContentInset:(UIEdgeInsets)insets

// Add constant values and refresh layout
[self slk_updateConstraintConstants];

[super layoutIfNeeded];
}

Expand All @@ -420,6 +457,11 @@ - (void)setEditing:(BOOL)editing

_editing = editing;
_editorContentView.hidden = !editing;

self.contentViewHC.active = editing;

[super setNeedsLayout];
[super layoutIfNeeded];
}

- (void)setHidden:(BOOL)hidden
Expand All @@ -428,6 +470,13 @@ - (void)setHidden:(BOOL)hidden
// 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
Expand All @@ -449,7 +498,7 @@ - (void)setCounterPosition:(SLKCounterPosition)counterPosition
};

NSDictionary *metrics = @{@"top" : @(self.contentInset.top),
@"bottom" : @(-self.contentInset.bottom/2.0)
@"bottom" : @(-self.slk_bottomMargin/2.0)
};

// Constraints are different depending of the counter's position type
Expand Down Expand Up @@ -595,12 +644,12 @@ - (void)slk_setupViewConstraints
NSDictionary *views = @{@"textView": self.textView,
@"leftButton": self.leftButton,
@"rightButton": self.rightButton,
@"contentView": self.editorContentView,
@"charCountLabel": self.charCountLabel
@"editorContentView": self.editorContentView,
@"charCountLabel": self.charCountLabel,
@"contentView": self.contentView,
};

NSDictionary *metrics = @{@"top" : @(self.contentInset.top),
@"bottom" : @(self.contentInset.bottom),
@"left" : @(self.contentInset.left),
@"right" : @(self.contentInset.right),
};
Expand All @@ -609,19 +658,24 @@ - (void)slk_setupViewConstraints
[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-[contentView(0)]-(<=top)-[textView(0@999)]-(bottom)-|" 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.leftMarginWC = [self slk_constraintsForAttribute:NSLayoutAttributeLeading][0];
self.bottomMarginWC = [self slk_constraintForAttribute:NSLayoutAttributeBottom firstItem:self secondItem:self.leftButton];
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][0];
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];
Expand All @@ -631,13 +685,16 @@ - (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.bottomMarginWC.constant = zero;
self.leftButtonBottomMarginC.constant = zero;
self.rightButtonWC.constant = zero;
self.rightMarginWC.constant = zero;
}
Expand All @@ -648,7 +705,7 @@ - (void)slk_updateConstraintConstants

if (leftButtonSize.width > 0) {
self.leftButtonHC.constant = roundf(leftButtonSize.height);
self.bottomMarginWC.constant = roundf((self.intrinsicContentSize.height - leftButtonSize.height) / 2.0);
self.leftButtonBottomMarginC.constant = roundf((self.intrinsicContentSize.height - leftButtonSize.height) / 2.0) + self.slk_contentViewHeight / 2.0;
}

self.leftButtonWC.constant = roundf(leftButtonSize.width);
Expand All @@ -657,10 +714,11 @@ - (void)slk_updateConstraintConstants
self.rightButtonWC.constant = [self slk_appropriateRightButtonWidth];
self.rightMarginWC.constant = [self slk_appropriateRightButtonMargin];

CGFloat rightVerMargin = (self.intrinsicContentSize.height - self.rightButton.intrinsicContentSize.height) / 2.0;
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 = rightVerMargin;
self.rightButtonBottomMarginC.constant = rightVerBottomMargin;
}
}

Expand Down Expand Up @@ -737,28 +795,6 @@ - (void)dealloc
[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
7 changes: 0 additions & 7 deletions Source/SLKTextView.m
Original file line number Diff line number Diff line change
Expand Up @@ -1141,13 +1141,6 @@ - (void)dealloc
[self slk_unregisterNotifications];

[self removeObserver:self forKeyPath:NSStringFromSelector(@selector(contentSize))];

_placeholderLabel = nil;

_registeredFormattingTitles = nil;
_registeredFormattingSymbols = nil;
_registeredKeyCommands = nil;
_registeredKeyCallbacks = nil;
}

@end
Loading

0 comments on commit ac17d97

Please sign in to comment.