mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
107 lines
3.3 KiB
Swift
107 lines
3.3 KiB
Swift
//
|
|
// CollapsingTextView.swift
|
|
// AltStore
|
|
//
|
|
// Created by Riley Testut on 7/23/19.
|
|
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
|
|
final class CollapsingTextView: UITextView {
|
|
var isCollapsed = true {
|
|
didSet {
|
|
setNeedsLayout()
|
|
}
|
|
}
|
|
|
|
var maximumNumberOfLines = 2 {
|
|
didSet {
|
|
setNeedsLayout()
|
|
}
|
|
}
|
|
|
|
var lineSpacing: CGFloat = 2 {
|
|
didSet {
|
|
setNeedsLayout()
|
|
}
|
|
}
|
|
|
|
let moreButton = UIButton(type: .system)
|
|
|
|
override func awakeFromNib() {
|
|
super.awakeFromNib()
|
|
|
|
layoutManager.delegate = self
|
|
|
|
textContainerInset = .zero
|
|
textContainer.lineFragmentPadding = 0
|
|
textContainer.lineBreakMode = .byTruncatingTail
|
|
textContainer.heightTracksTextView = true
|
|
textContainer.widthTracksTextView = true
|
|
|
|
moreButton.setTitle(NSLocalizedString("More", comment: ""), for: .normal)
|
|
moreButton.addTarget(self, action: #selector(CollapsingTextView.toggleCollapsed(_:)), for: .primaryActionTriggered)
|
|
addSubview(moreButton)
|
|
|
|
setNeedsLayout()
|
|
}
|
|
|
|
override func layoutSubviews() {
|
|
super.layoutSubviews()
|
|
|
|
guard let font = font else { return }
|
|
|
|
let buttonFont = UIFont.systemFont(ofSize: font.pointSize, weight: .medium)
|
|
moreButton.titleLabel?.font = buttonFont
|
|
|
|
let buttonY = (font.lineHeight + lineSpacing) * CGFloat(maximumNumberOfLines - 1)
|
|
let size = moreButton.sizeThatFits(CGSize(width: 1000, height: 1000))
|
|
|
|
let moreButtonFrame = CGRect(x: bounds.width - moreButton.bounds.width,
|
|
y: buttonY,
|
|
width: size.width,
|
|
height: font.lineHeight)
|
|
moreButton.frame = moreButtonFrame
|
|
|
|
if isCollapsed {
|
|
textContainer.maximumNumberOfLines = maximumNumberOfLines
|
|
|
|
let boundingSize = attributedText.boundingRect(with: CGSize(width: textContainer.size.width, height: .infinity), options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil)
|
|
let maximumCollapsedHeight = font.lineHeight * Double(maximumNumberOfLines)
|
|
|
|
if boundingSize.height.rounded() > maximumCollapsedHeight.rounded() {
|
|
var exclusionFrame = moreButtonFrame
|
|
exclusionFrame.origin.y += moreButton.bounds.midY
|
|
exclusionFrame.size.width = bounds.width // Extra wide to make sure it wraps to next line.
|
|
textContainer.exclusionPaths = [UIBezierPath(rect: exclusionFrame)]
|
|
|
|
moreButton.isHidden = false
|
|
} else {
|
|
textContainer.exclusionPaths = []
|
|
|
|
moreButton.isHidden = true
|
|
}
|
|
} else {
|
|
textContainer.maximumNumberOfLines = 0
|
|
textContainer.exclusionPaths = []
|
|
|
|
moreButton.isHidden = true
|
|
}
|
|
|
|
invalidateIntrinsicContentSize()
|
|
}
|
|
}
|
|
|
|
private extension CollapsingTextView {
|
|
@objc func toggleCollapsed(_: UIButton) {
|
|
isCollapsed.toggle()
|
|
}
|
|
}
|
|
|
|
extension CollapsingTextView: NSLayoutManagerDelegate {
|
|
func layoutManager(_: NSLayoutManager, lineSpacingAfterGlyphAt _: Int, withProposedLineFragmentRect _: CGRect) -> CGFloat {
|
|
lineSpacing
|
|
}
|
|
}
|