Fix handling of max content widths in the message cell layout.

This commit is contained in:
Matthew Chen 2021-01-06 12:58:24 -03:00
parent b64503cce2
commit 17befbeeee
2 changed files with 56 additions and 15 deletions

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
//
import Foundation
@ -515,6 +515,12 @@ public class CVComponentMessage: CVComponentBase, CVRootComponent {
}
}
if let contentWidth = cellMeasurement.value(key: contentWidthKey) {
componentView.layoutConstraints.append(outerContentView.autoSetDimension(.width, toSize: contentWidth))
} else {
owsFailDebug("Missing contentWidth.")
}
if let subview = outerAvatarView {
subview.setContentHuggingHigh()
subview.setCompressionResistanceHigh()
@ -694,21 +700,41 @@ public class CVComponentMessage: CVComponentBase, CVRootComponent {
return CVStackView.measure(config: config, subviewSizes: subviewSizes)
}
private let contentWidthKey = "contentWidthKey"
public func measure(maxWidth maxWidthChatHistory: CGFloat, measurementBuilder: CVCellMeasurement.Builder) -> CGSize {
owsAssertDebug(maxWidthChatHistory > 0)
let maxWidth = min(maxWidthChatHistory, conversationStyle.maxMessageWidth)
let outerStackViewMaxWidth = max(0, maxWidth - cellLayoutMargins.totalWidth)
let outerStackViewMaxWidth = max(0, maxWidthChatHistory - cellLayoutMargins.totalWidth)
var cellHStackSize = CGSize.zero
var outerStackViewSize: CGSize = .zero
if hasSenderAvatarLayout {
// Sender avatar in groups.
outerStackViewSize.width += ConversationStyle.groupMessageAvatarDiameter + ConversationStyle.messageStackSpacing
outerStackViewSize.height = max(outerStackViewSize.height, ConversationStyle.groupMessageAvatarDiameter)
}
if isShowingSelectionUI {
outerStackViewSize.width += selectionViewWidth + selectionViewSpacing
}
if !isIncoming, hasSendFailureBadge {
outerStackViewSize.width += sendFailureBadgeSize + sendFailureBadgeSpacing
}
// The message cell's "outer" stack can contain many views:
// sender avatar, selection UI, send failure badge.
// The message cell's "content" stack must fit within the
// remaining space in the "outer" stack.
let contentMaxWidth = max(0,
min(conversationStyle.maxMessageWidth,
outerStackViewMaxWidth - outerStackViewSize.width))
func measureStackView(config: CVStackViewConfig,
componentKeys keys: [CVComponentKey]) -> CGSize {
let contentMaxWidth = max(0, outerStackViewMaxWidth - outerStackViewSize.width)
let maxStackWidth = max(0, contentMaxWidth - config.layoutMargins.totalWidth)
return self.measureStackView(config: config,
measurementBuilder: measurementBuilder,
componentKeys: keys,
maxWidth: contentMaxWidth)
maxWidth: maxStackWidth)
}
let topFullWidthSubcomponents = subcomponents(forKeys: topFullWidthCVComponentKeys)
@ -720,6 +746,8 @@ public class CVComponentMessage: CVComponentBase, CVRootComponent {
func applyContentMeasurement(_ size: CGSize) {
outerStackViewSize.width += size.width
outerStackViewSize.height = max(outerStackViewSize.height, size.height)
measurementBuilder.setValue(key: contentWidthKey, value: size.width)
}
if nil != stickerOverlaySubcomponent {
@ -772,7 +800,7 @@ public class CVComponentMessage: CVComponentBase, CVRootComponent {
componentKeys: bottomNestedCVComponentKeys))
}
if let bottomButtons = bottomButtons {
let subviewSize = bottomButtons.measure(maxWidth: maxWidth,
let subviewSize = bottomButtons.measure(maxWidth: contentMaxWidth,
measurementBuilder: measurementBuilder)
// We store the measured size so that we can pin the
@ -787,12 +815,6 @@ public class CVComponentMessage: CVComponentBase, CVRootComponent {
subviewSizes: subviewSizes))
}
if hasSenderAvatarLayout {
// Sender avatar in groups.
outerStackViewSize.width += ConversationStyle.groupMessageAvatarDiameter + ConversationStyle.messageStackSpacing
outerStackViewSize.height = max(outerStackViewSize.height, ConversationStyle.groupMessageAvatarDiameter)
}
cellHStackSize.width += outerStackViewSize.width
let minBubbleWidth = kOWSMessageCellCornerRadius_Large * 2
cellHStackSize.width = max(cellHStackSize.width, minBubbleWidth)
@ -1140,7 +1162,6 @@ public class CVComponentMessage: CVComponentBase, CVRootComponent {
// MARK: -
override required init() {
avatarView.autoSetDimensions(to: CGSize(square: ConversationStyle.groupMessageAvatarDiameter))
bubbleView.layoutMargins = .zero

View File

@ -71,6 +71,7 @@ public class CVMediaAlbumView: UIStackView {
maxMessageWidth: CGFloat) {
if let measuredSize = cellMeasurement.size(key: Self.measurementKey) {
Logger.verbose("---- measuredSize.1: \(measuredSize), aspect: \(measuredSize.aspectRatio)")
layoutConstraints.append(self.autoSetDimension(.height, toSize: measuredSize.height))
} else {
owsFailDebug("Missing measuredSize.")
@ -78,12 +79,25 @@ public class CVMediaAlbumView: UIStackView {
for (index, itemView) in itemViews.enumerated() {
if let measuredSize = cellMeasurement.size(key: Self.measurementKey(imageIndex: index)) {
layoutConstraints.append(contentsOf: itemView.autoSetDimensions(to: measuredSize))
Logger.verbose("---- measuredSize.2: \(measuredSize), aspect: \(measuredSize.aspectRatio)")
itemView.logFrameLater(withLabel: "---- itemView")
// The item heights should always exactly match the layout.
layoutConstraints.append(itemView.autoSetDimension(.height, toSize: measuredSize.height))
// The media album view's width might be larger than
// expected due to other components in the message.
//
// Therefore item widths might need to adjust and
// should not be required.
NSLayoutConstraint.autoSetPriority(UILayoutPriority.defaultHigh) {
layoutConstraints.append(itemView.autoSetDimension(.width, toSize: measuredSize.width))
}
} else {
owsFailDebug("Missing measuredSize for image.")
}
}
logFrameLater(withLabel: "---- media album view")
switch itemViews.count {
case 0:
owsFailDebug("No item views.")
@ -279,9 +293,12 @@ public class CVMediaAlbumView: UIStackView {
let mediaSize = mediaAlbumItem.mediaSize
guard mediaSize.width > 0 && mediaSize.height > 0 else {
owsFailDebug("Invalid mediaSize.")
// owsFailDebug("Invalid mediaSize.")
return nil
}
Logger.verbose("---- maxWidth: \(maxWidth)")
Logger.verbose("---- mediaSize: \(mediaSize), aspect: \(mediaSize.width / mediaSize.height)")
// Honor the content aspect ratio for single media.
var contentAspectRatio = mediaSize.width / mediaSize.height
// Clamp the aspect ratio so that very thin/wide content is presented
@ -318,6 +335,9 @@ public class CVMediaAlbumView: UIStackView {
}
let size = buildSingleMediaSize() ?? CGSize(square: maxWidth)
Logger.verbose("---- size: \(size), aspect: \(size.aspectRatio)")
measurementBuilder.setSize(key: measurementKey(imageIndex: 0),
size: size)
measurementBuilder.setSize(key: measurementKey, size: size)