要求
- Xcode 8.3+
- Swift 3.2
- iOS 9.0+
分析
- .framework可以是静态库也可以是动态库,主要封装了代码文件。
- .bundle文件中主要存放framework用到的资源文件,如.storyboard、.xib、.png和音视频等。
实例
以封装一个IconButton自定义控件为例
-
新建一个空白工程,取名为
DemoBundleFramework
-
新建一个Framework的新Target,命名为
IconButton
-
新建一个Bundle的新Target,命名为
IconButtonBundle
注意:这个Bundle中主要存放Framework中的xib等资源文件。
-
创建完成的工程目录结构如下:
-
将Framework的最低系统版本改为9.0,因为稍后会用到xib布局里的StackView
创建IconButton.swift和IconButton.xib
-
IconButton.xib实现
IconButton.swift实现
import UIKit
public final class IconButtonItem: UIView{
public enum Position: UInt {
case left = 0, right = 1, top = 2, bottom = 3
}
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var iconImageView: UIImageView!
public override func awakeFromNib() {
super.awakeFromNib()
backgroundColor = .clear
}
static func item(withFrame frame: CGRect = .zero,
icon: UIImage?,
title: String?,
iconPosition position: Position) -> IconButtonItem{
guard
let path = Bundle.main.path(forResource: "Frameworks/IconButton.framework/IconButtonBundle", ofType: "bundle"),
let bundle = Bundle(path: path),
let xibs = bundle.loadNibNamed("IconButton", owner: nil, options: nil),
let button = xibs[Int(position.rawValue)] as? IconButtonItem else{
return IconButtonItem()
}
button.frame = frame
button.titleLabel.text = title
button.iconImageView.image = icon
return button
}
}
@IBDesignable
public final class IconButton: UIView {
// MARK: - IBOutlets
@IBInspectable
public var iconPosition: UInt = 0{
didSet{
iconPosition = iconPosition > 3 ? 3 : iconPosition
makeButtonItem()
}
}
@IBInspectable
public var iconImage: UIImage? = nil{
didSet{
item.iconImageView.image = iconImage
}
}
@IBInspectable
public var title: String? = nil{
didSet{
item.titleLabel.text = title
}
}
private var item: IconButtonItem!
// MARK: - Properties
// MARK: - Initial Method
private func setupUI() {
item.titleLabel.text = title
item.iconImageView.image = iconImage
}
override init(frame: CGRect) {
super.init(frame: frame)
makeButtonItem()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
makeButtonItem()
}
// MARK: - Lifecycle Method
override public func awakeFromNib() {
super.awakeFromNib()
setupUI()
}
// MARK: - Action & IBOutletAction
// MARK: - Override Method
// MARK: - Private method
private func makeButtonItem(){
item?.removeFromSuperview()
item = IconButtonItem.item(withFrame: bounds, icon: iconImage, title: title, iconPosition: IconButtonItem.Position(rawValue: iconPosition)!)
item.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(item)
}
// MARK: - Public Method
}
当然,本文重点要演示的是如何在.framework中打包.bundle文件,所以以上IconButton实现代码不做解释,相信读者很容易理解。
- 下面演示如何将.bundle打包到.framework中,并在代码中正确调用
-
将Products下生成的.bundle拖入工程并连接到IconButton的target中
- 下面重点说说.bundle的使用
大家都知道,.bundle中放了我们要用到.xib和.png等资源文件,但如何正确加载它们呢?
// 先拿到.bundle文件在被使用工程中的实际位置,
// 一般是在Bundle.main(使用者工程路径)下的Frameworks文件夹下的xxx.framwork下
let path = Bundle.main.path(forResource: "Frameworks/IconButton.framework/IconButtonBundle", ofType: "bundle")
// 创建资源bundle实例对象
let bundle = Bundle(path: path)
// 加载nib文件(一个xib文件中可能有多个nib文件)
let xibs = bundle.loadNibNamed("IconButton", owner: nil, options: nil)
// 根据传入的position值,加载对应的nib控件
let button = xibs[Int(position.rawValue)] as? IconButtonItem
- 上面是如何加载并使用.xib文件,下面演示如何使用.png文件
UIImage(named: "xxx", in: bundle, compatibleWith: nil)
-
上述工作准备完成后,选择IconButton,编译生成IconButton.framework
在Demo工程中使用framework
- 代码中验证
class ViewController: UIViewController {
@IBOutlet weak var iconButton: IconButton!
@IBAction func positionChanged(_ sender: UISegmentedControl) {
iconButton.iconPosition = UInt(sender.selectedSegmentIndex)
}
}
说明:当然,你可以手动替换.framework中的.bundle文件中的图片等资源,这里不做演示了。
-
最终你的.framework和.bundle文件所在的目录(被打包到app中)
Carthage
- 如果你的framework要支持carthage自动编译,就得在工程中做如下设置
- 编译
cd 工程目录
carthage build --no-skip-current
关于更多Carthage,可以参考我这篇文章https://www.jianshu.com/p/76b9ff09f99c
Github
https://github.com/BackWorld/DemoBundleFramework
以上就是简单演示了如何生成并使用一个含有bundle文件的framework,如果对你有帮助,欢迎加关注和点赞~