1够傍、Views

概述

UIView或者它的子類知道怎樣將自己繪制在一個(gè)矩形區(qū)域中形娇。我們app所有可視的的界面來自于視圖。創(chuàng)建和配置一個(gè)view非常的簡單示姿。比如你可以在Xib編輯器中拖一個(gè)UIButton或者其他view到一個(gè)View上甜橱,你也可以使用代碼來操作所有的繪制。你可以控制view顯示和消失栈戳、移動(dòng)岂傲、改變大小或者在它上面顯示其他view、給它做動(dòng)畫等子檀。

UIView是UIResponder的子類镊掖,所以也能相應(yīng)事件(和用戶交互,這也是UIView和CALayer本質(zhì)區(qū)別)褂痰。

視圖層次是視圖組織的主要模式亩进。一個(gè)view可以擁有很多子視圖,一個(gè)子視圖只能擁有一個(gè)直接父視圖缩歪,這樣就形成一個(gè)視圖的層級(jí)樹镐侯。如果一個(gè)視圖從界面上remove,它所有子視圖也會(huì)被remove掉。如果一個(gè)view隱藏(hidden)它所有子視圖hidden苟翻。其他變化同樣也會(huì)共享給他的子視圖韵卤。

我們應(yīng)該選擇xib還是code創(chuàng)建視圖,兩者沒有好壞這分崇猫,這取決于你的需求沈条、習(xí)慣和你的app的整體架構(gòu)。

Window

app的window是視圖層級(jí)最頂部的view诅炉。它是一個(gè)UIWindow對(duì)象(或者UIWindow的子類)蜡歹,UIWindow是UIView的子類。我們app擁有一個(gè)主window涕烧,在app運(yùn)行期間創(chuàng)建月而,而且不會(huì)被銷毀或者替換。其他所有可見的視圖都是它的子視圖议纯。

如果你的app可以在外部屏幕展示視圖父款,你將需要?jiǎng)?chuàng)建一個(gè)額外的UIWindow,但是在本章節(jié)瞻凤,我們假設(shè)只有一個(gè)屏幕憨攒,只有一個(gè)window

初始化一個(gè)window必須充滿設(shè)備的屏幕,確保設(shè)置window的frame等于屏幕的bounds阀参,如果你使用的是main.storyboard,在app加載的時(shí)候會(huì)自動(dòng)幫你創(chuàng)建肝集,AppDelegate頂部的那個(gè)注解@UIApplicationMain 。 如果你需要自己創(chuàng)建可以通過以下方式:

// 創(chuàng)建window iOS 9 之前
let w = UIWindow(frame: UIScreen.mainScreen().bounds)
// iOS 9 之后
let w = UIWindow()

window必須在app的整個(gè)生命周期都被持有蛛壳。所以AppDelegate擁有window的強(qiáng)引用杏瞻,我們一般不會(huì)將一個(gè)view直接放在主window上,而是通過將一個(gè)ViewController付給window的rootViewController屬性衙荐,
如果你使用的是main.stroyboard 這個(gè)事情也是系統(tǒng)做好的捞挥,rootViewController 會(huì)直接指向main.stroyboard的initial view controller 。 一個(gè)VC成為window的rootViewController 后赫模,它的view也會(huì)變成UIWindow的直接子視圖树肃。你的app將會(huì)在調(diào)用window?.makeKeyAndVisible()時(shí)顯示蒸矛。

總結(jié)下初始化瀑罗、創(chuàng)建、配置顯示主窗口的過程:(應(yīng)該考慮兩種情況)

  • 通過main storyboard 創(chuàng)建

    • storyboard 文件在 Info.plist 的鍵為 Main storyboard file base name 中指定(UIMainStoryboardFile)
    • UIapplicationMain 實(shí)例化 UIWindow 并設(shè)置好 frame
    • 把設(shè)置好的 UIWindow 的實(shí)例指定給 app delegate 的 window 屬性
    • 實(shí)例化 view controller 并指定給 window 的 rootViewController 屬性
    • 這些都發(fā)生在 app delegate 的 application:didFinishLaunchingWithOptions: 被調(diào)用之前
  • 不使用 main storyboard

    • 因?yàn)轫?xiàng)目模板都會(huì)自帶 storyboard雏掠,所以需要做以下步驟來獲得一個(gè)純凈的空白項(xiàng)目
      • 在 General pane斩祭,選擇 Main 并且刪除
      • 刪除 Main.storyboard 以及 ViewController.swift
      • 刪掉 AppDelegate.swift 中的所有內(nèi)容
      • 然后替換為以下代碼
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        
        self.window = UIWindow()
        self.window!.rootViewController = UIViewController()  // 也可以是自定義的子類
        self.window!.backgroundColor = UIColor.whiteColor()
        self.window!.makeKeyAndVisible()
        return true
    }
}

