From e3ae9caf8df24a3cbd850620ae8cacd1ae7dd39a Mon Sep 17 00:00:00 2001 From: Nicolas Mauri Date: Tue, 18 Apr 2023 11:37:34 +0200 Subject: [PATCH] Fix: Calculation of the frame for a bubble component --- .../MXKRoomBubbleTableViewCell+Riot.m | 38 ++++++++++++------- .../Models/Room/MXKRoomBubbleCellData.h | 9 +++++ .../Models/Room/MXKRoomBubbleCellData.m | 17 +++++++-- ...eOutgoingWithoutSenderInfoBubbleCell.swift | 9 +++++ changelog.d/pr-7512.bugfix | 1 + 5 files changed, 57 insertions(+), 17 deletions(-) create mode 100644 changelog.d/pr-7512.bugfix diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m index a7bfd69f14..907dd5ff22 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -600,36 +600,47 @@ - (CGRect)componentFrameInContentViewForIndex:(NSInteger)componentIndex } else if (roomBubbleTableViewCell.messageTextView) { + // Force the textView used underneath to layout its frame properly + [roomBubbleTableViewCell setNeedsLayout]; + [roomBubbleTableViewCell layoutIfNeeded]; + + // Compute the height CGFloat textMessageHeight = 0; - if ([bubbleCellData isKindOfClass:[RoomBubbleCellData class]]) { RoomBubbleCellData *roomBubbleCellData = (RoomBubbleCellData*)bubbleCellData; if (!roomBubbleCellData.attachment && selectedComponent.attributedTextMessage) { - textMessageHeight = [roomBubbleCellData rawTextHeight:selectedComponent.attributedTextMessage]; + // Get the width of messageTextView to compute the needed height + CGFloat maxTextWidth = CGRectGetWidth(roomBubbleTableViewCell.messageTextView.bounds); + + // Compute text message height + textMessageHeight = [roomBubbleCellData rawTextHeight:selectedComponent.attributedTextMessage withMaxWidth:maxTextWidth]; } } - - selectedComponentPositionY = selectedComponent.position.y; - + + // Get the messageText frame in the cell content view (as the messageTextView may be inside a stackView and not directly a child of the tableViewCell) + UITextView *messageTextView = roomBubbleTableViewCell.messageTextView; + CGRect messageTextViewFrame = [messageTextView convertRect:messageTextView.bounds toView:roomBubbleTableViewCell.contentView]; + if (textMessageHeight > 0) { selectedComponentHeight = textMessageHeight; } else { - selectedComponentHeight = roomBubbleTableViewCell.frame.size.height - selectedComponentPositionY; + // if we don't have a height, use the messageTextView height without the text container vertical insets to stay aligned with the text. + selectedComponentHeight = CGRectGetHeight(messageTextViewFrame) - messageTextView.textContainerInset.top - messageTextView.textContainerInset.bottom; } - // Force the textView used underneath to layout its frame properly - [roomBubbleTableViewCell setNeedsLayout]; - [roomBubbleTableViewCell layoutIfNeeded]; - - selectedComponenContentViewYOffset = roomBubbleTableViewCell.messageTextView.frame.origin.y; + // Get the vertical position of the messageTextView relative to the contentView + selectedComponenContentViewYOffset = CGRectGetMinY(messageTextViewFrame); + + // Get the position of the component inside the messageTextView + selectedComponentPositionY = selectedComponent.position.y; } - + if (roomBubbleTableViewCell.attachmentView || roomBubbleTableViewCell.messageTextView) { CGFloat x = 0; @@ -801,8 +812,7 @@ - (IBAction)onReceiptContainerTap:(UITapGestureRecognizer *)sender - (void)addTickView:(UIView *)tickView atIndex:(NSInteger)index { - CGRect componentFrame = [self componentFrameInContentViewForIndex: index]; - + CGRect componentFrame = [self componentFrameInContentViewForIndex:index]; tickView.frame = CGRectMake(self.contentView.bounds.size.width - tickView.frame.size.width - 2 * PlainRoomCellLayoutConstants.readReceiptsViewRightMargin, CGRectGetMaxY(componentFrame) - tickView.frame.size.height, tickView.frame.size.width, tickView.frame.size.height); [self.contentView addSubview:tickView]; diff --git a/Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleCellData.h b/Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleCellData.h index e934567b7c..df9d12900a 100644 --- a/Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleCellData.h +++ b/Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleCellData.h @@ -144,6 +144,15 @@ */ - (CGFloat)rawTextHeight:(NSAttributedString*)attributedText; +/** + Return the raw height of the provided text by removing any vertical margin/inset and constraining the width. + + @param attributedText the attributed text to measure + @param maxTextViewWidth the maximum text width + @return the computed height + */ +- (CGFloat)rawTextHeight:(NSAttributedString*)attributedText withMaxWidth:(CGFloat)maxTextViewWidth; + /** Return the content size of a text view initialized with the provided attributed text. CAUTION: This method runs only on main thread. diff --git a/Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleCellData.m b/Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleCellData.m index 53b084c3a5..c9a13d9797 100644 --- a/Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleCellData.m +++ b/Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleCellData.m @@ -500,23 +500,34 @@ - (NSInteger)bubbleComponentIndexForEventId:(NSString *)eventId // Return the raw height of the provided text by removing any margin - (CGFloat)rawTextHeight: (NSAttributedString*)attributedText +{ + return [self rawTextHeight:attributedText withMaxWidth:_maxTextViewWidth]; +} + +// Return the raw height of the provided text by removing any vertical margin/inset and constraining the width. +- (CGFloat)rawTextHeight: (NSAttributedString*)attributedText withMaxWidth:(CGFloat)maxTextViewWidth { __block CGSize textSize; if ([NSThread currentThread] != [NSThread mainThread]) { dispatch_sync(dispatch_get_main_queue(), ^{ - textSize = [self textContentSize:attributedText removeVerticalInset:YES]; + textSize = [self textContentSize:attributedText removeVerticalInset:YES maxTextViewWidth:maxTextViewWidth]; }); } else { - textSize = [self textContentSize:attributedText removeVerticalInset:YES]; + textSize = [self textContentSize:attributedText removeVerticalInset:YES maxTextViewWidth:maxTextViewWidth]; } return textSize.height; } - (CGSize)textContentSize:(NSAttributedString*)attributedText removeVerticalInset:(BOOL)removeVerticalInset +{ + return [self textContentSize:attributedText removeVerticalInset:removeVerticalInset maxTextViewWidth:_maxTextViewWidth]; +} + +- (CGSize)textContentSize:(NSAttributedString*)attributedText removeVerticalInset:(BOOL)removeVerticalInset maxTextViewWidth:(CGFloat)maxTextViewWidth { static UITextView* measurementTextView = nil; static UITextView* measurementTextViewWithoutInset = nil; @@ -536,7 +547,7 @@ - (CGSize)textContentSize:(NSAttributedString*)attributedText removeVerticalInse // Select the right text view for measurement UITextView *selectedTextView = (removeVerticalInset ? measurementTextViewWithoutInset : measurementTextView); - selectedTextView.frame = CGRectMake(0, 0, _maxTextViewWidth, 0); + selectedTextView.frame = CGRectMake(0, 0, maxTextViewWidth, 0); selectedTextView.attributedText = attributedText; // Force the layout manager to layout the text, fixes problems starting iOS 16 diff --git a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithoutSenderInfoBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithoutSenderInfoBubbleCell.swift index de15e91d38..f3f00f12f4 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithoutSenderInfoBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithoutSenderInfoBubbleCell.swift @@ -35,6 +35,15 @@ class TextMessageOutgoingWithoutSenderInfoBubbleCell: TextMessageBaseBubbleCell, self.textMessageContentView?.bubbleBackgroundView?.backgroundColor = theme.roomCellOutgoingBubbleBackgroundColor } + override func render(_ cellData: MXKCellData!) { + // This cell displays an outgoing message without any sender information. + // However, we need to set the following properties to our cellData, otherwise, to make room for the timestamp, a whitespace could be added when calculating the position of the components. + // If we don't, the component frame calculation will not work for this cell. + (cellData as? RoomBubbleCellData)?.shouldHideSenderName = false + (cellData as? RoomBubbleCellData)?.shouldHideSenderInformation = false + super.render(cellData) + } + // MARK: - Private private func setupBubbleConstraints() { diff --git a/changelog.d/pr-7512.bugfix b/changelog.d/pr-7512.bugfix new file mode 100644 index 0000000000..1c6d3a98d1 --- /dev/null +++ b/changelog.d/pr-7512.bugfix @@ -0,0 +1 @@ +Fix the position of the send confirmation icon.