小伙伴們?cè)陂_(kāi)發(fā)一款A(yù)PP的時(shí)候,為了優(yōu)化用戶的體驗(yàn)往往會(huì)用到多種顏色和多個(gè)圖片迅矛。純色往往讓人覺(jué)得單調(diào)节腐,使用顏色漸變能夠給用戶更好的體驗(yàn)。那我們接下來(lái)就來(lái)體驗(yàn)一場(chǎng)顏色的盛宴吧~~~
如何簡(jiǎn)單而又輕松的做出一個(gè)漸變效果惭墓?有三種方法。第一種方法是最捷徑的而姐,直接使用漸變效果的圖片腊凶。但是最大的缺點(diǎn)呢就是你無(wú)法控制漸變的幅度,除非你對(duì)每一種狀態(tài)制作一個(gè)圖像拴念。這個(gè)工程量就十分巨大了钧萍。第二種方法是使用 Core Graphics ,但是你需要掌握關(guān)于 CG 的知識(shí)(例如圖形的上下文政鼠,色彩空間风瘦,等等)。Core Graphics 框架是面向高級(jí)開(kāi)發(fā)者的缔俄,很多新手不善于使用弛秋,從而無(wú)法做出漸變效果(因?yàn)槲乙膊粫?huì)??)。
所以我給大家介紹的是下面這種方法俐载,即便捷又簡(jiǎn)單的方法:利用 CAGradientLayer 對(duì)象蟹略。
CAGradientLayer 是 CALayer 的子類《粲叮可以使用它來(lái)達(dá)到漸變效果挖炬。產(chǎn)生一個(gè)簡(jiǎn)單的顏色梯度超簡(jiǎn)單,同時(shí)它還提供了一些屬性用于調(diào)整效果状婶。需要大家注意的是:需要在 view 的 layer 層上展示 CAGradientLayer 意敛。所以我們所有的編碼都是在 layer 層上進(jìn)行馅巷。 CAGradientLayer 美中不足的是,它不支持輻射漸變草姻,但是如果你確實(shí)需要的話可以通過(guò) CAGradientLayer 來(lái)擴(kuò)展實(shí)現(xiàn)钓猬。
一、初步了解CAGradientLayer
下面我們先來(lái)寫(xiě)一個(gè)簡(jiǎn)單的顏色階梯:
創(chuàng)建一個(gè)CAGradientLayer并設(shè)置相關(guān)屬性
var gradientLayer: CAGradientLayer!
//初始化gradientLayer并設(shè)置相關(guān)屬性
func createGradientLayer() {
gradientLayer = CAGradientLayer()
gradientLayer.frame = self.view.bounds
//設(shè)置漸變的主顏色
gradientLayer.colors = [UIColor.redColor().CGColor, UIColor.yellowColor().CGColor]
//將gradientLayer作為子layer添加到主layer上
self.view.layer.addSublayer(gradientLayer)
}
在viewWillAppear中顯示顏色階梯:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.createGradientLayer()
}
運(yùn)行后的效果如下圖所示:
二撩独、漸變色
在createGradientLayer()這個(gè)方法中我們并沒(méi)有設(shè)置很多屬性敞曹,但是有一句是必不可少的,那就是設(shè)置漸變的主顏色综膀,我們還可以在數(shù)組中添加更多的顏色:
gradientLayer.colors = [UIColor.redColor().CGColor, UIColor.orangeColor().CGColor, UIColor.blueColor().CGColor, UIColor.magentaColor().CGColor, UIColor.yellowColor().CGColor]
運(yùn)行后的效果如下圖所示:
三澳迫、切換漸變色
colors 屬性是兼容動(dòng)畫(huà)的,也就是說(shuō)如果我們可以通過(guò)動(dòng)畫(huà)來(lái)改變顏色漸變效果剧劝。來(lái)實(shí)驗(yàn)一下橄登,先來(lái)構(gòu)造一個(gè)顏色數(shù)組的集合。然后我們將會(huì)讓每個(gè)顏色集(每個(gè)顏色數(shù)組)在我們點(diǎn)擊的時(shí)候進(jìn)行替換讥此,并且通過(guò)動(dòng)畫(huà)方式拢锹。
首先我們來(lái)創(chuàng)建一個(gè)colorSets 數(shù)組用來(lái)存儲(chǔ)顏色集和currentColorSet 將做為獲取顏色集的索引下標(biāo)。
var colorSets = [[CGColor]]()
var currentColorSet: Int!
然后我們來(lái)設(shè)置一下顏色集:
func createColorSets() {
colorSets.append([UIColor.redColor().CGColor, UIColor.yellowColor().CGColor])
colorSets.append([UIColor.greenColor().CGColor, UIColor.cyanColor().CGColor])
colorSets.append([UIColor.blackColor().CGColor, UIColor.lightGrayColor().CGColor])
colorSets.append([UIColor.blueColor().CGColor, UIColor.magentaColor().CGColor])
currentColorSet = 0
}
我們?cè)趘iewDidLoad中添加一個(gè)點(diǎn)擊手勢(shì):
override func viewDidLoad() {
super.viewDidLoad()
self.createColorSets()
//一個(gè)手指的點(diǎn)擊
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTapGesture(_:)))
self.view.addGestureRecognizer(tapGestureRecognizer)
}
//點(diǎn)擊事件
func handleTapGesture(tap: UITapGestureRecognizer) {
/**
首先我們需要確定下一個(gè)顏色集的下標(biāo)是多少萄喳。如果使用的顏色集合是數(shù)組中的最后一個(gè)面褐,我們需要重新計(jì)數(shù)下標(biāo)( currentColorSet = 0 ),如果不是上述情況取胎,讓 currentColorSet 自加一即可。
*/
if currentColorSet < colorSets.count - 1 {
currentColorSet! += 1
} else {
currentColorSet = 0
}
//添加漸變動(dòng)畫(huà)
let colorChangeAnimation = CABasicAnimation(keyPath: "colors")
colorChangeAnimation.delegate = self
colorChangeAnimation.duration = 2.0
colorChangeAnimation.toValue = colorSets[currentColorSet]
colorChangeAnimation.fillMode = kCAFillModeForwards
colorChangeAnimation.removedOnCompletion = false
gradientLayer.addAnimation(colorChangeAnimation, forKey: "colorChaneg")
}
接下來(lái)的代碼是關(guān)于動(dòng)畫(huà)的湃窍。這里面最重要的屬性是 duration 闻蛀,它代表著動(dòng)畫(huà)的過(guò)渡時(shí)長(zhǎng);另外您市, toValue 屬性用來(lái)設(shè)置終點(diǎn)狀態(tài)的期望顏色集 (這些是在 CABasicAnimation 類初始化時(shí)需要指定的屬性)觉痛。另外兩個(gè)屬性用來(lái)將動(dòng)畫(huà)的最終狀態(tài)保留在 layer 中,而不還原回之前狀態(tài)茵休。但是這不是持續(xù)的薪棒,我們需要在動(dòng)畫(huà)結(jié)束后,顯式設(shè)置漸變色榕莺。我們通過(guò)重寫(xiě)以下方法俐芯,可以在 CABasicAnimation 結(jié)束后執(zhí)行需要的操作:
override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
if flag {
gradientLayer.colors = colorSets[currentColorSet]
}
}
在這不要忘了修改一下createGradientLayer()中修改gradientLayer.colors
gradientLayer.colors = colorSets[currentColorSet]
運(yùn)行后的效果如下:
四、設(shè)置顏色的相對(duì)坐標(biāo)
我們前面看到的效果钉鸯,顏色的位置都是默認(rèn)的均分吧史,下面我們來(lái)自定義的實(shí)現(xiàn):
這個(gè)可以通過(guò) CAGradientLayer 的 locations 屬性來(lái)設(shè)置。該屬性需要傳入一個(gè) NSNumber 對(duì)象數(shù)組唠雕,每個(gè)數(shù)字確定了每個(gè)顏色的起始位置(starting location)贸营。另外吨述,這些數(shù)字是浮點(diǎn)數(shù),取值范圍在 0.0 到 1.0 之間钞脂。
gradientLayer.locations = [0.0, 0.35]
效果是這個(gè)樣子噠:
這個(gè)時(shí)候我突然后一個(gè)想法揣云,可不可以通過(guò)用兩個(gè)手指進(jìn)行縮放來(lái)實(shí)現(xiàn)顏色塊兒的布局呢?
我們來(lái)添加一個(gè)兩個(gè)手指的點(diǎn)擊手勢(shì):
let twoFingerTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTwoFingerTapGesture(_:)))
twoFingerTapGestureRecognizer.numberOfTouchesRequired = 2
self.view.addGestureRecognizer(twoFingerTapGestureRecognizer)
隨機(jī)創(chuàng)建兩個(gè)顏色的位置冰啃。并且邓夕,增加第一個(gè)位置總比第二個(gè)位置坐標(biāo)相對(duì)值小的約束。另外亿笤,每次在控制臺(tái)中輸出新的位置
func handleTwoFingerTapGesture(tap: UITapGestureRecognizer) {
let secondColorLocation = arc4random_uniform(100)
let firstColorLocation = arc4random_uniform(secondColorLocation - 1)
gradientLayer.locations = [NSNumber(double: Double(firstColorLocation)/100.0), NSNumber(double: Double(secondColorLocation)/100.0)]
print(gradientLayer.locations!)
}
效果是這個(gè)樣子噠:
五翎迁、漸變方向
上面所展現(xiàn)的效果都是豎直方向上的變化,那么可不可以實(shí)現(xiàn)360°的旋轉(zhuǎn)呢净薛? Of course汪榔!
首先我們來(lái)創(chuàng)建一個(gè)枚舉來(lái)描述梯度方向
enum PanDirections: Int {
case Right
case Left
case Bottom
case Top
case TopLeftToBottomRight
case TopRightToBottomLeft
case BottomLeftToTopRight
case BottomRightToTopLeft
}
創(chuàng)建一個(gè)新的屬性來(lái)描述漸變梯度方向
var panDirection: PanDirections!
panDirection 屬性根據(jù)手指的移動(dòng)將會(huì)得到相應(yīng)的值。我們需要解決的兩個(gè)問(wèn)題:首先肃拜,我們需要確定方向痴腌,并賦予該屬性對(duì)應(yīng)的數(shù)值。之后燃领,需要檢測(cè)手勢(shì)方向士聪,去確定 startPoint 和 endPoint 這兩個(gè)屬性的數(shù)值。
然后我們來(lái)創(chuàng)建一個(gè)拖動(dòng)手指:
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(ViewController.handlePanGestureRecognizer(_:)))
self.view.addGestureRecognizer(panGestureRecognizer)
我們將會(huì)使用 gesture recogniser 的 velocity 速度屬性猛蔽。如果速度在任意方向上(x 或 y)超過(guò) 300 個(gè) point 剥悟,則會(huì)產(chǎn)生效果。邏輯很簡(jiǎn)單:為了檢查在水平軸上的手勢(shì)速度曼库。然后在豎直方向上進(jìn)行二次檢測(cè)区岗。
func handlePanGestureRecognizer(pan: UIPanGestureRecognizer) {
let velocity = pan.velocityInView(self.view)
if pan.state == UIGestureRecognizerState.Changed {
if velocity.x > 300.0 {
// 水平向右的情況
// 之后檢測(cè)豎直方向上的速度
if velocity.y > 300.0 {
// 從左上到右下
panDirection = PanDirections.TopLeftToBottomRight
}
else if velocity.y < -300.0 {
// 從左下到右上
panDirection = PanDirections.BottomLeftToTopRight
}
else {
// 水平向右
panDirection = PanDirections.Right
}
}
else if velocity.x < -300.0 {
// 水平方向想左的情況
// 之后檢測(cè)數(shù)值方向上的速度
if velocity.y > 300.0 {
// 從右上到左下
panDirection = PanDirections.TopRightToBottomLeft
}
else if velocity.y < -300.0 {
// 從右下到左上
panDirection = PanDirections.BottomRightToTopLeft
}
else {
// 水平向左
panDirection = PanDirections.Left
}
}
else {
// 只有豎直方向上的狀態(tài)(向上或向下)
if velocity.y > 300.0 {
// 豎直向下
panDirection = PanDirections.Bottom
}
else if velocity.y < -300.0 {
// 豎直向上
panDirection = PanDirections.Top
}
else {
// 無(wú)手勢(shì)
panDirection = nil
}
}
}
else if pan.state == UIGestureRecognizerState.Ended {
changeGradientDirection()
}
}
需要注意兩點(diǎn)(除了確定手勢(shì)方向以外):
- 1.如果不滿足任何一個(gè)方向的情況,panDirection 應(yīng)賦 nil
- 2.如果方向是特殊的毁枯,并且手勢(shì)處于 Changed 狀態(tài)慈缔。當(dāng)手勢(shì)結(jié)束時(shí),將會(huì)調(diào)用 changeGradientDirection() 方法种玛,因此該 panDirection 屬性也適用于方向變化藐鹤。
下面的方法也很容易,正如之前設(shè)置 startPoint 和 endPoint 屬性一樣赂韵,通過(guò)觀測(cè) x 和 y 的坐標(biāo)來(lái)確定手勢(shì)方向:
func changeGradientDirection() {
if panDirection != nil {
switch panDirection.rawValue {
case PanDirections.Right.rawValue:
gradientLayer.startPoint = CGPointMake(0.0, 0.5)
gradientLayer.endPoint = CGPointMake(1.0, 0.5)
case PanDirections.Left.rawValue:
gradientLayer.startPoint = CGPointMake(1.0, 0.5)
gradientLayer.endPoint = CGPointMake(0.0, 0.5)
case PanDirections.Bottom.rawValue:
gradientLayer.startPoint = CGPointMake(0.5, 0.0)
gradientLayer.endPoint = CGPointMake(0.5, 1.0)
case PanDirections.Top.rawValue:
gradientLayer.startPoint = CGPointMake(0.5, 1.0)
gradientLayer.endPoint = CGPointMake(0.5, 0.0)
case PanDirections.TopLeftToBottomRight.rawValue:
gradientLayer.startPoint = CGPointMake(0.0, 0.0)
gradientLayer.endPoint = CGPointMake(1.0, 1.0)
case PanDirections.TopRightToBottomLeft.rawValue:
gradientLayer.startPoint = CGPointMake(1.0, 0.0)
gradientLayer.endPoint = CGPointMake(0.0, 1.0)
case PanDirections.BottomLeftToTopRight.rawValue:
gradientLayer.startPoint = CGPointMake(0.0, 1.0)
gradientLayer.endPoint = CGPointMake(1.0, 0.0)
default:
gradientLayer.startPoint = CGPointMake(1.0, 1.0)
gradientLayer.endPoint = CGPointMake(0.0, 0.0)
}
}
}
運(yùn)行后的結(jié)果如下:
(2016/12/28)更新:
最近在仿嗶哩嗶哩娱节,遇到了在cell上添加遮蓋的問(wèn)題,所以用到了CAGradientLayer 右锨,其實(shí)很簡(jiǎn)單括堤,就是在圖片的layer上添加CAGradientLayer 。
let gradientLayer = CAGradientLayer()
gradientLayer.frame = CGRectMake(0, 100, 100, 30)
gradientLayer.colors = [UIColor.clearColor().CGColor,UIColor.blackColor().CGColor]
cell.icon.layer.addSublayer(gradientLayer)
//也可以這樣:
cell.layer.insertSublayer(gradientLayer, atIndex: 0)
效果是這樣的:
總結(jié)
看到了上面的效果之后是不是覺(jué)得顏色漸變很容易實(shí)現(xiàn)呢?通過(guò)多個(gè)屬性賦以合適的數(shù)值并將其組合悄窃,你可以很容易地實(shí)現(xiàn)一個(gè)不錯(cuò)的漸變效果讥电。支持動(dòng)畫(huà)也是它的優(yōu)勢(shì)之一≡梗快來(lái)動(dòng)手實(shí)現(xiàn)你喜歡的效果吧恩敌! ( _ )/~~拜拜