上面是app運(yùn)行最少需要的代碼,運(yùn)行指揮得到一個(gè)白板乡话。

如何使用window的子類:

  • main storyboard

    在app運(yùn)行時(shí)摧玫,在UIApplicationMain初始化完appDelegate后,就會(huì)詢問appDelegate的window屬性是否有值,如果為nil诬像,UIApplicationMain就會(huì)創(chuàng)建一個(gè)UIWindow的實(shí)例屋群,如果不為nil,直接使用其值作為main window坏挠。
    ??:

lazy var window : UIWindow? = {
    return MyWindow()
}()
  • 不使用main storyboard

    這個(gè)就簡單了芍躏,window本來就是自己初始化的,直接 window = MyWondow()

app一運(yùn)行降狠,會(huì)有很多方法來引用主window

  • 如果一個(gè) UIView 在界面中对竣,它自動(dòng)會(huì)有一個(gè) window 屬性,里面有對(duì) window 的引用

    • 也可以使用 UIView 的 window 屬性來檢查這個(gè) view 是不是被嵌入到了 window 中榜配。如果不是否纬,那么 window 屬性為 nil。一個(gè) window 屬性為 nil 的 UIView 對(duì)用戶來說是不可見的.(self.view.window)
  • app delegate 實(shí)例會(huì)維護(hù)一個(gè)指向 window 的引用(window 屬性)蛋褥,可以通過 shared application 來獲取

    • let w = UIApplication.sharedApplication().delegate!.window
    • 如果想要不那么通用的方法临燃,可以顯式轉(zhuǎn)換成 app delegate 類
    • let w = (UIApplication.sharedApplication().delegate as! AppDelegate).window
  • shared application 會(huì)在 keyWindow 屬性中維護(hù)一個(gè)指向 window 的引用

    • let w = UIApplication.sharedApplication().keyWindow
    • 這個(gè)引用不是很穩(wěn)定,因?yàn)橄到y(tǒng)可能會(huì)創(chuàng)建臨時(shí)的 window 并且把它們當(dāng)做 key window,所以最好還是用第二種

Single View Application 模板

本章節(jié)重點(diǎn)是view壁拉,所以暫且不介紹ViewController相關(guān)內(nèi)容谬俄,后面章節(jié)會(huì)介紹

新建的Single View Application會(huì)自動(dòng)創(chuàng)建一個(gè)Main.storyboard 和 ViewController.swift作為window的rootViewController。

現(xiàn)在我們可以在ViewController.swift中通過代碼添加一個(gè)view上去
就在viewDidLoad方法中

 override func viewDidLoad() {
        super.viewDidLoad()

        let mainview = self.view
        let v = UIView(frame:CGRectMake(100,100,50,50))
        v.backgroundColor = UIColor.redColor() // 紅色小塊
        mainview.addSubview(v) // 添加到mainview
        
    }

不包含main storyboard的實(shí)現(xiàn)方式

func application(application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?)
        -> Bool {
            self.window = UIWindow()
            self.window!.rootViewController = UIViewController()
            // here we can add subviews
            let mainview = self.window!.rootViewController!.view
            let v = UIView(frame:CGRectMake(100,100,50,50))
            v.backgroundColor = UIColor.redColor() // small red square
            mainview.addSubview(v) // add it to main view
            // and the rest is as before...
            self.window!.backgroundColor = UIColor.whiteColor()
            self.window!.makeKeyAndVisible()
            return true
    }

Subview and Superview (子視圖和父視圖)



在iOS中弃理,一個(gè)subview的一部分或者全部都可以出現(xiàn)在其superview的外部溃论。一個(gè) view 可以和另一個(gè) view 重疊,即使不是其 subview 也可以繪制部分或全部繪制在另一個(gè) view 之前痘昌。

測(cè)試了下钥勋,在interface Builder中拖拽的時(shí)候是被擋住的

配圖

但是運(yùn)行結(jié)果是沒有擋住的

配圖

