Skip to content
This repository has been archived by the owner on Aug 14, 2019. It is now read-only.

Commit

Permalink
move UIMenuController handling from cell to collectionView. fix #458. f…
Browse files Browse the repository at this point in the history
…ix #254. close #463. ref: #393.
  • Loading branch information
jessesquires committed Sep 1, 2014
1 parent 9ed3f97 commit 00a503f
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,14 @@ @interface JSQMessagesViewController () <JSQMessagesInputToolbarDelegate,

@property (assign, nonatomic) BOOL jsq_isObserving;

@property (strong, nonatomic) NSIndexPath *selectedIndexPathForMenu;

- (void)jsq_configureMessagesViewController;

- (NSString *)jsq_currentlyComposedMessageText;

- (void)jsq_handleDidChangeStatusBarFrameNotification:(NSNotification *)notification;
- (void)jsq_didReceiveMenuWillShowNotification:(NSNotification *)notification;

- (void)jsq_updateKeyboardTriggerPoint;
- (void)jsq_setToolbarBottomLayoutGuideConstant:(CGFloat)constant;
Expand Down Expand Up @@ -272,6 +275,13 @@ - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrie
[self.collectionView.collectionViewLayout invalidateLayoutWithContext:[JSQMessagesCollectionViewFlowLayoutInvalidationContext context]];
}

#pragma mark - UIResponder

- (BOOL)canBecomeFirstResponder
{
return YES;
}

#pragma mark - Messages view controller

- (void)didPressSendButton:(UIButton *)button
Expand Down Expand Up @@ -464,11 +474,29 @@ - (CGSize)collectionView:(UICollectionView *)collectionView

#pragma mark - Collection view delegate

- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath
- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath
{
self.selectedIndexPathForMenu = indexPath;
return YES;
}

- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
{
if (action == @selector(copy:)) {
return YES;
}

return NO;
}

- (void)collectionView:(JSQMessagesCollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
{
if (action == @selector(copy:)) {
id<JSQMessageData> messageData = [self collectionView:collectionView messageDataForItemAtIndexPath:indexPath];
[[UIPasteboard generalPasteboard] setString:[messageData text]];
}
}

#pragma mark - Collection view delegate flow layout

- (CGSize)collectionView:(JSQMessagesCollectionView *)collectionView
Expand Down Expand Up @@ -590,6 +618,49 @@ - (void)jsq_handleDidChangeStatusBarFrameNotification:(NSNotification *)notifica
}
}

- (void)jsq_didReceiveMenuWillShowNotification:(NSNotification *)notification
{
if (!self.selectedIndexPathForMenu) {
return;
}

[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIMenuControllerWillShowMenuNotification
object:nil];

UIMenuController *menu = [notification object];
[menu setMenuVisible:NO animated:NO];

JSQMessagesCollectionViewCell *selectedCell = (JSQMessagesCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:self.selectedIndexPathForMenu];
CGRect selectedCellFrame = [self.collectionView convertRect:selectedCell.frame toView:self.view];

BOOL menuIsAboveCell = CGRectGetMinY(menu.menuFrame) < CGRectGetMinY(selectedCellFrame);

CGFloat finalFrameY = CGRectGetMinY(selectedCellFrame);

if (menuIsAboveCell) {
finalFrameY += CGRectGetHeight(selectedCell.messageBubbleTopLabel.frame) + CGRectGetHeight(selectedCell.cellTopLabel.frame);
}
else {
finalFrameY += CGRectGetHeight(selectedCell.cellBottomLabel.frame);
}

CGRect finalFrame = CGRectMake(CGRectGetMinX(selectedCellFrame),
finalFrameY,
CGRectGetWidth(selectedCellFrame),
CGRectGetHeight(selectedCellFrame));

[menu setTargetRect:finalFrame inView:self.view];
[menu setMenuVisible:YES animated:YES];

self.selectedIndexPathForMenu = nil;

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(jsq_didReceiveMenuWillShowNotification:)
name:UIMenuControllerWillShowMenuNotification
object:nil];
}

#pragma mark - Key-value observing

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
Expand Down Expand Up @@ -797,11 +868,20 @@ - (void)jsq_registerForNotifications:(BOOL)registerForNotifications
selector:@selector(jsq_handleDidChangeStatusBarFrameNotification:)
name:UIApplicationDidChangeStatusBarFrameNotification
object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(jsq_didReceiveMenuWillShowNotification:)
name:UIMenuControllerWillShowMenuNotification
object:nil];
}
else {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIApplicationDidChangeStatusBarFrameNotification
object:nil];

[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIMenuControllerWillShowMenuNotification
object:nil];
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,6 @@
*/
@property (weak, nonatomic) UIImageView *avatarImageView;

/**
* Returns the underlying gesture recognizer for long press gestures in the cell.
* This gesture handles the copy action for the cell.
* Access this property when you need to override or more precisely control the long press gesture.
*/
@property (weak, nonatomic, readonly) UILongPressGestureRecognizer *longPressGestureRecognizer;

