From 13ca0bcda9b6fdb29627540260f4cc2f6ccefa44 Mon Sep 17 00:00:00 2001 From: fish Date: Sun, 19 Jul 2026 21:35:25 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A2=9C=E8=89=B2=E5=8D=95=E8=AF=8D=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E9=A2=9C=E8=89=B2=E5=9C=86=E7=82=B9=E6=8C=87=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- LearnEnglish/LearnEnglish/Models/Word.swift | 7 +++- .../LearnEnglish/Resources/words.json | 40 +++++++++---------- .../LearnEnglish/Utilities/Theme.swift | 12 ++++++ .../LearnEnglish/Views/ColorDotView.swift | 28 +++++++++++++ .../LearnEnglish/Views/WordCardCell.swift | 17 +++++++- .../Views/WordDetailViewController.swift | 15 ++++++- 6 files changed, 96 insertions(+), 23 deletions(-) create mode 100644 LearnEnglish/LearnEnglish/Views/ColorDotView.swift diff --git a/LearnEnglish/LearnEnglish/Models/Word.swift b/LearnEnglish/LearnEnglish/Models/Word.swift index b52e4d8..471356a 100644 --- a/LearnEnglish/LearnEnglish/Models/Word.swift +++ b/LearnEnglish/LearnEnglish/Models/Word.swift @@ -1,4 +1,4 @@ -import Foundation +import UIKit struct WordBank: Codable { let version: Int @@ -19,4 +19,9 @@ struct Word: Codable, Identifiable, Hashable { let meaning: String let example: String let exampleMeaning: String + let colorHex: String? + + var color: UIColor? { + colorHex.flatMap { UIColor(hex: $0) } + } } diff --git a/LearnEnglish/LearnEnglish/Resources/words.json b/LearnEnglish/LearnEnglish/Resources/words.json index 2c02655..64e0c56 100644 --- a/LearnEnglish/LearnEnglish/Resources/words.json +++ b/LearnEnglish/LearnEnglish/Resources/words.json @@ -6,26 +6,26 @@ "name": "颜色", "icon": "paintpalette.fill", "words": [ - { "word": "red", "phonetic": "/red/", "meaning": "红色", "example": "The apple is red.", "exampleMeaning": "这个苹果是红色的。" }, - { "word": "blue", "phonetic": "/bluː/", "meaning": "蓝色", "example": "The sky is blue.", "exampleMeaning": "天空是蓝色的。" }, - { "word": "green", "phonetic": "/ɡriːn/", "meaning": "绿色", "example": "The grass is green.", "exampleMeaning": "草地是绿色的。" }, - { "word": "yellow", "phonetic": "/ˈjeloʊ/", "meaning": "黄色", "example": "The banana is yellow.", "exampleMeaning": "香蕉是黄色的。" }, - { "word": "black", "phonetic": "/blæk/", "meaning": "黑色", "example": "The cat is black.", "exampleMeaning": "这只猫是黑色的。" }, - { "word": "white", "phonetic": "/waɪt/", "meaning": "白色", "example": "The snow is white.", "exampleMeaning": "雪是白色的。" }, - { "word": "orange", "phonetic": "/ˈɔːrɪndʒ/", "meaning": "橙色", "example": "The orange is orange.", "exampleMeaning": "橙子是橙色的。" }, - { "word": "purple", "phonetic": "/ˈpɜːrpl/", "meaning": "紫色", "example": "The grapes are purple.", "exampleMeaning": "葡萄是紫色的。" }, - { "word": "pink", "phonetic": "/pɪŋk/", "meaning": "粉色", "example": "The flower is pink.", "exampleMeaning": "这朵花是粉色的。" }, - { "word": "brown", "phonetic": "/braʊn/", "meaning": "棕色", "example": "The bear is brown.", "exampleMeaning": "这只熊是棕色的。" }, - { "word": "gray", "phonetic": "/ɡreɪ/", "meaning": "灰色", "example": "The elephant is gray.", "exampleMeaning": "大象是灰色的。" }, - { "word": "gold", "phonetic": "/ɡoʊld/", "meaning": "金色", "example": "The ring is gold.", "exampleMeaning": "这枚戒指是金色的。" }, - { "word": "silver", "phonetic": "/ˈsɪlvər/", "meaning": "银色", "example": "The car is silver.", "exampleMeaning": "这辆车是银色的。" }, - { "word": "cyan", "phonetic": "/ˈsaɪæn/", "meaning": "青色", "example": "The sea looks cyan.", "exampleMeaning": "大海看起来是青色的。" }, - { "word": "magenta", "phonetic": "/məˈdʒentə/", "meaning": "品红色", "example": "The dress is magenta.", "exampleMeaning": "这条裙子是品红色的。" }, - { "word": "beige", "phonetic": "/beɪʒ/", "meaning": "米色", "example": "The wall is beige.", "exampleMeaning": "这面墙是米色的。" }, - { "word": "turquoise", "phonetic": "/ˈtɜːrkwɔɪz/", "meaning": "绿松石色", "example": "The lake is turquoise.", "exampleMeaning": "湖水是绿松石色的。" }, - { "word": "violet", "phonetic": "/ˈvaɪələt/", "meaning": "紫罗兰色", "example": "The violet flower is beautiful.", "exampleMeaning": "紫罗兰色的花很美。" }, - { "word": "navy", "phonetic": "/ˈneɪvi/", "meaning": "藏青色", "example": "He wears a navy coat.", "exampleMeaning": "他穿着一件藏青色外套。" }, - { "word": "ivory", "phonetic": "/ˈaɪvəri/", "meaning": "象牙色", "example": "The keys are ivory.", "exampleMeaning": "琴键是象牙色的。" } + { "word": "red", "colorHex": "#E53935", "phonetic": "/red/", "meaning": "红色", "example": "The apple is red.", "exampleMeaning": "这个苹果是红色的。" }, + { "word": "blue", "colorHex": "#1E88E5", "phonetic": "/bluː/", "meaning": "蓝色", "example": "The sky is blue.", "exampleMeaning": "天空是蓝色的。" }, + { "word": "green", "colorHex": "#43A047", "phonetic": "/ɡriːn/", "meaning": "绿色", "example": "The grass is green.", "exampleMeaning": "草地是绿色的。" }, + { "word": "yellow", "colorHex": "#FDD835", "phonetic": "/ˈjeloʊ/", "meaning": "黄色", "example": "The banana is yellow.", "exampleMeaning": "香蕉是黄色的。" }, + { "word": "black", "colorHex": "#212121", "phonetic": "/blæk/", "meaning": "黑色", "example": "The cat is black.", "exampleMeaning": "这只猫是黑色的。" }, + { "word": "white", "colorHex": "#FFFFFF", "phonetic": "/waɪt/", "meaning": "白色", "example": "The snow is white.", "exampleMeaning": "雪是白色的。" }, + { "word": "orange", "colorHex": "#FB8C00", "phonetic": "/ˈɔːrɪndʒ/", "meaning": "橙色", "example": "The orange is orange.", "exampleMeaning": "橙子是橙色的。" }, + { "word": "purple", "colorHex": "#8E24AA", "phonetic": "/ˈpɜːrpl/", "meaning": "紫色", "example": "The grapes are purple.", "exampleMeaning": "葡萄是紫色的。" }, + { "word": "pink", "colorHex": "#EC407A", "phonetic": "/pɪŋk/", "meaning": "粉色", "example": "The flower is pink.", "exampleMeaning": "这朵花是粉色的。" }, + { "word": "brown", "colorHex": "#6D4C41", "phonetic": "/braʊn/", "meaning": "棕色", "example": "The bear is brown.", "exampleMeaning": "这只熊是棕色的。" }, + { "word": "gray", "colorHex": "#9E9E9E", "phonetic": "/ɡreɪ/", "meaning": "灰色", "example": "The elephant is gray.", "exampleMeaning": "大象是灰色的。" }, + { "word": "gold", "colorHex": "#FFD700", "phonetic": "/ɡoʊld/", "meaning": "金色", "example": "The ring is gold.", "exampleMeaning": "这枚戒指是金色的。" }, + { "word": "silver", "colorHex": "#C0C0C0", "phonetic": "/ˈsɪlvər/", "meaning": "银色", "example": "The car is silver.", "exampleMeaning": "这辆车是银色的。" }, + { "word": "cyan", "colorHex": "#00BCD4", "phonetic": "/ˈsaɪæn/", "meaning": "青色", "example": "The sea looks cyan.", "exampleMeaning": "大海看起来是青色的。" }, + { "word": "magenta", "colorHex": "#E040FB", "phonetic": "/məˈdʒentə/", "meaning": "品红色", "example": "The dress is magenta.", "exampleMeaning": "这条裙子是品红色的。" }, + { "word": "beige", "colorHex": "#F5F5DC", "phonetic": "/beɪʒ/", "meaning": "米色", "example": "The wall is beige.", "exampleMeaning": "这面墙是米色的。" }, + { "word": "turquoise", "colorHex": "#40E0D0", "phonetic": "/ˈtɜːrkwɔɪz/", "meaning": "绿松石色", "example": "The lake is turquoise.", "exampleMeaning": "湖水是绿松石色的。" }, + { "word": "violet", "colorHex": "#7C4DFF", "phonetic": "/ˈvaɪələt/", "meaning": "紫罗兰色", "example": "The violet flower is beautiful.", "exampleMeaning": "紫罗兰色的花很美。" }, + { "word": "navy", "colorHex": "#1A237E", "phonetic": "/ˈneɪvi/", "meaning": "藏青色", "example": "He wears a navy coat.", "exampleMeaning": "他穿着一件藏青色外套。" }, + { "word": "ivory", "colorHex": "#FFFFF0", "phonetic": "/ˈaɪvəri/", "meaning": "象牙色", "example": "The keys are ivory.", "exampleMeaning": "琴键是象牙色的。" } ] }, { diff --git a/LearnEnglish/LearnEnglish/Utilities/Theme.swift b/LearnEnglish/LearnEnglish/Utilities/Theme.swift index 680e463..09e4327 100644 --- a/LearnEnglish/LearnEnglish/Utilities/Theme.swift +++ b/LearnEnglish/LearnEnglish/Utilities/Theme.swift @@ -34,6 +34,18 @@ enum Theme { } } +extension UIColor { + convenience init?(hex: String) { + var value = hex.trimmingCharacters(in: .whitespacesAndNewlines) + if value.hasPrefix("#") { value.removeFirst() } + guard value.count == 6, let rgb = UInt64(value, radix: 16) else { return nil } + self.init(red: CGFloat((rgb >> 16) & 0xFF) / 255, + green: CGFloat((rgb >> 8) & 0xFF) / 255, + blue: CGFloat(rgb & 0xFF) / 255, + alpha: 1) + } +} + extension UIView { func setPressed(_ pressed: Bool, animated: Bool = true) { let update = { diff --git a/LearnEnglish/LearnEnglish/Views/ColorDotView.swift b/LearnEnglish/LearnEnglish/Views/ColorDotView.swift new file mode 100644 index 0000000..ee48df5 --- /dev/null +++ b/LearnEnglish/LearnEnglish/Views/ColorDotView.swift @@ -0,0 +1,28 @@ +import UIKit + +final class ColorDotView: UIView { + override init(frame: CGRect) { + super.init(frame: frame) + layer.borderWidth = 1 + updateBorder() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { fatalError() } + + override func layoutSubviews() { + super.layoutSubviews() + layer.cornerRadius = bounds.width / 2 + } + + override func traitCollectionDidChange(_ previous: UITraitCollection?) { + super.traitCollectionDidChange(previous) + if traitCollection.hasDifferentColorAppearance(comparedTo: previous) { + updateBorder() + } + } + + private func updateBorder() { + layer.borderColor = UIColor.label.withAlphaComponent(0.15).cgColor + } +} diff --git a/LearnEnglish/LearnEnglish/Views/WordCardCell.swift b/LearnEnglish/LearnEnglish/Views/WordCardCell.swift index b3d6b3f..e81c700 100644 --- a/LearnEnglish/LearnEnglish/Views/WordCardCell.swift +++ b/LearnEnglish/LearnEnglish/Views/WordCardCell.swift @@ -2,6 +2,7 @@ import UIKit final class WordCardCell: UITableViewCell { private let cardView = UIView() + private let colorDot = ColorDotView() private let wordLabel = UILabel() private let phoneticLabel = UILabel() private let meaningLabel = UILabel() @@ -33,7 +34,19 @@ final class WordCardCell: UITableViewCell { meaningLabel.font = .preferredFont(forTextStyle: .subheadline) meaningLabel.textColor = .secondaryLabel - let textStack = UIStackView(arrangedSubviews: [wordLabel, phoneticLabel, meaningLabel]) + let wordRowSpacer = UIView() + wordRowSpacer.setContentHuggingPriority(.init(1), for: .horizontal) + let wordRow = UIStackView(arrangedSubviews: [wordLabel, colorDot, wordRowSpacer]) + wordRow.axis = .horizontal + wordRow.alignment = .center + wordRow.spacing = 8 + colorDot.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + colorDot.widthAnchor.constraint(equalToConstant: 12), + colorDot.heightAnchor.constraint(equalToConstant: 12) + ]) + + let textStack = UIStackView(arrangedSubviews: [wordRow, phoneticLabel, meaningLabel]) textStack.axis = .vertical textStack.spacing = 8 @@ -68,6 +81,8 @@ final class WordCardCell: UITableViewCell { wordLabel.text = word.word phoneticLabel.text = word.phonetic meaningLabel.text = word.meaning + colorDot.backgroundColor = word.color + colorDot.isHidden = word.color == nil updateSpeakerState() } diff --git a/LearnEnglish/LearnEnglish/Views/WordDetailViewController.swift b/LearnEnglish/LearnEnglish/Views/WordDetailViewController.swift index dfdf8fa..80fa87d 100644 --- a/LearnEnglish/LearnEnglish/Views/WordDetailViewController.swift +++ b/LearnEnglish/LearnEnglish/Views/WordDetailViewController.swift @@ -87,6 +87,19 @@ final class WordDetailViewController: UIViewController { wordLabel.font = Theme.Font.wordHero() wordLabel.textAlignment = .center + let colorDot = ColorDotView() + colorDot.backgroundColor = word.color + colorDot.isHidden = word.color == nil + colorDot.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + colorDot.widthAnchor.constraint(equalToConstant: 20), + colorDot.heightAnchor.constraint(equalToConstant: 20) + ]) + let wordRow = UIStackView(arrangedSubviews: [colorDot, wordLabel]) + wordRow.axis = .horizontal + wordRow.alignment = .center + wordRow.spacing = 12 + let phoneticIcon = UIImageView(image: UIImage( systemName: "speaker.wave.2", withConfiguration: UIImage.SymbolConfiguration(pointSize: 13, weight: .medium))) @@ -115,7 +128,7 @@ final class WordDetailViewController: UIViewController { speakButton.heightAnchor.constraint(equalToConstant: 56) ]) - let stack = UIStackView(arrangedSubviews: [wordLabel, phoneticRow, speakButton]) + let stack = UIStackView(arrangedSubviews: [wordRow, phoneticRow, speakButton]) stack.axis = .vertical stack.spacing = 16 stack.alignment = .center