View 層級(jí) 的特點(diǎn)

  • 如果一個(gè) view 被移出或者引入它的 superview,它的 subview 會(huì)跟著
  • 一個(gè) view 的透明度會(huì)被其 subview 繼承
  • 一個(gè) view 可以限制 subview 的顯示范圍辆苔,比如不讓 subview 超出 view 本身的范圍算灸,這叫做 clipping,被設(shè)置在 clipsToBounds
    屬性中
  • 一個(gè) superview 擁有它的 subview
  • 如果一個(gè) view 的尺寸變化了驻啤,它的 subview 也會(huì)自動(dòng)被重新設(shè)置尺寸

一個(gè)UIView有一個(gè)superview屬性和一個(gè)subviews(數(shù)組)屬性(都是可空類型)菲驴。可以據(jù)此來判斷視圖層級(jí)骑冗。另外也有一個(gè) isDescendantOfView: 方法來檢查一個(gè) view 是不是另一個(gè) view 的 subview (可以不是直接子視圖)赊瞬。View 還有一個(gè) tag 屬性,可以通過 viewWithTag: 來進(jìn)行引用贼涩。我們最好給所有subviews設(shè)置不同的tag


使用代碼操作視圖層級(jí):(可以直接操作巧涧,也可以配合動(dòng)畫)

  • addSubview: 方法添加一個(gè) subview
  • removeFromSuperview 移除一個(gè) subview
  • insertSubview:atIndex: 指定index層級(jí)
  • insertSubview:belowSubview: 在某個(gè)view下面添加一個(gè)subview
  • insertSubview:aboveSubview: 在某個(gè)view上面添加一個(gè)subview
  • exchangeSubviewAtIndex:withSubviewAtIndex: 交換兩個(gè)subview的位置
  • bringSubviewToFront: 將某個(gè)subview移動(dòng)到最前面
  • sendSubviewToBack: 將某個(gè)subview放到最后面

沒有一個(gè)方法可以直接移除一個(gè) view 的所有 subview。然而遥倦,因?yàn)橐粋€(gè) view 的 subview 數(shù)組是一個(gè)不可變的數(shù)組谤绳,所以可以用如下方法一次移除全部:

myView.subviews.forEach {$0.removeFromSuperview}


重寫下列方法就可以根據(jù)需要在不同的情況下進(jìn)行不同的操作:

  • didAddSubview:, willRemoveSubview:
  • didMoveToSuperview, willMoveToSuperview:
  • didMoveToWindow, willMoveToWindow:
Visibility and Opacity(可見性和透明度)

視圖的可見性可以通過設(shè)置 hidden 屬性來更改。一個(gè)隱藏的 view 無法接收觸摸事件,所以對(duì)于用戶來說相當(dāng)于不存在缩筛,但實(shí)際上是存在的消略,所以仍然可以在代碼中對(duì)其操作

View 的背景顏色可以通過其 backgroundColor 屬性來設(shè)置,顏色屬于 UIColor 類瞎抛。如果 backgroundColor 為 nil(默認(rèn)值) 那么背景就是透明的疑俭。

可以通過設(shè)置 view 的 alpha 屬性來修改透明程度,1.0 是完全不透明婿失,0.0 是透明钞艇。假設(shè)一個(gè) view 的 alpha 是 0.5,那么它的 subview 的 alpha 都是以 0.5 為基準(zhǔn)的豪硅,不可能高于 0.5哩照。而 UIColor 也有 alpha 這個(gè)屬性,所以即使一個(gè) view 的 alpha 是 1.0懒浮,它仍舊可能是透明的飘弧,因?yàn)槠?backgroundColor 可以是透明的。一個(gè) alpha 為 0.0 的 view 是完全透明的所以是不可見的砚著,通常來說也不可能被點(diǎn)擊次伶。

View 的 alpha 屬性不僅影響背景顏色,也會(huì)影響其內(nèi)容的透明度稽穆。(比如一個(gè)背景色將會(huì)滲透圖片)

我大概實(shí)驗(yàn)了下冠王,應(yīng)該是下面這個(gè)?? 的意思:

配圖

view的opaque(不透明度),并不會(huì)影響view的樣子,更多的是對(duì)于系統(tǒng)繪制時(shí)的提示舌镶。如果一個(gè) view 的 opaque 設(shè)為 true柱彻,因?yàn)椴挥每紤]透明的繪制,所以效率會(huì)高一點(diǎn)餐胀,并且再設(shè)置透明的背景顏色或者 alpha 屬性都無效哟楷。可能會(huì)讓人吃驚否灾,它的默認(rèn)值是 true