/**
* Returns the underlying gesture recognizer for tap gestures in the avatarImageView of the cell.
* This gesture handles the tap event for the avatarImageView and notifies the cell's delegate.
Expand Down
94 changes: 13 additions & 81 deletions JSQMessagesViewController/Views/JSQMessagesCollectionViewCell.m
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,10 @@ @interface JSQMessagesCollectionViewCell ()

@property (assign, nonatomic) CGSize avatarViewSize;

@property (weak, nonatomic, readwrite) UILongPressGestureRecognizer *longPressGestureRecognizer;
@property (weak, nonatomic, readwrite) UITapGestureRecognizer *tapGestureRecognizer;

- (void)jsq_handleLongPressGesture:(UILongPressGestureRecognizer *)longPress;
- (void)jsq_handleTapGesture:(UITapGestureRecognizer *)tap;

- (void)jsq_didReceiveMenuWillHideNotification:(NSNotification *)notification;
- (void)jsq_didReceiveMenuWillShowNotification:(NSNotification *)notification;

- (void)jsq_updateConstraint:(NSLayoutConstraint *)constraint withConstant:(CGFloat)constant;

@end
Expand Down Expand Up @@ -112,7 +107,7 @@ - (void)awakeFromNib

self.textView.textColor = [UIColor whiteColor];
self.textView.editable = NO;
self.textView.selectable = YES;
self.textView.selectable = NO;
self.textView.userInteractionEnabled = YES;
self.textView.dataDetectorTypes = UIDataDetectorTypeNone;
self.textView.showsHorizontalScrollIndicator = NO;
Expand All @@ -125,11 +120,6 @@ - (void)awakeFromNib
self.textView.linkTextAttributes = @{ NSForegroundColorAttributeName : [UIColor whiteColor],
NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle | NSUnderlinePatternSolid) };

UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(jsq_handleLongPressGesture:)];
longPress.minimumPressDuration = 0.4f;
[self addGestureRecognizer:longPress];
self.longPressGestureRecognizer = longPress;

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(jsq_handleTapGesture:)];
[self addGestureRecognizer:tap];
self.tapGestureRecognizer = tap;
Expand All @@ -146,9 +136,6 @@ - (void)dealloc
_messageBubbleImageView = nil;
_avatarImageView = nil;

[_longPressGestureRecognizer removeTarget:nil action:NULL];
_longPressGestureRecognizer = nil;

[_tapGestureRecognizer removeTarget:nil action:NULL];
_tapGestureRecognizer = nil;
}
Expand Down Expand Up @@ -203,6 +190,18 @@ - (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttribut
}
}

- (void)setHighlighted:(BOOL)highlighted
{
[super setHighlighted:highlighted];
self.messageBubbleImageView.highlighted = highlighted;
}

- (void)setSelected:(BOOL)selected
{
[super setSelected:selected];
self.messageBubbleImageView.highlighted = selected;
}

#pragma mark - Setters

- (void)setBackgroundColor:(UIColor *)backgroundColor
Expand Down Expand Up @@ -318,52 +317,8 @@ - (void)jsq_updateConstraint:(NSLayoutConstraint *)constraint withConstant:(CGFl
[self setNeedsUpdateConstraints];
}

#pragma mark - UIResponder

- (BOOL)canBecomeFirstResponder
{
return YES;
}

- (BOOL)becomeFirstResponder
{
return [super becomeFirstResponder];
}

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
return (action == @selector(copy:));
}

- (void)copy:(id)sender
{
[[UIPasteboard generalPasteboard] setString:self.textView.text];
[self resignFirstResponder];
}

#pragma mark - Gesture recognizers

- (void)jsq_handleLongPressGesture:(UILongPressGestureRecognizer *)longPress
{
if (longPress.state != UIGestureRecognizerStateBegan || ![self becomeFirstResponder]) {
return;
}

UIMenuController *menu = [UIMenuController sharedMenuController];
CGRect targetRect = [self convertRect:self.messageBubbleImageView.bounds fromView:self.messageBubbleImageView];

[menu setTargetRect:CGRectInset(targetRect, 0.0f, 4.0f) inView:self];

self.messageBubbleImageView.highlighted = YES;

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(jsq_didReceiveMenuWillShowNotification:)
name:UIMenuControllerWillShowMenuNotification
object:menu];

[menu setMenuVisible:YES animated:YES];
}

- (void)jsq_handleTapGesture:(UITapGestureRecognizer *)tap
{
CGPoint touchPt = [tap locationInView:self];
Expand All @@ -379,27 +334,4 @@ - (void)jsq_handleTapGesture:(UITapGestureRecognizer *)tap
}
}

#pragma mark - Notifications

- (void)jsq_didReceiveMenuWillHideNotification:(NSNotification *)notification
{
self.messageBubbleImageView.highlighted = NO;

[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIMenuControllerWillHideMenuNotification
object:nil];
}

- (void)jsq_didReceiveMenuWillShowNotification:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIMenuControllerWillShowMenuNotification
object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(jsq_didReceiveMenuWillHideNotification:)
name:UIMenuControllerWillHideMenuNotification
object:[notification object]];
}

@end

0 comments on commit 00a503f

Please sign in to comment.