Files
SideStore/SideStoreApp/Sources/SideStoreAppKit/Components/PillButton.swift

173 lines
4.9 KiB
Swift
Raw Normal View History

2023-03-01 00:48:36 -05:00
//
// PillButton.swift
// AltStore
//
// Created by Riley Testut on 7/15/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import UIKit
final class PillButton: UIButton {
override var accessibilityValue: String? {
get {
guard progress != nil else { return super.accessibilityValue }
return progressView.accessibilityValue
}
set { super.accessibilityValue = newValue }
}
var progress: Progress? {
didSet {
progressView.progress = Float(progress?.fractionCompleted ?? 0)
progressView.observedProgress = progress
let isUserInteractionEnabled = self.isUserInteractionEnabled
isIndicatingActivity = (progress != nil)
self.isUserInteractionEnabled = isUserInteractionEnabled
update()
}
}
var progressTintColor: UIColor? {
get {
progressView.progressTintColor
}
set {
progressView.progressTintColor = newValue
}
}
var countdownDate: Date? {
didSet {
isEnabled = (countdownDate == nil)
displayLink.isPaused = (countdownDate == nil)
if countdownDate == nil {
setTitle(nil, for: .disabled)
}
}
}
private let progressView = UIProgressView(progressViewStyle: .default)
private lazy var displayLink: CADisplayLink = {
let displayLink = CADisplayLink(target: self, selector: #selector(PillButton.updateCountdown))
displayLink.preferredFramesPerSecond = 15
displayLink.isPaused = true
displayLink.add(to: .main, forMode: .common)
return displayLink
}()
private let dateComponentsFormatter: DateComponentsFormatter = {
let dateComponentsFormatter = DateComponentsFormatter()
dateComponentsFormatter.zeroFormattingBehavior = [.pad]
dateComponentsFormatter.collapsesLargestUnit = false
return dateComponentsFormatter
}()
override var intrinsicContentSize: CGSize {
var size = super.intrinsicContentSize
size.width += 26
size.height += 3
return size
}
deinit {
self.displayLink.remove(from: .main, forMode: RunLoop.Mode.default)
}
override func awakeFromNib() {
super.awakeFromNib()
layer.masksToBounds = true
accessibilityTraits.formUnion([.updatesFrequently, .button])
activityIndicatorView.style = .medium
activityIndicatorView.isUserInteractionEnabled = false
progressView.progress = 0
progressView.trackImage = UIImage()
progressView.isUserInteractionEnabled = false
addSubview(progressView)
update()
}
override func layoutSubviews() {
super.layoutSubviews()
progressView.bounds.size.width = bounds.width
let scale = bounds.height / progressView.bounds.height
progressView.transform = CGAffineTransform.identity.scaledBy(x: 1, y: scale)
progressView.center = CGPoint(x: bounds.midX, y: bounds.midY)
layer.cornerRadius = bounds.midY
}
override func tintColorDidChange() {
super.tintColorDidChange()
update()
}
}
private extension PillButton {
func update() {
if progress == nil {
setTitleColor(.white, for: .normal)
backgroundColor = tintColor
} else {
setTitleColor(tintColor, for: .normal)
backgroundColor = tintColor.withAlphaComponent(0.15)
}
progressView.progressTintColor = tintColor
}
@objc func updateCountdown() {
guard let endDate = countdownDate else { return }
let startDate = Date()
let interval = endDate.timeIntervalSince(startDate)
guard interval > 0 else {
isEnabled = true
return
}
let text: String?
if interval < (1 * 60 * 60) {
dateComponentsFormatter.unitsStyle = .positional
dateComponentsFormatter.allowedUnits = [.minute, .second]
text = dateComponentsFormatter.string(from: startDate, to: endDate)
} else if interval < (2 * 24 * 60 * 60) {
dateComponentsFormatter.unitsStyle = .positional
dateComponentsFormatter.allowedUnits = [.hour, .minute, .second]
text = dateComponentsFormatter.string(from: startDate, to: endDate)
} else {
dateComponentsFormatter.unitsStyle = .full
dateComponentsFormatter.allowedUnits = [.day]
let numberOfDays = endDate.numberOfCalendarDays(since: startDate)
text = String(format: NSLocalizedString("%@ DAYS", comment: ""), NSNumber(value: numberOfDays))
}
if let text = text {
UIView.performWithoutAnimation {
self.isEnabled = false
self.setTitle(text, for: .disabled)
self.layoutIfNeeded()
}
} else {
isEnabled = true
}
}
}