但是我設(shè)置了view的alpha=0.3 還是有不透明的效果(或者是疊加),在設(shè)置前后都打印了opaque的值都是true

配圖

配圖

然后還手動(dòng)把opaque設(shè)置為false,但是也沒有什么用卖擅,懂的解釋下??


Frame

View 的 frame屬性(CGRect類) 是它本身的長方形在 superview 中的位置,注意是在 superview 的坐標(biāo)系中的位置墨技。默認(rèn)來說惩阶,superview 的坐標(biāo)系原點(diǎn)在左上,向右 x 增加健提,向下 y 增加琳猫。

看一個(gè)frame使用的簡單的例子:

let mainview = self.view
let v1 = UIView(frame:CGRectMake(113, 111, 132, 194))
v1.backgroundColor = UIColor(red: 1, green: 0.4, blue: 1, alpha: 1)
let v2 = UIView(frame:CGRectMake(41, 56, 132, 194))
v2.backgroundColor = UIColor(red: 0.5, green: 1, blue: 0, alpha: 1)
let v3 = UIView(frame:CGRectMake(43, 197, 160, 230))
v3.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 1)
mainview.addSubview(v1)
v1.addSubview(v2)
mainview.addSubview(v3)

以上很基礎(chǔ)的代碼伟叛,不贅述了私痹。


Bounds and Center

bounds 屬性對(duì)應(yīng)的是一個(gè) view 在自己的坐標(biāo)系統(tǒng)中的矩形尺寸(注意,frame 是在 superview 的坐標(biāo)系下的)

let v1 = UIView(frame:CGRectMake(113, 111, 132, 194))
v1.backgroundColor = UIColor(red: 1, green: 0.4, blue: 1, alpha: 1)
let v2 = UIView(frame:v1.bounds.insetBy(dx: 10, dy: 10))
v2.backgroundColor = UIColor(red: 0.5, green: 1, blue: 0, alpha: 1)
mainview.addSubview(v1)
v1.addSubview(v2)

這是一種很常見的 bounds 的用法,當(dāng)你需要往一個(gè) view 里放東西的時(shí)候紊遵,無論是手動(dòng)繪制還是放置一個(gè) subview账千,通常都要使用 view 的 bounds

配圖

當(dāng)你改變一個(gè) view 的 bounds 時(shí),它的 frame 也會(huì)對(duì)應(yīng)改變暗膜,frame 的改變是基于其中心點(diǎn)的(中心點(diǎn)不會(huì)變)匀奏,下面的代碼描述了這個(gè)情況:

let v1 = UIView(frame:CGRectMake(113, 111, 132, 194))
v1.backgroundColor = UIColor(red: 1, green: 0.4, blue: 1, alpha: 1)
let v2 = UIView(frame:v1.bounds.insetBy(dx: 10, dy: 10))
v2.backgroundColor = UIColor(red: 0.5, green: 1, blue: 0, alpha: 1)
mainview.addSubview(v1)
v1.addSubview(v2)
v2.bounds.size.height += 20
v2.bounds.size.width += 20\

效果就是從上圖變成了下圖,增加的 20 會(huì)被均勻分布在上下左右学搜,正好抵消了之前的設(shè)置娃善。

配圖

當(dāng)創(chuàng)建一個(gè) UIView 時(shí),其 bounds 的坐標(biāo)原點(diǎn)是 (0.0, 0.0)瑞佩,也就是左上角聚磺,如果改變了 bounds 的原點(diǎn),也就改變了其坐標(biāo)系炬丸,其 subview 一般也會(huì)有變化瘫寝,下面代碼描述了這種情況

let v1 = UIView(frame:CGRectMake(113, 111, 132, 194))
v1.backgroundColor = UIColor(red: 1, green: 0.4, blue: 1, alpha: 1)
let v2 = UIView(frame:v1.bounds.insetBy(dx: 10, dy: 10))
v2.backgroundColor = UIColor(red: 0.5, green: 1, blue: 0, alpha: 1)
mainview.addSubview(v1)
v1.addSubview(v2)
v1.bounds.origin.x += 10
v1.bounds.origin.y += 10
配圖

如果你把那兩個(gè)10改成20 效果如圖

配圖

