APNGKit 是一个用于在 iOS 和 macOS 中加载和显示 APNG 图像的高性能框架。它使用高级抽象构建,并提供了令人愉悦的 API。因此,您在使用 APNGKit 处理 APNG 格式的图像时会感到舒适和愉快。
APNG,是什么,为什么用它?
动画可移植网络图像(APNG)是已知PNG格式的扩展。它允许类似于动画GIF文件的动画PNG文件,同时支持24位图像和8位透明度,这在GIF中不可用。这意味着动画质量更好。同时,文件大小与GIF相当,如果精心制作,甚至更小。
光说不练假把式;给你看一下图像。您可以点击图像以查看它的动画效果。
真是太酷了。APNG 真的好!但是等等...我为什么之前没听说过 APNG 呢?它不是一个流行的格式,那为什么我应该在我的下一个优秀的 iOS/macOS 应用中使用它呢?
好问题!APNG 是常规 PNG 的优秀扩展,同时使用起来也非常简单,不会与现有的 PNG 标准冲突(它由标准 PNG 标头组成,因此如果您的平台不支持 APNG,它将识别为常规 PNG,并将第一帧作为静态图像显示)。但是遗憾的是,它是一个反叛格式,所以没有获得 PNG 小组的认可。然而,它被许多厂商接受,甚至在 W3C 标准 中提到了它。还有一个名为 MNG(多图像网络图形)的格式,由与 PNG 相同的团队创建。它是一个全面的格式,但非常非常复杂(重要的事情要说三遍)。它的复杂性如此之高,尽管它是一个“标准”,但它几乎被普遍拒绝。只有一个“流行”的浏览器叫做 Konqueror(至少我在高中时使用过它),它支持 MNG,这真是一个既悲惨又合理的故事。
iOS 9.0+ / macOS 10.11+ / tvOS 9.0+
$ gem install cocoapods
要将APNGKit集成到您的Xcode项目中使用CocoaPods,请在您的Podfile
中指定它。
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
use_frameworks!
target 'your_app' do
pod 'APNGKit', '~> 2.0'
end
然后,运行以下命令
$ pod install
在您使用CocoaPods安装任何内容后,应该打开{Project}.xcworkspace
而不是{Project}.xcodeproj
。
有关如何使用CocoaPods的更多信息,我建议查看这篇教程。
用法
基本
将APNGKit导入到您希望使用该框架的源文件中。
import APNGKit
加载一个APNG图像
// Load an APNG image from file in main bundle
var image = try APNGImage(named: "your_image")
// Or
// Load an APNG image from file at specified path
if let url = Bundle.main.url(forResource: "your_image", withExtension: "apng") {
image = try APNGImage(fileURL: path)
}
// Or
// Load an APNG image from data
let data: Data = ... // From disk or network or anywhere else.
image = try APNGImage(data: data)
您可能会注意到所有初始化器都是可抛出的。如果在创建图像时发生错误,它会明确地通知您错误,并且您有机会处理它。我们将在稍后介绍错误处理。
显示一个APNG图像
当您有一个APNGImage
对象时,您可以使用它初始化一个图像视图,并使用APNGImageView
将其显示在屏幕上,它是UIView
或NSView
的子类。
let image: APNGImage = ... // You already have an APNG image object.
let imageView = APNGImageView(image: image)
view.addSubview(imageView)
启动动画
当使用合法的APNG图像创建视图时,动画将自动播放。如果您不希望自动播放动画,请在分配图像之前将autoStartAnimationWhenSetImage
属性设置为false
。
let imageView = APNGImageView(frame: .zero)
imageView.autoStartAnimationWhenSetImage = false
imageView.image = image
// Start the animation manually:
imageView.startAnimating()
XIB或Storyboard
如果您喜欢Interface Builder,请将一个UIView
(或NSView
)(请注意,不是UIImageView
或NSImageView
)拖到画布上,并将其类修改为APNGImageView
。然后,您可以像往常一样拖动一个IBOutlet
并对其进行操作,比如设置其image
属性。
委托
APNG定义了播放循环次数为APNGImage
中的numberOfPlays
,APNGKit默认遵循它。要检查每个循环的结束,请将自己注册为APNGImageView.onOnePlayDone
的委托。
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let imageView = APNGImageView(image: image)
imageView.onOnePlayDone.delegate(on: self) { (self, count) in
print("Played: \(count)")
}
}
}
当numberOfPlays
为nil
时,动画将永远播放。如果它是一个有限的非零值,当循环次数达到限制时,动画将在最后一帧停止。要检查整个动画完成,请使用onAllPlaysDone
。
imageView.onAllPlaysDone.delegate(on: self) { (self, _) in
print("All done.")
}
APNGKit默认以流式方式加载数据,它播放动画时读取帧信息。由于APNG将每个帧的持续时间编码在内,因此在加载所有帧信息之前无法获取整个动画的持续时间。在第一次读取通过结束之前,您只能获取加载帧的部分持续时间。要获取完整持续时间,请使用APNGImage.onFramesInformationPrepared
。
let image = try APNGImage(named: "image")
image.onFramesInformationPrepared.delegate(on: self) { (self, _) in
switch image.duration {
case .full(let duration):
print("Full duration: \(duration)")
case .partial:
print("This should not happen.")
}
}
imageView.image = image
或者,您可以在创建APNGImage
时指定.fullFirstPass
选项。它将读取所有帧,然后再开始渲染和动画化图像。
let image = try APNGImage(named: "image", options: [.fullFirstPass])
print(image.duration) // .full(duration)
APNGKit还提供了一些其他读取选项。现在让我跳过这一点,您可以查看文档。
错误处理
在创建图像时
创建 APNGImage
可能会抛出错误。所有可能的错误都在 APNGKitError.decoderError
中定义。当创建图像时发生错误,您需要检查它是否应该被视为正常静态图像。如果是,尝试将其设置为静态图像。
do {
let image = try APNGImage(named: data.imageName)
imageView.image = image
} catch {
if let normalImage = error.apngError?.normalImage {
imageView.staticImage = normalImage
} else {
print("Error: \(error)")
}
}
在播放动画时
如果某些帧损坏,APNG中定义的默认图像应作为回退显示。您可以在APNGKit中免费获得此功能。要在此事件发生时收到通知,请监听 APNGImageView.onFallBackToDefaultImage
imageView.onDecodingFrameError.delegate(on: self) { (self, error) in
print("A frame cannot be decoded. After this, either onFallBackToDefaultImage or onFallBackToDefaultImageFailed happens.")
}
imageView.onFallBackToDefaultImage.delegate(on: self) { (self, _) in
print("Fall back to default image.")
}
imageView.onFallBackToDefaultImageFailed.delegate(on: self) { (self, error) in
print("Tried to fall back to default image, but it fails: \(error)")
}
PNG压缩
当您构建项目时,Xcode会压缩应用程序包中的所有PNG文件。由于APNG是PNG的扩展格式,Xcode会认为该文件中存在冗余数据并将其压缩成单个静态图像。在这种情况下,您可能会从APNGKit中查看日志消息。
检测到
CgBI
块。这表明输入图像被Xcode压缩且不受APNGKit支持。请考虑将其重命名为apng
来防止压缩。
通常,在使用APNG时,这不是您所希望的结果。您可以通过将应用目标构建设置中的“COMPRESS_PNG_FILES”设置为NO来禁用PNG压缩。然而,这也将阻止Xcode优化您其他常规的PNG文件。
更好的方法是更改您的APNG文件名,使其扩展名不是“png”。如果您这样做,Xcode将不再将您的APNG文件识别为PNG格式,并且不会对其应用压缩。建议的扩展名是“apng”,这将由APNGKit无缝检测和处理。
致谢
README文件中的示例大象图片是从ICS实验室偷来的,您可以在这里找到原始帖子。
参考
如果您对APNG感兴趣,您可以从中了解更多(其中一些是用中文写的)。
APNGKit现在只能加载和显示APNG图像。创建功能将在以后开发。如果您现在需要创建APNG文件,我建议您现在使用iSparta或apngasm。
许可证
APNGKit是在MIT许可证下发布的。有关详细信息,请参阅LICENSE。