当前位置: > > > Swift - XIB结合UIView制作自定义组件(xib加载绘制UIView)

Swift - XIB结合UIView制作自定义组件(xib加载绘制UIView)

本文相关:
Swift - 继承UIView实现自定义可视化组件(附记分牌样例)
Swift - 在StoryBoard中添加使用自定义组件(自定义进度条组件为例)
我原来写过几篇文章,介绍如何通过继承 UIView 来实现自定义组件(见上方列表)。当时自定义组件是使用纯代码实现的。但是如果组件里的元素比较多,布局比较复杂。那用纯代码写就比较麻烦了。对于这种复杂的自定义组件,我们可以结合 XIB 文件来实现。

一、自定义组件的创建
这里还是同前文一样,实现一个简单的进度条组件。用户可以自由设置进度条的进度、尺寸、文字颜色、进度条颜色、背景颜色。不同的是,我们这里创建的时候引入 xib 文件来实现布局。

(1)新建一个自定义组件实现类 MyProgressBar( 继承 UIView)。由于是继承 UIView,“Also create XIB file”无法勾选。这个没关系,后面我们会手动创建一个 xib 文件并关联。

(2)再新建一个同名的 XIB 文件(带有 View

(3)打开刚创建的 xib 文件,将 File's Owner Custom Class 设置为我们自定义组件类的名字。
注意:修改的是 MyProgressBar.xibFile’s Owner Custom Class,不要修改成 MyProgressBar.xib View Custom Class 了。

(4)将 xib 文件中 View Size 修改为 FreedomStatus bar 修改为 None。并将其调整成合适的尺寸。

(5)在 View 中添加一个 UIView 和一个 UILabel,并设置好约束和相关属性。分别作为当前进度区块,以及当前进度标签。

(6)将上面添加的两个组件拖拽关联到 MyProgressBar.swift。并补充相关的组件实现代码,以及 xib 视图加载的相关代码。
MyProgressBar.swift 完整代码如下:
import UIKit

@IBDesignable class MyProgressBar: UIView {
    
    //显示进度的文本标签
    @IBOutlet weak var textLabel: UILabel!

    //显示当前进度区域
    @IBOutlet weak var bar: UIView!
    
    //进度
    @IBInspectable var percent: Int = 0 {
        didSet {
            if percent > 100 {
                percent = 100
            }else if percent < 0 {
                percent = 0
            }
            textLabel.text =  "\(percent)%"
            setNeedsLayout()
        }
    }
    
    //文本颜色
    @IBInspectable var color: UIColor = .white {
        didSet {
            textLabel.textColor = color
        }
    }
    
    //进度条颜色
    @IBInspectable var barColor: UIColor = UIColor.orange {
        didSet {
            bar.backgroundColor = barColor
        }
    }
    
    //进度条背景颜色
    @IBInspectable var barBgColor: UIColor = UIColor.lightGray {
        didSet {
            layer.backgroundColor = barBgColor.cgColor
        }
    }
    
    //初始化默认属性配置
    func initialSetup(){
        textLabel.text =  "\(self.percent)%"
        textLabel.textColor = self.color
        bar.backgroundColor = self.barColor
        layer.backgroundColor = self.barBgColor.cgColor
    }
    
    //布局相关设置
    override func layoutSubviews() {
        super.layoutSubviews()

        var barFrame = bounds
        barFrame.size.width *= (CGFloat(self.percent) / 100)
        bar.frame = barFrame
    }
    
    /*** 下面的几个方法都是为了让这个自定义类能将xib里的view加载进来。这个是通用的,我们不需修改。 ****/
    var contentView:UIView!
    
    //初始化时将xib中的view添加进来
    override init(frame: CGRect) {
        super.init(frame: frame)
        contentView = loadViewFromNib()
        addSubview(contentView)
        addConstraints()
        //初始化属性配置
        initialSetup()
    }
    
     //初始化时将xib中的view添加进来
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        contentView = loadViewFromNib()
        addSubview(contentView)
        addConstraints()
        //初始化属性配置
        initialSetup()
    }
    //加载xib
    func loadViewFromNib() -> UIView {
        let className = type(of: self)
        let bundle = Bundle(for: className)
        let name = NSStringFromClass(className).components(separatedBy: ".").last
        let nib = UINib(nibName: name!, bundle: bundle)
        let view = nib.instantiate(withOwner: self, options: nil).first as! UIView
        return view
    }
    //设置好xib视图约束
    func addConstraints() {
        contentView.translatesAutoresizingMaskIntoConstraints = false
        var constraint = NSLayoutConstraint(item: contentView, attribute: .leading,
                                    relatedBy: .equal, toItem: self, attribute: .leading,
                                    multiplier: 1, constant: 0)
        addConstraint(constraint)
        constraint = NSLayoutConstraint(item: contentView, attribute: .trailing,
                                        relatedBy: .equal, toItem: self, attribute: .trailing,
                                        multiplier: 1, constant: 0)
        addConstraint(constraint)
        constraint = NSLayoutConstraint(item: contentView, attribute: .top, relatedBy: .equal,
                                        toItem: self, attribute: .top, multiplier: 1, constant: 0)
        addConstraint(constraint)
        constraint = NSLayoutConstraint(item: contentView, attribute: .bottom,
                                    relatedBy: .equal, toItem: self, attribute: .bottom,
                                    multiplier: 1, constant: 0)
        addConstraint(constraint)
    }
}

二、自定义组件的使用
通过 xib 创建的自定义组件与之前使用纯代码实现的自定义组件相比,在使用的时候是没什么区别的。
1,在代码中使用自定义组件
import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let myProgressBar = MyProgressBar(frame: CGRect(x:50, y:50, width:200, height:20))
        myProgressBar.percent = 50
        self.view.addSubview(myProgressBar)
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

2,在在StoryBoard中使用自定义组件
(1)打开Main.storyboard,从组件库里添加一个视图(View

(2)在Identity Inspector里把视图类改成MyProgressBar

(3)可以看到自定义组件已经渲染显示出来了

(4)在Attributes inspector面板中可以调整组件的各个自定义属性

源码下载hangge_1394.zip
评论2
  • 2楼
    2017-11-17 15:57
    xiaomeihua

    自定义控件的点击(或其他)事件如何监听呢

    站长回复

    可以在自定义组件里添加相关手势,比如:点击(UITapGestureRecognizer)、滑动(UISwipeGestureRecognizer)。然后通过代理在外面进行响应。

  • 1楼
    2017-10-13 13:12
    WhatsXie

    说实话这个网站对我帮助很大,很高兴看到一个引航灯式的教学平台

    站长回复

    我也很高兴能帮助到你。欢迎常来看看,我会一直创作更新下去的。