mirror of
https://github.com/jiayouzl/Bitcoin-Monitoring.git
synced 2025-11-25 03:15:24 +08:00
feat: 添加自定义币种图标生成器,支持基于首字母生成图标并更新相关视图
This commit is contained in:
@@ -146,9 +146,11 @@ class MenuBarManager: NSObject, ObservableObject {
|
||||
let symbolImage: NSImage?
|
||||
|
||||
if self.appSettings.isUsingCustomSymbol() {
|
||||
// 自定义币种:使用自定义图标
|
||||
symbolImage = self.customSymbolImage()
|
||||
} else {
|
||||
symbolImage = self.symbolImage(for: self.priceManager.selectedSymbol)
|
||||
// 默认币种:直接从AppSettings获取当前选中的币种,避免依赖可能尚未更新的priceManager
|
||||
symbolImage = self.symbolImage(for: self.appSettings.selectedSymbol)
|
||||
}
|
||||
symbolImage?.size = NSSize(width: 16, height: 16)
|
||||
|
||||
@@ -178,11 +180,22 @@ class MenuBarManager: NSObject, ObservableObject {
|
||||
return NSImage(systemSymbolName: "bitcoinsign.circle.fill", accessibilityDescription: "Crypto")
|
||||
}
|
||||
|
||||
// 获取自定义币种的图标(统一使用BTC图标)
|
||||
// 获取自定义币种的图标(基于首字母生成)
|
||||
private func customSymbolImage() -> NSImage? {
|
||||
if appSettings.isUsingCustomSymbol(),
|
||||
let index = appSettings.selectedCustomSymbolIndex,
|
||||
index >= 0 && index < appSettings.customCryptoSymbols.count {
|
||||
let customSymbol = appSettings.customCryptoSymbols[index]
|
||||
return customSymbol.customIcon()
|
||||
}
|
||||
return NSImage(systemSymbolName: "bitcoinsign.circle.fill", accessibilityDescription: "自定义币种")
|
||||
}
|
||||
|
||||
// 获取指定自定义币种的图标
|
||||
private func customSymbolImage(for customSymbol: CustomCryptoSymbol) -> NSImage? {
|
||||
return customSymbol.customIcon()
|
||||
}
|
||||
|
||||
// 格式化价格为千分位分隔形式
|
||||
private func formatPriceWithCommas(_ price: Double) -> String {
|
||||
let formatter = NumberFormatter()
|
||||
@@ -251,7 +264,7 @@ class MenuBarManager: NSObject, ObservableObject {
|
||||
let placeholderTitle = isCurrent ? "✓ \(customSymbol.displayName) (自定义): 加载中..." : " \(customSymbol.displayName) (自定义): 加载中..."
|
||||
let item = NSMenuItem(title: placeholderTitle, action: #selector(self.selectOrCopySymbol(_:)), keyEquivalent: "")
|
||||
item.target = self
|
||||
if let icon = customSymbolImage() {
|
||||
if let icon = customSymbolImage(for: customSymbol) {
|
||||
icon.size = NSSize(width: 16, height: 16)
|
||||
item.image = icon
|
||||
}
|
||||
@@ -508,18 +521,19 @@ class MenuBarManager: NSObject, ObservableObject {
|
||||
print("✅ 已切换到自定义币种: \(customSymbol.displayName)")
|
||||
}
|
||||
|
||||
// 立即更新价格管理器
|
||||
// 立即更新价格管理器和UI
|
||||
self.priceManager.updateCryptoSymbolSettings()
|
||||
self.updateMenuBarTitle(price: self.priceManager.currentPrice)
|
||||
// 使用0.0价格强制更新显示状态,确保图标和文字都正确更新
|
||||
self.updateMenuBarTitle(price: 0.0)
|
||||
} else if let symbol = data["symbol"] as? CryptoSymbol {
|
||||
// 选择默认币种
|
||||
appSettings.saveSelectedSymbol(symbol)
|
||||
|
||||
print("✅ 已切换到默认币种: \(symbol.displayName)")
|
||||
|
||||
// 立即更新价格管理器
|
||||
// 立即更新价格管理器和UI
|
||||
self.priceManager.updateCryptoSymbolSettings()
|
||||
self.updateMenuBarTitle(price: self.priceManager.currentPrice)
|
||||
// 使用0.0价格强制更新显示状态,确保图标和文字都正确更新
|
||||
self.updateMenuBarTitle(price: 0.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import AppKit
|
||||
|
||||
/// 自定义加密货币数据模型
|
||||
/// 支持用户定义的3-5字符币种符号,使用统一的BTC图标
|
||||
@@ -121,11 +122,18 @@ extension CustomCryptoSymbol {
|
||||
return "\(symbol)/USDT"
|
||||
}
|
||||
|
||||
/// 对应的SF Symbols图标名称(统一使用BTC图标)
|
||||
/// 对应的图标名称(基于首字母生成的自定义图标)
|
||||
var systemImageName: String {
|
||||
// 保留系统图标名称作为后备方案
|
||||
return "bitcoinsign.circle.fill"
|
||||
}
|
||||
|
||||
/// 获取基于首字母的自定义图标
|
||||
/// - Returns: 自定义生成的NSImage图标
|
||||
func customIcon() -> NSImage {
|
||||
return CryptoIconGenerator.generateSystemIcon(for: symbol)
|
||||
}
|
||||
|
||||
/// 菜单标题(带勾选标记和自定义标识)
|
||||
/// - Parameter isCurrent: 是否为当前选中币种
|
||||
/// - Returns: 菜单展示文本
|
||||
|
||||
185
Bitcoin-Monitoring/Utils/CryptoIconGenerator.swift
Normal file
185
Bitcoin-Monitoring/Utils/CryptoIconGenerator.swift
Normal file
@@ -0,0 +1,185 @@
|
||||
//
|
||||
// CryptoIconGenerator.swift
|
||||
// Bitcoin Monitoring
|
||||
//
|
||||
// Created by Mark on 2025/11/03.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AppKit
|
||||
|
||||
/**
|
||||
* 加密货币图标生成器
|
||||
* 为自定义币种生成基于首字母的彩色图标
|
||||
*/
|
||||
class CryptoIconGenerator {
|
||||
|
||||
/// 图标缓存,避免重复生成相同币种的图标
|
||||
private static var iconCache: [String: NSImage] = [:]
|
||||
|
||||
/// 预定义的颜色数组,用于生成不同币种的背景色
|
||||
private static let backgroundColors: [NSColor] = [
|
||||
NSColor(red: 0.2, green: 0.4, blue: 0.8, alpha: 1.0), // 蓝色
|
||||
NSColor(red: 0.8, green: 0.3, blue: 0.3, alpha: 1.0), // 红色
|
||||
NSColor(red: 0.3, green: 0.7, blue: 0.3, alpha: 1.0), // 绿色
|
||||
NSColor(red: 0.9, green: 0.6, blue: 0.2, alpha: 1.0), // 橙色
|
||||
NSColor(red: 0.7, green: 0.3, blue: 0.7, alpha: 1.0), // 紫色
|
||||
NSColor(red: 0.2, green: 0.7, blue: 0.8, alpha: 1.0), // 青色
|
||||
NSColor(red: 0.9, green: 0.4, blue: 0.6, alpha: 1.0), // 粉色
|
||||
NSColor(red: 0.6, green: 0.6, blue: 0.2, alpha: 1.0), // 黄色
|
||||
NSColor(red: 0.4, green: 0.8, blue: 0.6, alpha: 1.0), // 薄荷绿
|
||||
NSColor(red: 0.7, green: 0.5, blue: 0.3, alpha: 1.0), // 棕色
|
||||
]
|
||||
|
||||
/**
|
||||
* 为自定义币种生成基于首字母的图标
|
||||
* - Parameter symbol: 币种符号(如 BTC、ETH、ADA)
|
||||
* - Returns: 生成的NSImage图标
|
||||
*/
|
||||
static func generateIcon(for symbol: String) -> NSImage {
|
||||
// 检查缓存
|
||||
if let cachedIcon = iconCache[symbol] {
|
||||
return cachedIcon
|
||||
}
|
||||
|
||||
// 生成新图标
|
||||
let icon = createLetterIcon(symbol: symbol)
|
||||
|
||||
// 缓存图标
|
||||
iconCache[symbol] = icon
|
||||
|
||||
return icon
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建基于首字母的图标
|
||||
* - Parameter symbol: 币种符号
|
||||
* - Returns: 生成的NSImage图标
|
||||
*/
|
||||
private static func createLetterIcon(symbol: String) -> NSImage {
|
||||
let size = NSSize(width: 64, height: 64) // 使用较大的尺寸生成,保证清晰度
|
||||
|
||||
let image = NSImage(size: size)
|
||||
image.lockFocus()
|
||||
|
||||
// 创建圆形背景
|
||||
let rect = NSRect(origin: .zero, size: size)
|
||||
let circlePath = NSBezierPath(ovalIn: rect)
|
||||
|
||||
// 选择背景色
|
||||
let backgroundColor = selectBackgroundColor(for: symbol)
|
||||
backgroundColor.setFill()
|
||||
circlePath.fill()
|
||||
|
||||
// 绘制首字母
|
||||
let firstLetter = String(symbol.prefix(1)).uppercased()
|
||||
let attributedString = NSAttributedString(
|
||||
string: firstLetter,
|
||||
attributes: [
|
||||
.font: NSFont.boldSystemFont(ofSize: 28),
|
||||
.foregroundColor: NSColor.white
|
||||
]
|
||||
)
|
||||
|
||||
// 计算文字位置,使其居中
|
||||
let textSize = attributedString.size()
|
||||
let textRect = NSRect(
|
||||
x: (size.width - textSize.width) / 2,
|
||||
y: (size.height - textSize.height) / 2,
|
||||
width: textSize.width,
|
||||
height: textSize.height
|
||||
)
|
||||
|
||||
attributedString.draw(in: textRect)
|
||||
|
||||
image.unlockFocus()
|
||||
|
||||
// 设置图像属性,确保正确显示
|
||||
image.isTemplate = false
|
||||
|
||||
return image
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据币种符号选择背景色
|
||||
* 使用哈希算法确保相同币种总是得到相同的颜色
|
||||
* - Parameter symbol: 币种符号
|
||||
* - Returns: 背景色
|
||||
*/
|
||||
private static func selectBackgroundColor(for symbol: String) -> NSColor {
|
||||
// 使用简单的哈希算法
|
||||
var hash = 0
|
||||
for char in symbol {
|
||||
hash = hash * 31 + Int(char.unicodeScalars.first!.value)
|
||||
}
|
||||
|
||||
// 取绝对值并使用颜色数组长度取模
|
||||
let colorIndex = abs(hash) % backgroundColors.count
|
||||
return backgroundColors[colorIndex]
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成系统符号样式的图标(兼容SF Symbols)
|
||||
* - Parameter symbol: 币种符号
|
||||
* - Returns: SF Symbols兼容的NSImage
|
||||
*/
|
||||
static func generateSystemIcon(for symbol: String) -> NSImage {
|
||||
let icon = generateIcon(for: symbol)
|
||||
|
||||
// 调整大小为标准SF Symbols尺寸
|
||||
let standardSize = NSSize(width: 16, height: 16)
|
||||
let resizedIcon = resizeImage(icon, to: standardSize)
|
||||
|
||||
return resizedIcon
|
||||
}
|
||||
|
||||
/**
|
||||
* 调整图像尺寸
|
||||
* - Parameters:
|
||||
* - image: 原始图像
|
||||
* - size: 目标尺寸
|
||||
* - Returns: 调整尺寸后的图像
|
||||
*/
|
||||
private static func resizeImage(_ image: NSImage, to size: NSSize) -> NSImage {
|
||||
let resizedImage = NSImage(size: size)
|
||||
resizedImage.lockFocus()
|
||||
|
||||
let drawingRect = NSRect(origin: .zero, size: size)
|
||||
image.draw(in: drawingRect)
|
||||
|
||||
resizedImage.unlockFocus()
|
||||
resizedImage.isTemplate = false
|
||||
|
||||
return resizedImage
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空图标缓存(用于测试或重置)
|
||||
*/
|
||||
static func clearCache() {
|
||||
iconCache.removeAll()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存中的图标数量
|
||||
* - Returns: 缓存的图标数量
|
||||
*/
|
||||
static func cacheCount() -> Int {
|
||||
return iconCache.count
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SwiftUI 兼容扩展
|
||||
|
||||
extension CryptoIconGenerator {
|
||||
|
||||
/**
|
||||
* 为SwiftUI生成Image
|
||||
* - Parameter symbol: 币种符号
|
||||
* - Returns: SwiftUI Image
|
||||
*/
|
||||
static func generateSwiftUIImage(for symbol: String) -> Image {
|
||||
let nsImage = generateSystemIcon(for: symbol)
|
||||
return Image(nsImage: nsImage)
|
||||
}
|
||||
}
|
||||
@@ -449,10 +449,16 @@ struct PreferencesWindowView: View {
|
||||
.font(.system(size: 14))
|
||||
.foregroundColor(isSelected ? .blue : .secondary)
|
||||
|
||||
// 币种图标
|
||||
Image(systemName: customSymbol.systemImageName)
|
||||
.foregroundColor(.orange)
|
||||
.font(.system(size: 16))
|
||||
// 币种图标(使用基于首字母的自定义图标)
|
||||
Group {
|
||||
let nsImage = customSymbol.customIcon()
|
||||
Image(nsImage: nsImage)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
}
|
||||
.foregroundColor(.orange)
|
||||
.font(.system(size: 16))
|
||||
.frame(width: 16, height: 16)
|
||||
|
||||
// 币种信息
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
|
||||
Reference in New Issue
Block a user