You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
193 lines
7.1 KiB
193 lines
7.1 KiB
import UIKit
|
|
|
|
public class PlayerView: UIView {
|
|
private let titleLabel: UILabel = UILabel()
|
|
private let langLabel: UILabel = UILabel()
|
|
private let langContainer: UIView = UIView()
|
|
private let cnLabel: UILabel = UILabel()
|
|
private let enLabel: UILabel = UILabel()
|
|
private let stackView: UIStackView
|
|
private var lastDynamicTextLines: Int = -1
|
|
private let langRow: UIStackView = UIStackView()
|
|
private var lastBoundsSize: CGSize = .zero
|
|
private var lastLangText: String? = nil
|
|
|
|
public override init(frame: CGRect) {
|
|
stackView = UIStackView()
|
|
super.init(frame: frame)
|
|
setupUI()
|
|
}
|
|
|
|
public convenience init() {
|
|
self.init(frame: .zero)
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
stackView = UIStackView()
|
|
super.init(coder: coder)
|
|
setupUI()
|
|
}
|
|
|
|
private func setupUI() {
|
|
backgroundColor = UIColor(red: 40.0 / 255.0, green: 40.0 / 255.0, blue: 40.0 / 255.0, alpha: 90.0 / 255.0)
|
|
layer.cornerRadius = 16
|
|
layer.borderWidth = 1
|
|
layer.borderColor = UIColor(white: 1.0, alpha: 46.0 / 255.0).cgColor
|
|
layer.shadowColor = UIColor.black.cgColor
|
|
layer.shadowOpacity = 0.35
|
|
layer.shadowRadius = 12
|
|
layer.shadowOffset = CGSize(width: 0, height: 4)
|
|
|
|
titleLabel.text = "实时翻译"
|
|
titleLabel.font = .boldSystemFont(ofSize: 22)
|
|
titleLabel.textColor = UIColor(white: 1.0, alpha: 235.0 / 255.0)
|
|
titleLabel.textAlignment = .center
|
|
titleLabel.numberOfLines = 1
|
|
titleLabel.isHidden = true
|
|
|
|
langLabel.text = "src -> tgt"
|
|
langLabel.font = .systemFont(ofSize: 12)
|
|
langLabel.textColor = UIColor(white: 1.0, alpha: 235.0 / 255.0)
|
|
langLabel.textAlignment = .center
|
|
langLabel.numberOfLines = 1
|
|
langLabel.lineBreakMode = .byTruncatingTail
|
|
langLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
langLabel.setContentHuggingPriority(.required, for: .horizontal)
|
|
langLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
|
|
langContainer.backgroundColor = UIColor(white: 1.0, alpha: 36.0 / 255.0)
|
|
langContainer.layer.cornerRadius = 10
|
|
langContainer.translatesAutoresizingMaskIntoConstraints = false
|
|
langContainer.setContentHuggingPriority(.required, for: .horizontal)
|
|
langContainer.setContentCompressionResistancePriority(.required, for: .horizontal)
|
|
langContainer.addSubview(langLabel)
|
|
NSLayoutConstraint.activate([
|
|
langLabel.topAnchor.constraint(equalTo: langContainer.topAnchor, constant: 6),
|
|
langLabel.leadingAnchor.constraint(equalTo: langContainer.leadingAnchor, constant: 10),
|
|
langLabel.trailingAnchor.constraint(equalTo: langContainer.trailingAnchor, constant: -10),
|
|
langLabel.bottomAnchor.constraint(equalTo: langContainer.bottomAnchor, constant: -6),
|
|
])
|
|
|
|
cnLabel.font = .systemFont(ofSize: 14)
|
|
cnLabel.textColor = UIColor(white: 1.0, alpha: 235.0 / 255.0)
|
|
cnLabel.textAlignment = .left
|
|
cnLabel.numberOfLines = 0
|
|
cnLabel.lineBreakMode = .byTruncatingTail
|
|
|
|
enLabel.font = .systemFont(ofSize: 15)
|
|
enLabel.textColor = UIColor(red: 144.0 / 255.0, green: 202.0 / 255.0, blue: 249.0 / 255.0, alpha: 1.0)
|
|
enLabel.textAlignment = .left
|
|
enLabel.numberOfLines = 0
|
|
enLabel.lineBreakMode = .byTruncatingTail
|
|
|
|
let para = NSMutableParagraphStyle()
|
|
para.lineSpacing = 2
|
|
para.lineHeightMultiple = 1.1
|
|
cnLabel.attributedText = NSAttributedString(string: "", attributes: [.paragraphStyle: para])
|
|
enLabel.attributedText = NSAttributedString(string: "", attributes: [.paragraphStyle: para])
|
|
|
|
stackView.axis = .vertical
|
|
stackView.spacing = 10
|
|
stackView.alignment = .fill
|
|
stackView.distribution = .fill
|
|
stackView.translatesAutoresizingMaskIntoConstraints = false
|
|
stackView.addArrangedSubview(titleLabel)
|
|
langRow.axis = .horizontal
|
|
langRow.alignment = .fill
|
|
langRow.distribution = .fill
|
|
langRow.translatesAutoresizingMaskIntoConstraints = false
|
|
let leftSpacer = UIView()
|
|
let rightSpacer = UIView()
|
|
leftSpacer.translatesAutoresizingMaskIntoConstraints = false
|
|
rightSpacer.translatesAutoresizingMaskIntoConstraints = false
|
|
langRow.addArrangedSubview(leftSpacer)
|
|
langRow.addArrangedSubview(langContainer)
|
|
langRow.addArrangedSubview(rightSpacer)
|
|
NSLayoutConstraint.activate([
|
|
leftSpacer.widthAnchor.constraint(equalTo: rightSpacer.widthAnchor),
|
|
])
|
|
stackView.addArrangedSubview(langRow)
|
|
stackView.addArrangedSubview(cnLabel)
|
|
stackView.addArrangedSubview(enLabel)
|
|
|
|
layoutMargins = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
|
|
addSubview(stackView)
|
|
|
|
let guide = layoutMarginsGuide
|
|
NSLayoutConstraint.activate([
|
|
stackView.topAnchor.constraint(equalTo: guide.topAnchor),
|
|
stackView.leadingAnchor.constraint(equalTo: guide.leadingAnchor),
|
|
stackView.trailingAnchor.constraint(equalTo: guide.trailingAnchor),
|
|
stackView.bottomAnchor.constraint(lessThanOrEqualTo: guide.bottomAnchor),
|
|
])
|
|
}
|
|
|
|
public func play() {
|
|
}
|
|
|
|
public func pause() {
|
|
}
|
|
|
|
public func update(withLang lang: String?, cn: String?, en: String?) {
|
|
if let lang = lang {
|
|
langLabel.text = lang
|
|
}
|
|
let para = NSMutableParagraphStyle()
|
|
para.lineSpacing = 2
|
|
para.lineHeightMultiple = 1.1
|
|
if let cn = cn {
|
|
cnLabel.attributedText = NSAttributedString(string: cn, attributes: [.paragraphStyle: para])
|
|
}
|
|
if let en = en {
|
|
enLabel.attributedText = NSAttributedString(string: en, attributes: [.paragraphStyle: para])
|
|
}
|
|
setNeedsLayout()
|
|
}
|
|
|
|
public override func layoutSubviews() {
|
|
super.layoutSubviews()
|
|
layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: layer.cornerRadius).cgPath
|
|
let currentLangText = langLabel.text
|
|
if bounds.size != lastBoundsSize || currentLangText != lastLangText {
|
|
lastBoundsSize = bounds.size
|
|
lastLangText = currentLangText
|
|
updateDynamicTextLineLimit()
|
|
}
|
|
}
|
|
|
|
private func updateDynamicTextLineLimit() {
|
|
let fittingWidth = max(0, bounds.width - layoutMargins.left - layoutMargins.right)
|
|
if fittingWidth <= 0 { return }
|
|
|
|
let visibleArrangedSubviews = stackView.arrangedSubviews.filter { !$0.isHidden }
|
|
let fixedSubviews = visibleArrangedSubviews.filter { $0 !== cnLabel && $0 !== enLabel }
|
|
let fixedHeight = fixedSubviews.reduce(CGFloat(0)) { partialResult, view in
|
|
partialResult
|
|
+ view.systemLayoutSizeFitting(
|
|
CGSize(width: fittingWidth, height: UIView.layoutFittingCompressedSize.height),
|
|
withHorizontalFittingPriority: .required,
|
|
verticalFittingPriority: .fittingSizeLevel
|
|
).height
|
|
}
|
|
let totalSpacing = CGFloat(max(0, visibleArrangedSubviews.count - 1)) * stackView.spacing
|
|
|
|
let availableHeight =
|
|
bounds.height
|
|
- layoutMargins.top
|
|
- layoutMargins.bottom
|
|
- fixedHeight
|
|
- totalSpacing
|
|
|
|
let cnLineHeight = cnLabel.font.lineHeight * 1.1 + 2
|
|
let enLineHeight = enLabel.font.lineHeight * 1.1 + 2
|
|
let combinedLineHeight = cnLineHeight + enLineHeight
|
|
|
|
let perLabelLines = max(1, min(Int(availableHeight / combinedLineHeight), 6))
|
|
|
|
if perLabelLines != lastDynamicTextLines {
|
|
lastDynamicTextLines = perLabelLines
|
|
cnLabel.numberOfLines = perLabelLines
|
|
enLabel.numberOfLines = perLabelLines
|
|
}
|
|
}
|
|
}
|
|
|