- Fixes: disabled animations for the CollapsingMarkdownView when expanding/collapsing due to visual glitches

This commit is contained in:
mahee96
2025-02-28 01:02:41 +05:30
parent 2bea980d1f
commit cfaf79f878

View File

@@ -101,13 +101,30 @@ final class CollapsingMarkdownView: UIView {
initialize() initialize()
} }
private var needsCollapsing = false
private func checkIfNeedsCollapsing() {
let textSize = textView.sizeThatFits(CGSize(width: bounds.width, height: .greatestFiniteMagnitude))
let lineHeight = textView.font?.lineHeight ?? 0
let actualLines = textSize.height / lineHeight
needsCollapsing = actualLines > CGFloat(maximumNumberOfLines)
// Hide toggle button if no collapsing needed
toggleButton.isHidden = !needsCollapsing
}
private func updateCollapsedState() { private func updateCollapsedState() {
// Disable animations for this update to prevent gradual rearrangement
UIView.performWithoutAnimation {
// Update the button title // Update the button title
let title = isCollapsed ? NSLocalizedString("More", comment: "") : NSLocalizedString("Less", comment: "") let title = isCollapsed ? NSLocalizedString("More", comment: "") : NSLocalizedString("Less", comment: "")
toggleButton.setTitle(title, for: .normal) toggleButton.setTitle(title, for: .normal)
// Make sure toggle button is only visible when needed
toggleButton.isHidden = !needsCollapsing
// Update text view constraints // Update text view constraints
if isCollapsed { if isCollapsed && needsCollapsing {
textView.textContainer.maximumNumberOfLines = maximumNumberOfLines textView.textContainer.maximumNumberOfLines = maximumNumberOfLines
// Create exclusion path for button // Create exclusion path for button
@@ -127,10 +144,14 @@ final class CollapsingMarkdownView: UIView {
textView.textContainer.exclusionPaths = [] textView.textContainer.exclusionPaths = []
} }
// Force layout update // Force an immediate layout update
textView.layoutIfNeeded() textView.layoutIfNeeded()
self.layoutIfNeeded()
// Invalidate intrinsic content size to ensure proper sizing
self.invalidateIntrinsicContentSize() self.invalidateIntrinsicContentSize()
} }
}
private func initialize() { private func initialize() {
// Configure text view // Configure text view
@@ -187,9 +208,18 @@ final class CollapsingMarkdownView: UIView {
override func layoutSubviews() { override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
UIView.performWithoutAnimation {
textView.frame = bounds textView.frame = bounds
// Position toggle button // Check if content needs collapsing when layout changes
if shouldResetLayout || previousSize?.width != bounds.width {
checkIfNeedsCollapsing()
shouldResetLayout = false
previousSize = bounds.size
}
// Only position toggle button if it's needed
if !toggleButton.isHidden {
let buttonSize = toggleButton.sizeThatFits(CGSize(width: 1000, height: 1000)) let buttonSize = toggleButton.sizeThatFits(CGSize(width: 1000, height: 1000))
if isCollapsed { if isCollapsed {
@@ -212,10 +242,19 @@ final class CollapsingMarkdownView: UIView {
) )
} }
} }
}
}
@objc private func toggleCollapsed(_ sender: UIButton) { @objc private func toggleCollapsed(_ sender: UIButton) {
// Toggle the state instantly
isCollapsed.toggle() isCollapsed.toggle()
// Update the UI without animation
UIView.performWithoutAnimation {
updateToggleButtonTitle() updateToggleButtonTitle()
updateCollapsedState()
}
// Notify any observer that a toggle occurred // Notify any observer that a toggle occurred
didToggleCollapse?() didToggleCollapse?()
} }
@@ -248,6 +287,9 @@ final class CollapsingMarkdownView: UIView {
) )
textView.attributedText = mutableAttributedString textView.attributedText = mutableAttributedString
// Check if content needs collapsing after setting text
checkIfNeedsCollapsing()
} }
} }