1
0
mirror of https://github.com/mastodon/mastodon-ios.git synced 2024-12-15 02:14:50 +01:00
mastodon-app-ufficiale-ipho.../MastodonSDK/Sources/MastodonUI/View/Content/StatusCardControl.swift
2022-12-02 16:02:05 -05:00

200 lines
6.4 KiB
Swift

//
// OpenGraphView.swift
//
//
// Created by Kyle Bashour on 11/11/22.
//
import AlamofireImage
import Combine
import MastodonAsset
import MastodonCore
import CoreDataStack
import UIKit
public final class StatusCardControl: UIControl {
private var disposeBag = Set<AnyCancellable>()
private let containerStackView = UIStackView()
private let labelStackView = UIStackView()
private let highlightView = UIView()
private let imageView = UIImageView()
private let titleLabel = UILabel()
private let linkLabel = UILabel()
private var layout: Layout?
private var layoutConstraints: [NSLayoutConstraint] = []
public override var isHighlighted: Bool {
didSet { highlightView.isHidden = !isHighlighted }
}
public override init(frame: CGRect) {
super.init(frame: frame)
apply(theme: ThemeService.shared.currentTheme.value)
ThemeService.shared.currentTheme.sink { [weak self] theme in
self?.apply(theme: theme)
}.store(in: &disposeBag)
clipsToBounds = true
layer.cornerCurve = .continuous
layer.cornerRadius = 10
if #available(iOS 15, *) {
maximumContentSizeCategory = .accessibilityLarge
}
highlightView.backgroundColor = UIColor.black.withAlphaComponent(0.1)
highlightView.isHidden = true
titleLabel.numberOfLines = 2
titleLabel.textColor = Asset.Colors.Label.primary.color
titleLabel.font = .preferredFont(forTextStyle: .body)
linkLabel.numberOfLines = 1
linkLabel.textColor = Asset.Colors.Label.secondary.color
linkLabel.font = .preferredFont(forTextStyle: .subheadline)
imageView.tintColor = Asset.Colors.Label.secondary.color
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.setContentHuggingPriority(.zero, for: .horizontal)
imageView.setContentHuggingPriority(.zero, for: .vertical)
imageView.setContentCompressionResistancePriority(.zero, for: .horizontal)
imageView.setContentCompressionResistancePriority(.zero, for: .vertical)
labelStackView.addArrangedSubview(titleLabel)
labelStackView.addArrangedSubview(linkLabel)
labelStackView.layoutMargins = .init(top: 10, left: 10, bottom: 10, right: 10)
labelStackView.isLayoutMarginsRelativeArrangement = true
labelStackView.axis = .vertical
labelStackView.spacing = 2
containerStackView.addArrangedSubview(imageView)
containerStackView.addArrangedSubview(labelStackView)
containerStackView.isUserInteractionEnabled = false
containerStackView.distribution = .fill
addSubview(containerStackView)
addSubview(highlightView)
containerStackView.translatesAutoresizingMaskIntoConstraints = false
highlightView.translatesAutoresizingMaskIntoConstraints = false
containerStackView.pinToParent()
highlightView.pinToParent()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func configure(card: Card) {
if let host = card.url?.host {
accessibilityLabel = "\(card.title) \(host)"
} else {
accessibilityLabel = card.title
}
titleLabel.text = card.title
linkLabel.text = card.url?.host
imageView.contentMode = .center
imageView.sd_setImage(
with: card.imageURL,
placeholderImage: icon(for: card.layout)
) { [weak self] image, _, _, _ in
if image != nil {
self?.imageView.contentMode = .scaleAspectFill
}
self?.containerStackView.setNeedsLayout()
self?.containerStackView.layoutIfNeeded()
}
updateConstraints(for: card.layout)
}
public override func didMoveToWindow() {
super.didMoveToWindow()
if let window = window {
layer.borderWidth = 1 / window.screen.scale
}
}
private func updateConstraints(for layout: Layout) {
guard layout != self.layout else { return }
self.layout = layout
NSLayoutConstraint.deactivate(layoutConstraints)
switch layout {
case .large(let aspectRatio):
containerStackView.alignment = .fill
containerStackView.axis = .vertical
layoutConstraints = [
imageView.widthAnchor.constraint(
equalTo: imageView.heightAnchor,
multiplier: aspectRatio
)
// This priority is important or constraints break;
// it still renders the card correctly.
.priority(.defaultLow - 1),
// set a reasonable max height for very tall images
imageView.heightAnchor
.constraint(lessThanOrEqualToConstant: 400)
]
case .compact:
containerStackView.alignment = .center
containerStackView.axis = .horizontal
layoutConstraints = [
imageView.heightAnchor.constraint(equalTo: heightAnchor),
imageView.widthAnchor.constraint(equalToConstant: 85),
heightAnchor.constraint(equalToConstant: 85).priority(.defaultLow - 1),
heightAnchor.constraint(greaterThanOrEqualToConstant: 85)
]
}
NSLayoutConstraint.activate(layoutConstraints)
}
private func icon(for layout: Layout) -> UIImage? {
switch layout {
case .compact:
return UIImage(systemName: "newspaper.fill")
case .large:
let configuration = UIImage.SymbolConfiguration(pointSize: 32)
return UIImage(systemName: "photo", withConfiguration: configuration)
}
}
private func apply(theme: Theme) {
layer.borderColor = theme.separator.cgColor
imageView.backgroundColor = theme.systemElevatedBackgroundColor
}
}
private extension StatusCardControl {
enum Layout: Equatable {
case compact
case large(aspectRatio: CGFloat)
}
}
private extension Card {
var layout: StatusCardControl.Layout {
let aspectRatio = CGFloat(width) / CGFloat(height)
return abs(aspectRatio - 1) < 0.05 || image == nil
? .compact
: .large(aspectRatio: aspectRatio)
}
}
private extension UILayoutPriority {
static let zero = UILayoutPriority(rawValue: 0)
}