-
在iOS中,所有的控件都有大小(size)、位置(frame、bounds、center)、颜色等...这些共有属性全部抽象在了UIView里面,所以几乎所有的控件(UIImageView、UILabel)都是继承自UIView。
-
每个UIView都是一个容器,能够容纳其他容器,所以UIView有superView和subViews属性,也有addSubViews和removeFromSuperView等方法。
属性
基本属性
- frame: frame是指控件矩形框在父控件中的位置和尺寸,其中位置是以父控件左上角为坐标原点。
- bounds: bounds是值控件矩形框的位置和尺寸,其中位置是以自己的左上角为坐标原点。(一般控件的bounds的x,y分别为0,0)
- center: center是指控件矩形框在父控件中心点的位置,以父控件左上角为原点。
- superView:superView是本控件的直接父控件,对于一个控制器(UIViewController)来说,会有一个最顶层的UIView来管理其子空间
- subViews:subViews指的是本控件下的直接子控件,本控件负责管理子控件。
基本方法
- addSubView: 添加子控件
- removeFromSuperView: 从父控件中移除
- layoutSubViews: 布局子控件
构造方法
- 由于UIView 既可以从xib加载也可以通过代码创建,所以有多种构造方法。
- init()无参构造方法 var aView = UIView()
- init(frame: CGrect) var bView = UIView(frame: CGrect)
- init?(coder aDecoder: NSCoder) var cView = UIView(coder: NSCoder)
区别
1.对于通过代码创建的自定义控件来说,一般有两种情况:
- 第一种是先实例化控件,再设置控件位置
var aView = UIView()
aView.frame = CGRectMake(0,0,10,10)
- 第二种就是在实例化控件的同时就指定控件的位置
var bView = UIView(frame: CGRectMake(0,0,10,10))
- 这两个方法区别在于如果是直接调用的无参构造方法,系统会再次调用init(frame:CGRect)方法,而如果我们调用的是UIView(frame: CGRect)方法,系统不会再去调用无参构造方法。
2.如果是通过xib去创建自定义控件来说,则会去调用init?(coder aDecoder:NSCoder)方法
- 注意:如果是通过xib或者storyBoard自定义控件,并且重写 了init?(coder aDecoder: NSCoder)方法,在该方法里面对控件里面做一些初始化操作,如颜色、大小等,很可能会出问题。因为在调用该方法的时候,有些控件很可能还未创建出来,所以要进行初始化操作最好在另一个方法awakeFromNib中进行,用来辅助该方法进行一些初始化操作。
3.自定义控件最好的做法是
override init(frame: CGRect){
super.init(frame: frame);
prepareForView()
}
required init?(coder aDecoder: NSCoder){
super.init(coder: aDecoder);
prepareForView()
}
func prepareForView(){
//初始化操作
}
- 这样的话,无论是通过xib\storyBoard里面加载还是从代码加载,都能顺利地初始化自定义控件。
layoutSubViews调用
- 我们通常会在layoutSubViews 这个方法中设置子控件的frame,让其跟随父控件的frame变化而变化,那么layoutSubViews方法在什么时候调用呢?
- 来看看下面的场景,我有一个红色的父控件,让其紧靠控制器View的左上方,长宽都为100,首先来构造这个控件,假设为RedView,不重写其构造方法
import UIKit
class RedView: UIView {
override func layoutSubviews() {
super.layoutSubviews()
pritn("call layoutSubVies method")
}
}
- 其实例化过程有一下两种方式:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let redView = RedView() //方式1
let redView = RedView(frame: CGRectMake(0, 0, 200, 200))//方式2
view.addSubview(redView)
}
}
- 可以通过运行发现,对于方式1,"call layoutSubVies method"这句话不会被打印出来,而在方式2中则会打印出这句话
- 通过对比不难发现,方式2设置了redView的frame,而我们只需要将方式1中控件的frame设置,自然会调用其内部的layoutSubView方法。
注意:
- 实际只要父控件的frame有变化,layoutSubView就会被调用
- 如果是这样实例化的
let redView = RedView(frame: CGRectZero)
或者let redView = RedView(frame: CGRectMake(0,0,0,0,))
,其layoutSubView方法不会被调用,因为他们的frame相当于没有设置或者说没有发生改变。 - 在实例化的时候,如果将控件的frame连续改变,只会调用一次layoutSubView,例如:
let redView = RedView(frame: CGRectMake(0,0,100,100))
redView.frame = CGRectMake(10,10,50,50)
- 这样操作的化,只会调一次layoutSubView,以最后一次的为准,这个原因好像是在一次循环机制(runloop)引起的。
网友评论