我們并沒有設(shè)置subview的任何屬性,然而subview移動(dòng)了稠炬,可以看到 subviw 向著原點(diǎn)移動(dòng)方向的反方向進(jìn)行了移動(dòng)焕阿,這是因?yàn)橐粋€(gè) view 的原點(diǎn)與其 frame 的左上角一致

我們看到改變 boundssize 會(huì)影響其 framesize ,改變 framesize 會(huì)影響其 boundssize 首启。只有 view 的 center 不會(huì)影響 boundssize 暮屡。 這個(gè)屬性代表了 subviewsuperview 中的 position

書中給出獲取一個(gè)view的center的方法

let c = CGPointMake(theView.bounds.midX,theView.bounds.midY)

但是經(jīng)過我驗(yàn)證毅桃,這個(gè)方法獲得的是view相對(duì)于自己坐標(biāo)的栽惶,我自己測(cè)試代碼如下 , 或許應(yīng)該加上x 疾嗅, y 坐標(biāo)

let v3 = UIView(frame:CGRectMake(113, 111, 132, 194))
print(v3.center)    // (179.0, 208.0)
let c = CGPointMake(v3.bounds.midX, v3.bounds.midY)
print(c)    // (66.0, 97.0)
let c1 = CGPoint(x: v3.bounds.midX+113, y: v3.bounds.midY+111)
print(c1)   // (179.0, 208.0)

view的bounds和center互不影響外厂,相互獨(dú)立的。frame是center和bounds便捷的表達(dá)代承。大多數(shù)情況我們只需要使用frame就可以了汁蝶。一般會(huì)通過init(frame:) 來創(chuàng)建一個(gè)view。注意有些情況下 frame 會(huì)沒有什么意義论悴,但是 bounds 和 center 總是有效的掖棉,所以建議多用 bounds 和 center 的組合,也比較容易理解膀估。

  • bounds: 一個(gè) view 自己的坐標(biāo)系統(tǒng)
  • center: 一個(gè) view 的坐標(biāo)系統(tǒng)和其 superview 的坐標(biāo)系統(tǒng)的關(guān)系

可以用如下方法來進(jìn)行不同 view 之間的坐標(biāo)轉(zhuǎn)換

  • convertPoint:fromView:, convertPoint:toView:
  • convertRect:fromView:, convertRect:toView:

如果第二個(gè)參數(shù)是nil幔亥,系統(tǒng)自動(dòng)填補(bǔ)為window

比如我們上面算center的例子也可以這樣轉(zhuǎn)換下

print(v3.center)    // (179.0, 208.0)
let c = CGPointMake(v3.bounds.midX, v3.bounds.midY)
let c2 = v3.convertPoint(c, toView: self.view)
print(c)    // (66.0, 97.0)
print(c2)   // (179.0, 208.0)

注意,通過改變 center 來設(shè)置 view 的位置時(shí)察纯,如果高或?qū)挷皇桥紨?shù)帕棉,那么可能會(huì)導(dǎo)致 misaligned(錯(cuò)位)针肥。可以通過打開模擬器的 Debug -> Color Misaligned Images 來進(jìn)行檢測(cè)香伴。一個(gè)簡單的方法是調(diào)整好位置之后調(diào)用 makeIntegralInPlace 來設(shè)置 view 的 frame


Window Coordinates and Screen Coordinates(窗口坐標(biāo)和屏幕坐標(biāo))

設(shè)備屏幕是沒有 frame 的慰枕,但是有 bounds。Main window 也沒有 superview即纲,不過其 frame 被設(shè)置為屏幕的 bounds具帮,如:

let w = UIWindow(frame: UIScreen.mainScreen().bounds)
//iOS 9
let w = UIWindow() //系統(tǒng)自動(dòng)設(shè)置為上面代碼

大多數(shù)情況下,window是充滿整個(gè)屏幕的低斋,所以大多數(shù)情況下window的坐標(biāo)和screen的坐標(biāo)是一樣的蜂厅。

