Xcode中Assets.xcassets添加GIF后UIImage识别报错的解决方法
问题根因
- Xcode 12.4 的资产目录(Assets.xcassets)本身就不支持把GIF当普通图片资源识别:直接拖进普通Image Set的GIF,系统不会把它注册为可通过
UIImage(named:)直接加载的图片类型,反而会归类成通用数据文件,这就是你碰到CoreUI: attempting to lookup a named image ...报错的根本原因,你在资产目录里看到GIF的展示样式和PNG/JPG静态图不一样,就是系统识别错了资源类型的直观表现。 - 你当前的调用逻辑本身也存在缺陷:即便资源识别正常,
UIImage(named:)接口仅会返回GIF的第一帧静态图,无法读取动图的全量帧序列;且UIImage.animatedImage(with:duration:)需要传入完整的帧数组才能生成动图效果,你传入的单帧图片数组无论设置多长的duration,都只会显示静态内容。
解决方案
按照资源存放位置分为两种可落地的实现方式,可根据项目习惯选择:
方案1:将GIF移出Assets.xcassets,作为Bundle直接资源加载(适配性最稳定)
- 操作流程:
- 将之前存放在Assets.xcassets中的GIF文件移动到项目主目录或自定义资源文件夹中,在文件的Target Membership面板勾选对应应用的target,确保文件会被打包进应用Bundle
- 使用自定义工具方法从Bundle读取GIF二进制数据,拆分为帧序列后生成动图,示例代码如下:
static func loadGif(named fileName: String) -> UIImage? { // 从Bundle读取GIF资源 guard let resourceURL = Bundle.main.url(forResource: fileName, withExtension: "gif"), let gifData = try? Data(contentsOf: resourceURL), let imageSource = CGImageSourceCreateWithData(gifData as CFData, nil) else { return nil } let frameCount = CGImageSourceGetCount(imageSource) var frameList = [UIImage]() var totalDuration: TimeInterval = 0 // 逐帧读取图片和帧时长 for index in 0..<frameCount { guard let cgFrame = CGImageSourceCreateImageAtIndex(imageSource, index, nil) else { continue } frameList.append(UIImage(cgImage: cgFrame)) // 读取GIF内置的每帧播放间隔 let frameProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, index, nil) as? [String: Any] let gifFrameInfo = frameProperties?[kCGImagePropertyGIFDictionary as String] as? [String: Any] let frameDuration = gifFrameInfo?[kCGImagePropertyGIFDelayTime as String] as? TimeInterval ?? 0.1 totalDuration += frameDuration } // 生成完整动图 return UIImage.animatedImage(with: frameList, duration: totalDuration) } - 调用时直接传入GIF的文件名即可,不会触发CoreUI类型识别报错:
let displayImage = loadGif(named: "img_name")
方案2:必须保留GIF在Assets.xcassets中存储时的适配方式
Xcode 12.4 的资产目录仅支持通过数据通道读取GIF资源,不能走普通图片资源的加载逻辑:
- 操作流程:
- 在Assets.xcassets中删除之前存放GIF的普通Image Set,新建Data类型的资源集合,不要新建Image Set,将GIF文件拖入这个Data集合中,将集合命名为和GIF一致的名称(如
img_name) - 加载时不能直接使用
UIImage(named:)读取,需要先通过NSDataAsset接口读取集合中的二进制数据,再执行和方案1一致的拆帧逻辑生成动图,示例代码如下:static func loadGifFromAssets(named assetName: String) -> UIImage? { guard let gifAsset = NSDataAsset(name: assetName), let imageSource = CGImageSourceCreateWithData(gifAsset.data as CFData, nil) else { return nil } let frameCount = CGImageSourceGetCount(imageSource) var frameList = [UIImage]() var totalDuration: TimeInterval = 0 for index in 0..<frameCount { guard let cgFrame = CGImageSourceCreateImageAtIndex(imageSource, index, nil) else { continue } frameList.append(UIImage(cgImage: cgFrame)) let frameProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, index, nil) as? [String: Any] let gifFrameInfo = frameProperties?[kCGImagePropertyGIFDictionary as String] as? [String: Any] totalDuration += gifFrameInfo?[kCGImagePropertyGIFDelayTime as String] as? TimeInterval ?? 0.1 } return UIImage.animatedImage(with: frameList, duration: totalDuration) }
- 在Assets.xcassets中删除之前存放GIF的普通Image Set,新建Data类型的资源集合,不要新建Image Set,将GIF文件拖入这个Data集合中,将集合命名为和GIF一致的名称(如
- 注意:如果仍然尝试对这个Data类型的集合调用
UIImage(named:)接口,会再次触发原有的类型不匹配报错,必须通过NSDataAsset读取二进制数据后再做处理。
内容的提问来源于stack exchange,提问作者Gabul
相关产品推荐
相关产品推荐

