Dynamic Type support in status cells

This commit is contained in:
Shadowfacts 2022-11-03 22:15:54 -04:00
parent bfdce07d81
commit a991e0f429
8 changed files with 185 additions and 29 deletions

View File

@ -292,6 +292,8 @@ class AttachmentsContainerView: UIView {
let imageView = UIImageView(image: image)
imageView.translatesAutoresizingMaskIntoConstraints = false
let label = UILabel()
label.font = .preferredFont(forTextStyle: .body)
label.adjustsFontForContentSizeCategory = true
label.text = "Sensitive Content"
let stack = UIStackView(arrangedSubviews: [
imageView,

View File

@ -49,16 +49,35 @@ class ConversationMainStatusTableViewCell: BaseStatusTableViewCell {
moreButton!,
]
contentTextView.defaultFont = .systemFont(ofSize: 18)
profileDetailContainerView.addInteraction(UIContextMenuInteraction(delegate: self))
displayNameLabel.font = UIFontMetrics(forTextStyle: .title1).scaledFont(for: .systemFont(ofSize: 24, weight: .semibold))
displayNameLabel.adjustsFontForContentSizeCategory = true
usernameLabel.font = UIFontMetrics(forTextStyle: .title2).scaledFont(for: .systemFont(ofSize: 17, weight: .light))
usernameLabel.adjustsFontForContentSizeCategory = true
metaIndicatorsView.allowedIndicators = [.visibility, .localOnly]
metaIndicatorsView.squeezeHorizontal = true
metaIndicatorsView.primaryAxis = .horizontal
contentWarningLabel.font = .preferredFont(forTextStyle: .body).withTraits(.traitBold)!
contentWarningLabel.adjustsFontForContentSizeCategory = true
contentTextView.defaultFont = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 18))
contentTextView.adjustsFontForContentSizeCategory = true
contentTextView.dataDetectorTypes = [.flightNumber, .address, .shipmentTrackingNumber, .phoneNumber]
if #available(iOS 16.0, *) {
contentTextView.dataDetectorTypes.formUnion([.money, .physicalValue])
}
profileDetailContainerView.addInteraction(UIContextMenuInteraction(delegate: self))
metaIndicatorsView.allowedIndicators = [.visibility, .localOnly]
metaIndicatorsView.squeezeHorizontal = true
let metaFont = UIFontMetrics(forTextStyle: .caption1).scaledFont(for: .systemFont(ofSize: 15))
totalFavoritesButton.titleLabel!.font = metaFont
totalFavoritesButton.titleLabel!.adjustsFontForContentSizeCategory = true
totalReblogsButton.titleLabel!.font = metaFont
totalReblogsButton.titleLabel!.adjustsFontForContentSizeCategory = true
timestampAndClientLabel.font = metaFont
timestampAndClientLabel.adjustsFontForContentSizeCategory = true
}
override func doUpdateUI(status: StatusMO, state: StatusState) {

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<capability name="Image references" minToolsVersion="12.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
@ -261,13 +261,13 @@
</view>
</objects>
<resources>
<image name="arrowshape.turn.up.left.fill" catalog="system" width="128" height="106"/>
<image name="chevron.down" catalog="system" width="128" height="72"/>
<image name="arrowshape.turn.up.left.fill" catalog="system" width="128" height="104"/>
<image name="chevron.down" catalog="system" width="128" height="70"/>
<image name="ellipsis" catalog="system" width="128" height="37"/>
<image name="repeat" catalog="system" width="128" height="98"/>
<image name="star.fill" catalog="system" width="128" height="116"/>
<systemColor name="labelColor">
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="opaqueSeparatorColor">
<color red="0.77647058823529413" green="0.77647058823529413" blue="0.78431372549019607" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>

View File

@ -51,10 +51,12 @@ class StatusCardView: UIView {
titleLabel = UILabel()
titleLabel.font = UIFont(descriptor: UIFontDescriptor.preferredFontDescriptor(withTextStyle: .subheadline).withSymbolicTraits(.traitBold)!, size: 0)
titleLabel.adjustsFontForContentSizeCategory = true
titleLabel.numberOfLines = 2
descriptionLabel = UILabel()
descriptionLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .caption1), size: 0)
descriptionLabel.adjustsFontForContentSizeCategory = true
descriptionLabel.numberOfLines = 2
descriptionLabel.setContentCompressionResistancePriority(.defaultLow, for: .vertical)

