144 lines
5.9 KiB
Swift
144 lines
5.9 KiB
Swift
//
|
|
// WizardCardView.swift
|
|
// Mastodon
|
|
//
|
|
// Created by Cirno MainasuK on 2021-9-15.
|
|
//
|
|
|
|
import UIKit
|
|
import MastodonAsset
|
|
import MastodonLocalization
|
|
|
|
final class WizardCardView: UIView {
|
|
|
|
static let bubbleArrowHeight: CGFloat = 17
|
|
static let bubbleArrowWidth: CGFloat = 20
|
|
|
|
let contentView = UIView()
|
|
|
|
let backgroundShapeLayer = CAShapeLayer()
|
|
var arrowRectCorner: UIRectCorner = .bottomRight
|
|
|
|
let titleLabel: UILabel = {
|
|
let label = UILabel()
|
|
label.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .semibold))
|
|
label.textColor = .black
|
|
return label
|
|
}()
|
|
|
|
let descriptionLabel: UILabel = {
|
|
let label = UILabel()
|
|
label.font = UIFontMetrics(forTextStyle: .subheadline).scaledFont(for: .systemFont(ofSize: 13, weight: .regular))
|
|
label.textColor = .black
|
|
label.numberOfLines = 0
|
|
return label
|
|
}()
|
|
|
|
override init(frame: CGRect) {
|
|
super.init(frame: frame)
|
|
_init()
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
super.init(coder: coder)
|
|
_init()
|
|
}
|
|
|
|
}
|
|
|
|
extension WizardCardView {
|
|
private func _init() {
|
|
layer.masksToBounds = false
|
|
layer.addSublayer(backgroundShapeLayer)
|
|
|
|
contentView.translatesAutoresizingMaskIntoConstraints = false
|
|
addSubview(contentView)
|
|
NSLayoutConstraint.activate([
|
|
contentView.topAnchor.constraint(equalTo: topAnchor, constant: WizardCardView.bubbleArrowHeight),
|
|
contentView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
|
trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
|
|
bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: WizardCardView.bubbleArrowHeight),
|
|
])
|
|
|
|
let containerStackView = UIStackView()
|
|
containerStackView.axis = .vertical
|
|
containerStackView.spacing = 2
|
|
containerStackView.translatesAutoresizingMaskIntoConstraints = false
|
|
contentView.addSubview(containerStackView)
|
|
NSLayoutConstraint.activate([
|
|
containerStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5),
|
|
containerStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 7),
|
|
contentView.trailingAnchor.constraint(equalTo: containerStackView.trailingAnchor, constant: 24),
|
|
contentView.bottomAnchor.constraint(equalTo: containerStackView.bottomAnchor, constant: 5),
|
|
])
|
|
|
|
containerStackView.addArrangedSubview(titleLabel)
|
|
containerStackView.addArrangedSubview(descriptionLabel)
|
|
}
|
|
|
|
override func layoutSubviews() {
|
|
super.layoutSubviews()
|
|
|
|
let radius: CGFloat = 5
|
|
let rect = contentView.frame
|
|
let path = UIBezierPath()
|
|
|
|
switch arrowRectCorner {
|
|
case .bottomRight:
|
|
path.move(to: CGPoint(x: rect.maxX - WizardCardView.bubbleArrowWidth, y: rect.maxY + radius))
|
|
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.maxY), radius: radius, startAngle: .pi / 2, endAngle: .pi, clockwise: true)
|
|
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.minY), radius: radius, startAngle: .pi, endAngle: .pi / 2 * 3, clockwise: true)
|
|
path.addArc(withCenter: CGPoint(x: rect.maxX, y: rect.minY), radius: radius, startAngle: .pi / 2 * 3, endAngle: .pi * 2, clockwise: true)
|
|
path.addLine(to: CGPoint(x: rect.maxX + radius, y: rect.maxY + radius + WizardCardView.bubbleArrowHeight))
|
|
path.close()
|
|
case .bottomLeft:
|
|
path.move(to: CGPoint(x: rect.minX + WizardCardView.bubbleArrowWidth, y: rect.maxY + radius))
|
|
path.addArc(withCenter: CGPoint(x: rect.maxX, y: rect.maxY), radius: radius, startAngle: .pi / 2, endAngle: 0, clockwise: false)
|
|
path.addArc(withCenter: CGPoint(x: rect.maxX, y: rect.minY), radius: radius, startAngle: 0, endAngle: -.pi / 2, clockwise: false)
|
|
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.minY), radius: radius, startAngle: -.pi / 2, endAngle: -.pi, clockwise: false)
|
|
path.addLine(to: CGPoint(x: rect.minX - radius, y: rect.maxY + radius + WizardCardView.bubbleArrowHeight))
|
|
path.close()
|
|
case .allCorners: // no arrow
|
|
path.move(to: CGPoint(x: rect.maxX, y: rect.maxY + radius))
|
|
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.maxY), radius: radius, startAngle: .pi / 2, endAngle: .pi, clockwise: true)
|
|
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.minY), radius: radius, startAngle: .pi, endAngle: .pi / 2 * 3, clockwise: true)
|
|
path.addArc(withCenter: CGPoint(x: rect.maxX, y: rect.minY), radius: radius, startAngle: .pi / 2 * 3, endAngle: .pi * 2, clockwise: true)
|
|
path.addArc(withCenter: CGPoint(x: rect.maxX, y: rect.maxY), radius: radius, startAngle: .pi * 2, endAngle: .pi / 2 * 5, clockwise: true)
|
|
path.close()
|
|
default:
|
|
assertionFailure("FIXME")
|
|
}
|
|
|
|
backgroundShapeLayer.lineCap = .round
|
|
backgroundShapeLayer.lineJoin = .round
|
|
backgroundShapeLayer.lineWidth = 3
|
|
backgroundShapeLayer.strokeColor = UIColor.white.cgColor
|
|
backgroundShapeLayer.fillColor = UIColor.white.cgColor
|
|
backgroundShapeLayer.path = path.cgPath
|
|
}
|
|
|
|
override var isAccessibilityElement: Bool {
|
|
get { true }
|
|
set { }
|
|
}
|
|
|
|
override var accessibilityLabel: String? {
|
|
get {
|
|
return [
|
|
titleLabel.text,
|
|
descriptionLabel.text
|
|
]
|
|
.compactMap { $0 }
|
|
.joined(separator: " ")
|
|
}
|
|
set { }
|
|
}
|
|
|
|
override var accessibilityHint: String? {
|
|
get {
|
|
return L10n.Scene.Wizard.accessibilityHint
|
|
}
|
|
set { }
|
|
}
|
|
}
|