現(xiàn)在的 iOS 中坐標(biāo)系和手機(jī)是否選擇是有關(guān)的,有如下兩個(gè)屬性:(在實(shí)際開發(fā)中基本不會(huì)碰到

  • UIScreen 的 coordinateSpace 屬性
    • 這個(gè)坐標(biāo)空間會(huì)旋轉(zhuǎn)膊畴,就是高和寬在設(shè)備旋轉(zhuǎn)時(shí)會(huì)呼喚葛峻,(0.0, 0.0) 是這個(gè) app 本身的左上方
  • UIScreen 的 fixedCoordinateSpace 屬性
    • 這個(gè)坐標(biāo)空間不會(huì)變化,就是物理上的左上角巴比,從用戶來看术奖,這里的 (0.0, 0.0) 可能是 app 本身的任何一個(gè)角

可以用下面的方法來對(duì)不同坐標(biāo)空間進(jìn)行轉(zhuǎn)換:

  • convertPoint:fromCoordinateSpace:,convertPoint:toCoordinateSpace:
  • convertRect:fromCoordinateSpace:, convertRect:toCoordinateSpace:

假設(shè)界面中有一個(gè) UIView v,我們想知道它的實(shí)際設(shè)備坐標(biāo)轻绞,可以用下面的代碼:

let r = v.superview!convertRect(v.frame, toCoordinateSpace: UIScreen.mainScreen().fixedCoordinateSpace)

但實(shí)際上你需要這種信息的機(jī)會(huì)非常少(反正我是沒遇到過需要使用的)采记,或者其實(shí)幾乎都不用擔(dān)心 window 坐標(biāo),因?yàn)樗械目梢姴僮鞫紩?huì)在 root view contoller 的 main view 中進(jìn)行政勃,它的 bounds 是會(huì)自動(dòng)調(diào)整的唧龄。


Transform ( 變換 )

一個(gè) view 的 transform 屬性改變這個(gè) view 是如何被繪制的,實(shí)際上就是一個(gè) CGAffineTransform類的 3x3 矩陣(線性代數(shù)中的概念)奸远。所有的變換都是以這個(gè) view 的 center 做基準(zhǔn)的既棺。

??

let v1 = UIView(frame:CGRectMake(113, 111, 132, 194))
v1.backgroundColor = UIColor(red: 1, green: 0.4, blue: 1, alpha: 1)
let v2 = UIView(frame:v1.bounds.insetBy(dx: 10, dy: 10))
v2.backgroundColor = UIColor(red: 0.5, green: 1, blue: 0, alpha: 1)
mainview.addSubview(v1)
v1.addSubview(v2)
v1.transform = CGAffineTransformMakeRotation(45 * CGFloat(M_PI)/180.0)
旋轉(zhuǎn)

上面代碼的例子只是對(duì)前面例子的v1做了45度的旋轉(zhuǎn)。

我們?nèi)绻蛴∠聉1旋轉(zhuǎn)前后的三個(gè)屬性:

print("frame : \(v1.frame) , bounds:\(v1.bounds) , center:\(v1.center)")
//        frame : (113.0, 111.0, 132.0, 194.0) , bounds:(0.0, 0.0, 132.0, 194.0) , center:(179.0, 208.0)
 v1.transform = CGAffineTransformMakeRotation(45 * CGFloat(M_PI)/180.0)
print("frame : \(v1.frame) , bounds:\(v1.bounds) , center:\(v1.center)")
//        frame : (63.7415946665928, 92.7415946665928, 230.516810666815, 230.516810666815) , bounds:(0.0, 0.0, 132.0, 194.0) , center:(179.0, 208.0)

發(fā)現(xiàn)只有frame變了懒叛,center和bounds都沒有變 丸冕,但是 frame 的數(shù)值已經(jīng)沒有意義,因?yàn)楝F(xiàn)在它的尺寸是能夠覆蓋當(dāng)前 view 的最小的矩形薛窥,并不會(huì)隨著 view 的旋轉(zhuǎn)而選擇胖烛。

如果我們把旋轉(zhuǎn)換成縮放

       print("frame : \(v1.frame) , bounds:\(v1.bounds) , center:\(v1.center)")
       v1.transform = CGAffineTransformMakeScale(1.8, 1)
       print("frame : \(v1.frame) , bounds:\(v1.bounds) , center:\(v1.center)")
//        frame : (113.0, 111.0, 132.0, 194.0) , bounds:(0.0, 0.0, 132.0, 194.0) , center:(179.0, 208.0)
//        frame : (60.2, 111.0, 237.6, 194.0) , bounds:(0.0, 0.0, 132.0, 194.0) , center:(179.0, 208.0)

還是只有frame發(fā)生了改變 。因?yàn)樗奈恢貌]有變只是被拉長了诅迷。bouds并不會(huì)因此而改變

