玩轉(zhuǎn)swift -- UIKit 之 UIView(1)

概述

UIView類通過(guò)定義一個(gè)在屏幕和界面上的矩形區(qū)域來(lái)管理這塊區(qū)域的內(nèi)容。在運(yùn)行時(shí),視圖對(duì)象處理其區(qū)域內(nèi)的任何內(nèi)容渲染,還處理與該內(nèi)容的任何相互作用。

進(jìn)入正題

一、初始化視圖對(duì)象

// 純代碼初始化執(zhí)行
public init(frame: CGRect)

// 使用 Interface Builder 構(gòu)造界面執(zhí)行這個(gè)
public init?(coder aDecoder: NSCoder)

現(xiàn)在開始測(cè)試:

先構(gòu)建一個(gè)自定義View: TestInitView, 并添加兩個(gè)初始化函數(shù),如下:

// 純代碼走這個(gè)
override init(frame: CGRect) {
        
    super.init(frame: frame);
        
    print("執(zhí)行了 init(frame: CGRect)")
}
    
// 使用 Interface Builder 構(gòu)造界面走這個(gè)
required init?(coder aDecoder: NSCoder) {
        
    super.init(coder: aDecoder);
    print("執(zhí)行了 init?(coder aDecoder: NSCoder)")
}

1、純代碼構(gòu)造界面

測(cè)試代碼如下:

// test view 純代碼初始化
func testViewInit() {
        
    let testView = TestInitView.init(frame: CGRect.init(x: 100, y: 100, width: 100, height: 100));
    testView.backgroundColor = #colorLiteral(red: 0.1215686277, green: 0.01176470611, blue: 0.4235294163, alpha: 1);
        
    self.view.addSubview(testView);
}

測(cè)試結(jié)果:

執(zhí)行了 init(frame: CGRect)

注意:此方法是由自己調(diào)用,來(lái)初始化對(duì)象的。

2、使用 Interface Builder 構(gòu)造界面

① 使用storyBoard來(lái)構(gòu)建界面,并拖入一個(gè)View,并將此View的Class設(shè)置為 TestInitView,如下:

② 運(yùn)行程序,結(jié)果如下:

執(zhí)行了 init?(coder aDecoder: NSCoder)

注意:由系統(tǒng)來(lái)調(diào)用,自己不能調(diào)用。

二、配置視圖的視覺外觀

// 視圖的背景顏色
@NSCopying open var backgroundColor: UIColor?
// 視圖的alpha值
open var alpha: CGFloat
// 確定視圖是否不透明的布爾值(它卻決定不了當(dāng)前UIView是不是不透明),用處在于給繪圖系統(tǒng)提供一個(gè)性能優(yōu)化開關(guān)。
open var isOpaque: Bool
// 確定視圖是否被隱藏的布爾值
open var isHidden: Bool
// 這個(gè)屬性定義了一個(gè)非默認(rèn)的著色顏色值,其值的設(shè)置會(huì)影響到以視圖為根視圖的整個(gè)視圖層次結(jié)構(gòu)。
@available(iOS 7.0, *)
open var tintColor: UIColor!
// 本視圖及父視圖的tintColor或tintAdjustmentMode屬性改變時(shí)自動(dòng)調(diào)用
@available(iOS 7.0, *)
open func tintColorDidChange()
// 一個(gè)枚舉值,定義了tint color的調(diào)整模式。
@available(iOS 7.0, *)
open var tintAdjustmentMode: UIViewTintAdjustmentMode
// 決定子視圖是否被限定在當(dāng)前視圖的bounds中
open var clipsToBounds: Bool
// 決定在視圖重畫之前是否先清理視圖以前的內(nèi)容
open var clearsContextBeforeDrawing: Bool
// 一個(gè)通過(guò)alpha通道來(lái)掩蓋一個(gè)view的內(nèi)容的可選view。
@available(iOS 8.0, *)
open var mask: UIView?
// 為此類的實(shí)例創(chuàng)建圖層的類
open class var layerClass: Swift.AnyClass { get }
// UIView的視圖層
open var layer: CALayer { get } 

1、alpha && isOpaque && isHidden

測(cè)試代碼如下:

func testalpha_isOpaque_isHidden() {
        
    // test alpha
    let leftAlphaView = self.getNewTestView(index_i: 0, index_j: 0);
    leftAlphaView.alpha = 0.0;
        
    let centerAlphaView = self.getNewTestView(index_i: 1, index_j: 0);
    centerAlphaView.alpha = 0.5;
        
    let rightAlphaView = self.getNewTestView(index_i: 2, index_j: 0);
    rightAlphaView.alpha = 1.0;
        
    // test isHidden
        
    let leftIsHiddenView = self.getNewTestView(index_i: 0, index_j: 1);
    leftIsHiddenView.isHidden = true;
        
    let rightIsHiddenView = self.getNewTestView(index_i: 1, index_j: 1);
    rightIsHiddenView.isHidden = false;
        
    // test isOpaque
        
    let leftDownOpaqueView = self.getNewTestView(index_i: 0, index_j: 2);
    let leftUpOpaqueView = self.getNewTestView(index_i: 0, index_j: 3);
    leftUpOpaqueView.alpha = 1.0;
    leftUpOpaqueView.isOpaque = true;
    leftUpOpaqueView.backgroundColor = #colorLiteral(red: 0.2196078449, green: 0.007843137719, blue: 0.8549019694, alpha: 1);
    leftUpOpaqueView.center = CGPoint.init(x: leftDownOpaqueView.center.x, y: leftDownOpaqueView.center.y + 25);
        
    let rightDownOpaqueView = self.getNewTestView(index_i: 1, index_j: 2);
    let rightUpOpaqueView = self.getNewTestView(index_i: 1, index_j: 3);
    rightUpOpaqueView.alpha = 0.5;
    rightUpOpaqueView.isOpaque = false;
    rightUpOpaqueView.backgroundColor = #colorLiteral(red: 0.2196078449, green: 0.007843137719, blue: 0.8549019694, alpha: 1)
    rightUpOpaqueView.center = CGPoint.init(x: rightDownOpaqueView.center.x, y: rightDownOpaqueView.center.y + 25);
    }
    
func getNewTestView(index_i: NSInteger, index_j: NSInteger) -> UIView {
        
    let testView = UIView.init(frame: CGRect.init(x: 30 + index_i * 80, y: 80 + index_j * 80, width: 50, height: 50));
    testView.backgroundColor = #colorLiteral(red: 0.7450980544, green: 0.1568627506, blue: 0.07450980693, alpha: 1);
        
    self.view.addSubview(testView);
        
    return testView;
}

測(cè)試結(jié)果如下:

注意:當(dāng)把UIView的alpha屬性設(shè)成0,或者把isHidden設(shè)成true的時(shí)候,當(dāng)前UIView和它所包含的子UIView都會(huì)變成不可見,同時(shí)也不會(huì)再響應(yīng)event事件。isOpaque,而不是決定View的是否是不透明。即View不透明時(shí),isOpaque需要設(shè)置為true,來(lái)優(yōu)化性能,有透明度時(shí),isOpaque需要設(shè)置為false,防止不可預(yù)測(cè)事情發(fā)生(我也不知道啥事情,測(cè)試顯示沒(méi)啥區(qū)別!)。

2、tintColor && tintAdjustmentMode && tintColorDidChange

① 首先測(cè)試一下,tintColor的設(shè)置:

自定義 TIntColorTestView

