feat: 添加自定义币种图标生成器,支持基于首字母生成图标并更新相关视图

This commit is contained in:
ZhangLei
2025-11-04 00:06:09 +08:00
parent 4c59c42f5f
commit c92b03d6c4
4 changed files with 226 additions and 13 deletions

View File

@@ -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)
// AppSettingspriceManager
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)
}
}
}

View File

@@ -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:

View 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: BTCETHADA
* - 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 SymbolsNSImage
*/
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 {
/**
* SwiftUIImage
* - Parameter symbol:
* - Returns: SwiftUI Image
*/
static func generateSwiftUIImage(for symbol: String) -> Image {
let nsImage = generateSystemIcon(for: symbol)
return Image(nsImage: nsImage)
}
}

View File

@@ -449,10 +449,16 @@ struct PreferencesWindowView: View {
.font(.system(size: 14))
.foregroundColor(isSelected ? .blue : .secondary)
//
Image(systemName: customSymbol.systemImageName)
// 使
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) {