變換矩陣的計(jì)算可以連接的佩番,所以不同的變換是可以疊加的,并且順序是重要的(矩陣乘法不滿足交換律)

??

let v1 = UIView(frame:CGRectMake(20, 111, 132, 194))
v1.backgroundColor = UIColor(red: 1, green: 0.4, blue: 1, alpha: 1)
let v2 = UIView(frame:v1.bounds)
v2.backgroundColor = UIColor(red: 0.5, green: 1, blue: 0, alpha: 1)
mainview.addSubview(v1)
v1.addSubview(v2)
        
v2.transform = CGAffineTransformMakeTranslation(100, 0)
v2.transform = CGAffineTransformRotate(v2.transform, 45 * CGFloat(M_PI)/180.0)

這個(gè)例子我們先在主view上放了兩個(gè)完全重疊的view罢杉,然后對(duì)v2做了平移和旋轉(zhuǎn)的變換 趟畏,兩個(gè)疊加起來的

效果:

先平移后旋轉(zhuǎn)
v2.transform = CGAffineTransformMakeRotation(45 * CGFloat(M_PI)/180.0)
v2.transform = CGAffineTransformTranslate(v2.transform, 100, 0)
先旋轉(zhuǎn)后平移

也可以使用這個(gè)方法CGAffineTransformConcat:

 let r = CGAffineTransformMakeRotation(45 * CGFloat(M_PI)/180.0)
 let t = CGAffineTransformMakeTranslation(100, 0)
 v2.transform = CGAffineTransformConcat(t,r)

這個(gè)也需要注意順序


Trait Collections and Size Classes

界面上的每個(gè) view(或者ViewController) 都有一個(gè) traitCollection 屬性 , 值是一個(gè) UITraitCollection,包含下面四個(gè)屬性:

  • displayScale 由當(dāng)前屏幕決定的縮放尺寸滩租,1 4以前的機(jī)型 基本沒有了應(yīng)該 2 赋秀、(4利朵,5,6 3) 3沃琅、 (iPhone 6 plus/6s Plus) 。(和UIScreen的scale值是一樣的)
  • userInterfaceIdiom 一個(gè) UserIterfaceIdiom 值蜘欲,可能是 .Phone 或 .Pad益眉,來標(biāo)志不同的設(shè)備,默認(rèn)來說和 UIDevice 的 userInterfaceIdiom 屬性一致
  • horizontalSizeClass, verticalSizeClass姥份,是 UIUserInterfaceSizeClass 值郭脂,可能是 .Regular.Compact
    • 水平和豎直都是 .Regular -> iPad
    • 水平是 .Compact 豎直是 .Regular -> iPhone 在垂直方向,或者 iPad 的分屏應(yīng)用
    • 水平和豎直都是 .Compact -> iPhone 在水平方向(iPhone 6/6s plus除外)
    • 水平是 .Regular 豎直是 .Compact -> iPhone 6/6s Plus 在水平方向

當(dāng)應(yīng)用運(yùn)行時(shí)如果 trait collection 發(fā)生改變澈歉,會(huì)調(diào)用traitCollectionDidChange 方法

traitCollectionDidChange傳入的參數(shù)是舊的traitCollection值:

override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
    print("old----\(previousTraitCollection?.verticalSizeClass.rawValue)")
    print("old----\(previousTraitCollection?.horizontalSizeClass.rawValue)")
    print("new----\(self.view.traitCollection.verticalSizeClass.rawValue)")
    print("new----\(self.view.traitCollection.horizontalSizeClass.rawValue)")
}

切換幾次橫豎屏的結(jié)果:

配圖

traitCollection還可以自己設(shè)定展鸡,這個(gè)特性將在后面章節(jié)講到


Layout

superview 移動(dòng)的時(shí)候 subview 就會(huì)移動(dòng)。subview 大小和位置 會(huì)隨著 superview改變埃难,這就是layout莹弊。

一些superview動(dòng)態(tài)改變的例子:

  • 屏幕旋轉(zhuǎn)的時(shí)候,左上角會(huì)發(fā)生變化涡尘,長寬也要對(duì)調(diào)
  • 我們的app需要等比例匹配不同的設(shè)備尺寸
  • universal app需要運(yùn)行在iPad和iPhone上忍弛,所以自己需要知道自己運(yùn)行的環(huán)境來適應(yīng)不同的屏幕
  • 從xib初始化的view,需要resize去適應(yīng)所在的view
  • view需要適應(yīng)別的view的變化對(duì)自己的影響考抄,比如navagationBar隱藏和顯示
  • 细疚。。川梅。

