如何创建动画 GIF [英] How to create an animated GIF
问题描述
我想在 SwiftUI 中创建动画 GIF.
我试过了:
- 向我的资产添加 GIF 并通过
Image(myGif")
加载它 - 通过下载 GIF 图片创建 UIImage,并将其传递给
Image
- 使用this脚本创建一个UIImage,然后重复#2.
以上都没有奏效,有人发现了吗?
您可以通过从 UIView
创建 UIViewRepresentable
来实现.UIViewRepresentable 为你创建View
,你可以在SwiftUI
类中使用它.
第一步:为 Gifview 创建 UIView
类
class GIFPlayerView: UIView {私人让 imageView = UIImageView()方便初始化(gifName:字符串){self.init()让 gif = UIImage.gif(资产:gifName)imageView.image = gifimageView.contentMode = .scaleAspectFitself.addSubview(imageView)}覆盖初始化(框架:CGRect){super.init(框架:框架)}需要初始化?(编码器:NSCoder){fatalError("init(coder:) 尚未实现")}覆盖 func layoutSubviews() {super.layoutSubviews()imageView.frame = 边界}}
Step2:为 GifView 创建 UIViewRepresentable
类
struct GIFView: UIViewRepresentable {var gifName: 字符串func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<GIFView>) {}func makeUIView(context: Context) ->界面视图{返回 GIFPlayerView(gifName: gifName)}}
步骤 3: 使用 UIViewRepresentable
视图.
var body: some View {虚拟堆栈{GIFView(gifName: "你的 gif 名称")}}
<块引用>
注意: Gif to Image 的扩展.参考:https://stackoverflow.com/a/45554784
扩展 UIImageView {public func loadGif(名称:字符串){DispatchQueue.global().async {让 image = UIImage.gif(name: name)DispatchQueue.main.async {self.image = 图像}}}@available(iOS 9.0, *)public func loadGif(资产:字符串){DispatchQueue.global().async {让图像 = UIImage.gif(资产:资产)DispatchQueue.main.async {self.image = 图像}}}}扩展 UIImage {公共类func gif(数据:数据)->用户界面图像?{//从数据创建源守卫让源= CGImageSourceCreateWithData(数据作为CFData,零)其他{print("SwiftGif: 图片来源不存在")返回零}返回 UIImage.animatedImageWithSource(source)}公共类 func gif(url: String) ->用户界面图像?{//验证网址守卫让 bundleURL = URL(string: url) else {print("SwiftGif: 这个名为\"\(url)\" 的图片不存在")返回零}//验证数据守卫让 imageData = 试试?数据(内容:bundleURL)其他{print("SwiftGif: 无法将名为 \"\(url)\" 的图像转换为 NSData")返回零}返回 gif(数据:imageData)}公共类 func gif(name: String) ->用户界面图像?{//检查gif是否存在守卫让 bundleURL = Bundle.main.url(forResource: name, withExtension: "gif") else {print("SwiftGif: 这张名为 \"\(name)\" 的图片不存在")返回零}//验证数据守卫让 imageData = 试试?数据(内容:bundleURL)其他{print("SwiftGif: 无法将名为 \"\(name)\" 的图像转换为 NSData")返回零}返回 gif(数据:imageData)}@available(iOS 9.0, *)公共类func gif(资产:字符串)->用户界面图像?{//从资产目录创建源守卫让数据资产= NSDataAsset(名称:资产)其他{print("SwiftGif: 无法将名为 \"\(asset)\" 的图像转换为 NSDataAsset")返回零}返回 gif(数据:dataAsset.data)}内部类 func delayForImageAtIndex(_ index: Int, source: CGImageSource!) ->双倍的 {无功延迟 = 0.1//获取字典让 cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)让 gifPropertiesPointer = UnsafeMutablePointer.allocate(capacity: 0)推迟 {gifPropertiesPointer.deallocate()}让 unsafePointer = Unmanaged.passUnretained(kCGImagePropertyGIFDictionary).toOpaque()如果 CFDictionaryGetValueIfPresent(cfProperties, unsafePointer, gifPropertiesPointer) == false {返回延迟}让 gifProperties: CFDictionary = unsafeBitCast(gifPropertiesPointer.pointee, to: CFDictionary.self)//获取延迟时间var delayObject: AnyObject = unsafeBitCast(CFDictionaryGetValue(gifProperties,Unmanaged.passUnretained(kCGImagePropertyGIFUnclampedDelayTime).toOpaque()),到:AnyObject.self)如果 delayObject.doubleValue == 0 {delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties,Unmanaged.passUnretained(kCGImagePropertyGIFDelayTime).toOpaque()), to: AnyObject.self)}if let delayObject = delayObject as?双,delayObject >0 {延迟 = 延迟对象} 别的 {delay = 0.1//确保它们不会太快}返回延迟}内部类 func gcdForPair(_ lhs: Int?, _ rhs: Int?) ->整数{var lhs = lhsvar rhs = rhs//检查其中一个是否为零如果rhs == nil ||lhs == nil {如果rhs != nil {回归吧!} else if lhs != nil {返回 lhs!} 别的 {返回 0}}//交换模数如果 lhs!<啊!{让 ctp = lhslhs = rhsrhs = ctp}//获取最大公约数var 休息:Int而真{休息= lhs!%rs!如果休息 == 0 {回归吧!//找到了} 别的 {lhs = rhsrhs = 休息}}}内部类 func gcdForArray(_ array: [Int]) ->整数{如果 array.isEmpty {返回 1}var gcd = 数组[0]对于数组中的 val {gcd = UIImage.gcdForPair(val, gcd)}返回 gcd}内部类 func animationImageWithSource(_ source: CGImageSource) ->用户界面图像?{让计数 = CGImageSourceGetCount(source)var 图像 = [CGImage]()var 延迟 = [Int]()//填充数组对于索引在 0..
I’d like to create an animated GIF in SwiftUI.
I tried:
- Adding a GIF to my assets and loading that through
Image("myGif")
- Creating a UIImage through downloading a GIF image, and passing that to
Image
- Using this script to create a UIImage and then repeating #2.
None of the above worked, has anyone figured this out?
You can do it by creating UIViewRepresentable
from UIView
.
UIViewRepresentable create View
for you, which you can use it in SwiftUI
class.
Step1: Create UIView
class for Gifview
class GIFPlayerView: UIView {
private let imageView = UIImageView()
convenience init(gifName: String) {
self.init()
let gif = UIImage.gif(asset: gifName)
imageView.image = gif
imageView.contentMode = .scaleAspectFit
self.addSubview(imageView)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
imageView.frame = bounds
}
}
Step2: Create UIViewRepresentable
class for GifView
struct GIFView: UIViewRepresentable {
var gifName: String
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<GIFView>) {
}
func makeUIView(context: Context) -> UIView {
return GIFPlayerView(gifName: gifName)
}
}
Step3: Use that UIViewRepresentable
view.
var body: some View {
VStack {
GIFView(gifName: "your gif name")
}
}
Note: Extension for Gif to Image. Refer: https://stackoverflow.com/a/45554784
extension UIImageView {
public func loadGif(name: String) {
DispatchQueue.global().async {
let image = UIImage.gif(name: name)
DispatchQueue.main.async {
self.image = image
}
}
}
@available(iOS 9.0, *)
public func loadGif(asset: String) {
DispatchQueue.global().async {
let image = UIImage.gif(asset: asset)
DispatchQueue.main.async {
self.image = image
}
}
}
}
extension UIImage {
public class func gif(data: Data) -> UIImage? {
// Create source from data
guard let source = CGImageSourceCreateWithData(data as CFData, nil) else {
print("SwiftGif: Source for the image does not exist")
return nil
}
return UIImage.animatedImageWithSource(source)
}
public class func gif(url: String) -> UIImage? {
// Validate URL
guard let bundleURL = URL(string: url) else {
print("SwiftGif: This image named \"\(url)\" does not exist")
return nil
}
// Validate data
guard let imageData = try? Data(contentsOf: bundleURL) else {
print("SwiftGif: Cannot turn image named \"\(url)\" into NSData")
return nil
}
return gif(data: imageData)
}
public class func gif(name: String) -> UIImage? {
// Check for existance of gif
guard let bundleURL = Bundle.main
.url(forResource: name, withExtension: "gif") else {
print("SwiftGif: This image named \"\(name)\" does not exist")
return nil
}
// Validate data
guard let imageData = try? Data(contentsOf: bundleURL) else {
print("SwiftGif: Cannot turn image named \"\(name)\" into NSData")
return nil
}
return gif(data: imageData)
}
@available(iOS 9.0, *)
public class func gif(asset: String) -> UIImage? {
// Create source from assets catalog
guard let dataAsset = NSDataAsset(name: asset) else {
print("SwiftGif: Cannot turn image named \"\(asset)\" into NSDataAsset")
return nil
}
return gif(data: dataAsset.data)
}
internal class func delayForImageAtIndex(_ index: Int, source: CGImageSource!) -> Double {
var delay = 0.1
// Get dictionaries
let cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)
let gifPropertiesPointer = UnsafeMutablePointer<UnsafeRawPointer?>.allocate(capacity: 0)
defer {
gifPropertiesPointer.deallocate()
}
let unsafePointer = Unmanaged.passUnretained(kCGImagePropertyGIFDictionary).toOpaque()
if CFDictionaryGetValueIfPresent(cfProperties, unsafePointer, gifPropertiesPointer) == false {
return delay
}
let gifProperties: CFDictionary = unsafeBitCast(gifPropertiesPointer.pointee, to: CFDictionary.self)
// Get delay time
var delayObject: AnyObject = unsafeBitCast(
CFDictionaryGetValue(gifProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFUnclampedDelayTime).toOpaque()),
to: AnyObject.self)
if delayObject.doubleValue == 0 {
delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFDelayTime).toOpaque()), to: AnyObject.self)
}
if let delayObject = delayObject as? Double, delayObject > 0 {
delay = delayObject
} else {
delay = 0.1 // Make sure they're not too fast
}
return delay
}
internal class func gcdForPair(_ lhs: Int?, _ rhs: Int?) -> Int {
var lhs = lhs
var rhs = rhs
// Check if one of them is nil
if rhs == nil || lhs == nil {
if rhs != nil {
return rhs!
} else if lhs != nil {
return lhs!
} else {
return 0
}
}
// Swap for modulo
if lhs! < rhs! {
let ctp = lhs
lhs = rhs
rhs = ctp
}
// Get greatest common divisor
var rest: Int
while true {
rest = lhs! % rhs!
if rest == 0 {
return rhs! // Found it
} else {
lhs = rhs
rhs = rest
}
}
}
internal class func gcdForArray(_ array: [Int]) -> Int {
if array.isEmpty {
return 1
}
var gcd = array[0]
for val in array {
gcd = UIImage.gcdForPair(val, gcd)
}
return gcd
}
internal class func animatedImageWithSource(_ source: CGImageSource) -> UIImage? {
let count = CGImageSourceGetCount(source)
var images = [CGImage]()
var delays = [Int]()
// Fill arrays
for index in 0..<count {
// Add image
if let image = CGImageSourceCreateImageAtIndex(source, index, nil) {
images.append(image)
}
// At it's delay in cs
let delaySeconds = UIImage.delayForImageAtIndex(Int(index),
source: source)
delays.append(Int(delaySeconds * 1000.0)) // Seconds to ms
}
// Calculate full duration
let duration: Int = {
var sum = 0
for val: Int in delays {
sum += val
}
return sum
}()
// Get frames
let gcd = gcdForArray(delays)
var frames = [UIImage]()
var frame: UIImage
var frameCount: Int
for index in 0..<count {
frame = UIImage(cgImage: images[Int(index)])
frameCount = Int(delays[Int(index)] / gcd)
for _ in 0..<frameCount {
frames.append(frame)
}
}
// Heyhey
let animation = UIImage.animatedImage(with: frames,
duration: Double(duration) / 1000.0)
return animation
}
}
这篇关于如何创建动画 GIF的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!