玩轉swift -- UIKit 之 UIView(1)

概述

UIView類經過定義一個在屏幕和界面上的矩形區域來管理這塊區域的內容。在運行時,視圖對象處理其區域內的任何內容渲染,還處理與該內容的任何相互做用。html

進入正題

1、初始化視圖對象

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

// 使用 Interface Builder 構造界面執行這個
public init?(coder aDecoder: NSCoder)複製代碼

如今開始測試:ios

先構建一個自定義View: TestInitView, 並添加兩個初始化函數,以下:數組

// 純代碼走這個
override init(frame: CGRect) {

    super.init(frame: frame);

    print("執行了 init(frame: CGRect)")
}

// 使用 Interface Builder 構造界面走這個
required init?(coder aDecoder: NSCoder) {

    super.init(coder: aDecoder);
    print("執行了 init?(coder aDecoder: NSCoder)")
}複製代碼

一、純代碼構造界面

測試代碼以下:性能優化

// 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);
}複製代碼

測試結果:iview

執行了 init(frame: CGRect)複製代碼

注意:此方法是由本身調用,來初始化對象的。dom

二、使用 Interface Builder 構造界面

① 使用storyBoard來構建界面,並拖入一個View,並將此View的Class設置爲 TestInitView,以下:ide

② 運行程序,結果以下:函數

執行了 init?(coder aDecoder: NSCoder)複製代碼

注意:由系統來調用,本身不能調用。佈局

2、配置視圖的視覺外觀

// 視圖的背景顏色
@NSCopying open var backgroundColor: UIColor?
// 視圖的alpha值
open var alpha: CGFloat
// 肯定視圖是否不透明的布爾值(它卻決定不了當前UIView是否是不透明),用處在於給繪圖系統提供一個性能優化開關。
open var isOpaque: Bool
// 肯定視圖是否被隱藏的布爾值
open var isHidden: Bool
// 這個屬性定義了一個非默認的着色顏色值,其值的設置會影響到以視圖爲根視圖的整個視圖層次結構。
@available(iOS 7.0, *)
open var tintColor: UIColor!
// 本視圖及父視圖的tintColor或tintAdjustmentMode屬性改變時自動調用
@available(iOS 7.0, *)
open func tintColorDidChange()
// 一個枚舉值,定義了tint color的調整模式。
@available(iOS 7.0, *)
open var tintAdjustmentMode: UIViewTintAdjustmentMode
// 決定子視圖是否被限定在當前視圖的bounds中
open var clipsToBounds: Bool
// 決定在視圖重畫以前是否先清理視圖之前的內容
open var clearsContextBeforeDrawing: Bool
// 一個經過alpha通道來掩蓋一個view的內容的可選view。
@available(iOS 8.0, *)
open var mask: UIView?
// 爲此類的實例建立圖層的類
open class var layerClass: Swift.AnyClass { get }
// UIView的視圖層
open var layer: CALayer { get }複製代碼

一、alpha && isOpaque && isHidden

測試代碼以下:性能

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;
}複製代碼

測試結果以下:

注意:當把UIView的alpha屬性設成0,或者把isHidden設成true的時候,當前UIView和它所包含的子UIView都會變成不可見,同時也不會再響應event事件。isOpaque,而不是決定View的是不是不透明。即View不透明時,isOpaque須要設置爲true,來優化性能,有透明度時,isOpaque須要設置爲false,防止不可預測事情發生(我也不知道啥事情,測試顯示沒啥區別!)。

二、tintColor && tintAdjustmentMode && tintColorDidChange

① 首先測試一下,tintColor的設置:

自定義 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;
}複製代碼

初始化這個View:

func testTintColor() {

    let tintColorView = TIntColorTestView.init(frame: self.view.bounds);
    self.view.addSubview(tintColorView);
}複製代碼

運行程序,並不斷點擊btn,結果以下:

由上可知,tintColor會影響到以視圖爲根視圖的整個視圖層次結構。主要是改變系統的某些控件,好比 UIButton, UISlider, UIProgressView, UIStepper, UIImageView等等。假如想要更改label的文字顏色,或者某些View的背景顏色等,能夠監聽 tintColorDidChange,來作相應更改!

② 再來測試一下 tintAdjustmentMode

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

    // 視圖的着色調整模式與父視圖一致
    case automatic
    // 視圖的tintColor屬性返回UIExtendedSRGBColorSpace 顏色空間的顏色
    case normal
    // 視圖的tintColor屬性返回 UIExtendedGrayColorSpace 顏色空間的顏色
    case dimmed
}複製代碼

測試代碼,在上面的基礎上 加上如下代碼:

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;複製代碼

測試結果以下:

normal ----- 1
normal ----- Optional(UIExtendedSRGBColorSpace 0 0.478431 1 1)
dimmd ----- 2
dimmd ----- Optional(UIExtendedGrayColorSpace 0.484669 0.8)複製代碼

由圖及打印可知,normaldimmd,確實對應着 UIExtendedSRGBColorSpaceUIExtendedGrayColorSpace 兩個顏色空間!而且,imageView設置的 .automatic 是繼承了父View的 .dimmed 屬性。

