mirror of
https://github.com/zhigang1992/MessagesTableViewController.git
synced 2026-03-29 07:38:35 +08:00
Merge ImplementCopy
This commit is contained in:
@@ -66,6 +66,11 @@
|
||||
self.textLabel.hidden = YES;
|
||||
self.detailTextLabel.text = nil;
|
||||
self.detailTextLabel.hidden = YES;
|
||||
|
||||
UILongPressGestureRecognizer *recognizer =
|
||||
[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
|
||||
[recognizer setMinimumPressDuration:0.5];
|
||||
[self addGestureRecognizer:recognizer];
|
||||
}
|
||||
|
||||
- (void)configureTimestampLabel
|
||||
@@ -159,4 +164,78 @@
|
||||
timeStyle:NSDateFormatterShortStyle];
|
||||
}
|
||||
|
||||
#pragma mark - Implement Copy
|
||||
- (BOOL) canBecomeFirstResponder
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL) becomeFirstResponder
|
||||
{
|
||||
return [super becomeFirstResponder];
|
||||
}
|
||||
|
||||
- (BOOL) canPerformAction:(SEL)action withSender:(id)sender
|
||||
{
|
||||
if (action == @selector(copy:))
|
||||
return YES;
|
||||
|
||||
return [super canPerformAction:action withSender:sender];
|
||||
}
|
||||
|
||||
- (void)copy:(id)sender {
|
||||
[[UIPasteboard generalPasteboard] setString:self.bubbleView.text];
|
||||
[self resignFirstResponder];
|
||||
}
|
||||
|
||||
- (void) handleLongPress:(UILongPressGestureRecognizer *)longPressRecognizer
|
||||
{
|
||||
if (longPressRecognizer.state != UIGestureRecognizerStateBegan)
|
||||
return;
|
||||
|
||||
if ([self becomeFirstResponder] == NO)
|
||||
return;
|
||||
|
||||
UIMenuController *menu = [UIMenuController sharedMenuController];
|
||||
[menu setTargetRect:CGRectInset([self.bubbleView bubbleFrame], 0, 4.f) inView:self];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(menuWillShow:)
|
||||
name:UIMenuControllerWillShowMenuNotification
|
||||
object:nil];
|
||||
[menu setMenuVisible:YES animated:YES];
|
||||
}
|
||||
|
||||
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[super touchesEnded:touches withEvent:event];
|
||||
if ([self isFirstResponder] == NO)
|
||||
return;
|
||||
|
||||
UIMenuController *menu = [UIMenuController sharedMenuController];
|
||||
[menu setMenuVisible:NO animated:YES];
|
||||
[menu update];
|
||||
[self resignFirstResponder];
|
||||
}
|
||||
|
||||
- (void) menuWillHide:(NSNotification *)notification
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIMenuControllerWillHideMenuNotification object:nil];
|
||||
self.bubbleView.selectedToShowCopyMenu = NO;
|
||||
}
|
||||
|
||||
- (void) menuWillShow:(NSNotification *)notification
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIMenuControllerWillShowMenuNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(menuWillHide:)
|
||||
name:UIMenuControllerWillHideMenuNotification
|
||||
object:nil];
|
||||
self.bubbleView.selectedToShowCopyMenu = YES;
|
||||
}
|
||||
|
||||
- (void)dealloc{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -40,7 +40,7 @@ typedef enum {
|
||||
JSBubbleMessageStyleIncomingSquare,
|
||||
JSBubbleMessageStyleOutgoingDefault,
|
||||
JSBubbleMessageStyleOutgoingDefaultGreen,
|
||||
JSBubbleMessageStyleOutgoingSquare
|
||||
JSBubbleMessageStyleOutgoingSquare,
|
||||
} JSBubbleMessageStyle;
|
||||
|
||||
|
||||
@@ -53,6 +53,9 @@ typedef enum {
|
||||
#pragma mark - Initialization
|
||||
- (id)initWithFrame:(CGRect)frame bubbleStyle:(JSBubbleMessageStyle)bubbleStyle;
|
||||
|
||||
- (CGRect)bubbleFrame;
|
||||
@property (nonatomic) BOOL selectedToShowCopyMenu;
|
||||
|
||||
#pragma mark - Bubble view
|
||||
+ (UIImage *)bubbleImageForStyle:(JSBubbleMessageStyle)style;
|
||||
+ (UIFont *)font;
|
||||
|
||||
@@ -86,16 +86,36 @@
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (void)setSelectedToShowCopyMenu:(BOOL)selectedToShowCopyMenu{
|
||||
_selectedToShowCopyMenu = selectedToShowCopyMenu;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
#pragma mark - Drawing
|
||||
- (void)drawRect:(CGRect)frame
|
||||
{
|
||||
UIImage *image = [JSBubbleView bubbleImageForStyle:self.style];
|
||||
|
||||
- (CGRect)bubbleFrame{
|
||||
CGSize bubbleSize = [JSBubbleView bubbleSizeForText:self.text];
|
||||
CGRect bubbleFrame = CGRectMake(([self styleIsOutgoing] ? self.frame.size.width - bubbleSize.width : 0.0f),
|
||||
kMarginTop,
|
||||
bubbleSize.width,
|
||||
bubbleSize.height);
|
||||
|
||||
return bubbleFrame;
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)frame
|
||||
{
|
||||
UIImage *image = nil;
|
||||
if (self.selectedToShowCopyMenu) {
|
||||
if ([self styleIsOutgoing]) {
|
||||
image = [[UIImage imageNamed:@"messageBubbleHighlighted"] stretchableImageWithLeftCapWidth:15 topCapHeight:15];
|
||||
} else {
|
||||
image = [[UIImage imageNamed:@"messageBubbleSelected"] stretchableImageWithLeftCapWidth:23 topCapHeight:15];
|
||||
}
|
||||
} else {
|
||||
image = [JSBubbleView bubbleImageForStyle:self.style];
|
||||
}
|
||||
[JSBubbleView bubbleImageForStyle:self.style];
|
||||
CGRect bubbleFrame = [self bubbleFrame];
|
||||
[image drawInRect:bubbleFrame];
|
||||
|
||||
CGSize textSize = [JSBubbleView textSizeForText:self.text];
|
||||
|
||||
@@ -76,7 +76,7 @@ typedef enum {
|
||||
@property (weak, nonatomic) id<JSMessagesViewDelegate> delegate;
|
||||
@property (weak, nonatomic) id<JSMessagesViewDataSource> dataSource;
|
||||
@property (strong, nonatomic) UITableView *tableView;
|
||||
@property (strong, nonatomic) JSMessageInputView *inputView;
|
||||
@property (strong, nonatomic) JSMessageInputView *inputToolBarView;
|
||||
@property (assign, nonatomic) CGFloat previousTextViewContentHeight;
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
@@ -71,22 +71,23 @@
|
||||
[self setBackgroundColor:[UIColor messagesBackgroundColor]];
|
||||
|
||||
CGRect inputFrame = CGRectMake(0.0f, size.height - INPUT_HEIGHT, size.width, INPUT_HEIGHT);
|
||||
self.inputView = [[JSMessageInputView alloc] initWithFrame:inputFrame delegate:self];
|
||||
self.inputView.textView.dismissivePanGestureRecognizer = self.tableView.panGestureRecognizer;
|
||||
self.inputView.textView.keyboardDelegate = self;
|
||||
self.inputToolBarView = [[JSMessageInputView alloc] initWithFrame:inputFrame delegate:self];
|
||||
self.inputToolBarView.textView.dismissivePanGestureRecognizer = self.tableView.panGestureRecognizer;
|
||||
self.inputToolBarView.textView.keyboardDelegate = self;
|
||||
|
||||
UIButton *sendButton = [self sendButton];
|
||||
sendButton.enabled = NO;
|
||||
sendButton.frame = CGRectMake(self.inputView.frame.size.width - 65.0f, 8.0f, 59.0f, 26.0f);
|
||||
sendButton.frame = CGRectMake(self.inputToolBarView.frame.size.width - 65.0f, 8.0f, 59.0f, 26.0f);
|
||||
[sendButton addTarget:self
|
||||
action:@selector(sendPressed:)
|
||||
forControlEvents:UIControlEventTouchUpInside];
|
||||
[self.inputView setSendButton:sendButton];
|
||||
[self.view addSubview:self.inputView];
|
||||
[self.inputToolBarView setSendButton:sendButton];
|
||||
[self.view addSubview:self.inputToolBarView];
|
||||
|
||||
UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipe:)];
|
||||
swipe.direction = UISwipeGestureRecognizerDirectionDown;
|
||||
swipe.numberOfTouchesRequired = 1;
|
||||
[self.inputView addGestureRecognizer:swipe];
|
||||
[self.inputToolBarView addGestureRecognizer:swipe];
|
||||
}
|
||||
|
||||
- (UIButton *)sendButton
|
||||
@@ -150,12 +151,12 @@
|
||||
- (void)sendPressed:(UIButton *)sender
|
||||
{
|
||||
[self.delegate sendPressed:sender
|
||||
withText:[self.inputView.textView.text trimWhitespace]];
|
||||
withText:[self.inputToolBarView.textView.text trimWhitespace]];
|
||||
}
|
||||
|
||||
- (void)handleSwipe:(UIGestureRecognizer *)guestureRecognizer
|
||||
{
|
||||
[self.inputView.textView resignFirstResponder];
|
||||
[self.inputToolBarView.textView resignFirstResponder];
|
||||
}
|
||||
|
||||
#pragma mark - Table view data source
|
||||
@@ -244,8 +245,8 @@
|
||||
|
||||
- (void)finishSend
|
||||
{
|
||||
[self.inputView.textView setText:nil];
|
||||
[self textViewDidChange:self.inputView.textView];
|
||||
[self.inputToolBarView.textView setText:nil];
|
||||
[self textViewDidChange:self.inputToolBarView.textView];
|
||||
[self.tableView reloadData];
|
||||
[self scrollToBottomAnimated:YES];
|
||||
}
|
||||
@@ -291,10 +292,12 @@
|
||||
BOOL isShrinking = textViewContentHeight < self.previousTextViewContentHeight;
|
||||
CGFloat changeInHeight = textViewContentHeight - self.previousTextViewContentHeight;
|
||||
|
||||
changeInHeight = (textViewContentHeight + changeInHeight >= maxHeight) ? 0.0f : changeInHeight;
|
||||
if (changeInHeight > 0 && self.previousTextViewContentHeight == maxHeight) {
|
||||
changeInHeight = 0;
|
||||
}
|
||||
|
||||
if(!isShrinking)
|
||||
[self.inputView adjustTextViewHeightBy:changeInHeight];
|
||||
[self.inputToolBarView adjustTextViewHeightBy:changeInHeight];
|
||||
|
||||
if(changeInHeight != 0.0f) {
|
||||
[UIView animateWithDuration:0.25f
|
||||
@@ -304,22 +307,22 @@
|
||||
self.tableView.scrollIndicatorInsets = insets;
|
||||
|
||||
[self scrollToBottomAnimated:NO];
|
||||
|
||||
CGRect inputViewFrame = self.inputView.frame;
|
||||
self.inputView.frame = CGRectMake(0.0f,
|
||||
|
||||
CGRect inputViewFrame = self.inputToolBarView.frame;
|
||||
self.inputToolBarView.frame = CGRectMake(0.0f,
|
||||
inputViewFrame.origin.y - changeInHeight,
|
||||
inputViewFrame.size.width,
|
||||
inputViewFrame.size.height + changeInHeight);
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
if(isShrinking)
|
||||
[self.inputView adjustTextViewHeightBy:changeInHeight];
|
||||
[self.inputToolBarView adjustTextViewHeightBy:changeInHeight];
|
||||
}];
|
||||
|
||||
self.previousTextViewContentHeight = MIN(textViewContentHeight, maxHeight);
|
||||
}
|
||||
|
||||
self.inputView.sendButton.enabled = ([textView.text trimWhitespace].length > 0);
|
||||
self.inputToolBarView.sendButton.enabled = ([textView.text trimWhitespace].length > 0);
|
||||
}
|
||||
|
||||
#pragma mark - Keyboard notifications
|
||||
@@ -345,22 +348,22 @@
|
||||
animations:^{
|
||||
CGFloat keyboardY = [self.view convertRect:keyboardRect fromView:nil].origin.y;
|
||||
|
||||
CGRect inputViewFrame = self.inputView.frame;
|
||||
CGRect inputViewFrame = self.inputToolBarView.frame;
|
||||
CGFloat inputViewFrameY = keyboardY - inputViewFrame.size.height;
|
||||
|
||||
// for ipad modal form presentations
|
||||
CGFloat messageViewFrameBottom = self.view.frame.size.height - INPUT_HEIGHT;
|
||||
if(inputViewFrameY > messageViewFrameBottom)
|
||||
inputViewFrameY = messageViewFrameBottom;
|
||||
|
||||
self.inputView.frame = CGRectMake(inputViewFrame.origin.x,
|
||||
|
||||
self.inputToolBarView.frame = CGRectMake(inputViewFrame.origin.x,
|
||||
inputViewFrameY,
|
||||
inputViewFrame.size.width,
|
||||
inputViewFrame.size.height);
|
||||
|
||||
UIEdgeInsets insets = UIEdgeInsetsMake(0.0f,
|
||||
0.0f,
|
||||
self.view.frame.size.height - self.inputView.frame.origin.y - INPUT_HEIGHT,
|
||||
self.view.frame.size.height - self.inputToolBarView.frame.origin.y - INPUT_HEIGHT,
|
||||
0.0f);
|
||||
|
||||
self.tableView.contentInset = insets;
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
@@ -9,6 +9,8 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
041A9E7117A0FE8500923B72 /* DemoAvatarExample.png in Resources */ = {isa = PBXBuildFile; fileRef = 041A9E7017A0FE8500923B72 /* DemoAvatarExample.png */; };
|
||||
04E5799617A0E15C0022CD77 /* JSMADismissiveTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E5799517A0E15C0022CD77 /* JSMADismissiveTextView.m */; };
|
||||
049A2A3717A16EA0000D4811 /* messageBubbleSelected.png in Resources */ = {isa = PBXBuildFile; fileRef = 049A2A3517A16E9E000D4811 /* messageBubbleSelected.png */; };
|
||||
049A2A3817A16EA0000D4811 /* messageBubbleSelected@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 049A2A3617A16E9F000D4811 /* messageBubbleSelected@2x.png */; };
|
||||
881AE55D16D13CDC008F7636 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 881AE55C16D13CDC008F7636 /* UIKit.framework */; };
|
||||
881AE55F16D13CDC008F7636 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 881AE55E16D13CDC008F7636 /* Foundation.framework */; };
|
||||
881AE56116D13CDC008F7636 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 881AE56016D13CDC008F7636 /* CoreGraphics.framework */; };
|
||||
@@ -59,6 +61,8 @@
|
||||
041A9E7017A0FE8500923B72 /* DemoAvatarExample.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = DemoAvatarExample.png; sourceTree = "<group>"; };
|
||||
04E5799417A0E15C0022CD77 /* JSMADismissiveTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSMADismissiveTextView.h; sourceTree = "<group>"; };
|
||||
04E5799517A0E15C0022CD77 /* JSMADismissiveTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSMADismissiveTextView.m; sourceTree = "<group>"; };
|
||||
049A2A3517A16E9E000D4811 /* messageBubbleSelected.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = messageBubbleSelected.png; sourceTree = "<group>"; };
|
||||
049A2A3617A16E9F000D4811 /* messageBubbleSelected@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "messageBubbleSelected@2x.png"; sourceTree = "<group>"; };
|
||||
881AE55916D13CDC008F7636 /* MessagesDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MessagesDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
881AE55C16D13CDC008F7636 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
|
||||
881AE55E16D13CDC008F7636 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
@@ -230,6 +234,8 @@
|
||||
88301EF216F77B3D0037524D /* Images */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
049A2A3517A16E9E000D4811 /* messageBubbleSelected.png */,
|
||||
049A2A3617A16E9F000D4811 /* messageBubbleSelected@2x.png */,
|
||||
88244C0116FE5BA300034667 /* bubbleSquareIncoming.png */,
|
||||
88244C0216FE5BA300034667 /* bubbleSquareIncoming@2x.png */,
|
||||
88244C0316FE5BA300034667 /* bubbleSquareOutgoing.png */,
|
||||
@@ -346,6 +352,8 @@
|
||||
88244C0716FE5BA400034667 /* bubbleSquareOutgoing.png in Resources */,
|
||||
88244C0816FE5BA400034667 /* bubbleSquareOutgoing@2x.png in Resources */,
|
||||
041A9E7117A0FE8500923B72 /* DemoAvatarExample.png in Resources */,
|
||||
049A2A3717A16EA0000D4811 /* messageBubbleSelected.png in Resources */,
|
||||
049A2A3817A16EA0000D4811 /* messageBubbleSelected@2x.png in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user