forked from shadowfacts/Tusker
186 lines
6.9 KiB
Swift
186 lines
6.9 KiB
Swift
//
|
|
// StatusMetaIndicatorsView.swift
|
|
// Tusker
|
|
//
|
|
// Created by Shadowfacts on 1/22/22.
|
|
// Copyright © 2022 Shadowfacts. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
import Pachyderm
|
|
|
|
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 statusID: String?
|
|
|
|
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)
|
|
}
|
|
|
|
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
|
super.traitCollectionDidChange(previousTraitCollection)
|
|
if isUsingSingleAxis != needsSingleAxis {
|
|
for image in images {
|
|
configureImageView(image)
|
|
}
|
|
placeImageViews(images)
|
|
}
|
|
}
|
|
|
|
@objc private func configureImageViews() {
|
|
for image in images {
|
|
configureImageView(image)
|
|
}
|
|
}
|
|
|
|
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) {
|
|
guard statusID != status.id else {
|
|
return
|
|
}
|
|
statusID = status.id
|
|
|
|
var images: [UIImage] = []
|
|
|
|
if allowedIndicators.contains(.reply) && Preferences.shared.showIsStatusReplyIcon && status.inReplyToID != nil {
|
|
images.append(UIImage(systemName: "bubble.left.and.bubble.right")!)
|
|
}
|
|
|
|
if allowedIndicators.contains(.visibility) && Preferences.shared.alwaysShowStatusVisibilityIcon {
|
|
images.append(UIImage(systemName: status.visibility.unfilledImageName)!)
|
|
}
|
|
|
|
if allowedIndicators.contains(.localOnly) && status.localOnly {
|
|
images.append(UIImage(named: "link.broken")!)
|
|
}
|
|
|
|
let views = images.map {
|
|
let v = UIImageView(image: $0)
|
|
v.translatesAutoresizingMaskIntoConstraints = false
|
|
v.contentMode = .scaleAspectFit
|
|
v.tintColor = .secondaryLabel
|
|
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 {
|
|
if index == images.count - 1 {
|
|
v.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
|
|
v.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor).isActive = true
|
|
} else {
|
|
v.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
|
|
}
|
|
} else {
|
|
if squeezeHorizontal {
|
|
v.leadingAnchor.constraint(equalTo: self.images[index - 1].trailingAnchor, constant: 4).isActive = true
|
|
} else {
|
|
v.leadingAnchor.constraint(greaterThanOrEqualTo: self.images[index - 1].trailingAnchor, constant: 4).isActive = true
|
|
}
|
|
v.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
|
|
}
|
|
|
|
let row = index / 2
|
|
if row == 0 {
|
|
v.topAnchor.constraint(equalTo: topAnchor).isActive = true
|
|
} else {
|
|
v.topAnchor.constraint(equalTo: self.images[index - 1].bottomAnchor, constant: 4).isActive = true
|
|
}
|
|
v.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor).isActive = true
|
|
}
|
|
}
|
|
|
|
struct Indicator: OptionSet {
|
|
let rawValue: Int
|
|
|
|
static let reply = Indicator(rawValue: 1 << 0)
|
|
static let visibility = Indicator(rawValue: 1 << 1)
|
|
static let localOnly = Indicator(rawValue: 1 << 2)
|
|
|
|
static let all: Indicator = [.reply, .visibility, .localOnly]
|
|
}
|
|
|
|
enum Alignment {
|
|
case leading, trailing
|
|
}
|
|
}
|