iOS Swift xib 动态桥接工具

4 篇文章 0 订阅
前言:开发一直用@sunnyxx 的 OC 版的动态桥接库XXNibBridge,然而最近在学习 Swift , 很多非常好用的开源库都没有 Swift 版,例如 XXNibBridge ,虽说可以用 OC 混编,但那不是我的风格。研究了一下 XXNibBridge 的代码,以及 @sunnyxx 提供的思路,自己尝试着给转成 Swift 版的。

遇到的几个问题

1、替换系统的方法

xib桥接所用到的最重要的一个方法,

func awakeAfterUsingCoder(aDecoder: NSCoder) -> AnyObject?

这个方法在加载 xib 的时候调用,给了我们偷天换日的机会

OC 由于 load 方法是在编译的时候就已经调用的,所以通过runtime 在load方法里执行替换方法,但是在Swift行不通,为此我在网上找了一个资料。发现用 extension 来处理比较好 。

对 UIView 进行拓展 ,重写 initialize() 方法进行系统方法替换。

extension UIView{
    public override class func initialize(){
        struct Static {
            static var token: dispatch_once_t = 0
        }
        dispatch_once(&Static.token) {
            //做方法替换
            //篇幅问题就不用在这里贴那么多代码了
            //所有的问题在我给的库里都能找到
        }
}
2、加载xib时的递归问题
从 storyboard 中创建 view 的时候会调用 (问题1)中所说道的方法,而且从 xib 创建真正的 view 时也会调用这个方法,所以会造成递归。

@sunnyxx 给出的解决问题的思路是采用设置标志位的方式来避免递归调用

- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder {
    self = [super awakeAfterUsingCoder:aDecoder];
    if (这个类的Loading标志位 -> NO)
    {
          Loading标志位 -> YES
          从xib加载真实的View (这里会递归调用这个函数)
          return 真实View
    }
    Loading标志位 -> NO
    return self
}

考虑了一下我也以这种思路来做。

试验了很多种方法,最后确定以动态的向类中添加方法来作为类的标志位,来区分是从 storyboard 中加载还是从 xib 加载

1、首先从类中获取获取对应的方法

  let isLoading = class_getInstanceMethod(self.classForCoder,NSSelectorFromString("LC_NibBridgeIsLoading"))

2、判断 如果为空 isLoading == nil 就说明说明从 storyboard 中来,这时切换到从 xib 加载 ,然后添加上该方法。

class_addMethod(self.classForCoder, NSSelectorFromString("LC_NibBridgeIsLoading"), nil, nil)

3、这样等从 xib 加载调用这个方法的时候, isLoading 不为 nil 那么就返回 self 加载自身。

天真如我以为这样就成功了,后来发现自己还是 too young.

这样做只能用一次,如果 storyboard 有两个地方都用到了同一个 xib 那么就只有第一次加载的时候有用,由于第一次加载后添加了 LC_NibBridgeIsLoading 方法,那后面再加载的话这份代码就不起作用了。

对于这个问题我采用了笨方法。

给类添加一对标志,一个标明正在加载,另一个标明已经加载,每次加载都添加一对标志,然后通过 for 循环来判断当前这次加载的状态,然后做相应的处理。
func hackedAwakeAfterUsingCoder(decoder:NSCoder) -> AnyObject? {

    if self.respondsToSelector(NSSelectorFromString("LC_NibBridgeUsefull")){//首先确定类遵守了LCNibBridge 协议

        //通过往类里边添加方法来实现对类的标记
        for index in 0...Int.max-1 {
            //通过一个for循环来解决多次调用
            let isLoading = class_getInstanceMethod(self.classForCoder,NSSelectorFromString("LC_NibBridgeIsLoading\(index)"))
            let isLoadover = class_getInstanceMethod(self.classForCoder,NSSelectorFromString("LC_NibBridgeIsLoadover\(index)"))

            if isLoadover == nil && isLoading == nil {
                //标记该类正在桥接
                class_addMethod(self.classForCoder, NSSelectorFromString("LC_NibBridgeIsLoading\(index)"), nil, nil)

                return self.instantiateRealViewFromPlaceholder(self)
            }else if isLoading != nil && isLoadover == nil {
                //标记该类已经桥接完毕
                class_addMethod(self.classForCoder, NSSelectorFromString("LC_NibBridgeIsLoadover\(index)"), nil, nil)

                return self;
            }
        }
    }
    return self
}
方法虽然笨但是有效

开源库 LCNibBridge

demo以及源码已经放到 我的github上面的LCNibBridge项目

下面简单说一下 LCNibBridge 的用法

首先让大家看一下效果图
这里写图片描述

LCNibBridge 用法

1、创建View类以及同名的xib
这里写图片描述

2、遵守 LCNibBridge 协议,并实现协议方法。

class LCMokeView: UIView,LCNibBridge{
    func LC_NibBridgeUsefull() -> Bool {
         return true
    }
}

这里要说明一下注意事项,本来源码是想要检查该类是否遵从协议,但是用到 NSProtocolFromString() 这个方法老是出错,搞了好久,最后取了折中的办法,检测该类是否实现了协议中的方法。所以造成了一定缺陷,这个问题以后会再进行研究。

注意:如果该类不想用xib动态桥接了,请把遵守的协议以及已经实现的协议方法一并清除,施主切记!!!

3、 storyboard 中设置成同名的Class
这里写图片描述

至此所有工作全部做完,是不是很简单,是不是想尽快感受一下

PS: LCNibBridge 正在加入 cocoa pods 豪华午餐~
最后重述一下LCNibBridge github 地址 :

https://github.com/liutongchao/LCNibBridge

以及对这篇文章贡献最大的一篇博客 @sunnyxx

http://blog.sunnyxx.com/2014/07/01/ios_ib_bridge/

以我的水平 LCNibBridge 还有很多问题需要优化, 如果你有好的建议欢迎来与我互相探讨 – LC.West
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值