// test tintColor
func testTintColor() {
        
    self.label.frame = CGRect.init(x: 30, y: 80, width: 250, height: 50);
    self.label.text = "這是王隆帥的label";
    self.label.textColor = self.tintColor;
    self.addSubview(self.label);
        
    let button = UIButton.init(type: .system);
    button.frame = CGRect.init(x: 30, y: 140, width: 250, height: 50);
    button.addTarget(self, action: #selector(btnClick), for: .touchUpInside)
    button.setTitle("這是王隆帥的btn", for: .normal);

    self.addSubview(button);
        
    var image = UIImage.init(named: "imageToColor");
    image = image?.withRenderingMode(.alwaysTemplate);
    let imageView = UIImageView.init(frame: CGRect.init(x: 30, y: 200, width: 250, height: 50));
    imageView.image = image;
    self.addSubview(imageView);
        
}
    
func btnClick() {
        
    self.tintColor = UIColor.init(red: CGFloat(Double(arc4random() % 255) / 255.0), green: CGFloat(Double(arc4random() % 255) / 255.0), blue: CGFloat(Double(arc4random() % 255) / 255.0), alpha: 1.0);
}

override func tintColorDidChange() {
        
    self.label.textColor = self.tintColor;
}

初始化這個(gè)View:

func testTintColor() {
        
    let tintColorView = TIntColorTestView.init(frame: self.view.bounds);
    self.view.addSubview(tintColorView);
}

運(yùn)行程序,并不斷點(diǎn)擊btn,結(jié)果如下:

由上可知,tintColor會(huì)影響到以視圖為根視圖的整個(gè)視圖層次結(jié)構(gòu)。主要是改變系統(tǒng)的某些控件,比如 UIButton, UISlider, UIProgressView, UIStepper, UIImageView等等。假如想要更改label的文字顏色,或者某些View的背景顏色等,可以監(jiān)聽 tintColorDidChange,來(lái)做相應(yīng)更改!

② 再來(lái)測(cè)試一下 tintAdjustmentMode

@available(iOS 7.0, *)
public enum UIViewTintAdjustmentMode : Int {

    // 視圖的著色調(diào)整模式與父視圖一致
    case automatic
    // 視圖的tintColor屬性返回UIExtendedSRGBColorSpace 顏色空間的顏色
    case normal
    // 視圖的tintColor屬性返回 UIExtendedGrayColorSpace 顏色空間的顏色
    case dimmed
}

測(cè)試代碼,在上面的基礎(chǔ)上 加上以下代碼:

print("normal ----- \(self.tintAdjustmentMode.rawValue)");
print("normal ----- \(self.tintColor)");

self.tintAdjustmentMode = .dimmed;
        
print("dimmd ----- \(self.tintAdjustmentMode.rawValue)");
        print("dimmd ----- \(self.tintColor)");
        
button.tintAdjustmentMode = .normal;
imageView.tintAdjustmentMode = .automatic;

測(cè)試結(jié)果如下:

normal ----- 1
normal ----- Optional(UIExtendedSRGBColorSpace 0 0.478431 1 1)
dimmd ----- 2
dimmd ----- Optional(UIExtendedGrayColorSpace 0.484669 0.8)

由圖及打印可知,normaldimmd,確實(shí)對(duì)應(yīng)著 UIExtendedSRGBColorSpaceUIExtendedGrayColorSpace 兩個(gè)顏色空間!并且,imageView設(shè)置的 .automatic 是繼承了父View的 .dimmed 屬性。

3、clipsToBounds

測(cè)試代碼如下:

// 測(cè)試 clipsToBounds
func testClipsToBounds() {
        
    // clipsToBounds false
    let clipsView1_down = self.getNewTestView(index_i: 0, index_j: 0);
    clipsView1_down.clipsToBounds = false;

    let clipsView1_up = self.getNewTestView(index_i: 0, index_j: 1);
    clipsView1_up.backgroundColor = #colorLiteral(red: 0.1294117719, green: 0.2156862766, blue: 0.06666667014, alpha: 1);
    clipsView1_up.center = CGPoint.init(x: 25, y: 50);
        
    clipsView1_down.addSubview(clipsView1_up);
        
    // clipsToBounds true
    let clipsView2_down = self.getNewTestView(index_i: 1, index_j: 0);
    clipsView2_down.clipsToBounds = true;
        
    let clipsView2_up = self.getNewTestView(index_i: 1, index_j: 1);
    clipsView2_up.backgroundColor = #colorLiteral(red: 0.1294117719, green: 0.2156862766, blue: 0.06666667014, alpha: 1);
    clipsView2_up.center = CGPoint.init(x: 25, y: 50);
        
    clipsView2_down.addSubview(clipsView2_up);
}

測(cè)試結(jié)果:

4、clearsContextBeforeDrawing

見名知意,此屬性決定繪制前是否清屏,默認(rèn)為true。當(dāng)這個(gè)屬性被設(shè)置為時(shí)true,UIKIt會(huì)在調(diào)用 drawRect: 方法之前,把即將被該方法更新的區(qū)域填充為透明的黑色。將這個(gè)屬性設(shè)置為false可以取消相應(yīng)的填充操作,view中原有內(nèi)容會(huì)保留。

如果將此屬性的值設(shè)置為false,則我們應(yīng)該確保在 draw(:) 方法中正確繪制視圖的內(nèi)容。在此前提下,可以提高性能。

5、mask

測(cè)試代碼如下:

// 測(cè)試 mask
func testMask() {
        
    let view1 = UIView.init(frame: CGRect.init(x: 20, y: 80, width: 80, height: 80));
    view1.backgroundColor = UIColor.blue;
        
    let maskView1 = UIView.init(frame: view1.bounds);
    maskView1.backgroundColor = UIColor.red;
    maskView1.alpha = 0.1;

    view1.mask = maskView1;
    self.view.addSubview(view1);
        
        
    let view2 = UIView.init(frame: CGRect.init(x: 120, y: 80, width: 80, height: 80));
    view2.backgroundColor = UIColor.blue;
        
    let maskView2 = UIView.init(frame: view2.bounds);
    maskView2.backgroundColor = UIColor.green;
    maskView2.alpha = 0.5;
        
    view2.mask = maskView2;
    self.view.addSubview(view2);

        
    let view3 = UIView.init(frame: CGRect.init(x: 220, y: 80, width: 80, height: 80));
    view3.backgroundColor = UIColor.blue;
        
    let maskView3 = UIView.init(frame: view3.bounds);
    maskView3.backgroundColor = UIColor.brown;
    maskView3.alpha = 1.0;
        
    view3.mask = maskView3;
    self.view.addSubview(view3);
}
    

測(cè)試結(jié)果如下:

由上圖可知,mask自帶的顏色不會(huì)顯示,最終效果圖怎么顯示只跟mask每個(gè)point的alpha相關(guān)。(本例是全部都是一樣的alpha,假如想要部分為透明,可以添加含有alpha通道的圖片)

可以這樣理解,是將mask的每個(gè)point的alpha賦值給View的重疊部分相對(duì)應(yīng)的point,這樣view的重疊每個(gè)point都有個(gè)alpha值了,view重疊部分就可能顯示多種透明色。

5、layer && layerClass

測(cè)試代碼:

// 測(cè)試 layer
func testLayer() {
        
    let layer = CALayer.init();
    layer.bounds = CGRect.init(x: 0, y: 0, width: 100, height: 100);
    layer.position = CGPoint.init(x: 100, y: 200);
    layer.contents = UIImage.init(named: "imageToColor")?.cgImage;
    layer.cornerRadius = 10.0;
    layer.masksToBounds = true;
        
    self.view.layer.addSublayer(layer);
}
    

測(cè)試結(jié)果:

總結(jié):UIView和CALayer是相互依賴的關(guān)系。UIView依賴于CALayer提供的內(nèi)容,CALayer依賴UIView提供的容器來(lái)顯示繪制的內(nèi)容。歸根到底CALayer是這一切的基礎(chǔ),如果沒(méi)有CALayer,UIView自身也不會(huì)存在,UIView是一個(gè)特殊的CALayer實(shí)現(xiàn),添加了響應(yīng)事件的能力。

想要更詳細(xì)了解兩者詳細(xì)可以查看這兩篇文章(第一篇第二篇),這里就不再贅述了!

三、配置事件相關(guān)行為

// 設(shè)置視圖的可交互性
open var isUserInteractionEnabled: Bool
// 設(shè)置是否支持多點(diǎn)觸控
open var isMultipleTouchEnabled: Bool
// 設(shè)置控件接受事件時(shí)的排他性
open var isExclusiveTouch: Bool

1、isUserInteractionEnabled

當(dāng)一個(gè)視圖對(duì)象的 isUserInteractionEnabled 被置為 false ,則這個(gè)視圖對(duì)象就被從響應(yīng)者鏈里移除,它所負(fù)責(zé)響應(yīng)的事件全部無(wú)效。它的 subviews 事件也會(huì)被丟棄。當(dāng)重新設(shè)為 true 時(shí),則事件可以正常的傳遞給該視圖對(duì)象。額外的,UIImageViewUILabel 默認(rèn)的 isUserInteractionEnabledfalseUIViewisUserInteractionEnabled 默認(rèn)是 true

2、isMultipleTouchEnabled

測(cè)試代碼如下:

// 測(cè)試 isMultipleTouchEnabled
var touchNums = 0;
func testIsMultipleTouchEnabled() {
        
    self.view.isMultipleTouchEnabled = true;
}
    
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        
    touchNums = touchNums + touches.count;
    print(touchNums);
}
    
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        
    touchNums = touchNums - touches.count;
    print(touchNums);
}
    
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        
    touchNums = touchNums - touches.count;
    print(touchNums);
}
    

