iOS基礎(chǔ)05—-UIView與CALayer的聯(lián)系與區(qū)別

iOS基礎(chǔ)05—-UIView與CALayer的聯(lián)系與區(qū)別

UIView

所有的視圖都是由UIView派生而來皂甘,UIView可以處理觸摸事件佳魔,可以支持Core Graphics繪圖凭迹,可以做仿射變換(旋轉(zhuǎn)或縮放)罚屋,以及簡單的滑動和漸變動畫。

CALayer

CALayer和UIView一樣嗅绸,最大的不同是CALayer不響應(yīng)事件脾猛。每一個UIView都對應(yīng)一個CALayer圖層屬性(但是也可以添加無數(shù)個子圖層)。實際上CALayer才是真正用來在屏幕上顯示和做動畫的鱼鸠,UIView只是對它的一個封裝猛拴,提供了一些處理觸摸的功能以及Core Animation底層方法的高級接口。
這樣分離功能的原因就是因為OSX上的NSView是用鼠標(biāo)處理的蚀狰,這樣iOS和MacOS就可以共享CALayer的代碼了愉昆。

CALayer最常用的地方就是在content上放置寄宿圖:
contents: 是Any類型,但是實際上只有CGImage類型才能起效果麻蹋;它常用的處理contents的屬性包括:
contentRect:顯示圖片的某一部分跛溉;
contentGravity:類似于UIImage的contentMode
contentScale:圖片的縮放尺寸;
maskToBounds:是否顯示超出layer范圍的內(nèi)容
contentsCenter:設(shè)置可拉伸區(qū)域
cornerRadius:設(shè)置圓角
borderWidth:邊框?qū)挾?br> borderColor:邊框顏色
shadowRadius:陰影圓角
shadowOpacity:陰影透明數(shù)
shadowOffset:陰影size
shadowColor:陰影color
shadowPath:使用CGPath對圖層指定陰影形狀扮授,可以提高性能

blueLayer = CALayer()

blueLayer.frame = CGRect(x: 50, y: 50, width: 100, height: 100)

blueLayer.backgroundColor = UIColor.blue.cgColor

layerView.layer.addSublayer(blueLayer)

let img = UIImage(named: "BROOK")

blueLayer.contents = img?.cgImage //基本是UIImage類型芳室,其他類型沒有效果

blueLayer.contentsRect = CGRect.init(x: 0, y: 0, width: 0.5, height: 0.5) //顯示圖的某一部分,按坐標(biāo)來的糙箍,1的話就是顯示全部

blueLayer.contentsGravity = kCAGravityCenter //類似于UIImage的contentMode

blueLayer.contentsScale = (img?.scale)! //按圖片大小縮放

blueLayer.masksToBounds = true // 是否顯示超出Layer.frame范圍的內(nèi)容
blueLayer.contentsCenter = CGRect.init(x: 0.25, y: 0.25, width: 0.5, height: 0.5) //它定義了一個圖片可拉伸的區(qū)域

Custome Drawing:如果不使用圖片的話渤愁,還可以直接用Core Graphics直接在contents繪制寄宿圖,通過繼承UIView的drawRect(這個方法容易造成CPU和內(nèi)存的浪費深夯,莫要輕易使用)方法進(jìn)行繪制抖格,當(dāng)視圖在屏幕上出現(xiàn)時,drawRect會被調(diào)用咕晋;手動調(diào)用setNeedDisplay方法雹拄,drawRect也會被調(diào)用。不過drawRect通常在UIView中使用掌呜,所以如果要在controller上使用滓玖,就可以直接實現(xiàn)CALayerDelegate。

extension CALayerTestController:CALayerDelegate{
//事先會先調(diào)用draw(_ layer: CALayer)质蕉,如果沒有實現(xiàn)就會找draw(_ layer: CALayer, in ctx: CGContext)函數(shù)
    func draw(_ layer: CALayer, in ctx: CGContext) {
       //使用Core Graphics畫圖,這里會返回圖層的Context給你势篡,直接用就好
        ctx.setLineWidth(10)
        ctx.setStrokeColor(UIColor.red.cgColor)
        ctx.strokeEllipse(in: layer.bounds)
    }
}