在以上的任何情況下其他view可能需要Layout


Layout 有三種主要的執(zhí)行方式

  • 手動(dòng) layout:superview 在被更改尺寸會(huì)會(huì)發(fā)送 layoutSubviews 消息疯兼,如果你新建自己的子類并且重寫 layoutSubviews 就可以手動(dòng)進(jìn)行更改,這很麻煩贫途,但是可以做任何你想做的事情
  • Autoresizing: iOS 6 之前的方式吧彪,主要是通過自己的 autoresizingMask 屬性來變化
  • Autolayout:根據(jù) view 的 constraints(NSLayoutConstraint) 來進(jìn)行變化,是很強(qiáng)大的功能丢早,不用寫代碼就可以進(jìn)行復(fù)雜的定制

Autoresizing(自動(dòng)調(diào)整大欣囱酢)

Autoresizing 是一種自動(dòng)拉伸和固定大小的一種概念,view有一個(gè)autoresizingMask 屬性 香拉,這個(gè)屬性是一個(gè)UIViewAutoresizing 的值 啦扬。默認(rèn)是.None 。下面舉例來看看它的用法:

let mainview = self.view
let v1 = UIView(frame:CGRectMake(100, 111, 132, 194))
v1.backgroundColor = UIColor(red: 1, green: 0.4, blue: 1, alpha: 1)
let v2 = UIView(frame:CGRectMake(0, 0, 132, 10))
v2.backgroundColor = UIColor(red: 0.5, green: 1, blue: 0, alpha: 1)
let v3 = UIView(frame:CGRectMake(v1.bounds.width-20, v1.bounds.height-20, 20, 20))
v3.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 1)
mainview.addSubview(v1)
v1.addSubview(v2)
v1.addSubview(v3)

v2 和 v1 寬度相等

執(zhí)行結(jié)果:

配圖

如果我加上一句代碼凫碌,改變了v1的寬度呢扑毡?

v1.bounds.size.width += 40

結(jié)果如圖:

就變成這樣了

這時(shí)候就可以利用autoresizingMask屬性來指定v2的寬度可伸縮。

v2.autoresizingMask = .FlexibleWidth

結(jié)果:

配圖

這里注定一點(diǎn)盛险,要先指定可伸縮瞄摊,再改變大小

同理v3也可以:

v2.autoresizingMask = .FlexibleWidth
v3.autoresizingMask = [.FlexibleTopMargin, .FlexibleLeftMargin]

v1.bounds.size.width += 40
v1.bounds.size.height -= 50

v3就相當(dāng)于在左邊和上邊放了彈簧

配圖

其實(shí)這種類似于約束布局勋又,現(xiàn)在大部分會(huì)選擇使用Autolayout,下面小結(jié)看下Autolayout的用法换帜。
Autolayout部分請(qǐng)戳:Views - AutoLayout

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末楔壤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子惯驼,更是在濱河造成了極大的恐慌蹲嚣,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祟牲,死亡現(xiàn)場(chǎng)離奇詭異隙畜,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)说贝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門议惰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人乡恕,你說我怎么就攤上這事言询。” “怎么了傲宜?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵倍试,是天一觀的道長。 經(jīng)常有香客問我蛋哭,道長县习,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任谆趾,我火速辦了婚禮躁愿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘沪蓬。我一直安慰自己彤钟,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布跷叉。 她就那樣靜靜地躺著逸雹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪云挟。 梳的紋絲不亂的頭發(fā)上梆砸,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音园欣,去河邊找鬼帖世。 笑死,一個(gè)胖子當(dāng)著我的面吹牛沸枯,可吹牛的內(nèi)容都是我干的日矫。 我是一名探鬼主播赂弓,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼哪轿!你這毒婦竟也來了盈魁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤窃诉,失蹤者是張志新(化名)和其女友劉穎杨耙,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體褐奴,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡按脚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年于毙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了敦冬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡唯沮,死狀恐怖脖旱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情介蛉,我是刑警寧澤萌庆,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站币旧,受9級(jí)特大地震影響践险,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜吹菱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一巍虫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鳍刷,春花似錦占遥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至尤揣,卻和暖如春搔啊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背北戏。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國打工坯癣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人最欠。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓示罗,卻偏偏與公主長得像惩猫,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蚜点,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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