View File

@ -11,7 +11,8 @@ import UIKit
class StatusContentContainer: UIView {
let contentTextView = StatusContentTextView().configure {
$0.defaultFont = .systemFont(ofSize: 16)
$0.defaultFont = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 16))
$0.adjustsFontForContentSizeCategory = true
$0.isScrollEnabled = false
$0.backgroundColor = nil
$0.isEditable = false

View File

@ -13,11 +13,48 @@ class StatusMetaIndicatorsView: UIView {
var allowedIndicators: Indicator = .all
var squeezeHorizontal = false
// The axis in which the indicators grow
var primaryAxis: NSLayoutConstraint.Axis = .vertical
// Only used when using single axis mode
var secondaryAxisAlignment: Alignment = .leading
private var images: [UIImageView] = []
private var isUsingSingleAxis = false
private var needsSingleAxis: Bool {
traitCollection.preferredContentSizeCategory > .extraLarge
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
NotificationCenter.default.addObserver(self, selector: #selector(configureImageViews), name: UIAccessibility.boldTextStatusDidChangeNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(configureImageViews), name: UIContentSizeCategory.didChangeNotification, object: nil)
}
@objc private func configureImageViews() {
for image in images {
configureImageView(image)
}
if isUsingSingleAxis != needsSingleAxis {
placeImageViews(images)
}
}
private func configureImageView(_ imageView: UIImageView) {
let weight: UIImage.SymbolWeight = UIAccessibility.isBoldTextEnabled ? .regular : traitCollection.preferredContentSizeCategory > .large ? .light : .thin
let scale: UIImage.SymbolScale = traitCollection.preferredContentSizeCategory > .extraLarge ? .large : .default
imageView.preferredSymbolConfiguration = .init(pointSize: 0, weight: weight, scale: scale)
}
func updateUI(status: StatusMO) {
images.forEach { $0.removeFromSuperview() }
var images: [UIImage] = []
if allowedIndicators.contains(.reply) && Preferences.shared.showIsStatusReplyIcon && status.inReplyToID != nil {
@ -32,13 +69,66 @@ class StatusMetaIndicatorsView: UIView {
images.append(UIImage(named: "link.broken")!)
}
self.images = []
for (index, image) in images.enumerated() {
let v = UIImageView(image: image)
let views = images.map {
let v = UIImageView(image: $0)
v.translatesAutoresizingMaskIntoConstraints = false
v.contentMode = .scaleAspectFit
v.tintColor = .secondaryLabel
v.preferredSymbolConfiguration = .init(weight: .thin)
configureImageView(v)
return v
}
placeImageViews(views)
}
private func placeImageViews(_ imageViews: [UIImageView]) {
images.forEach { $0.removeFromSuperview() }
images = imageViews
guard !images.isEmpty else {
return
}
isUsingSingleAxis = needsSingleAxis
if needsSingleAxis {
for v in images {
addSubview(v)
switch (primaryAxis, secondaryAxisAlignment) {
case (.horizontal, .leading):
v.topAnchor.constraint(equalTo: topAnchor).isActive = true
case (.horizontal, .trailing):
v.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
case (.vertical, .leading):
v.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
case (.vertical, .trailing):
v.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
case (_, _):
fatalError()
}
}
if primaryAxis == .vertical {
images.first!.topAnchor.constraint(equalTo: topAnchor).isActive = true
images.last!.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
} else {
images.first!.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
images.last!.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
}
for (a, b) in zip(images, images.dropFirst()) {
if primaryAxis == .vertical {
b.topAnchor.constraint(equalTo: a.bottomAnchor, constant: 4).isActive = true
} else {
b.leadingAnchor.constraint(equalTo: a.trailingAnchor, constant: 4).isActive = true
}
}
return
}
guard primaryAxis == .vertical || imageViews.count <= 2 else {
fatalError("StatusMetaIndicatorsView does not support horizontal primary axis with more than 2 views yet")
}
for (index, v) in images.enumerated() {
addSubview(v)
if index % 2 == 0 {
@ -64,14 +154,9 @@ class StatusMetaIndicatorsView: UIView {
v.topAnchor.constraint(equalTo: self.images[index - 1].bottomAnchor, constant: 4).isActive = true
}
v.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor).isActive = true
self.images.append(v)
}
}
}
extension StatusMetaIndicatorsView {
struct Indicator: OptionSet {
let rawValue: Int
@ -81,4 +166,8 @@ extension StatusMetaIndicatorsView {
static let all: Indicator = [.reply, .visibility, .localOnly]
}
enum Alignment {
case leading, trailing
}
}

View File

@ -18,6 +18,8 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
private lazy var reblogLabel = EmojiLabel().configure {
$0.textColor = .secondaryLabel
$0.font = .preferredFont(forTextStyle: .body)
$0.adjustsFontForContentSizeCategory = true
// this needs to have a higher priorty than the content container's zero height constraint
$0.setContentHuggingPriority(.defaultHigh, for: .vertical)
$0.isUserInteractionEnabled = true
@ -58,7 +60,10 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
$0.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
}
private let metaIndicatorsView = StatusMetaIndicatorsView()
private let metaIndicatorsView = StatusMetaIndicatorsView().configure {
$0.primaryAxis = .vertical
$0.secondaryAxisAlignment = .trailing
}
private lazy var contentVStack = UIStackView(arrangedSubviews: [
nameHStack,
@ -87,6 +92,7 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
UIFontDescriptor.TraitKey.weight: UIFont.Weight.semibold.rawValue,
]
]), size: 0)
$0.adjustsFontForContentSizeCategory = true
$0.setContentHuggingPriority(.init(251), for: .horizontal)
$0.setContentCompressionResistancePriority(.init(749), for: .horizontal)
}
@ -98,6 +104,7 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
UIFontDescriptor.TraitKey.weight: UIFont.Weight.light.rawValue,
]
]), size: 0)
$0.adjustsFontForContentSizeCategory = true
$0.setContentHuggingPriority(.init(249), for: .horizontal)
$0.setContentCompressionResistancePriority(.init(748), for: .horizontal)
}
@ -114,6 +121,7 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
UIFontDescriptor.TraitKey.weight: UIFont.Weight.light.rawValue,
]
]), size: 0)
$0.adjustsFontForContentSizeCategory = true
}
private(set) lazy var contentWarningLabel = EmojiLabel().configure {
@ -124,6 +132,7 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
UIFontDescriptor.TraitKey.weight: UIFont.Weight.bold.rawValue,
]
]), size: 0)
$0.adjustsFontForContentSizeCategory = true
// this needs to have a higher priorty than the content container's zero height constraint
$0.setContentHuggingPriority(.defaultHigh, for: .vertical)
$0.isUserInteractionEnabled = true

