wake-ios/wake/Utils/SVGImage.swift
2025-09-02 20:31:13 +08:00

148 lines
4.6 KiB
Swift
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import SwiftUI
import SVGKit
struct SVGImage: UIViewRepresentable {
let svgName: String
var contentMode: ContentMode = .fit
var tintColor: Color?
private var svgPath: String {
return svgName
}
private func createImageView() -> SVGKFastImageView {
let emptySVGString = """
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1 1" preserveAspectRatio="xMidYMid meet">
<rect width="1" height="1" fill="transparent"/>
</svg>
"""
if let data = emptySVGString.data(using: .utf8),
let svgImage = SVGKImage(data: data) {
let imageView = SVGKFastImageView(svgkImage: svgImage) ?? SVGKFastImageView()
imageView.contentMode = .scaleAspectFit
imageView.backgroundColor = .clear
return imageView
}
let fallbackView = SVGKFastImageView()
fallbackView.backgroundColor = .clear
return fallbackView
}
func makeUIView(context: Context) -> SVGKFastImageView {
print("🔄 SVG: \(svgName)")
let imageView = createImageView()
loadSVG(into: imageView)
configureView(imageView)
return imageView
}
private func loadSVG(into imageView: SVGKFastImageView) {
guard let path = Bundle.main.path(forResource: svgPath, ofType: "svg") else {
print(" main bundle中找不到文件: \(svgPath).svg")
return
}
let url = URL(fileURLWithPath: path)
guard let svgImage = SVGKImage(contentsOf: url) else {
print(" URL创建SVG: \(path)")
return
}
// 设置SVG的尺寸为容器大小
let containerSize = imageView.bounds.size
if containerSize != .zero {
svgImage.size = containerSize
} else {
// 如果容器大小未知,设置一个默认大小
svgImage.size = CGSize(width: 100, height: 100)
}
print(" SVG: \(svgName), : \(svgImage.size)")
DispatchQueue.main.async {
imageView.image = svgImage
imageView.setNeedsLayout()
imageView.layoutIfNeeded()
}
}
private func configureView(_ imageView: SVGKFastImageView) {
imageView.contentMode = contentMode == .fit ? .scaleAspectFit : .scaleAspectFill
imageView.clipsToBounds = true
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.backgroundColor = .clear
// 确保图片视图可以正确缩放
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
if let tintColor = tintColor?.uiColor {
imageView.tintColor = tintColor
DispatchQueue.main.async {
self.applyTintColor(tintColor, to: imageView.layer)
}
}
}
private func applyTintColor(_ color: UIColor, to layer: CALayer) {
if let shapeLayer = layer as? CAShapeLayer {
shapeLayer.fillColor = color.cgColor
}
layer.sublayers?.forEach { sublayer in
applyTintColor(color, to: sublayer)
}
}
func updateUIView(_ uiView: SVGKFastImageView, context: Context) {
loadSVG(into: uiView)
if let tintColor = tintColor?.uiColor {
uiView.tintColor = tintColor
DispatchQueue.main.async {
self.applyTintColor(tintColor, to: uiView.layer)
}
}
uiView.contentMode = contentMode == .fit ? .scaleAspectFit : .scaleAspectFill
}
func sizeThatFits(_ proposal: ProposedViewSize, uiView: SVGKFastImageView, context: Context) -> CGSize? {
return nil
}
}
// MARK: - ContentMode
extension SVGImage {
enum ContentMode {
case fit // 保持宽高比,适应容器
case fill // 保持宽高比,填充容器(可能被裁剪)
}
}
// MARK: - Preview
#Preview {
VStack(spacing: 20) {
Text("IP SVG")
SVGImage(svgName: "IP")
.frame(width: 100, height: 100)
.background(Color.gray.opacity(0.2))
.border(Color.red, width: 1)
Text("Pioneer SVG")
SVGImage(svgName: "Pioneer", contentMode: .fill)
.frame(width: 100, height: 50)
.background(Color.gray.opacity(0.2))
.border(Color.blue, width: 1)
.clipped()
}
.padding()
}
// MARK: - Color Extension
private extension Color {
var uiColor: UIColor {
return UIColor(self)
}
}