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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容