CALayer雖然不關(guān)心任何響應(yīng)事件,但是它有一系列方法幫你處理事件:contains和hitTest;
contains可以判斷一個點是否在layer的frame里面模暗;
hitTest則可以獲取到點擊事件所在圖層:

override func touchesBegan(_ touches: Set, with event: UIEvent?) {

    let point = touches.first?.location(in: self.view)

    //contains

    if self.layerView.layer.contains(point!) {

        if blueLayer.contains(point!) {

            print("contain")

        }

    }

    //hittest

    let layer = self.layerView.layer.hitTest(point!)

    if layer == blueLayer {

        print("contain")

    }

}

CALayer的錨點anchorPoint可以修改layer的位置:

//這是一個時鐘的例子禁悠,在xib里讓時鐘分鐘秒鐘的center相同,然后對錨點進(jìn)行調(diào)整兑宇,使三個圖片的一端在同一個地方碍侦。

override func viewDidLoad() {
super.viewDidLoad()
changeAnchorPoint()

timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(tick), userInfo: nil, repeats: true)

}
extension CALayerTestController{

//修改圖片的錨點

func changeAnchorPoint(){

self.hour.layer.anchorPoint = CGPoint(x: 0.5, y: 0.9)

self.min.layer.anchorPoint = CGPoint(x: 0.5, y: 0.8)

self.second.layer.anchorPoint = CGPoint(x: 0.1, y: 0.5)

}

func tick(){

//從當(dāng)前時間拿到時分秒

let calendar = Calendar(identifier: .gregorian)

let components = calendar.dateComponents([.hour,.minute,.second], from: Date())

let hourAngle = (CGFloat(components.hour!)/12.0) * CGFloat(Double.pi * 2.0)

let minAngle = (CGFloat(components.minute!)/12.0) * CGFloat(Double.pi * 2.0)

let secondAngle = (CGFloat(components.second!)/12.0) * CGFloat(Double.pi * 2.0)

//轉(zhuǎn)起來

self.hour.transform = CGAffineTransform(rotationAngle: hourAngle)

self.min.transform = CGAffineTransform(rotationAngle: minAngle)

self.second.transform = CGAffineTransform(rotationAngle: secondAngle)

}

}

CALayer的陰影shadow,陰影是根據(jù)寄宿圖的輪廓來確定的:

layerView.layer.shadowOffset = CGSize(width: 5, height: 5)

    layerView.layer.shadowOpacity = 0.9

    layerView.layer.shadowColor = UIColor.black.cgColor

    layerView.layer.shadowRadius = 0 

陰影和視圖直接的邊界線,為0邊界線最明顯
但是有一個問題瓷产,那就是如果圖層需要masksToBounds剪裁站玄,那么陰影就會被剪裁掉。那么要解決這個問題的方法就是用到兩個圖層:一個畫陰影的外圖層濒旦,一個用masksToBounds剪裁內(nèi)容的內(nèi)圖層株旷。(其實就是在需要陰影的view下面插入一個view,然后對其陰影進(jìn)行設(shè)置疤估,這樣在視覺效果上就是一樣的了)

