• 为UIView自定义Xib


     

    一、需求

     通过Interface Builder的形式创建Xib,并将其和一个UIView的子类绑定,如何实现?

    二、解决

     这个问题通过搜索,有大量的答案,大概答案的代码如下:

      

     也就是在你的子类中,在初始化方法initWithFrame、initWithCoder中主动加载一个xib对应的类,作为子view添加到当前的view中

     这种方式,会明显产生一个问题,会产生两个相同的自定义View对象

     

     

      先不说两个View带来的危害,可能导致业务代码的冲突,最关键的是,xib中拖出的reference指向只能在loadnib返回的对象中初始化好,你自定义的对象的指向为空。

      这样太坑爹了

      这也是下面这个问题产生的原因,想通过修改initwithCoder的返回值为自己主动反序列化xib产生的对象

      

      这种方案我也测试了,测试代码如下:

      

    #import "XibHackInit.h"
    
    @implementation XibHackInit
    
    - (instancetype)initWithCoder:(NSCoder *)aDecoder
    {
        static BOOL alreadyInitMark = NO;
        if(self = [super initWithCoder:aDecoder])
        {
            if(!alreadyInitMark)
            {
                NSString *className = NSStringFromClass([self class]);
                if([className containsString:@"."])
                {
                    className = [[className componentsSeparatedByString:@"."] lastObject];
                }
                alreadyInitMark = YES;
                UIView *targetView = [[self class] loadFromNib:className];
                alreadyInitMark = NO;
                return (XibHackInit *)(targetView?targetView:self);
            }
        }
        return self;
    }
    
    + (UIView *)loadFromNib:(NSString *)nibName
    {
        NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:nibName
                                                          owner:nil
                                                        options:nil];
        UIView* nibView = (UIView*)[nibViews objectAtIndex:0];
        return nibView;
    }
    
    @end
    

      注意initWithCoder中用了一个BOOL变量,这个变量主要是为了避免产生的递归,loadFromNib方法一调用、又会调用到initWithCoder中,简直就成了一个不可解的死bug

      上面的代码是OC实现的,为什么不用swift实现,主要是因为swift中init方法除了返回空值之外,不能返回其他值

      这么做的结果是什么呢?程序直接Crash

      

      有没有更优美的方案?

     这里的折中方案是,先创建一个容器View,在容器View中加载你想要的子View,这样可以充分考虑到Xib中复用的问题

     效果:

      

    除了清晰的View层次之外,还可以在Interface Builder中进行可视化,实现的关键代码如下:

    核心代码:

    import UIKit
    
    @IBDesignable
    class XibElementContainer: UIView {
    
        var elementView:UIView?
        @IBInspectable var elementNibName:String?
    
        override func awakeFromNib() {
            super.awakeFromNib()
            xibSetup()
        }
        
        func xibSetup() {
            guard let view = loadViewFromNib() else { return }
            view.frame = bounds
            view.autoresizingMask =
                [.flexibleWidth, .flexibleHeight]
            addSubview(view)
            elementView = view
        }
        
        func loadViewFromNib() -> UIView? {
    
            guard let nibName = elementNibName else { return nil }
            let bundle = Bundle(for: type(of: self))
            let nib = UINib(nibName: nibName, bundle: bundle)
            return nib.instantiate(
                withOwner: self,
                options: nil).first as? UIView
        }
        
        override func prepareForInterfaceBuilder() {
            super.prepareForInterfaceBuilder()
            xibSetup()
            elementView?.prepareForInterfaceBuilder()
        }
    }
    

    有两个关键字:

    @IBDesignable  InterfaceBuilder 会尝试对这个类的对象进行可视化,会调用prepareForInterfaceBuilder方法

    @IBInspectable InterfaceBuilder 会显示这个字段的输入框,可以在界面上面直接输入值

    三、最终代码

      代码:https://github.com/liqiushui/CustomXibForUIView

  • 相关阅读:
    JavaScript的正则表达式的基础
    运用JS判断代码可以参考学习
    调用百度地图代码
    运用了css,js
    中国地图(Highmaps)
    Centos
    代理模式【 动态代理与静态代理】
    java集合 collection-list-LinkedList 模拟一个堆栈或者队列数据结构。
    java集合 collection-list-LinkedList
    java集合 collection-list-vector
  • 原文地址:https://www.cnblogs.com/doudouyoutang/p/9780668.html
Copyright © 2020-2023  润新知