mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 23:32:41 +00:00
vector-im/element-ios/issues/4976 - Replaced GrowingTextView with simpler, custom implementation. Cleaned up the RoomInputToolbar header.
This commit is contained in:
parent
8768eb2e76
commit
efa726b515
5 changed files with 172 additions and 87 deletions
1
Podfile
1
Podfile
|
@ -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']
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
1
changelog.d/4976.change
Normal file
|
@ -0,0 +1 @@
|
|||
Replaced GrowingTextView with simpler, custom implementation. Cleaned up the RoomInputToolbar header.
|
Loading…
Reference in a new issue