想要了解更多關於顏色空間,能夠看這篇文章

三、clipsToBounds

測試代碼以下:

// 測試 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);
}複製代碼

測試結果:

四、clearsContextBeforeDrawing

見名知意,此屬性決定繪製前是否清屏,默認爲true。當這個屬性被設置爲時true,UIKIt會在調用 drawRect: 方法以前,把即將被該方法更新的區域填充爲透明的黑色。將這個屬性設置爲false能夠取消相應的填充操做,view中原有內容會保留。

若是將此屬性的值設置爲false,則咱們應該確保在 draw(:) 方法中正確繪製視圖的內容。在此前提下,能夠提升性能。

五、mask

測試代碼以下:

// 測試 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);
}複製代碼

測試結果以下:

由上圖可知,mask自帶的顏色不會顯示,最終效果圖怎麼顯示只跟mask每一個point的alpha相關。(本例是所有都是同樣的alpha,假如想要部分爲透明,能夠添加含有alpha通道的圖片)

能夠這樣理解,是將mask的每一個point的alpha賦值給View的重疊部分相對應的point,這樣view的重疊每一個point都有個alpha值了,view重疊部分就可能顯示多種透明色。

五、layer && layerClass

測試代碼:

// 測試 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);
}複製代碼

測試結果:

總結:UIView和CALayer是相互依賴的關係。UIView依賴於CALayer提供的內容,CALayer依賴UIView提供的容器來顯示繪製的內容。歸根到底CALayer是這一切的基礎,若是沒有CALayer,UIView自身也不會存在,UIView是一個特殊的CALayer實現,添加了響應事件的能力。

想要更詳細瞭解二者詳細能夠查看這兩篇文章(第一篇第二篇),這裏就再也不贅述了!

3、配置事件相關行爲

// 設置視圖的可交互性
open var isUserInteractionEnabled: Bool
// 設置是否支持多點觸控
open var isMultipleTouchEnabled: Bool
// 設置控件接受事件時的排他性
open var isExclusiveTouch: Bool複製代碼

一、isUserInteractionEnabled

當一個視圖對象的 isUserInteractionEnabled 被置爲 false ,則這個視圖對象就被從響應者鏈裏移除,它所負責響應的事件所有無效。它的 subviews 事件也會被丟棄。當從新設爲 true 時,則事件能夠正常的傳遞給該視圖對象。額外的,UIImageViewUILabel 默認的 isUserInteractionEnabledfalseUIViewisUserInteractionEnabled 默認是 true

二、isMultipleTouchEnabled

測試代碼以下:

// 測試 isMultipleTouchEnabled
var touchNums = 0;
func testIsMultipleTouchEnabled() {

    self.view.isMultipleTouchEnabled = true;
}

override func touchesBegan(_ touches: Set
  
  
  

 
  
  , with event: UIEvent?) { touchNums = touchNums + touches.count; print(touchNums); } override func touchesEnded(_ touches: Set 
 
  
    , with event: UIEvent?) { touchNums = touchNums - touches.count; print(touchNums); } override func touchesCancelled(_ touches: Set 
   
     , with event: UIEvent?) { touchNums = touchNums - touches.count; print(touchNums); } 
    
   

 複製代碼

分別用十個指頭點擊屏幕,測試結果以下:

2
4
5
2
0複製代碼

由結果可知,屏幕最多支持五點觸控。

isMultipleTouchEnabled 設置爲 false,可得如下結果:

1
0
1
0複製代碼

即,最多支持一個觸摸事件。

三、isExclusiveTouch

測試代碼以下:

// 測試 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 被點擊了!!");
}複製代碼

運行程序,手指先點擊紅色按鈕,摁住不放,另外一個手指點擊藍色按鈕,而後同時放開,得出下面結果:

btn 被點擊了!!
btn 被點擊了!!複製代碼

由此可知,兩個按鈕同時響應了點擊事件。

將代碼中註釋的代碼打開,從新運行程序,並執行上面的操做,得出下面的結果:

btn 被點擊了!!複製代碼

即,設置了View接收事件的排他性爲true,則同一時間在兩個同級的View之間,只能有一個事件觸發!

4、設置位置及大小

// 設置視圖在其父視圖中的位置及大小
open var frame: CGRect
// 設置視圖在父座標系下的大小
open var bounds: CGRect
// 設置視圖在父視圖中的位置
open var center: CGPoint
// 經過此屬性能夠修改對象的平移、縮放比例和旋轉角度
open var transform: CGAffineTransform複製代碼

一、frame && bounds && center

測試代碼以下:

// 測試 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);
}複製代碼

測試結果以下:

(100.0, 200.0, 50.0, 50.0)
(0.0, 0.0, 50.0, 50.0)
(125.0, 225.0)複製代碼

由結果可知,bounds屬性只是提供了視圖對象的大小,並不包含位置!

二、transform

transform 是一個很是重要的屬性,它在矩陣變換的層面上改變視圖的顯示效果,完成旋轉、形變、平移等等操做。
偷個懶,這個屬性牽涉面較多,能夠參看這篇文章

