mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
- Fixes: disabled animations for the CollapsingMarkdownView when expanding/collapsing due to visual glitches
This commit is contained in:
@@ -101,35 +101,56 @@ 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() {
|
||||||
// Update the button title
|
// Disable animations for this update to prevent gradual rearrangement
|
||||||
let title = isCollapsed ? NSLocalizedString("More", comment: "") : NSLocalizedString("Less", comment: "")
|
UIView.performWithoutAnimation {
|
||||||
toggleButton.setTitle(title, for: .normal)
|
// Update the button title
|
||||||
|
let title = isCollapsed ? NSLocalizedString("More", comment: "") : NSLocalizedString("Less", comment: "")
|
||||||
|
toggleButton.setTitle(title, for: .normal)
|
||||||
|
|
||||||
// Update text view constraints
|
// Make sure toggle button is only visible when needed
|
||||||
if isCollapsed {
|
toggleButton.isHidden = !needsCollapsing
|
||||||
textView.textContainer.maximumNumberOfLines = maximumNumberOfLines
|
|
||||||
|
|
||||||
// Create exclusion path for button
|
// Update text view constraints
|
||||||
let buttonSize = toggleButton.sizeThatFits(CGSize(width: 1000, height: 1000))
|
if isCollapsed && needsCollapsing {
|
||||||
let buttonY = (textView.font?.lineHeight ?? 0) * CGFloat(maximumNumberOfLines - 1)
|
textView.textContainer.maximumNumberOfLines = maximumNumberOfLines
|
||||||
|
|
||||||
let exclusionFrame = CGRect(
|
// Create exclusion path for button
|
||||||
x: bounds.width - buttonSize.width - 5, // Add some padding
|
let buttonSize = toggleButton.sizeThatFits(CGSize(width: 1000, height: 1000))
|
||||||
y: buttonY,
|
let buttonY = (textView.font?.lineHeight ?? 0) * CGFloat(maximumNumberOfLines - 1)
|
||||||
width: buttonSize.width + 10, // Add padding around button
|
|
||||||
height: (textView.font?.lineHeight ?? 0) + 5
|
|
||||||
)
|
|
||||||
|
|
||||||
textView.textContainer.exclusionPaths = [UIBezierPath(rect: exclusionFrame)]
|
let exclusionFrame = CGRect(
|
||||||
} else {
|
x: bounds.width - buttonSize.width - 5, // Add some padding
|
||||||
textView.textContainer.maximumNumberOfLines = 0
|
y: buttonY,
|
||||||
textView.textContainer.exclusionPaths = []
|
width: buttonSize.width + 10, // Add padding around button
|
||||||
|
height: (textView.font?.lineHeight ?? 0) + 5
|
||||||
|
)
|
||||||
|
|
||||||
|
textView.textContainer.exclusionPaths = [UIBezierPath(rect: exclusionFrame)]
|
||||||
|
} else {
|
||||||
|
textView.textContainer.maximumNumberOfLines = 0
|
||||||
|
textView.textContainer.exclusionPaths = []
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force an immediate layout update
|
||||||
|
textView.layoutIfNeeded()
|
||||||
|
self.layoutIfNeeded()
|
||||||
|
|
||||||
|
// Invalidate intrinsic content size to ensure proper sizing
|
||||||
|
self.invalidateIntrinsicContentSize()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force layout update
|
|
||||||
textView.layoutIfNeeded()
|
|
||||||
self.invalidateIntrinsicContentSize()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func initialize() {
|
private func initialize() {
|
||||||
@@ -187,35 +208,53 @@ final class CollapsingMarkdownView: UIView {
|
|||||||
override func layoutSubviews() {
|
override func layoutSubviews() {
|
||||||
super.layoutSubviews()
|
super.layoutSubviews()
|
||||||
|
|
||||||
textView.frame = bounds
|
UIView.performWithoutAnimation {
|
||||||
|
textView.frame = bounds
|
||||||
|
|
||||||
// Position toggle button
|
// Check if content needs collapsing when layout changes
|
||||||
let buttonSize = toggleButton.sizeThatFits(CGSize(width: 1000, height: 1000))
|
if shouldResetLayout || previousSize?.width != bounds.width {
|
||||||
|
checkIfNeedsCollapsing()
|
||||||
|
shouldResetLayout = false
|
||||||
|
previousSize = bounds.size
|
||||||
|
}
|
||||||
|
|
||||||
if isCollapsed {
|
// Only position toggle button if it's needed
|
||||||
let buttonY = (textView.font?.lineHeight ?? 0) * CGFloat(maximumNumberOfLines - 1)
|
if !toggleButton.isHidden {
|
||||||
toggleButton.frame = CGRect(
|
let buttonSize = toggleButton.sizeThatFits(CGSize(width: 1000, height: 1000))
|
||||||
x: bounds.width - buttonSize.width,
|
|
||||||
y: buttonY,
|
if isCollapsed {
|
||||||
width: buttonSize.width,
|
let buttonY = (textView.font?.lineHeight ?? 0) * CGFloat(maximumNumberOfLines - 1)
|
||||||
height: textView.font?.lineHeight ?? 0
|
toggleButton.frame = CGRect(
|
||||||
)
|
x: bounds.width - buttonSize.width,
|
||||||
} else {
|
y: buttonY,
|
||||||
// Position at the end of content when expanded
|
width: buttonSize.width,
|
||||||
let textHeight = textView.sizeThatFits(bounds.size).height
|
height: textView.font?.lineHeight ?? 0
|
||||||
let lineHeight = textView.font?.lineHeight ?? 0
|
)
|
||||||
toggleButton.frame = CGRect(
|
} else {
|
||||||
x: bounds.width - buttonSize.width,
|
// Position at the end of content when expanded
|
||||||
y: textHeight - lineHeight,
|
let textHeight = textView.sizeThatFits(bounds.size).height
|
||||||
width: buttonSize.width,
|
let lineHeight = textView.font?.lineHeight ?? 0
|
||||||
height: lineHeight
|
toggleButton.frame = CGRect(
|
||||||
)
|
x: bounds.width - buttonSize.width,
|
||||||
|
y: textHeight - lineHeight,
|
||||||
|
width: buttonSize.width,
|
||||||
|
height: lineHeight
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func toggleCollapsed(_ sender: UIButton) {
|
@objc private func toggleCollapsed(_ sender: UIButton) {
|
||||||
|
// Toggle the state instantly
|
||||||
isCollapsed.toggle()
|
isCollapsed.toggle()
|
||||||
updateToggleButtonTitle()
|
|
||||||
|
// Update the UI without animation
|
||||||
|
UIView.performWithoutAnimation {
|
||||||
|
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()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user