let newShadowViewLayer = UIView()
newShadowViewLayer.backgroundColor = UIColor.white //必須要有顏色或其他填充物灾常,否則就沒有寄宿圖,而陰影正好是圍繞寄宿圖的輪廓來確定的
newShadowViewLayer.frame = CGRect(x: (ContentWidth-200)/2, y: (ContentHeight-200)/2+220, width: 200, height: 200)
newShadowViewLayer.layer.shadowOffset = CGSize(width: 5, height: 5)
newShadowViewLayer.layer.shadowOpacity = 0.5
newShadowViewLayer.layer.shadowColor = UIColor.black.cgColor
newShadowViewLayer.layer.shadowRadius = 0
newShadowViewLayer.layer.cornerRadius = 4.0
newShadowViewLayer.layer.masksToBounds = false
self.view.addSubview(newShadowViewLayer) //注意不是加在layerView上,而且在layerView的更底部圖層上铃拇,如果和self.view.addSubview(layerView)調(diào)個位置钞瀑,則一樣沒有效果

layerView = UIView(frame: CGRect(x: (ContentWidth-200)/2, y: (ContentHeight-200)/2+220, width: 200, height: 200))
layerView.backgroundColor = UIColor.red
self.view.addSubview(layerView)

shadowPath屬性,實時計算陰影是非常耗資源的一件事情慷荔,尤其是有多個字圖層雕什,而且還有透明效果的時候。所以為了提高性能就可以用Core Graphics提供的CGPath給圖層設(shè)置任意形狀的陰影:

//給鐘表添加一個圓形的陰影
self.clock.layer.shadowOpacity = 0.5
let circlePath = CGMutablePath() //CGPath
circlePath.addEllipse(in: self.clock.bounds)
// self.clock.layer.shadowPath = circlePath
//給鐘表添加一個矩形的陰影
let squarePath = CGMutablePath()
squarePath.addRect(self.clock.bounds)
self.clock.layer.shadowPath = squarePath

圖層蒙版Mask显晶, 給一張圖片加蒙版贷岸,那么就會被剪裁成擁有蒙版的外形(待續(xù))

Core Graphics 實現(xiàn)繪圖的話會越來越慢!

總結(jié):
1磷雇、UIView有一個CALayer的類型屬性layer偿警;而所有與繪圖和坐標(biāo)相關(guān)的屬性及動畫實際都是訪問的layer的相關(guān)屬性;
2唯笙、UIView繼承自UIResponder螟蒸,所以能接受響應(yīng)事件,CALayer繼承自NSObject崩掘,不能響應(yīng)事件七嫌;
3、UIView可以有多個CALayer苞慢;

可以去github上查看Demo诵原,喜歡的話star一下哦
github
CSDN

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市挽放,隨后出現(xiàn)的幾起案子绍赛,更是在濱河造成了極大的恐慌,老刑警劉巖辑畦,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惹资,死亡現(xiàn)場離奇詭異,居然都是意外死亡航闺,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來潦刃,“玉大人侮措,你說我怎么就攤上這事」愿埽” “怎么了分扎?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長胧洒。 經(jīng)常有香客問我畏吓,道長,這世上最難降的妖魔是什么卫漫? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任菲饼,我火速辦了婚禮,結(jié)果婚禮上列赎,老公的妹妹穿的比我還像新娘宏悦。我一直安慰自己,他們只是感情好包吝,可當(dāng)我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布饼煞。 她就那樣靜靜地躺著,像睡著了一般诗越。 火紅的嫁衣襯著肌膚如雪砖瞧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天嚷狞,我揣著相機(jī)與錄音块促,去河邊找鬼。 笑死感耙,一個胖子當(dāng)著我的面吹牛褂乍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播即硼,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼逃片,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了只酥?” 一聲冷哼從身側(cè)響起褥实,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎裂允,沒想到半個月后损离,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡绝编,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年僻澎,在試婚紗的時候發(fā)現(xiàn)自己被綠了貌踏。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡窟勃,死狀恐怖祖乳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情秉氧,我是刑警寧澤眷昆,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站汁咏,受9級特大地震影響亚斋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜攘滩,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一帅刊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧轰驳,春花似錦厚掷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至勤哗,卻和暖如春抡爹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背芒划。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工冬竟, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人民逼。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓泵殴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親拼苍。 傳聞我的和親對象是個殘疾皇子笑诅,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,762評論 2 345

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