Skip to content

Commit

Permalink
/issues/4976 - Replaced GrowingTextView with simpler, custom implemen…
Browse files Browse the repository at this point in the history
…tation. Cleaned up the RoomInputToolbar header.
  • Loading branch information
stefanceriu committed Oct 26, 2021
1 parent 8768eb2 commit efa726b
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 87 deletions.
1 change: 0 additions & 1 deletion Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ abstract_target 'RiotPods' do
pod 'SideMenu', '~> 6.5'
pod 'DSWaveformImage', '~> 6.1.1'
pod 'ffmpeg-kit-ios-audio', '~> 4.5'
pod 'GrowingTextView', '~> 0.7.2'

pod 'FLEX', '~> 4.5.0', :configurations => ['Debug']

Expand Down
115 changes: 106 additions & 9 deletions Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarTextView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,91 @@
// limitations under the License.
//

import GrowingTextView

@objc protocol RoomInputToolbarTextViewDelegate: AnyObject {
func textView(_ textView: RoomInputToolbarTextView, didChangeHeight height: CGFloat)
func textView(_ textView: RoomInputToolbarTextView, didReceivePasteForMediaFromSender sender: Any?)
}

class RoomInputToolbarTextView: GrowingTextView {
@objcMembers
class RoomInputToolbarTextView: UITextView {

@objc weak var toolbarDelegate: RoomInputToolbarTextViewDelegate?
private var heightConstraint: NSLayoutConstraint!

weak var toolbarDelegate: RoomInputToolbarTextViewDelegate?

var placeholder: String?
var placeholderColor: UIColor = UIColor(white: 0.8, alpha: 1.0)

override var keyCommands: [UIKeyCommand]? {
return [UIKeyCommand(input: "\r", modifierFlags: [], action: #selector(keyCommandSelector(_:)))]
var minHeight: CGFloat = 30.0 {
didSet {
updateUI()
}
}

@objc private func keyCommandSelector(_ keyCommand: UIKeyCommand) {
guard keyCommand.input == "\r", let delegate = (self.delegate as? RoomInputToolbarView) else {
var maxHeight: CGFloat = 0.0 {
didSet {
updateUI()
}
}

override var text: String! {
didSet {
updateUI()
}
}

override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
commonInit()
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}

private func commonInit() {
contentMode = .redraw

NotificationCenter.default.addObserver(self, selector: #selector(textDidChange), name: UITextView.textDidChangeNotification, object: self)

if let heightConstraint = constraints.filter({ $0.firstAttribute == .height && $0.relation == .equal }).first {
self.heightConstraint = heightConstraint
} else {
heightConstraint = self.heightAnchor.constraint(equalToConstant: minHeight)
addConstraint(heightConstraint)
}
}

// MARK: - Overrides

override func layoutSubviews() {
super.layoutSubviews()
updateUI()
}

override func draw(_ rect: CGRect) {
super.draw(rect)

guard text.isEmpty, let placeholder = placeholder else {
return
}

delegate.onTouchUp(inside: delegate.rightInputToolbarButton)
var attributes: [NSAttributedString.Key: Any] = [.foregroundColor: placeholderColor]
if let font = font {
attributes[.font] = font
}

let frame = rect.inset(by: .init(top: textContainerInset.top,
left: textContainerInset.left + textContainer.lineFragmentPadding,
bottom: textContainerInset.bottom,
right: textContainerInset.right))

placeholder.draw(in: frame, withAttributes: attributes)
}

override var keyCommands: [UIKeyCommand]? {
return [UIKeyCommand(input: "\r", modifierFlags: [], action: #selector(keyCommandSelector(_:)))]
}

/// Overrides paste to handle images pasted from Safari, passing them up to the input toolbar.
Expand All @@ -49,4 +114,36 @@ class RoomInputToolbarTextView: GrowingTextView {
super.paste(sender)
}
}

// MARK: - Private

@objc private func textDidChange(notification: Notification) {
if let sender = notification.object as? RoomInputToolbarTextView, sender == self {
updateUI()
}
}

private func updateUI() {
var height = sizeThatFits(CGSize(width: bounds.size.width, height: CGFloat.greatestFiniteMagnitude)).height
height = minHeight > 0 ? max(height, minHeight) : height
height = maxHeight > 0 ? min(height, maxHeight) : height

// Update placeholder
self.setNeedsDisplay()

guard height != heightConstraint.constant else {
return
}

heightConstraint.constant = height
toolbarDelegate?.textView(self, didChangeHeight: height)
}

@objc private func keyCommandSelector(_ keyCommand: UIKeyCommand) {
guard keyCommand.input == "\r", let delegate = (self.delegate as? RoomInputToolbarView) else {
return
}

delegate.onTouchUp(inside: delegate.rightInputToolbarButton)
}
}
44 changes: 23 additions & 21 deletions Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,28 +60,10 @@ typedef enum : NSUInteger
*/
@property (nonatomic, weak) id<RoomInputToolbarViewDelegate> delegate;

@property (weak, nonatomic) IBOutlet UIView *mainToolbarView;

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mainToolbarMinHeightConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mainToolbarHeightConstraint;

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *messageComposerContainerTrailingConstraint;

@property (weak, nonatomic) IBOutlet UIButton *attachMediaButton;

@property (weak, nonatomic) IBOutlet UIImageView *inputTextBackgroundView;

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *inputContextViewHeightConstraint;
@property (weak, nonatomic) IBOutlet UIImageView *inputContextImageView;
@property (weak, nonatomic) IBOutlet UILabel *inputContextLabel;
@property (weak, nonatomic) IBOutlet UIButton *inputContextButton;
@property (weak, nonatomic) IBOutlet RoomActionsBar *actionsBar;
@property (weak, nonatomic) UIView *voiceMessageToolbarView;

/**
Tell whether the filled data will be sent encrypted. NO by default.
*/
@property (nonatomic) BOOL isEncryptionEnabled;
@property (nonatomic, assign) BOOL isEncryptionEnabled;

/**
Sender of the event being edited / replied.
Expand All @@ -91,11 +73,31 @@ typedef enum : NSUInteger
/**
Destination of the message in the composer.
*/
@property (nonatomic) RoomInputToolbarViewSendMode sendMode;
@property (nonatomic, assign) RoomInputToolbarViewSendMode sendMode;

/**
YES if action menu is opened. NO otherwise
*/
@property (nonatomic, getter=isActionMenuOpened) BOOL actionMenuOpened;
@property (nonatomic, assign) BOOL actionMenuOpened;

/**
The input toolbar's main height constraint
*/
@property (nonatomic, weak, readonly) NSLayoutConstraint *mainToolbarHeightConstraint;

/**
The input toolbar's action bar
*/
@property (nonatomic, weak, readonly) RoomActionsBar *actionsBar;

/**
The attach media button
*/
@property (nonatomic, weak, readonly) UIButton *attachMediaButton;

/**
Adds a voice message toolbar view to be displayed inside this input toolbar
*/
- (void)setVoiceMessageToolbarView:(UIView *)toolbarView;

@end
98 changes: 42 additions & 56 deletions Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,57 +17,51 @@

#import "RoomInputToolbarView.h"

#import "ThemeService.h"
#import "Riot-Swift.h"

#import "GBDeviceInfo_iOS.h"

#import "UINavigationController+Riot.h"
static const CGFloat kContextBarHeight = 24;
static const CGFloat kActionMenuAttachButtonSpringVelocity = 7;
static const CGFloat kActionMenuAttachButtonSpringDamping = .45;

#import "WidgetManager.h"
#import "IntegrationManagerViewController.h"
static const NSTimeInterval kSendModeAnimationDuration = .15;
static const NSTimeInterval kActionMenuAttachButtonAnimationDuration = .4;
static const NSTimeInterval kActionMenuContentAlphaAnimationDuration = .2;
static const NSTimeInterval kActionMenuComposerHeightAnimationDuration = .3;

@import GrowingTextView;
@interface RoomInputToolbarView() <UITextViewDelegate, RoomInputToolbarTextViewDelegate>

const double kContextBarHeight = 24;
const NSTimeInterval kSendModeAnimationDuration = .15;
const NSTimeInterval kActionMenuAttachButtonAnimationDuration = .4;
const CGFloat kActionMenuAttachButtonSpringVelocity = 7;
const CGFloat kActionMenuAttachButtonSpringDamping = .45;
const NSTimeInterval kActionMenuContentAlphaAnimationDuration = .2;
const NSTimeInterval kActionMenuComposerHeightAnimationDuration = .3;
const CGFloat kComposerContainerTrailingPadding = 12;
@property (nonatomic, weak) IBOutlet UIView *mainToolbarView;

@interface RoomInputToolbarView() <GrowingTextViewDelegate, RoomInputToolbarTextViewDelegate>
{
// The intermediate action sheet
UIAlertController *actionSheet;
}
@property (nonatomic, weak) IBOutlet UIButton *attachMediaButton;

@property (nonatomic, weak) IBOutlet RoomInputToolbarTextView *textView;
@property (nonatomic, weak) IBOutlet UIImageView *inputTextBackgroundView;

@property (nonatomic, weak) IBOutlet UIImageView *inputContextImageView;
@property (nonatomic, weak) IBOutlet UILabel *inputContextLabel;
@property (nonatomic, weak) IBOutlet UIButton *inputContextButton;

@property (nonatomic, weak) IBOutlet RoomActionsBar *actionsBar;

@property (nonatomic, weak) IBOutlet NSLayoutConstraint *mainToolbarMinHeightConstraint;
@property (nonatomic, weak) IBOutlet NSLayoutConstraint *mainToolbarHeightConstraint;
@property (nonatomic, weak) IBOutlet NSLayoutConstraint *messageComposerContainerTrailingConstraint;
@property (nonatomic, weak) IBOutlet NSLayoutConstraint *inputContextViewHeightConstraint;

@property (nonatomic, weak) UIView *voiceMessageToolbarView;

@property (nonatomic, assign) CGFloat expandedMainToolbarHeight;

@end

@implementation RoomInputToolbarView
@dynamic delegate;

+ (UINib *)nib
{
return [UINib nibWithNibName:NSStringFromClass([RoomInputToolbarView class])
bundle:[NSBundle bundleForClass:[RoomInputToolbarView class]]];
}

+ (instancetype)roomInputToolbarView
{
if ([[self class] nib])
{
return [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
}
else
{
return [[self alloc] init];
}
UINib *nib = [UINib nibWithNibName:NSStringFromClass([RoomInputToolbarView class]) bundle:nil];
return [nib instantiateWithOwner:nil options:nil].firstObject;
}

- (void)awakeFromNib
Expand Down Expand Up @@ -315,6 +309,11 @@ - (void)setPlaceholder:(NSString *)inPlaceholder
self.textView.placeholder = inPlaceholder;
}

- (void)pasteText:(NSString *)text
{
self.textMessage = [self.textView.text stringByReplacingCharactersInRange:self.textView.selectedRange withString:text];
}

#pragma mark - Actions

- (IBAction)cancelAction:(id)sender
Expand All @@ -325,7 +324,7 @@ - (IBAction)cancelAction:(id)sender
}
}

#pragma mark - GrowingTextViewDelegate
#pragma mark - UITextViewDelegate

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
Expand All @@ -351,7 +350,9 @@ - (void)textViewDidChange:(UITextView *)textView
[self.delegate roomInputToolbarViewDidChangeTextMessage:self];
}

- (void)textViewDidChangeHeight:(GrowingTextView *)textView height:(CGFloat)height
#pragma mark - RoomInputToolbarTextViewDelegate

- (void)textView:(RoomInputToolbarTextView *)textView didChangeHeight:(CGFloat)height
{
// Update height of the main toolbar (message composer)
CGFloat updatedHeight = height + (self.messageComposerContainerTopConstraint.constant + self.messageComposerContainerBottomConstraint.constant) + self.inputContextViewHeightConstraint.constant;
Expand All @@ -376,13 +377,18 @@ - (void)textViewDidChangeHeight:(GrowingTextView *)textView height:(CGFloat)heig
}
}

- (void)textView:(RoomInputToolbarTextView *)textView didReceivePasteForMediaFromSender:(id)sender
{
[self paste:sender];
}

#pragma mark - Override MXKRoomInputToolbarView

- (IBAction)onTouchUpInside:(UIButton*)button
{
if (button == self.attachMediaButton)
{
self.actionMenuOpened = !self.isActionMenuOpened;
self.actionMenuOpened = !self.actionMenuOpened;
}

[super onTouchUpInside:button];
Expand All @@ -400,12 +406,6 @@ - (void)dismissKeyboard

- (void)destroy
{
if (actionSheet)
{
[actionSheet dismissViewControllerAnimated:NO completion:nil];
actionSheet = nil;
}

[super destroy];
}

Expand Down Expand Up @@ -462,20 +462,6 @@ - (void)setActionMenuOpened:(BOOL)actionMenuOpened
}
}

#pragma mark - Clipboard - Handle image/data paste from general pasteboard

- (void)paste:(id)sender
{
// TODO Custom here the validation screen for each available item

[super paste:sender];
}

- (void)textView:(GrowingTextView *)textView didReceivePasteForMediaFromSender:(id)sender
{
[self paste:sender];
}

#pragma mark - Private

- (void)updateUIWithTextMessage:(NSString *)textMessage animated:(BOOL)animated
Expand Down
1 change: 1 addition & 0 deletions changelog.d/4976.change
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Replaced GrowingTextView with simpler, custom implementation. Cleaned up the RoomInputToolbar header.

0 comments on commit efa726b

Please sign in to comment.