vector-im/element-ios/issues/4976 - Replaced GrowingTextView with simpler, custom implementation. Cleaned up the RoomInputToolbar header.

This commit is contained in:
Stefan Ceriu 2021-10-26 16:27:51 +03:00
parent 8768eb2e76
commit efa726b515
5 changed files with 172 additions and 87 deletions

View file

@ -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']

View file

@ -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!
override var keyCommands: [UIKeyCommand]? {
return [UIKeyCommand(input: "\r", modifierFlags: [], action: #selector(keyCommandSelector(_:)))]
weak var toolbarDelegate: RoomInputToolbarTextViewDelegate?
var placeholder: String?
var placeholderColor: UIColor = UIColor(white: 0.8, alpha: 1.0)
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.
@ -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)
}
}

View file

@ -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.
@ -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

View file

@ -17,34 +17,40 @@
#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
@ -52,22 +58,10 @@ const CGFloat kComposerContainerTrailingPadding = 12;
@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
@ -315,6 +309,11 @@ const CGFloat kComposerContainerTrailingPadding = 12;
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
@ -325,7 +324,7 @@ const CGFloat kComposerContainerTrailingPadding = 12;
}
}
#pragma mark - GrowingTextViewDelegate
#pragma mark - UITextViewDelegate
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
@ -351,7 +350,9 @@ const CGFloat kComposerContainerTrailingPadding = 12;
[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;
@ -376,13 +377,18 @@ const CGFloat kComposerContainerTrailingPadding = 12;
}
}
- (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];
@ -400,12 +406,6 @@ const CGFloat kComposerContainerTrailingPadding = 12;
- (void)destroy
{
if (actionSheet)
{
[actionSheet dismissViewControllerAnimated:NO completion:nil];
actionSheet = nil;
}
[super destroy];
}
@ -462,20 +462,6 @@ const CGFloat kComposerContainerTrailingPadding = 12;
}
}
#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

1
changelog.d/4976.change Normal file
View file

@ -0,0 +1 @@
Replaced GrowingTextView with simpler, custom implementation. Cleaned up the RoomInputToolbar header.