5、管理視圖層次結構

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

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

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

// 移動某個子視圖到全部子視圖的最上方   
open func bringSubview(toFront view: UIView)
// 移動某個子視圖到全部子視圖的最下方 
open func sendSubview(toBack view: UIView)

// 
open func isDescendant(of view: UIView) -> Bool複製代碼

測試代碼以下:

// 測試 視圖的層次結構
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; }));
}複製代碼

結果以下:

superView:
Optional(
  
  
  

 
  
  >) subviews: [0, 1, 2] window2: Optional( 
 
  
    ; layer = 
   
     >) 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 這個屬性在對比兩個 View 的時候,前者是後者的同一 View 或子 view 才爲 true

6、配置自動佈局行爲

// 控制autoresizingMask模式的開啓與關閉
open var autoresizesSubviews: Bool
// 子視圖相對於父視圖的調整模式
open var autoresizingMask: UIViewAutoresizing
// 視圖計算最合適的size(容納子視圖)並返回
open func sizeThatFits(_ size: CGSize) -> CGSize
// 計算合適Size,並更改本視圖的size去包含子視圖
open func sizeToFit()
// 當一個view的bounds變化的時候用於決定其內容怎麼變化(變化模式)
open var contentMode: UIViewContentMode複製代碼

一、autoresizingMask && autoresizesSubviews

public struct UIViewAutoresizing : OptionSet {

    public init(rawValue: UInt)

    // 自動調整view與父視圖左邊距,以保證右邊距不變
    public static var flexibleLeftMargin: UIViewAutoresizing { get }
    // 自動調整view的寬度,保證左邊距和右邊距不變
    public static var flexibleWidth: UIViewAutoresizing { get }
    // 自動調整view與父視圖右邊距,以保證左邊距不變
    public static var flexibleRightMargin: UIViewAutoresizing { get }
    // 自動調整view與父視圖上邊距,以保證下邊距不變
    public static var flexibleTopMargin: UIViewAutoresizing { get }
    // 自動調整view的高度,以保證上邊距和下邊距不變
    public static var flexibleHeight: UIViewAutoresizing { get }
    // 自動調整view與父視圖的下邊距,以保證上邊距不變
    public static var flexibleBottomMargin: UIViewAutoresizing { get }
}複製代碼

結構體的各個屬性,如上所述,具體測試代碼以下:

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]);
}複製代碼

不斷點擊按鈕,獲得以下圖結果:

由結果可知,API與其描述相符,設置 flexibleRight 則左側距離保持不變,設置 flexibleTop 則子View距離底部距離保持不變,即關鍵字(如 leftrighttopbottom)表明的反方向的相對距離保持不變。

接着上面的測試,添加以下代碼:

self.firstView.autoresizesSubviews = false;複製代碼

重複上面的測試方法,不斷點擊按鈕,獲得結果以下圖:

由上圖可知,以前設置的 autoresizingMask沒有效果了,autoresizesSubviews就是控制 autoresizingMask模式的開關,默認是開啓的。

二、sizeThatFits && sizeToFit

測試代碼以下:

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 = "王隆帥的簡書 王隆帥的博客 王隆帥!!!";
    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));
}複製代碼

結果以下:

sizeThatFits ------- 
(333.5, 24.0)
sizeThatFits 後 Label的尺寸 ------- 
(0.0, 0.0)
sizeToFit 後 Label的尺寸 ------- 
(333.5, 24.0)複製代碼

由結果可知 sizeThatFits 方法獲得的自適應後的尺寸,可是並無更改標籤的實際大小,而sizeToFit將自適應獲得的尺寸(內部也是調用sizeThatFits獲取自適應尺寸)應用到相應的 label 上,是label的實際尺寸更改成自適應的尺寸。

三、contentMode

public enum UIViewContentMode : Int { 

    // 改變內容的高寬比例,縮放內容,UIView中完整顯示內容,填滿UIView 
    case scaleToFill
    // 保持內容的高寬比,縮放內容,完整顯示內容,最大化填充UIview,沒填充上的區域透明
    case scaleAspectFit 
    // 保持內容高寬比,縮放內容,超出視圖的部份內容會被裁減,填充UIView
    case scaleAspectFill
    // 當View的bounds改變,系統會調用setNeedsDisplay,從新繪製視圖
    case redraw 
    // 不縮放,內容在視圖中間
    case center 
    // 不縮放,內容在視圖頭部
    case top
    // 不縮放,內容在視圖底部
    case bottom
    // 不縮放,內容在視圖左側
    case left
    // 不縮放,內容在視圖右側
    case right
    // 不縮放,內容在視圖頭部及左側
    case topLeft
    // 不縮放,內容在視圖頭部及右側
    case topRight
    // 不縮放,內容在視圖底部及左側
    case bottomLeft
    // 不縮放,內容在視圖底部及右側
    case bottomRight
}複製代碼

測試代碼就忽略不計了,具體效果以下圖(圖片來源是這裏):

7、待續...

篇幅所限,由於UIView的內容略多,因此接下來會分篇來整理。

相關文章
相關標籤/搜索