APNGKit 2.3.0

APNGKit 2.3.0

测试已测试
语言语言 CC
许可证 MIT
发布上次发布2024年3月

Wei Wang 维护。




APNGKit 2.3.0

APNGKit

APNGKit 是一个用于在 iOS 和 macOS 中加载和显示 APNG 图像的高性能框架。它使用高级抽象构建,并提供了令人愉悦的 API。因此,您在使用 APNGKit 处理 APNG 格式的图像时会感到舒适和愉快。

APNG,是什么,为什么用它?

动画可移植网络图像(APNG)是已知PNG格式的扩展。它允许类似于动画GIF文件的动画PNG文件,同时支持24位图像和8位透明度,这在GIF中不可用。这意味着动画质量更好。同时,文件大小与GIF相当,如果精心制作,甚至更小。

光说不练假把式;给你看一下图像。您可以点击图像以查看它的动画效果。

APNGKit Demo

真是太酷了。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将其显示在屏幕上,它是UIViewNSView的子类。

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)(请注意,不是UIImageViewNSImageView)拖到画布上,并将其类修改为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)")
        }
    }
}

numberOfPlaysnil时,动画将永远播放。如果它是一个有限的非零值,当循环次数达到限制时,动画将在最后一帧停止。要检查整个动画完成,请使用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文件,我建议您现在使用iSpartaapngasm

许可证

APNGKit是在MIT许可证下发布的。有关详细信息,请参阅LICENSE。