iOS模块化中的通用图片访问器

前言

本文旨在解决,多模块、多 bundle 的图片资源的统一访问方法。

目前,我们开发的项目实现了模块化,不同模块使用的图片资源也都抽到了相应的模块内部,可以共用的图片资源和资源访问器,单独封装成一个基础模块。图片资源使用 xcassets 进行管理,模块通过 Cocoapods 进行管理。需要在每个模块中的 podspec 这样定义资源包:

s.resource_bundle = {
  'ModuleName' => ['Resources/*']
}

这样定义的好处是,pod 会将资源打包成模块同名的 bundle 文件。不会将资源整合到 main bundle ,这样也可以避免命名冲突的问题。

当然,如果你喜欢的话,你也可以选择另一种方式,将所有的资源整合到 main bundle 。那么,你可能就没有从非 main bundle 取图片的烦恼,你也可以关闭这篇文章了。

在介绍这个访问器之前,有必要介绍下 Swift 的下标语法,我在 Swift 中实现的图片资源访问器是基于此语法进行设计。

Subscript-下标语法

下标语法可以定义在类、结构体和枚举中,是访问集合、列表或序列中元素的快捷方式。可以使用下标的索引,设置和获取值,而不需要再调用对应的存取方法。

在使用 Swift 进行开发时,下标语法几乎每天都会用到,比如我们从数组中取一个元素:

let item = array[i]

从字典中通过 key 来取值:

let value = dict["key"]

下标允许你通过在实例名称后面的方括号中传入一个或者多个索引值来对实例进行查询。它的语法类似于实例方法语法和计算型属性语法。定义下标使用 subscript 关键字,与定义实例方法类似,都是指定一个或多个输入参数和一个返回类型。与实例方法不同的是,下标可以设定为读写或只读。这种行为由 getter 和 setter 实现,类似计算型属性:

subscript(index: Int) -> Int {
    get {
      // 返回一个适当的 Int 类型的值
    }
    set(newValue) {
      // 执行适当的赋值操作
    }
}

newValue 的类型和下标操作的返回类型相同。如同计算型属性,可以不指定 setter 的参数(newValue)。如果不指定参数,setter 会提供一个名为 newValue 的默认参数。

如同只读计算型属性,对于只读下标的声明,你可以通过省略 get 关键字和对应的大括号组来进行简写:

subscript(index: Int) -> Int {
    // 返回一个适当的 Int 类型的值
}

更多关于下标语法的信息,你可以通过这个链接了解更全面的信息。

使用下标语法实现通用的图片访问器

这里,其实是利用了下标的多维特性来实现。一个类型可以定义多个下标,通过不同索引类型进行对应的重载。

多维特性示例:

subscript(row: Int, column: Int) -> Double {
    get {
        assert(indexIsValid(row: row, column: column), "Index out of range")
        return grid[(row * columns) + column]
    }
    set {
        assert(indexIsValid(row: row, column: column), "Index out of range")
        grid[(row * columns) + column] = newValue
    }
}

最终实现大概如下:

public struct MyImage {
    /// 通过图片名,获取一个 UIImage
    public static subscript(named: String) -> UIImage? {
        UIImage(named: named)
    }
  
    /// 通过 module 和图片名,获取一个 UIImage
    public static subscript(module: Any.Type?, named: String) -> UIImage? {
        var buldel: Bundle?
        if let module = module {
            buldel = Bundle.named(String(describing: module.self))
        }
        return UIImage(named: named, in: buldel, compatibleWith: nil)
    }
}

用法:

// 从 ModuleName.bundle 中获取图片
MyImage[ModuleName.self, "image name"]

// 或者,从 main bundle 中获取图片
MyImage["image name"]

结语

以上就是一个通用的图片访问器的简单实现,这只是一个基础版本,可以支持 pngjpg 格式的图片访问了,基本满足我们的需求。

由于项目需要,我的项目中,对其进行了扩展,使其可以访问诸如 webpgif 等其他格式文件。

对于共用的图片资源,我们可以封装在 MyImage 模块下,通过脚本生成类似于下面这样的文件:

extension MyImage {

    public static iconX = MyImage[MyImage.self, "icon name"]
  
    ...
}

访问这些共用的资源,只需要这样使用即可:

MyImage.iconX

另外,想要提的一点是,并不是所有的多个模块使用的图片,都适合放到这个基础组件中,这个需要各位自己权衡。比如导航上面的按钮,一些返回按钮或者箭头之类的就很适合放在这里面。


如果这篇文章对你有帮助,不妨随手点个赞!谢谢❤️

⚠️原创,禁止未授权转载,只接收链接转载,不接受内容拷贝转载!

你可能感兴趣的:(iOS模块化中的通用图片访问器)