mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-21 03:43:16 +09:00
181 lines
7 KiB
Swift
181 lines
7 KiB
Swift
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
import Foundation
|
|
import Shared
|
|
import SnapKit
|
|
|
|
struct ButtonToastUX {
|
|
static let ToastPadding = 15.0
|
|
static let TitleSpacing = 2.0
|
|
static let ToastButtonPadding: CGFloat = 10.0
|
|
static let TitleButtonPadding: CGFloat = 5.0
|
|
static let ToastDelay = DispatchTimeInterval.milliseconds(900)
|
|
static let ToastButtonBorderRadius: CGFloat = 5
|
|
static let ToastButtonBorderWidth: CGFloat = 1
|
|
}
|
|
|
|
private class HighlightableButton: UIButton {
|
|
override var isHighlighted: Bool {
|
|
didSet {
|
|
if isHighlighted {
|
|
self.backgroundColor = UIColor.white
|
|
} else {
|
|
self.backgroundColor = UIColor.clear
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class ButtonToast: UIView {
|
|
|
|
fileprivate var dismissed = false
|
|
fileprivate var completionHandler: ((Bool) -> Void)?
|
|
fileprivate lazy var toast: UIView = {
|
|
let toast = UIView()
|
|
toast.backgroundColor = SimpleToastUX.ToastDefaultColor
|
|
return toast
|
|
}()
|
|
fileprivate var animationConstraint: Constraint?
|
|
fileprivate lazy var gestureRecognizer: UITapGestureRecognizer = {
|
|
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ButtonToast.handleTap(_:)))
|
|
gestureRecognizer.cancelsTouchesInView = false
|
|
return gestureRecognizer
|
|
}()
|
|
|
|
init(labelText: String, descriptionText: String? = nil, buttonText: String, completion:@escaping (_ buttonPressed: Bool) -> Void) {
|
|
super.init(frame: CGRect.zero)
|
|
completionHandler = completion
|
|
|
|
self.clipsToBounds = true
|
|
self.addSubview(createView(labelText, descriptionText: descriptionText, buttonText: buttonText))
|
|
|
|
toast.snp.makeConstraints { make in
|
|
make.left.right.height.equalTo(self)
|
|
animationConstraint = make.top.equalTo(self).offset(SimpleToastUX.ToastHeight).constraint
|
|
}
|
|
self.snp.makeConstraints { make in
|
|
make.height.equalTo(SimpleToastUX.ToastHeight)
|
|
}
|
|
}
|
|
|
|
required init?(coder aDecoder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
fileprivate func createView(_ labelText: String, descriptionText: String?, buttonText: String) -> UIView {
|
|
let label = UILabel()
|
|
label.textColor = UIColor.white
|
|
label.font = SimpleToastUX.ToastFont
|
|
label.text = labelText
|
|
label.lineBreakMode = .byWordWrapping
|
|
label.numberOfLines = 0
|
|
toast.addSubview(label)
|
|
|
|
let button = HighlightableButton()
|
|
button.layer.cornerRadius = ButtonToastUX.ToastButtonBorderRadius
|
|
button.layer.borderWidth = ButtonToastUX.ToastButtonBorderWidth
|
|
button.layer.borderColor = UIColor.white.cgColor
|
|
button.setTitle(buttonText, for: UIControlState())
|
|
button.setTitleColor(self.toast.backgroundColor, for: .highlighted)
|
|
button.titleLabel?.font = SimpleToastUX.ToastFont
|
|
button.titleLabel?.numberOfLines = 1
|
|
button.titleLabel?.lineBreakMode = .byClipping
|
|
button.titleLabel?.adjustsFontSizeToFitWidth = true
|
|
button.titleLabel?.minimumScaleFactor = 0.1
|
|
|
|
let recognizer = UITapGestureRecognizer(target: self, action: #selector(ButtonToast.buttonPressed(_:)))
|
|
button.addGestureRecognizer(recognizer)
|
|
toast.addSubview(button)
|
|
var descriptionLabel: UILabel?
|
|
|
|
if let text = descriptionText {
|
|
let textLabel = UILabel()
|
|
textLabel.textColor = UIColor.white
|
|
textLabel.font = SimpleToastUX.ToastFont
|
|
textLabel.text = text
|
|
textLabel.lineBreakMode = .byTruncatingTail
|
|
toast.addSubview(textLabel)
|
|
descriptionLabel = textLabel
|
|
}
|
|
|
|
if let description = descriptionLabel {
|
|
label.numberOfLines = 1 // if showing a description we cant wrap to the second line
|
|
label.lineBreakMode = .byClipping
|
|
label.adjustsFontSizeToFitWidth = true
|
|
label.snp.makeConstraints { (make) in
|
|
make.leading.equalTo(toast).offset(ButtonToastUX.ToastPadding)
|
|
make.top.equalTo(toast).offset(ButtonToastUX.TitleButtonPadding)
|
|
make.trailing.equalTo(button.snp.leading).offset(-ButtonToastUX.TitleButtonPadding)
|
|
}
|
|
description.snp.makeConstraints { (make) in
|
|
make.leading.equalTo(toast).offset(ButtonToastUX.ToastPadding)
|
|
make.top.equalTo(label.snp.bottom).offset(ButtonToastUX.TitleSpacing)
|
|
make.trailing.equalTo(button.snp.leading).offset(-ButtonToastUX.TitleButtonPadding)
|
|
}
|
|
} else {
|
|
label.snp.makeConstraints { (make) in
|
|
make.leading.equalTo(toast).offset(ButtonToastUX.ToastPadding)
|
|
make.centerY.equalTo(toast)
|
|
make.trailing.equalTo(button.snp.leading).offset(-ButtonToastUX.TitleButtonPadding)
|
|
}
|
|
}
|
|
|
|
button.snp.makeConstraints { (make) in
|
|
make.trailing.equalTo(toast).offset(-ButtonToastUX.ToastPadding)
|
|
make.centerY.equalTo(toast)
|
|
make.width.equalTo(button.titleLabel!.intrinsicContentSize.width + 2*ButtonToastUX.ToastButtonPadding)
|
|
}
|
|
|
|
return toast
|
|
}
|
|
|
|
fileprivate func dismiss(_ buttonPressed: Bool) {
|
|
guard dismissed == false else {
|
|
return
|
|
}
|
|
dismissed = true
|
|
superview?.removeGestureRecognizer(gestureRecognizer)
|
|
|
|
UIView.animate(withDuration: SimpleToastUX.ToastAnimationDuration, animations: {
|
|
self.animationConstraint?.update(offset: SimpleToastUX.ToastHeight)
|
|
self.layoutIfNeeded()
|
|
},
|
|
completion: { finished in
|
|
self.removeFromSuperview()
|
|
if !buttonPressed {
|
|
self.completionHandler?(false)
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
func showToast(duration: DispatchTimeInterval = SimpleToastUX.ToastDismissAfter) {
|
|
layoutIfNeeded()
|
|
UIView.animate(withDuration: SimpleToastUX.ToastAnimationDuration, animations: {
|
|
self.animationConstraint?.update(offset: 0)
|
|
self.layoutIfNeeded()
|
|
},
|
|
completion: { finished in
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + duration) {
|
|
self.dismiss(false)
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
@objc func buttonPressed(_ gestureRecognizer: UIGestureRecognizer) {
|
|
self.completionHandler?(true)
|
|
self.dismiss(true)
|
|
}
|
|
|
|
override func didMoveToSuperview() {
|
|
super.didMoveToSuperview()
|
|
superview?.addGestureRecognizer(gestureRecognizer)
|
|
}
|
|
|
|
func handleTap(_ gestureRecognizer: UIGestureRecognizer) {
|
|
dismiss(false)
|
|
}
|
|
}
|