diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index 9952dc815..7f665a6e1 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -24,6 +24,8 @@ NS_ASSUME_NONNULL_BEGIN @end +#pragma mark - + @implementation BubbleMaskingView - (void)setFrame:(CGRect)frame @@ -65,6 +67,26 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - +@interface OWSMessageTextView : UITextView + +@end + +#pragma mark - + +@implementation OWSMessageTextView + +// Our message text views are never used for editing; +// suppress their ability to become first responder +// so that tapping on them doesn't hide keyboard. +- (BOOL)canBecomeFirstResponder +{ + return NO; +} + +@end + +#pragma mark - + @interface OWSMessageCell () // The nullable properties are created as needed. @@ -90,6 +112,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, nullable) NSArray *dateHeaderConstraints; @property (nonatomic, nullable) NSArray *contentConstraints; @property (nonatomic, nullable) NSArray *footerConstraints; +@property (nonatomic) BOOL isPresentingMenuController; @end @@ -132,7 +155,7 @@ NS_ASSUME_NONNULL_BEGIN [self.payloadView addSubview:self.bubbleImageView]; [self.bubbleImageView autoPinToSuperviewEdges]; - self.textView = [UITextView new]; + self.textView = [OWSMessageTextView new]; self.textView.backgroundColor = [UIColor clearColor]; self.textView.opaque = NO; self.textView.editable = NO; @@ -1038,6 +1061,8 @@ NS_ASSUME_NONNULL_BEGIN [self.expirationTimerView clearAnimations]; [self.expirationTimerView removeFromSuperview]; self.expirationTimerView = nil; + + self.isPresentingMenuController = NO; } #pragma mark - Notifications @@ -1062,6 +1087,16 @@ NS_ASSUME_NONNULL_BEGIN } else { [self.expirationTimerView clearAnimations]; } + + [self debugFirstResponder:self depth:0]; +} + +- (void)debugFirstResponder:(UIView *)view depth:(int)depth +{ + DDLogError(@"debugFirstResponder[%@ / %d]: %d", view.class, depth, [view canBecomeFirstResponder]); + for (UIView *subview in view.subviews) { + [self debugFirstResponder:subview depth:depth + 1]; + } } // case TSInfoMessageAdapter: { @@ -1159,6 +1194,11 @@ NS_ASSUME_NONNULL_BEGIN - (void)showMenuController:(CGPoint)fromLocation { + // We don't want taps on messages to hide the keyboard, + // so we only let messages become first responder + // while they are trying to present the menu controller. + self.isPresentingMenuController = YES; + [self becomeFirstResponder]; if ([UIMenuController sharedMenuController].isMenuVisible) { @@ -1208,7 +1248,35 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)canBecomeFirstResponder { - return YES; + return self.isPresentingMenuController; +} + +- (void)didHideMenuController:(NSNotification *)notification +{ + self.isPresentingMenuController = NO; +} + +- (void)setIsPresentingMenuController:(BOOL)isPresentingMenuController +{ + if (_isPresentingMenuController == isPresentingMenuController) { + return; + } + + if (isPresentingMenuController) { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(didHideMenuController:) + name:UIMenuControllerDidHideMenuNotification + object:nil]; + } else { + [[NSNotificationCenter defaultCenter] removeObserver:self + name:UIMenuControllerDidHideMenuNotification + object:nil]; + } +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; } #pragma mark - Logging