View File

@ -45,18 +45,52 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
reblogLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(reblogLabelPressed)))
isAccessibilityElement = true
// todo: double check this on RTL layouts
replyButton.imageView!.leadingAnchor.constraint(equalTo: contentTextView.leadingAnchor).isActive = true
contentTextView.defaultFont = .systemFont(ofSize: 16)
reblogLabel.font = .preferredFont(forTextStyle: .body)
reblogLabel.adjustsFontForContentSizeCategory = true
reblogLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(reblogLabelPressed)))
avatarImageView.addInteraction(UIContextMenuInteraction(delegate: self))
displayNameLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).addingAttributes([
.traits: [
UIFontDescriptor.TraitKey.weight: UIFont.Weight.semibold.rawValue,
]
]), size: 0)
displayNameLabel.adjustsFontForContentSizeCategory = true
usernameLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).addingAttributes([
.traits: [
UIFontDescriptor.TraitKey.weight: UIFont.Weight.light.rawValue,
]
]), size: 0)
usernameLabel.adjustsFontForContentSizeCategory = true
timestampLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).addingAttributes([
.traits: [
UIFontDescriptor.TraitKey.weight: UIFont.Weight.light.rawValue,
]
]), size: 0)
timestampLabel.adjustsFontForContentSizeCategory = true
metaIndicatorsView.primaryAxis = .vertical
metaIndicatorsView.secondaryAxisAlignment = .trailing
contentWarningLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).addingAttributes([
.traits: [
UIFontDescriptor.TraitKey.weight: UIFont.Weight.bold.rawValue,
]
]), size: 0)
contentWarningLabel.adjustsFontForContentSizeCategory = true
contentTextView.defaultFont = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 16))
contentTextView.adjustsFontForContentSizeCategory = true
// todo: double check this on RTL layouts
replyButton.imageView!.leadingAnchor.constraint(equalTo: contentTextView.leadingAnchor).isActive = true
updateActionsVisibility()
}