如何创建动画 GIF
Posted
技术标签:
【中文标题】如何创建动画 GIF【英文标题】:How to create an animated GIF 【发布时间】:2019-07-14 04:42:57 【问题描述】:我想在 SwiftUI 中创建一个动画 GIF。
我试过了:
-
将 GIF 添加到我的资产并通过
Image(“myGif”)
加载它
通过下载 GIF 图像创建 UIImage,并将其传递给Image
使用this script 创建一个UIImage,然后重复#2。
以上方法都不行,有没有人解决这个问题?
【问题讨论】:
ios 不显示动画 gif,那么为什么使用 SwiftUI 会改变呢? 我实际上不是在谈论 iOS,而是在谈论 Apple Watch(因此是“apple-watch”标签)。 WatchKit 为显示 GIF 提供了一个非常简单的界面,我正在尝试在 SwiftUI 中重新创建它 【参考方案1】:您可以通过从UIView
创建UIViewRepresentable
来实现。
UIViewRepresentable 为您创建View
,您可以在SwiftUI
类中使用它。
第一步:为 Gifview 创建UIView
类
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
第二步:为 GifView 创建UIViewRepresentable
类
struct GIFView: UIViewRepresentable
var gifName: String
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<GIFView>)
func makeUIView(context: Context) -> UIView
return GIFPlayerView(gifName: gifName)
第 3 步:使用 UIViewRepresentable
视图。
var body: some View
VStack
GIFView(gifName: "your gif name")
注意: 将 Gif 扩展为图像。参考:https://***.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
【讨论】:
对不起,我应该在我的帖子中说得更清楚。这是针对 WatchOS 的。我通过采用类似的方法并使用 WkInterfaceInlineMovie 解决了这个问题 如何在swiftui中使用扩展? 您询问的是哪个组件的扩展? @q8yas 嗨 Vishal Patel,感谢您的响应,您放置的扩展名 extension UIImageView public func loadGif(name: String) DispatchQueue.global().async let image = UIImage.gif(name: name) DispatchQueue.main.async self.image = image ....................以上是关于如何创建动画 GIF的主要内容,如果未能解决你的问题,请参考以下文章