分別用十個(gè)指頭點(diǎn)擊屏幕,測(cè)試結(jié)果如下:

2
4
5
2
0

由結(jié)果可知,屏幕最多支持五點(diǎn)觸控。

isMultipleTouchEnabled 設(shè)置為 false,可得以下結(jié)果:

1
0
1
0

即,最多支持一個(gè)觸摸事件。

3、isExclusiveTouch

測(cè)試代碼如下:

// 測(cè)試 isExclusiveTouch
func testIsExclusiveTouch() {
        
    let btn1 = UIButton.init(frame: CGRect.init(x: 100, y: 100, width: 100, height: 100))
    btn1.backgroundColor = UIColor.red;
//        btn1.isExclusiveTouch = true;
    btn1.addTarget(self, action: #selector(touchClickBtn), for: .touchUpInside);
    self.view.addSubview(btn1);
        
    let btn2 = UIButton.init(frame: CGRect.init(x: 220, y: 100, width: 100, height: 100))
    btn2.backgroundColor = UIColor.blue;
//        btn2.isExclusiveTouch = true;
    btn2.addTarget(self, action: #selector(touchClickBtn), for: .touchUpInside);
    self.view.addSubview(btn2);
}
    
func touchClickBtn() {
        
    print("btn 被點(diǎn)擊了!!");
}

運(yùn)行程序,手指先點(diǎn)擊紅色按鈕,摁住不放,另一個(gè)手指點(diǎn)擊藍(lán)色按鈕,然后同時(shí)放開,得出下面結(jié)果:

btn 被點(diǎn)擊了!!
btn 被點(diǎn)擊了!!

由此可知,兩個(gè)按鈕同時(shí)響應(yīng)了點(diǎn)擊事件。

將代碼中注釋的代碼打開,重新運(yùn)行程序,并執(zhí)行上面的操作,得出下面的結(jié)果:

btn 被點(diǎn)擊了!!

即,設(shè)置了View接收事件的排他性為true,則同一時(shí)間在兩個(gè)同級(jí)的View之間,只能有一個(gè)事件觸發(fā)!

四、設(shè)置位置及大小

// 設(shè)置視圖在其父視圖中的位置及大小
open var frame: CGRect
// 設(shè)置視圖在父坐標(biāo)系下的大小
open var bounds: CGRect
// 設(shè)置視圖在父視圖中的位置
open var center: CGPoint
// 通過(guò)此屬性可以修改對(duì)象的平移、縮放比例和旋轉(zhuǎn)角度
open var transform: CGAffineTransform

1、frame && bounds && center

測(cè)試代碼如下:

// 測(cè)試 frame && bounds && center
func testFrame() {
        
    let view = UIView.init(frame: CGRect.init(x: 100, y: 200, width: 50, height: 50));
    self.view.addSubview(view);
        
    print(view.frame);
    print(view.bounds);
    print(view.center);
}
    

測(cè)試結(jié)果如下:

(100.0, 200.0, 50.0, 50.0)
(0.0, 0.0, 50.0, 50.0)
(125.0, 225.0)

由結(jié)果可知,bounds屬性只是提供了視圖對(duì)象的大小,并不包含位置!

2、transform

transform 是一個(gè)非常重要的屬性,它在矩陣變換的層面上改變視圖的顯示效果,完成旋轉(zhuǎn)、形變、平移等等操作。
偷個(gè)懶,這個(gè)屬性牽涉面較多,可以參看這篇文章

五、管理視圖層次結(jié)構(gòu)

// 父視圖
open var superview: UIView? { get }
// 子視圖數(shù)組
open var subviews: [UIView] { get }
// 該視圖所在的窗口視圖
open var window: UIWindow? { get }

// 從父視圖移除
open func removeFromSuperview()
// 在指定索引處插入某個(gè)子視圖
open func insertSubview(_ view: UIView, at index: Int)
// 交換兩個(gè)指定索引的子視圖
open func exchangeSubview(at index1: Int, withSubviewAt index2: Int)

// 添加視圖到子視圖列表的末尾    
open func addSubview(_ view: UIView)
// 在指定的子視圖之上插入某個(gè)子視圖
open func insertSubview(_ view: UIView, belowSubview siblingSubview: UIView)
// 在指定的子視圖之下插入某個(gè)子視圖
open func insertSubview(_ view: UIView, aboveSubview siblingSubview: UIView)

// 移動(dòng)某個(gè)子視圖到所有子視圖的最上方   
open func bringSubview(toFront view: UIView)
// 移動(dòng)某個(gè)子視圖到所有子視圖的最下方 
open func sendSubview(toBack view: UIView)

// 
open func isDescendant(of view: UIView) -> Bool

測(cè)試代碼如下:

// 測(cè)試 視圖的層次結(jié)構(gòu)
func testHierarchicalStructure() {
   
    let view1 = self.createView(tag: 0);
    self.view.addSubview(view1);

    let view2 = self.createView(tag: 1);
    self.view.addSubview(view2);
  
    let view3 = self.createView(tag: 2);
    self.view.addSubview(view3);
        
    print("superView:\n" + String(describing: view1.superview));
    self.printSubviews(name: "subviews");

    let view101 = self.createView(tag: 100);
    UIApplication.shared.keyWindow?.addSubview(view101);
    print("\nwindow2:\n" + String(describing: view101.window))
        
    view1.removeFromSuperview();
    self.printSubviews(name: "removeFromSuperview");
        
    self.view.insertSubview(view1, at: 1);
    self.printSubviews(name: "insertSubviewAtIndex");

    self.view.exchangeSubview(at: 0, withSubviewAt: 1);
    self.printSubviews(name: "exchangeSubview");

    let view4 = self.createView(tag: 3);
    self.view.addSubview(view4);
    self.printSubviews(name: "addSubview");
        
        
    let view5 = self.createView(tag: 4);
    self.view.insertSubview(view5, aboveSubview: view3);
    self.printSubviews(name: "insertSubview:aboveSubview");

        
    let view6 = self.createView(tag: 5);
    self.view.insertSubview(view6, belowSubview: view3);
    self.printSubviews(name: "insertSubview:belowSubview");

    self.view.bringSubview(toFront: view2);
    self.printSubviews(name: "bringSubview:toFront");
        
    self.view.sendSubview(toBack: view2);
    self.printSubviews(name: "sendSubview:Toback");
        
    print("\nview2 && view3  isDescendant: " + String(view2.isDescendant(of: view3)));
    print("view2 && self.view isDescendant: " + String(view2.isDescendant(of: self.view)));
    print("self.view && view2 isDescendant: " + String(self.view.isDescendant(of: view2)));
    print("view2 && view2 isDescendant: " + String(view2.isDescendant(of: view2)));

}
    
func createView(tag: NSInteger) -> UIView {
        
    let view = UIView.init(frame: CGRect.init(x: 100, y: 100, width: 200, height: 200));
    view.tag = tag;
        
    return view;
}
    
func printSubviews(name: String) {
        
    print("\n\(name):\n" + String(describing: self.view.subviews.map { (view: UIView) -> NSInteger in return view.tag; }));
}
    

結(jié)果如下:

superView:
Optional(<UIView: 0x7ffe63104190; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x610000029fa0>>)

subviews:
[0, 1, 2]

window2:
Optional(<UIWindow: 0x7ffe5f707510; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x618000050da0>; layer = <UIWindowLayer: 0x61800002fbc0>>)

removeFromSuperview:
[1, 2]

insertSubviewAtIndex:
[1, 0, 2]

exchangeSubview:
[0, 1, 2]

addSubview:
[0, 1, 2, 3]

insertSubview:aboveSubview:
[0, 1, 2, 4, 3]

insertSubview:belowSubview:
[0, 1, 5, 2, 4, 3]

bringSubview:toFront:
[0, 5, 2, 4, 3, 1]

sendSubview:Toback:
[1, 0, 5, 2, 4, 3]
view2 && view3  isDescendant: false
view2 && self.view isDescendant: true
self.view && view2 isDescendant: false
view2 && view2 isDescendant: true

注意:isDescendant 這個(gè)屬性在對(duì)比兩個(gè) View 的時(shí)候,前者是后者的同一 View 或子 view 才為 true

六、配置自動(dòng)布局行為

// 控制autoresizingMask模式的開啟與關(guān)閉
open var autoresizesSubviews: Bool
// 子視圖相對(duì)于父視圖的調(diào)整模式
open var autoresizingMask: UIViewAutoresizing
// 視圖計(jì)算最合適的size(容納子視圖)并返回
open func sizeThatFits(_ size: CGSize) -> CGSize
// 計(jì)算合適Size,并更改本視圖的size去包含子視圖
open func sizeToFit()
// 當(dāng)一個(gè)view的bounds變化的時(shí)候用于決定其內(nèi)容怎么變化(變化模式)
open var contentMode: UIViewContentMode

1、autoresizingMask && autoresizesSubviews

public struct UIViewAutoresizing : OptionSet {

    public init(rawValue: UInt)
    
    // 自動(dòng)調(diào)整view與父視圖左邊距,以保證右邊距不變
    public static var flexibleLeftMargin: UIViewAutoresizing { get }
    // 自動(dòng)調(diào)整view的寬度,保證左邊距和右邊距不變
    public static var flexibleWidth: UIViewAutoresizing { get }
    // 自動(dòng)調(diào)整view與父視圖右邊距,以保證左邊距不變
    public static var flexibleRightMargin: UIViewAutoresizing { get }
    // 自動(dòng)調(diào)整view與父視圖上邊距,以保證下邊距不變
    public static var flexibleTopMargin: UIViewAutoresizing { get }
    // 自動(dòng)調(diào)整view的高度,以保證上邊距和下邊距不變
    public static var flexibleHeight: UIViewAutoresizing { get }
    // 自動(dòng)調(diào)整view與父視圖的下邊距,以保證上邊距不變
    public static var flexibleBottomMargin: UIViewAutoresizing { get }
}

結(jié)構(gòu)體的各個(gè)屬性,如上所述,具體測(cè)試代碼如下:

var firstView = UIView.init();
    
func testAutoresizingMask() {
        
    self.firstView.frame = CGRect.init(x: 20, y: 80, width: 200, height: 200);
    self.firstView.backgroundColor = UIColor.red;
        
    self.view.addSubview(self.firstView);
        
    let secondView = UIView.init(frame: CGRect.init(x: 10, y: 10, width: 180, height: 20));
    secondView.backgroundColor = UIColor.brown;
    secondView.autoresizingMask = [.flexibleWidth, .flexibleBottomMargin];
    self.firstView.addSubview(secondView);
        
    let thirdView = UIView.init(frame: CGRect.init(x: 10, y: 40, width: 180, height: 20));
    thirdView.backgroundColor = UIColor.cyan;
    thirdView.autoresizingMask = .flexibleLeftMargin;
    self.firstView.addSubview(thirdView);
        
    let fourthView = UIView.init(frame: CGRect.init(x: 10, y: 70, width: 180, height: 20));
    fourthView.backgroundColor = UIColor.blue;
    fourthView.autoresizingMask = .flexibleRightMargin;
    self.firstView.addSubview(fourthView);
        
    let fifthView = UIView.init(frame: CGRect.init(x: 10, y: 110, width: 180, height: 50));
    fifthView.backgroundColor = UIColor.yellow;
    fifthView.autoresizingMask = [.flexibleTopMargin, .flexibleHeight];
    self.firstView.addSubview(fifthView);
        
    let changeBtn = UIButton.init(type: .custom);
    changeBtn.setTitle("更改frame", for: .normal);
    changeBtn.frame = CGRect.init(x: 20, y: 500, width: 120, height: 40);
    changeBtn.backgroundColor = UIColor.green;
    changeBtn.addTarget(self, action: #selector(changeAutoresizingMaskFrame), for: .touchUpInside);
    self.view.addSubview(changeBtn);
        
}
    
func changeAutoresizingMaskFrame() {
        
    let framesArray = [220,250,270,300,320];
        
    let index = Int(arc4random() % 5);
        
    self.firstView.frame = CGRect.init(x: 20, y: 80, width: framesArray[index], height: framesArray[index]);
}
    

不斷點(diǎn)擊按鈕,得到如下圖結(jié)果:

由結(jié)果可知,API與其描述相符,設(shè)置 flexibleRight 則左側(cè)距離保持不變,設(shè)置 flexibleTop 則子View距離底部距離保持不變,即關(guān)鍵字(如 leftrighttopbottom)代表的反方向的相對(duì)距離保持不變。

接著上面的測(cè)試,添加如下代碼:

self.firstView.autoresizesSubviews = false;

重復(fù)上面的測(cè)試方法,不斷點(diǎn)擊按鈕,得到結(jié)果如下圖:

由上圖可知,之前設(shè)置的 autoresizingMask沒(méi)有效果了,autoresizesSubviews就是控制 autoresizingMask模式的開關(guān),默認(rèn)是開啟的。

2、sizeThatFits && sizeToFit

測(cè)試代碼如下:

func testSizeFits() {
        
    let sizeLabel = UILabel.init(frame: CGRect.init(x: 20, y: 100, width: 0, height: 0));
    sizeLabel.font = UIFont.systemFont(ofSize: 20);
    sizeLabel.text = "王隆帥的簡(jiǎn)書 王隆帥的博客 王隆帥!!!";
    self.view.addSubview(sizeLabel);
        
    let fitSize = sizeLabel.sizeThatFits(CGSize.zero);
    print("sizeThatFits ------- \n" + String(describing: fitSize));
    print("sizeThatFits 后 Label的尺寸 ------- \n" + String(describing: sizeLabel.frame.size));
        
    sizeLabel.sizeToFit();
    print("sizeToFit 后 Label的尺寸 ------- \n" + String(describing: sizeLabel.frame.size));
}
    

結(jié)果如下:

sizeThatFits ------- 
(333.5, 24.0)
sizeThatFits 后 Label的尺寸 ------- 
(0.0, 0.0)
sizeToFit 后 Label的尺寸 ------- 
(333.5, 24.0)

由結(jié)果可知 sizeThatFits 方法得到的自適應(yīng)后的尺寸,但是并沒(méi)有更改標(biāo)簽的實(shí)際大小,而sizeToFit將自適應(yīng)得到的尺寸(內(nèi)部也是調(diào)用sizeThatFits獲取自適應(yīng)尺寸)應(yīng)用到相應(yīng)的 label 上,是label的實(shí)際尺寸更改為自適應(yīng)的尺寸。

3、contentMode

public enum UIViewContentMode : Int { 
     
    // 改變內(nèi)容的高寬比例,縮放內(nèi)容,UIView中完整顯示內(nèi)容,填滿UIView 
    case scaleToFill
    // 保持內(nèi)容的高寬比,縮放內(nèi)容,完整顯示內(nèi)容,最大化填充UIview,沒(méi)填充上的區(qū)域透明
    case scaleAspectFit 
    // 保持內(nèi)容高寬比,縮放內(nèi)容,超出視圖的部分內(nèi)容會(huì)被裁減,填充UIView
    case scaleAspectFill
    // 當(dāng)View的bounds改變,系統(tǒng)會(huì)調(diào)用setNeedsDisplay,重新繪制視圖
    case redraw 
    // 不縮放,內(nèi)容在視圖中間
    case center 
    // 不縮放,內(nèi)容在視圖頭部
    case top
    // 不縮放,內(nèi)容在視圖底部
    case bottom
    // 不縮放,內(nèi)容在視圖左側(cè)
    case left
    // 不縮放,內(nèi)容在視圖右側(cè)
    case right
    // 不縮放,內(nèi)容在視圖頭部及左側(cè)
    case topLeft
    // 不縮放,內(nèi)容在視圖頭部及右側(cè)
    case topRight
    // 不縮放,內(nèi)容在視圖底部及左側(cè)
    case bottomLeft
    // 不縮放,內(nèi)容在視圖底部及右側(cè)
    case bottomRight
}

測(cè)試代碼就忽略不計(jì)了,具體效果如下圖(圖片來(lái)源是這里):

七、待續(xù)...

篇幅所限,因?yàn)閁IView的內(nèi)容略多,所以接下來(lái)會(huì)分篇來(lái)整理。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評(píng)論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,538評(píng)論 3 417
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評(píng)論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評(píng)論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,761評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,207評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,419評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,959評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,782評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,983評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,222評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評(píng)論 1 286
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,678評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,978評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容

  • *面試心聲:其實(shí)這些題本人都沒(méi)怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來(lái)就是把...
    Dove_iOS閱讀 27,195評(píng)論 30 471
  • Core Animation基礎(chǔ) Core Animation 利用了硬件加速和架構(gòu)上的優(yōu)化來(lái)實(shí)現(xiàn)快速渲染和實(shí)時(shí)動(dòng)...
    獨(dú)木舟的木閱讀 1,551評(píng)論 0 3
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,151評(píng)論 4 61
  • 一、初始化方法 1、- initWithFrame: UIView *view = [[UIView alloc]...
    默默_David閱讀 2,534評(píng)論 1 3
  • 最煩秀恩愛了,秀恩愛死得快! 翻開朋友圈,除了廣告,緊接著惹人厭的就是無(wú)窮無(wú)盡的自拍和秀恩愛。 秀恩愛有錯(cuò)嗎?我秀...
    墨花閱讀 4,934評(píng)論 8 42