如果仅支持iOS 15+/macOS 12+,并且不在乎动画图像格式,请尝试Swiftui的异步图像
sdwebimageswiftui是一个SwiftUI图像加载框架,它基于SDWebimage。
它带来了SDWebimage的所有喜欢的功能,例如异步图像加载,内存/磁盘缓存,动画图像播放和表演。
该框架提供了不同的视图结构,API与SwiftUI框架指南相匹配。如果您熟悉Image ,您会发现易于使用WebImage和AnimatedImage 。
从v3.0.0来看,可以为Visionos平台编辑SDWebimagesWiftUI。但是,由于缺乏软件包管理器支持(需要工具更新),因此我们不支持Cocoapods/spm。
您只能使用Xcode的内置软件包管理器依赖项来构建Visionos。
要运行visionos示例,您需要克隆并同时添加SDWebImage和SDWebImageSwiftUI ,打开SDWebImageSwiftUI.xcworkspace并拖动这些文件夹以成为本地软件包依赖关系,请参阅:将包装依赖性编辑为本地软件包,
如果您真的想构建框架而不是使用Xcode的软件包依赖项,请按照以下手册步骤进行操作:
SDWebImage.xcodeproj和构建Visionos平台的SDWebImage目标(如果需要,请更改MACH_O_TYPE到静态库)Carthage/Build/visionOS上创建目录,然后复制SDWebImage.frameworkSDWebImageSwiftUI.xcodeproj并构建SDWebImageSwiftUI visionOS目标由于SDWeBimagesWiftUI建立在SDWebimage之上,因此它既可以提供盒子外功能,又提供您在现实世界应用中可能想要的高级功能。在需要时检查我们的Wiki:
您还可以通过SDWebimage从现有社区中获得所有利益。您可以通过编码器插件,通过SDWEBIMAGEPHOTOSPLUGIN,通过FireBaseui进行的Firebase集成等等,可以通过编码器插件获得大量的图像格式支持(GIF/APNG/WebP/heif/avif/svg/pdf)。
除了所有这些功能外,我们还对Swiftui进行优化,例如绑定,查看修饰符,使用相同的设计模式成为一名优秀的Swiftui公民。
该框架正在大量开发中,建议尽可能多地使用最新版本(包括sdwebimage依赖性)。
该框架遵循语义版本控制。每个源折断API更改都会碰到主要版本。
此项目使用保留更改格式以记录更改。检查版本之间的更改changelog.md。更改也将在发布页面中更新。
欢迎所有问题报告,功能请求,贡献和GitHub星星。如果您发现此框架有用,希望有积极的反馈和促销。
iOS 14(MACOS 11)介绍了SwiftUI 2.0,它保持最多的API兼容,但会改变许多内部行为,从而破坏了SDWeBimagesWiftUI的功能。
来自v3.0.0,sdwebimageswiftui降低iOS 13支持。要在iOS 13上使用,请检查最新的v2.x版本(或使用2.x分支)。
由于SDWebimage 6.0将引入混合的Swift/OBJC代码库,因此此存储库将迁移到SDWebimage Core Repo中。
但是不用担心,我们将使用自动交叉模块覆盖层,手段,您可以使用:
import SwiftUI
import SDWebImage做类似的作品:
import SwiftUI
import SDWebImage
import SDWebImageSwiftUI // <-- Automatic infer this
您将自动链接SDWebImageSwiftUI ,该库的命名仍将保存在SPM目标中。因此,对于大多数人来说,过渡很顺利,我不想碰到另一个主要版本。 3.x是SDWebimagesWiftui专用repo的最终版本
注意:对于超级高级用户,如果您使用一些自定义的Swift工具链,请确保通过-Xfrontend -enable-cross-import-overlays
SdwebimagesWiftUI可通过Swift Package Manager获得。
对于应用集成,您应该使用Xcode 12或更高版本,以将此软件包添加到应用程序目标中。为此,请使用XCode查看有关逐步教程的应用程序添加软件包依赖项。
对于下游框架作者,您应该在git repo中创建一个Package.swift文件,然后添加以下行以标记您的框架,取决于我们的sdwebimageswiftui。
let package = Package (
dependencies : [
. package ( url : " https://github.com/SDWebImage/SDWebImageSwiftUI.git " , from : " 3.0.0 " )
] ,
) 可通过可可录提供SDWebimagesWiftUI。要安装它,只需将以下行添加到您的podfile:
pod 'SDWebImageSwiftUI' SdwebimagesWiftUI可通过迦太基购买。
github "SDWebImage/SDWebImageSwiftUI"
WebImage加载网络图像 var body : some View {
WebImage ( url : URL ( string : " https://nokiatech.github.io/heif/content/images/ski_jump_1440x960.heic " ) ) { image in
image . resizable ( ) // Control layout like SwiftUI.AsyncImage, you must use this modifier or the view will use the image bitmap size
} placeholder : {
Rectangle ( ) . foregroundColor ( . gray )
}
// Supports options and context, like `.delayPlaceholder` to show placeholder only when error
. onSuccess { image , data , cacheType in
// Success
// Note: Data exist only when queried from disk cache or network. Use `.queryMemoryData` if you really need data
}
. indicator ( . activity ) // Activity Indicator
. transition ( . fade ( duration : 0.5 ) ) // Fade Transition with duration
. scaledToFit ( )
. frame ( width : 300 , height : 300 , alignment : . center )
}注意:使用Image进行内部实现的WebImage ,这是SwiftUI布局和动画系统的最佳兼容性。但是,与不支持动画图像或向量图像的Swiftui Image不同, WebImage也支持动画图像(默认情况下,v2.0.0)。
但是, WebImage动画提供了简单的常见用例,因此仍然建议使用AnimatedImage用于诸如渐进式动画渲染或矢量图像渲染之类的高级控件。
@ State var isAnimating : Bool = true
var body : some View {
WebImage ( url : URL ( string : " https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif " ) , isAnimating : $isAnimating ) ) // Animation Control, supports dynamic changes
// The initial value of binding should be true
. customLoopCount ( 1 ) // Custom loop count
. playbackRate ( 2.0 ) // Playback speed rate
. playbackMode ( . bounce ) // Playback normally to the end, then reversely back to the start
// `WebImage` supports advanced control just like `AnimatedImage`, but without the progressive animation support
}注意:对于指示器,您也可以自定义。例如,iOS 14/watchOS 7介绍了新的ProgressView ,可以通过以下方式轻松使用:
WebImage ( url : url )
. indicator ( . activity )或者,您可以像以下方式一样写作:
WebImage ( url : url )
. indicator {
Indicator { _ , _ in
ProgressView ( )
}
}AnimatedImage来播放动画 var body : some View {
Group {
AnimatedImage ( url : URL ( string : " https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif " ) , placeholderImage : . init ( systemName : " photo " ) ) // Placeholder Image
// Supports options and context, like `.progressiveLoad` for progressive animation loading
. onFailure { error in
// Error
}
. resizable ( ) // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
. indicator ( . activity ) // Activity Indicator
. transition ( . fade ) // Fade Transition
. scaledToFit ( ) // Attention to call it on AnimatedImage, but not `some View` after View Modifier (Swift Protocol Extension method is static dispatched)
// Supports SwiftUI ViewBuilder placeholder as well
AnimatedImage ( url : url ) {
Circle ( ) . foregroundColor ( . gray )
}
// Data
AnimatedImage ( data : try ! Data ( contentsOf : URL ( fileURLWithPath : " /tmp/foo.webp " ) ) )
. customLoopCount ( 1 ) // Custom loop count
. playbackRate ( 2.0 ) // Playback speed rate
// Bundle (not Asset Catalog)
AnimatedImage ( name : " animation1.gif " , isAnimating : $isAnimating ) // Animation control binding
. maxBufferSize ( . max )
. onViewUpdate { view , context in // Advanced native view coordinate
// AppKit tooltip for mouse hover
view . toolTip = " Mouseover Tip "
// UIKit advanced content mode
view . contentMode = . topLeft
// Coordinator, used for Cocoa Binding or Delegate method
let coordinator = context . coordinator
}
}
}注意: AnimatedImage支持动画图像格式的图像URL或图像数据。使用SDWebimage的动画imageView进行内部实现。请注意,由于代表Uikit/AppKit上的这一基础,因此某些先进的SwiftUI布局和动画系统可能无法正常工作。您可能需要UIKIT/APPKIT和核心动画来修改本机视图。
注意: AnimatedImage某些方法,例如.transition , .indicator和.aspectRatio具有与SwiftUI.View相同的命名。但是ARGS会收到不同的类型。这是因为AnimatedImage支持用于与Uikit/AppKit组件和动画一起使用。如果发现歧义,请使用完整的声明而不是点表达语法。
注意: AnimatedImage上的某些方法将返回some View ,即新的修改内容。您将丢失相关的修饰符方法。对于这种情况,您可以在.onViewUpdate中重新排序方法调用,或使用本机视图(实际上是SDAnimatedImageView ),请使用Uikit/AppKit API进行recusue。
// Using UIKit components
var body : some View {
AnimatedImage ( name : " animation2.gif " )
. indicator ( SDWebImageProgressIndicator . default ) // UIKit indicator component
. transition ( SDWebImageTransition . flipFromLeft ) // UIKit animation transition
}
// Using SwiftUI components
var body : some View {
AnimatedImage ( name : " animation2.gif " )
. indicator ( Indicator . progress ) // SwiftUI indicator component
. transition ( AnyTransition . flipFromLeft ) // SwiftUI animation transition
}为什么我们在这里有两种不同的视图类型,这是因为当前的SwiftUI限制。但是我们旨在为所有用例提供最佳解决方案。
如果您不需要动画映像,请首先使用WebImage 。它表现得无缝作为内置的Swiftui视图。如果Swiftui起作用,它可以正常工作。如果Swiftui不起作用,它也可以:)
如果您需要简单的动画图像,请使用WebImage 。提供基本的动画图像支持。但是,如果您不在乎,它不支持渐进式动画渲染,也不支持矢量图像。
如果您需要功能强大的动画图像,则可以选择AnimatedImage 。请记住,它也支持静态图像,您无需检查格式,只需使用它即可。另外,一些功能强大的功能,例如Uikit/AppKit色调,矢量图像,符号图像配置,TVOS分层图像,仅在AnimatedImage中可用,但当前不在SWFITUI中。
但是,由于AnimatedImage使用UIViewRepresentable并驱动Uikit驱动,因此目前Uikit和SwiftUI布局和动画系统之间可能存在一些小问题,或者与Swiftui本身有关的错误。我们会尽力匹配SwiftUI的行为,并提供与WebImage相同的API,这使得在需要时可以轻松在这两种类型之间切换。
ImageManager用于您自己的视图类型ImageManager是一个符合可观察对象协议的类。这是我们提供的核心获取WebImage源。
对于高级用例,例如将图像加载到您不想使用WebImage复杂视图图中。您可以将自己的视图类型与经理直接绑定。
它看起来像SDWebImageManager一样熟悉,但它是为Swiftui World建造的,它为加载图像提供了真实的来源。您最好使用SwiftUi的@ObservedObject绑定视图实例的每个单个管理器实例,当图像状态更改时,该实例会自动更新视图的身体。
struct MyView : View {
@ ObservedObject var imageManager = ImageManager ( )
var body : some View {
// Your custom complicated view graph
Group {
if imageManager . image != nil {
Image ( uiImage : imageManager . image! )
} else {
Rectangle ( ) . fill ( Color . gray )
}
}
// Trigger image loading when appear
. onAppear { self . imageManager . load ( url : url ) }
// Cancel image loading when disappear
. onDisappear { self . imageManager . cancel ( ) }
}
}该框架基于SDWebimage,该框架支持高级自定义和配置以满足不同用户的需求。
您可以为外部图像格式注册多个编码器插件。您可以注册多个缓存(不同的路径和配置),多个加载程序(urlsession和Photos URL)。您可以控制我们Wiki中的缓存到期日期,大小,下载优先级等。
为Swiftui应用程序放置这些设置代码的最佳场所,是AppDelegate.swift :
func application ( _ application : UIApplication , didFinishLaunchingWithOptions launchOptions : [ UIApplication . LaunchOptionsKey : Any ] ? ) -> Bool {
// Add WebP/SVG/PDF support
SDImageCodersManager . shared . addCoder ( SDImageWebPCoder . shared )
SDImageCodersManager . shared . addCoder ( SDImageAVIFCoder . shared )
SDImageCodersManager . shared . addCoder ( SDImageSVGCoder . shared )
SDImageCodersManager . shared . addCoder ( SDImagePDFCoder . shared )
// Add default HTTP header
SDWebImageDownloader . shared . setValue ( " image/webp,image/apng,image/*,*/*;q=0.8 " , forHTTPHeaderField : " Accept " )
// Add multiple caches
let cache = SDImageCache ( namespace : " tiny " )
cache . config . maxMemoryCost = 100 * 1024 * 1024 // 100MB memory
cache . config . maxDiskSize = 50 * 1024 * 1024 // 50MB disk
SDImageCachesManager . shared . addCache ( cache )
SDWebImageManager . defaultImageCache = SDImageCachesManager . shared
// Add multiple loaders with Photos Asset support
SDImageLoadersManager . shared . addLoader ( SDImagePhotosLoader . shared )
SDWebImageManager . defaultImageLoader = SDImageLoadersManager . shared
return true
}有关更多信息,真正建议检查我们的演示,以了解详细的API使用情况。您还可以在最新的API文档上进行检查,以供高级使用。
Swiftui在List/LazyStack/LazyGrid中使用状态视图时具有已知的行为(错误?)。只有顶级视图才能保持自己的@State/@StateObject ,但是在滚动滚动时,子结构将丢失状态。但是,Webimage/Animated都是有状态的。为了确保即使滚出屏幕,状态也保持同步。您可以使用一些技巧。
请参阅更多:https://twitter.com/fatbobman/status/1572507700436807683?s = 21&t = Z4FKAWTMVJSGL-WKDJGREQ
简而言之,不建议这样做:
struct ContentView {
@ State var imageURLs : [ String ]
var body : some View {
List {
ForEach ( imageURLs , id : . self ) { url in
VStack {
WebImage ( url ) // The top level is `VStack`
}
}
}
}
}相反,使用这种方法:
struct ContentView {
struct BodyView {
@ State var url : String
var body : some View {
VStack {
WebImage ( url )
}
}
}
@ State var imageURLs : [ String ]
var body : some View {
List {
ForEach ( imageURLs , id : . self ) { url in
BodyView ( url : url )
}
}
}
} Swiftui的Button将覆盖层应用于其内容(默认Text下),这是这样写代码的常见错误,这会导致奇怪的行为:
// Wrong
Button ( action : {
// Clicked
} ) {
WebImage ( url : url )
}
// NavigationLink create Button implicitly
NavigationView {
NavigationLink ( destination : Text ( " Detail view here " ) ) {
WebImage ( url : url )
}
}取而代之的是,您必须覆盖.buttonStyle以使用纯样式,或使用.renderingMode使用原始模式。您也可以将.onTapGesture修饰符用于触摸处理。查看如何禁用覆盖颜色以获取按钮内部的图像和NavigationLink中的图像
// Correct
Button ( action : {
// Clicked
} ) {
WebImage ( url : url )
}
. buttonStyle ( PlainButtonStyle ( ) )
// Or
NavigationView {
NavigationLink ( destination : Text ( " Detail view here " ) ) {
WebImage ( url : url )
. renderingMode ( . original )
}
} WebImage/AnimatedImage均支持使用SVG/PDF外部编码器渲染向量图像。但是它们在内部不同。
AnimatedImage :使用Apple符号图像和矢量图中的技术,支持动态大小的变化而不会丢失细节。它使用基于UIKIT/APPKIT的实现和API。如果需要,请通过.context(.imageThumbnailPixelSize: size)使用位图渲染并获得更多像素。WebImage :将矢量图像绘制到位图版本中。就像普通PNG一样。默认情况下,我们使用矢量图像内容大小(SVG帆布大小或PDF媒体框大小)。如果需要,请通过.context(.imageThumbnailPixelSize: size)获得更多像素。对于位图渲染,您还可以使用自定义颜色(例如符号映像)为SVG/PDF图标着色,使用.renderingMode(.template)和.tint(:)或.foregroundColor(:)修饰符,该修饰符与SwiftUI.Image行为匹配。
var body : some View {
WebImage ( url : URL ( string : " https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/w3c.svg " ) )
. resizable ( )
. renderingMode ( . template )
. foregroundColor ( . red ) // or `.tint(:)`, `.accentColor(:)`
. scaledToFit ( )
} var body : some View {
AnimatedImage ( url : URL ( string : " https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/w3c.svg " ) , context : [ . imageThumbnailPixelSize : CGSize ( width : 100 , height : 100 ) ] )
. resizable ( )
. renderingMode ( . template )
// seems `.foregroundColor(:)` does effect `UIView.tintColor`, use `tint(:)` or `.accentColor(:)` instead.
// Or you can use `onViewCreate(:)` to get native `SDAnimatedImageView` and set `tintColor` (AppKit use `contentTintColor`)
. tint ( . red )
. scaledToFit ( )
}查看更多:在UI中配置和显示符号图像
SDWebimage本身,支持许多自定义加载程序(例如Firebase Storage和Photoskit),Caches(例如Yycache和Pincache)以及编码器(例如WebP和Avif,甚至Lottie)。
这是通过SwiftUI环境设置这些外部组件的教程。
您可以将设置代码放入SwiftUi App.init()方法中。
@ main
struct MyApp : App {
init ( ) {
// Custom Firebase Storage Loader
FirebaseApp . configure ( )
SDImageLoadersManager . shared . loaders = [ FirebaseUI . StorageImageLoader . shared ]
SDWebImageManager . defaultImageLoader = SDImageLoadersManager . shared
// WebP support
SDImageCodersManager . shared . addCoder ( SDImageWebPCoder . shared )
// AVIF support
SDImageCodersManager . shared . addCoder ( SDImageAVIFCoder . shared )
}
var body : some Scene {
WindowGroup {
ContentView ( )
}
}
}或者,如果您的应用具有复杂的AppDelegate类,请将设置代码放在那里:
class AppDelegate : NSObject , UIApplicationDelegate {
func application ( _ application : UIApplication , didFinishLaunchingWithOptions launchOptions : [ UIApplication . LaunchOptionsKey : Any ] ? = nil ) -> Bool {
SDImageCachesManager . shared . caches = [ YYCache ( name : " default " ) ]
SDWebImageManager . defaultImageCache = SDImageCachesManager . shared
return true
}
}
@ main
struct MyApp : App {
@ UIApplicationDelegateAdaptor ( AppDelegate . self ) var appDelegate
var body : some Scene {
WindowGroup {
ContentView ( )
}
}
} 对于某些自定义加载程序,您需要使用一些特殊的API创建URL结构,以便SDWebimage可以从其他SDK中检索上下文,例如:
let storageRef : StorageReference
let storageURL = NSURL . sd_URL ( with : storageRef ) as URL ?
// Or via convenience extension
let storageURL = storageRef . sd_URLRepresentation let asset : PHAsset
let photosURL = NSURL . sd_URL ( with : asset ) as URL ?
// Or via convenience extension
let photosURL = asset . sd_URLRepresentation对于某些自定义编码器,您需要使用一些选项请求图像来控制行为,例如向量图像SVG/PDF。因为SwiftUi.Image或Webimage完全不支持向量图。
let vectorURL : URL ? // URL to SVG or PDF
WebImage ( url : vectorURL , context : [ . imageThumbnailPixelSize : CGSize ( width : 100 , height : 100 ) ] ) let lottieURL : URL ? // URL to Lottie.json
WebImage ( url : lottieURL , isAnimating : $isAnimating )对于缓存,您实际上无需担心任何事情。它只是在设置后起作用。
当您的应用程序目标的部署目标版本小于iOS 14/macOS 11/tvos 14/watchOS 7时,SDWEBIMAGESWIFTUI支持要使用的是,它将较弱的Swiftui(组合)链接以允许在运行时使用可用检查的代码。
要使用向后部署,您必须执行以下操作:
添加-weak_framework SwiftUI -weak_framework Combine在应用程序目标的Other Linker Flags构建设置中结合在一起。您也可以使用Xcode的Optional Framework复选框执行此操作,并具有相同的效果。
您应该注意到,所有第三方Swiftui框架也应该具有此构建设置,而不仅仅是sdwebimageswiftui。或在iOS 12设备上运行时,它将在启动时触发运行时染色器错误。
对于iOS 12.2下方的部署目标版本(在iOS系统中捆绑5运行时的第一个版本),您必须更改SDWebimagesWiftUI的最小部署目标版本。这可能会对编译器的优化产生一些副作用,并触发某些框架的大规模警告。
但是,对于iOS 12.2+,您仍然可以将最小部署目标版本保留到iOS 14,对于iOS 14客户端,没有额外的警告或性能放慢速度。
因为Swift使用最小部署目标版本来检测是链接应用程序捆绑的Swift运行时还是内置的系统( /usr/lib/swift/libswiftCore.dylib )。
post_install do | installer |
installer . pods_project . targets . each do | target |
target . build_configurations . each do | config |
config . build_settings [ 'IPHONEOS_DEPLOYMENT_TARGET' ] = '11.0' # version you need
end
end
end对于Carthage用户,您可以使用carthage update --no-build下载依赖项,然后更改Xcode Project的部署目标版本并构建二进制框架。
对于SwiftPM用户,您必须使用本地依赖关系(与Git子模块)更改部署目标版本。
对于迦太基用户,构建的二进制框架将使用库Evolution来支持向后部署。
对于Cocoapods用户,您可以在podfile中跳过平台版本验证:
platform :ios , '14.0' # This does not effect your App Target's deployment target version, just a hint for CocoaPods将所有SwiftUI代码添加到可用的注释和运行时检查,例如:
// AppDelegate.swift
func application ( _ application : UIApplication , didFinishLaunchingWithOptions launchOptions : [ UIApplication . LaunchOptionsKey : Any ] ? ) -> Bool {
// ...
if #available ( iOS 14 , * ) {
window . rootViewController = UIHostingController ( rootView : ContentView ( ) )
} else {
window . rootViewController = ViewController ( )
}
// ...
}
// ViewController.swift
class ViewController : UIViewController {
var label : UILabel = UILabel ( )
override func viewDidLoad ( ) {
super . viewDidLoad ( )
view . backgroundColor = . white
view . addSubview ( label )
label . text = " Hello World iOS 12! "
label . sizeToFit ( )
label . center = view . center
}
}
// ContentView.swift
@ available ( iOS 14 . 0 , macOS 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
struct ContentView : View {
var body : some View {
Group {
Text ( " Hello World iOS 14! " )
WebImage ( url : URL ( string : " https://i.loli.net/2019/09/24/rX2RkVWeGKIuJvc.jpg " ) )
}
}
} 使用swiftui运行示例,按照以下步骤:
SDWebImageSwiftUI.xcworkspace ,等待SwiftPM完成下载测试依赖项。SDWebImageSwiftUIDemo (或其他平台)方案并运行演示应用程序。注意:这里的Podfile是因为历史记录我们使用Cocoapods将Libs整合到演示中,但是现在我们使用了SPM。
由于Swiftui的目标是支持所有Apple平台,因此我们的演示也可以做到这一点,其中一个代码库包括:
演示提示:
Switch (右键单击MacOS/Tak On WatchOS上的点击)在WebImage和AnimatedImage之间切换。Reload (右键单击WatchOS上的MACOS/按钮)清除缓存。Swipe Left (TVOS上的菜单按钮)从列表中删除一个图像URL。SDWEBIMAGESWIFTUI具有单位测试以提高代码质量。对于Swiftui,苹果没有提供官方的单位测试解决方案。
但是,由于Swiftui是基于州的且归因于实施布局系统,因此有开源项目提供解决方案:
.frame修改器, .image值)。我们使用它来测试AnimatedImage和WebImage 。它还允许检查到本机UIVIEW/NSVIEW。进行测试:
pod install以安装依赖关系。SDWebImageSwiftUI.xcworkspace ,等待SwiftPM完成下载测试依赖项。SDWebImageSwiftUITests方案并开始测试。我们已经设置了CI管道,每个PR将运行测试案例并将测试报告上传到Codecov。
除上述所有内容外,该项目还可以确保Swift平台上可用于SDWebimage本身的以下功能。
这意味着,该项目是一个核心用例和下游依赖性,这使SDWebimage本身未来发展。
Dreampiggy
SDWebimagesWiftUI可根据MIT许可获得。有关更多信息,请参见许可证文件。