Files
language4english/LearnEnglish/LearnEnglish/Views/QuizResultView.swift
T

152 lines
6.2 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import UIKit
final class QuizResultView: UIView {
private let total: Int
private let wrongWords: [Word]
private let duration: TimeInterval
private let onRestart: () -> Void
private let onExit: () -> Void
private let onSelectWord: (Word) -> Void
private var correctCount: Int { total - wrongWords.count }
private var durationText: String {
let seconds = Int(duration)
return seconds >= 60 ? "\(seconds / 60)\(seconds % 60) 秒" : "\(seconds) 秒"
}
init(total: Int, wrongWords: [Word], duration: TimeInterval,
onRestart: @escaping () -> Void, onExit: @escaping () -> Void,
onSelectWord: @escaping (Word) -> Void) {
self.total = total
self.wrongWords = wrongWords
self.duration = duration
self.onRestart = onRestart
self.onExit = onExit
self.onSelectWord = onSelectWord
super.init(frame: .zero)
backgroundColor = .systemBackground
buildLayout()
}
@available(*, unavailable)
required init?(coder: NSCoder) { fatalError() }
private func buildLayout() {
let scrollView = UIScrollView()
addSubview(scrollView)
scrollView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor),
scrollView.leadingAnchor.constraint(equalTo: leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: trailingAnchor),
scrollView.bottomAnchor.constraint(equalTo: bottomAnchor)
])
let stack = UIStackView()
stack.axis = .vertical
stack.spacing = 24
scrollView.addSubview(stack)
stack.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stack.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor, constant: 24),
stack.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor, constant: 16),
stack.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor, constant: -16),
stack.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor, constant: -24),
stack.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor, constant: -32)
])
let scoreLabel = UILabel()
scoreLabel.text = "\(correctCount) / \(total)"
scoreLabel.font = .systemFont(ofSize: 44, weight: .bold)
scoreLabel.textAlignment = .center
let durationLabel = UILabel()
durationLabel.text = "用时 \(durationText)"
durationLabel.font = .preferredFont(forTextStyle: .subheadline)
durationLabel.textColor = .secondaryLabel
durationLabel.textAlignment = .center
let scoreStack = UIStackView(arrangedSubviews: [scoreLabel, durationLabel])
scoreStack.axis = .vertical
scoreStack.spacing = 8
stack.addArrangedSubview(scoreStack)
if !wrongWords.isEmpty {
stack.addArrangedSubview(makeWrongWordsSection())
}
let restartButton = makeButton(title: "再来一轮", prominent: true) { [weak self] in
self?.onRestart()
}
let exitButton = makeButton(title: "返回", prominent: false) { [weak self] in
self?.onExit()
}
let buttonStack = UIStackView(arrangedSubviews: [restartButton, exitButton])
buttonStack.axis = .vertical
buttonStack.spacing = 12
stack.addArrangedSubview(buttonStack)
}
private func makeWrongWordsSection() -> UIView {
let titleLabel = UILabel()
titleLabel.text = "错题(\(wrongWords.count)"
titleLabel.font = .preferredFont(forTextStyle: .headline)
let section = UIStackView(arrangedSubviews: [titleLabel])
section.axis = .vertical
section.spacing = 8
for word in wrongWords {
let button = UIButton(type: .system)
var config = UIButton.Configuration.plain()
config.contentInsets = .init(top: 8, leading: 12, bottom: 8, trailing: 12)
button.configuration = config
button.contentHorizontalAlignment = .fill
let wordLabel = UILabel()
wordLabel.text = word.word
wordLabel.font = .preferredFont(forTextStyle: .headline)
let meaningLabel = UILabel()
meaningLabel.text = word.meaning
meaningLabel.textColor = .secondaryLabel
let chevron = UIImageView(image: UIImage(systemName: "chevron.right"))
chevron.tintColor = .tertiaryLabel
chevron.setContentHuggingPriority(.required, for: .horizontal)
let row = UIStackView(arrangedSubviews: [wordLabel, UIView(), meaningLabel, chevron])
row.axis = .horizontal
row.alignment = .center
row.spacing = 8
row.isUserInteractionEnabled = false
button.addSubview(row)
row.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
row.topAnchor.constraint(equalTo: button.topAnchor, constant: 8),
row.leadingAnchor.constraint(equalTo: button.leadingAnchor, constant: 12),
row.trailingAnchor.constraint(equalTo: button.trailingAnchor, constant: -12),
row.bottomAnchor.constraint(equalTo: button.bottomAnchor, constant: -8)
])
button.addAction(UIAction { [weak self] _ in
self?.onSelectWord(word)
}, for: .touchUpInside)
section.addArrangedSubview(button)
let divider = HairlineView()
section.addArrangedSubview(divider)
}
return section
}
private func makeButton(title: String, prominent: Bool, action: @escaping () -> Void) -> UIButton {
let button = UIButton(type: .system)
var config = prominent ? UIButton.Configuration.borderedProminent() : UIButton.Configuration.plain()
config.title = title
config.contentInsets = .init(top: 12, leading: 16, bottom: 12, trailing: 16)
button.configuration = config
button.addAction(UIAction { _ in action() }, for: .touchUpInside)
return button
}
}