CoreGraphic框架解析 (十八) —— 如何制作Glossy效果的按钮(一)

版本记录

版本号 时间
V1.0 2019.02.14 星期四

前言

quartz是一个通用的术语,用于描述在iOSMAC OS X 中整个媒体层用到的多种技术 包括图形、动画、音频、适配。Quart 2D 是一组二维绘图和渲染APICore Graphic会使用到这组APIQuartz Core专指Core Animation用到的动画相关的库、API和类。CoreGraphicsUIKit下的主要绘图系统,频繁的用于绘制自定义视图。Core Graphics是高度集成于UIView和其他UIKit部分的。Core Graphics数据结构和函数可以通过前缀CG来识别。在app中很多时候绘图等操作我们要利用CoreGraphic框架,它能绘制字符串、图形、渐变色等等,是一个很强大的工具。感兴趣的可以看我另外几篇。
1. CoreGraphic框架解析(一)—— 基本概览
2. CoreGraphic框架解析(二)—— 基本使用
3. CoreGraphic框架解析(三)—— 类波浪线的实现
4. CoreGraphic框架解析(四)—— 基本架构补充
5. CoreGraphic框架解析 (五)—— 基于CoreGraphic的一个简单绘制示例 (一)
6. CoreGraphic框架解析 (六)—— 基于CoreGraphic的一个简单绘制示例 (二)
7. CoreGraphic框架解析 (七)—— 基于CoreGraphic的一个简单绘制示例 (三)
8. CoreGraphic框架解析 (八)—— 基于CoreGraphic的一个简单绘制示例 (四)
9. CoreGraphic框架解析 (九)—— 一个简单小游戏 (一)
10. CoreGraphic框架解析 (十)—— 一个简单小游戏 (二)
11. CoreGraphic框架解析 (十一)—— 一个简单小游戏 (三)
12. CoreGraphic框架解析 (十二)—— Shadows 和 Gloss (一)
13. CoreGraphic框架解析 (十三)—— Shadows 和 Gloss (二)
14. CoreGraphic框架解析 (十四)—— Arcs 和 Paths (一)
15. CoreGraphic框架解析 (十五)—— Arcs 和 Paths (二)
16. CoreGraphic框架解析 (十六)—— Lines, Rectangles 和 Gradients (一)
17. CoreGraphic框架解析 (十七)—— Lines, Rectangles 和 Gradients (二)

开始

首先看下写作环境

Swift 4.2, iOS 12, Xcode 10

Core Graphics是iOS开发人员容易避免的框架之一。 它有点模糊,语法不是超现代的,Apple并没有像WWDC那样给予它尽可能多的爱! 另外,只需使用图像即可轻松避免使用它。

但是,Core Graphics是一个非常强大的工具! 你可以摆脱平面设计师的束缚,使用强大的CG剑自己创造出令人惊叹的UI美。

在本教程中,您将学习如何仅使用Core Graphics创建可自定义,可重复使用的光泽按钮。 难道你没有听说过skeuomorphism的风格吗?

在此过程中,您将学习如何绘制圆角矩形,如何轻松地为Core Graphics绘图着色以及如何创建渐变和光泽效果。

自定义UIButton有很多选项,从完整的自定义UIButton类到较小的扩展。 但是在这次讨论中缺少的是一个详细的核心图形教程,用于自定义按钮,从头到尾。 这很简单;你可以使用它来获得你想要的应用程序的确切外观。

现在是时候开始吧!

打开启动项目:CoolButton Starter

项目结构非常简单,仅包含选择Single View App Xcode模板时创建的文件。 唯一的两个major exceptions是:

  • Assets.xcassets目录中的几个图像。
  • 您可以在Main.storyboard中找到ViewController的预先设计的UI。

现在转到File ▸ New ▸ File…,选择iOS▸CocoaTouch Class,然后单击Next

在下一个菜单中,输入CoolButton作为类名。 在子类字段中,键入UIButton。 对于语言,请选择Swift。 现在单击Next,然后单击Create

打开CoolButton.swift并用以下内容替换类定义:

class CoolButton: UIButton {
  var hue: CGFloat {
    didSet {
      setNeedsDisplay()
    }
  }
  
  var saturation: CGFloat {
    didSet {
      setNeedsDisplay()
    }
  }
  
  var brightness: CGFloat {
    didSet {
      setNeedsDisplay()
    }
  }
}

在这里,您将创建三个属性,用于自定义颜色的色调,饱和度和亮度(hue, saturation and brightness)

设置属性后,将触发对setNeedsDisplay的调用,以强制UIButton在用户更改颜色时重绘按钮。

现在,将以下代码粘贴到CoolButton的底部,就在最后一个花括号之前:

required init?(coder aDecoder: NSCoder) {
  self.hue = 0.5
  self.saturation = 0.5
  self.brightness = 0.5
  
  super.init(coder: aDecoder)
  
  self.isOpaque = false
  self.backgroundColor = .clear
}

override func draw(_ rect: CGRect) {
  guard let context = UIGraphicsGetCurrentContext() else {
    return
  }

  let color = UIColor(hue: hue, 
    saturation: saturation, 
    brightness: brightness, 
    alpha: 1.0)

  context.setFillColor(color.cgColor)
  context.fill(bounds)
}

在这里,您初始化变量并使用预先配置的颜色填充按钮,以确保一切从一开始就正常工作。


Configuring the Button’s UI

打开Main.storyboard以配置UI。 有一个视图控制器包含三个滑块 - 一个用于色调,一个用于饱和度,一个用于亮度。

您错过了UIButton,因此请将其添加到屏幕顶部。 转到Object Library,键入UIButton,然后将其拖到屏幕中。

一些自动布局的时间到了! 按住Ctrl键并从按钮拖动到视图,然后在“安全区域”中选择Center Horizontally

按住Ctrl键并从按钮拖动到hue label,然后选择Vertical Spacing

现在,将按钮的大小调整为您喜欢的大小。 按住Ctrl键并从按钮向左或向右拖动,然后选择Width

按住control键,从按钮向上或向下拖动并选择Height

注意:如果通过拖动设置宽度和高度约束有困难,请选择按钮并单击画布右下角的Add New Contraints按钮。 它看起来有点像星球大战中的领带战士。

接下来,通过双击并按Delete键删除显示Button的文本。

仍然选中该按钮,转到屏幕右侧的Inspectors侧栏,然后单击Identity inspector。 在Custom Class ▸ Class中,输入CoolButton以使您的按钮成为CoolButton类的实例。

按钮就位后,您就可以开始将UI连接到代码了!


Making Your Button Functional

打开ViewController.swift并使用以下内容替换类定义:

class ViewController: UIViewController {
  @IBOutlet weak var coolButton: CoolButton!
  @IBAction func hueValueChanged(_ sender: Any) { }
  @IBAction func saturationValueChanged(_ sender: Any) { }
  @IBAction func brightnessValueChanged(_ sender: Any) { }
}

在这里,您将声明对刚刚在故事板中创建的按钮的引用。 您还要声明配置滑块的值更改时发生的回调。

现在,再次打开Main.storyboard并在顶部栏中选择Assistant Editor以并排显示ViewController.swiftMain.storyboard

按住control键从View Controller Scene ▸ ViewController将按钮拖动到左侧并将其连接到coolButton outlet

类似的,按住control键从View Controller Scene ▸ ViewController拖出slider,并选择appropriate value-changed回调。

接下来,切换到ViewController.swift并实现滑块的值更改回调:

@IBAction func hueValueChanged(_ sender: Any) {
  guard let slider = sender as? UISlider else { return }
  coolButton.hue = CGFloat(slider.value)
}

@IBAction func saturationValueChanged(_ sender: Any) {
  guard let slider = sender as? UISlider else { return }
  coolButton.saturation = CGFloat(slider.value)
}

@IBAction func brightnessValueChanged(_ sender: Any) {
  guard let slider = sender as? UISlider else { return }
  coolButton.brightness = CGFloat(slider.value)
}

默认情况下,UISliders的范围为0.0到1.0。 这非常适合您的色调,饱和度和亮度值,范围也从0.0到1.0,因此您可以直接设置它们。

经过所有这些工作,它终于建立并运行了! 如果一切正常,您可以使用滑块来填充各种颜色的按钮:


Drawing Rounded Rectangles

确实,您可以轻松创建方形按钮,但按钮样式比芝加哥的天气变化更快!

事实上,由于我们最初发布本教程,方形按钮和圆角矩形按钮在按钮选美中的第一位来回翻转,所以知道如何制作这两个版本是个好主意。

您还可以说,通过简单地改变UIViewcorner radius来创建圆角矩形非常容易,但那里的乐趣在哪里?用这种方式做得更加满足,或者说是疯狂。

制作圆角矩形的一种方法是使用CGContextAddArc API绘制弧。使用该API,您可以在每个角落绘制弧线并绘制线条以连接它们。但这很麻烦,需要很多几何形状。

幸运的是,有一种更简单的方法!你不需要做太多的数学运算,它可以很好地绘制圆角矩形。这是CGContextAddArcToPoint API

1. Using the CGContextAddArcToPoint API

CGContextAddArcToPoint API允许您通过指定两条切线和一条半径来描述要绘制的弧。 Quartz2D Programming Guide中的下图显示了它的工作原理:

当您使用矩形时,您知道要绘制的每个弧的切线 - 它们只是矩形的边缘! 您可以根据矩形的圆角来指定半径 - 圆弧越大,圆角越圆。

关于这个函数的另一个好处是,如果路径中的当前点没有设置为告诉弧开始绘制的位置,它将从当前点到路径的开头绘制一条线。 因此,您可以将其用作快捷方式,只需几次调用即可绘制圆角矩形。

2. Drawing Your Arcs

由于您要在此Core Graphics教程中创建一堆圆角矩形,并且希望代码尽可能可重用,因此请为所有绘图方法创建单独的文件。

转到File ▸ New ▸ File…,然后选择iOS ▸ Swift File。 按Next,将其命名为Drawing,然后单击Create

现在,使用以下内容替换import Foundation

import UIKit
import CoreGraphics

extension UIView {
  func createRoundedRectPath(for rect: CGRect, radius: CGFloat) -> CGMutablePath {
    let path = CGMutablePath()
    
    // 1
    let midTopPoint = CGPoint(x: rect.midX, y: rect.minY)
    path.move(to: midTopPoint)
    
    // 2
    let topRightPoint = CGPoint(x: rect.maxX, y: rect.minY)
    let bottomRightPoint = CGPoint(x: rect.maxX, y: rect.maxY)
    let bottomLeftPoint = CGPoint(x: rect.minX, y: rect.maxY)
    let topLeftPoint = CGPoint(x: rect.minX, y: rect.minY)
    
    // 3
    path.addArc(tangent1End: topRightPoint, 
      tangent2End: bottomRightPoint, 
      radius: radius)

    path.addArc(tangent1End: bottomRightPoint, 
      tangent2End: bottomLeftPoint, 
      radius: radius)

    path.addArc(tangent1End: bottomLeftPoint,
      tangent2End: topLeftPoint, 
      radius: radius)

    path.addArc(tangent1End: topLeftPoint,
      tangent2End: topRightPoint, 
      radius: radius)

    // 4
    path.closeSubpath()
    
    return path
  }
}

上面的代码为UIView类型的所有内容创建了一个全局扩展,因此您不仅可以将它用于UIButton

该代码还指示createRoundedRectPath(for:radius :)按以下顺序绘制圆角矩形:

以下是代码中发生的情况的细分:

  • 1) 您移动到顶线部分的中心。
  • 2) 您将每个角声明为局部常量。
  • 3) 您将每个弧添加到路径:
    • i) 首先,在右上角添加一个圆弧。 在绘制弧之前,CGPathAddArcToPoint将从矩形中间的当前位置到弧的开始绘制一条线。
    • ii) 同样,您可以为右下角和连接线添加弧。
    • iii) 然后为左下角和连接线添加弧。
    • iv) 最后,为左上角和连接线添加弧。
  • 4) 最后,使用closeSubpath()连接弧的终点和起点。

3. Making Your Button Rounded

好的,现在是时候让这个方法起作用了! 打开CoolButton.swift并用以下内容替换draw(_ :)

override func draw(_ rect: CGRect) {
  guard let context = UIGraphicsGetCurrentContext() else {
    return
  }
  
  // 1
  let outerColor = UIColor(
    hue: hue, saturation: saturation, brightness: brightness, alpha: 1.0)
  let shadowColor = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 0.5)
  
  // 2
  let outerMargin: CGFloat = 5.0
  let outerRect = rect.insetBy(dx: outerMargin, dy: outerMargin)
  // 3
  let outerPath = createRoundedRectPath(for: outerRect, radius: 6.0)
  
  // 4
  if state != .highlighted {
    context.saveGState()
    context.setFillColor(outerColor.cgColor)
    context.setShadow(offset: CGSize(width: 0, height: 2), 
      blur: 3.0, color: shadowColor.cgColor)
    context.addPath(outerPath)
    context.fillPath()
    context.restoreGState()
  }
}

下面进行细分:

  • 1) 您定义了两种颜色。
  • 2) 然后你使用insetBy(dx:dy :)得到一个稍小的矩形(每边5个像素),你将绘制圆角矩形。 你把它变小了,这样你就有空间在外面画一个阴影。
  • 3) 接下来,您调用刚刚编写的函数createRoundedRectPath(for:radius :),为圆角矩形创建路径。
  • 4) 最后,设置填充颜色和阴影,添加上下文的路径,并调用fillPath()以使用当前颜色填充它。

注意:如果您的按钮当前未突出显示,您只想运行代码;例如,它没有被点击。

构建并运行应用程序;如果一切正常,您应该看到以下内容:


Adding a Gradient

好吧,按钮开始看起来很不错,但你可以做得更好! 添加渐变怎么样?

将以下函数添加到Drawing.swift,以使其普遍适用于任何UIView

func drawLinearGradient(
  context: CGContext, rect: CGRect, startColor: CGColor, endColor: CGColor) {
  // 1
  let colorSpace = CGColorSpaceCreateDeviceRGB()
  
  // 2
  let colorLocations: [CGFloat] = [0.0, 1.0]
  
  // 3
  let colors: CFArray = [startColor, endColor] as CFArray
  
  // 4
  let gradient = CGGradient(
    colorsSpace: colorSpace, colors: colors, locations: colorLocations)!

  // More to come...
}

看起来并不多,但这个函数做了很多!

  • 1) 您需要的第一件事是获得一个用于绘制渐变的颜色空间。

注意:您可以使用色彩空间做很多事情,但99%的时间您只需要一个标准的设备相关RGB色彩空间。因此,只需使用函数CGColorSpaceCreateDeviceRGB()来获取所需的引用。

  • 2) 接下来,设置一个数组,跟踪渐变范围内每种颜色的位置。值0表示渐变的开始,1表示渐变的结束。您只有两种颜色,并且您希望第一种颜色位于开头,第二种颜色位于结尾,因此您传入0和1。

注意:如果需要,可以在渐变中使用三种或更多颜色,并且可以设置渐变中每种颜色的开始位置。这对某些效果很有用。

  • 3) 之后,使用传递给函数的颜色创建一个数组。为方便起见,您在此处使用普通旧数组,但您需要将其转换为CFArray,因为这就是API所需要的。

  • 4) 然后使用CGGradient(colorsSpace:colors:locations :)创建渐变,传入颜色空间,颜色数组和先前创建的位置。

你现在有一个渐变引用,但它实际上还没有绘制任何东西。它只是指向稍后实际绘制时使用的信息的指针。

通过在drawLinearGradient(context:rect:startColor:endColor :)的末尾添加以下内容来完成该函数:

// 5
let startPoint = CGPoint(x: rect.midX, y: rect.minY)
let endPoint = CGPoint(x: rect.midX, y: rect.maxY)

context.saveGState()

// 6
context.addRect(rect)
// 7
context.clip()

// 8
context.drawLinearGradient(
  gradient, start: startPoint, end: endPoint, options: [])

context.restoreGState()
  • 5) 您要做的第一件事是计算要绘制渐变的起点和终点。您只需将其设置为矩形的top middlebottom middle的直线。

其余的代码可以帮助您在提供的矩形中绘制渐变,关键函数是drawLinearGradient(_:start:end:options :)

1. Constraining a Gradient to a Sub-area

然而,关于该函数的奇怪之处在于它用渐变填充整个绘图区域 - 没有办法将其设置为仅用渐变填充子区域!

好吧,没有裁剪(clipping),那就是!

裁剪是Core Graphics的一个很棒的功能,可以将绘图限制为任意形状。您所要做的就是将形状添加到上下文中,但是不要像通常那样填充它,而是调用clip()。现在,您已将所有未来的绘图限制在该区域!

这就是你在这里做的:

  • 6) 您将矩形添加到上下文中。
  • 7) 剪辑到该区域。
  • 8) 然后调用drawLinearGradient(_:start:end:options :),传入之前设置的所有变量。

那么关于saveGState()/ restoreGState()的这些东西究竟是什么呢?

好吧,Core Graphics是一个状态机。您可以配置所需的一组状态,例如颜色和线条粗细,然后执行操作以实际绘制它们。这意味着一旦你设置了某些东西,它就会一直保持这种状态,直到你改回来。

好吧,你刚刚修剪到一个区域,所以除非你做了一些事情,否则你再也无法在那个区域之外画画了!

这就是saveGState()/ restoreGState()可以帮到的地方。

通过这些,您可以将上下文的当前设置保存到堆栈中,然后在完成后再将其pop回到原来的位置。

就是这样,现在尝试一下吧!

打开CoolButton.swift并将其添加到draw(_ :)的底部:

// Outer Path Gradient:
// 1
let outerTop = UIColor(hue: hue, saturation: saturation, 
  brightness: brightness, alpha: 1.0)
let outerBottom = UIColor(hue: hue, saturation: saturation, 
  brightness: brightness * 0.8, alpha: 1.0)

// 2
context.saveGState()
context.addPath(outerPath)
context.clip()
drawLinearGradient(context: context, rect: outerRect, 
  startColor: outerTop.cgColor, endColor: outerBottom.cgColor)
context.restoreGState()

建立并运行;你应该看到这样的东西:

  • 1) 首先,定义顶部和底部颜色。
  • 2) 然后,通过在堆栈上保存当前图形状态,添加路径,剪切它,绘制渐变并再次恢复状态来绘制渐变。

很好,你的按钮看起来很漂亮! 一些额外的pizazz怎么样?!


Adding a Gloss Effect

现在是时候让这个按钮闪亮了,因为skeuomorphism应该永远不会过时!

当您为Core Graphics中的按钮添加光泽(gloss)效果时,事情会变得非常复杂。 如果你感觉很难,请看看 Matt GallagherMichael Heyeck关于此事的一些伟大作品。

但是对于我可怜的眼睛,你可以通过应用渐变alpha蒙版获得一个相当好看的光泽效果近似,这更容易理解和编码。 所以你要去这么做。

这是你可以全面应用于UIView的东西,所以将以下函数添加到Drawing.swift中的UIView扩展:

func drawGlossAndGradient(
  context: CGContext, rect: CGRect, startColor: CGColor, endColor: CGColor) {

  // 1
  drawLinearGradient(
    context: context, rect: rect, startColor: startColor, endColor: endColor)
  
  let glossColor1 = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.35)
  let glossColor2 = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.1)
  
  let topHalf = CGRect(origin: rect.origin, 
    size: CGSize(width: rect.width, height: rect.height/2))
  
  drawLinearGradient(context: context, rect: topHalf, 
    startColor: glossColor1.cgColor, endColor: glossColor2.cgColor)
}

此函数基本上是从开始到结束颜色在矩形上绘制渐变,然后在上半部分添加光泽。 下面进行细分:

  • 1) 要绘制渐变,请调用之前编写的函数。
  • 2) 要绘制光泽,然后在其上绘制另一个渐变,从很透明(白色,0.35 alpha)到非常透明(白色,0.1 alpha)。

看看它的外观。 回到CoolButton.swift并在draw(_ :)中做一个小改动。 替换此行,这是draw(_ :)中的倒数第二行:

drawLinearGradient(context: context, rect: outerRect, 
  startColor: outerTop.cgColor, endColor: outerBottom.cgColor)

使用

drawGlossAndGradient(context: context, rect: outerRect, 
  startColor: outerTop.cgColor, endColor: outerBottom.cgColor)

如果您无法发现差异,您只需将drawLinearGradient(context:rect:startColor:endColor :)更改为drawGlossAndGradient(context:rect:startColor:endColor :),即Drawing.swift中新添加的方法。

构建并运行,您的按钮现在应如下所示:

噢,有光泽!


Styling the Button

现在为超细,挑剔的细节。 如果您正在制作3D按钮,那么您可以全力以赴。 要做到这一点,你需要一个斜角。

要创建斜角类型效果,请添加一条内部路径,该路径的渐变略微不同于外部路径。 将它添加到CoolButton.swift中的draw(_ :)底部:

// 1: Inner Colors
let innerTop = UIColor(
  hue: hue, saturation: saturation, brightness: brightness * 0.9, alpha: 1.0)
let innerBottom = UIColor(
  hue: hue, saturation: saturation, brightness: brightness * 0.7, alpha: 1.0)

// 2: Inner Path
let innerMargin: CGFloat = 3.0
let innerRect = outerRect.insetBy(dx: innerMargin, dy: innerMargin)
let innerPath = createRoundedRectPath(for: innerRect, radius: 6.0)

// 3: Draw Inner Path Gloss and Gradient
context.saveGState()
context.addPath(innerPath)
context.clip()
drawGlossAndGradient(context: context, 
  rect: innerRect, startColor: innerTop.cgColor, endColor: innerBottom.cgColor)
context.restoreGState()

在这里,您使用insetBy(dx:dy :)再次缩小矩形,然后获得一个圆角矩形并在其上运行渐变。 构建并运行,您将看到一个微妙的改进:


Highlighting the Button

你的按钮看起来很酷,但它不像一个按钮。 没有指示用户是否按下了按钮。

要处理此问题,您需要覆盖触摸事件以告诉您的按钮重新显示自身,因为在用户选择它之后可能需要更新。

将以下内容添加到CoolButton.swift

@objc func hesitateUpdate() {
  setNeedsDisplay()
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
  super.touchesBegan(touches, with: event)
  setNeedsDisplay()
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
  super.touchesMoved(touches, with: event)
  setNeedsDisplay()
}

override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
  super.touchesCancelled(touches, with: event)
  setNeedsDisplay()
  
  perform(#selector(hesitateUpdate), with: nil, afterDelay: 0.1)
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
  super.touchesEnded(touches, with: event)
  setNeedsDisplay()
  
  perform(#selector(hesitateUpdate), with: nil, afterDelay: 0.1)
}

构建并运行项目,当你点击按钮时,你会发现存在差异 - 突出显示和斜角消失。

但是你可以通过draw(_:)来改善效果:

当用户按下按钮时,整个按钮应该变暗。

您可以通过为名为actualBrightness的亮度创建临时变量,然后根据按钮的状态适当调整它来实现此目的:

var actualBrightness = brightness

if state == .highlighted {
  actualBrightness -= 0.1
}

然后,在draw(_ :)内,用actualBrightness替换所有亮度实例。

总而言之,draw(_ :)函数现在看起来像这样。 它有点长,但重复是值得的:

override func draw(_ rect: CGRect) {
  guard let context = UIGraphicsGetCurrentContext() else {
    return
  }
  
  var actualBrightness = brightness
  
  if state == .highlighted {
    actualBrightness -= 0.1
  }
  
  let outerColor = UIColor(
    hue: hue, saturation: saturation, brightness: actualBrightness, alpha: 1.0)
  let shadowColor = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 0.5)
  
  let outerMargin: CGFloat = 5.0
  let outerRect = rect.insetBy(dx: outerMargin, dy: outerMargin)
  let outerPath = createRoundedRectPath(for: outerRect, radius: 6.0)
  
  if state != .highlighted {
    context.saveGState()
    context.setFillColor(outerColor.cgColor)
    context.setShadow(
      offset: CGSize(width: 0, height: 2), blur: 3.0, color: shadowColor.cgColor)
    context.addPath(outerPath)
    context.fillPath()
    context.restoreGState()
  }
  
  // Outer Path Gloss & Gradient
  let outerTop = UIColor(hue: hue, saturation: saturation, 
    brightness: actualBrightness, alpha: 1.0)
  let outerBottom = UIColor(hue: hue, saturation: saturation, 
    brightness: actualBrightness * 0.8, alpha: 1.0)
  
  context.saveGState()
  context.addPath(outerPath)
  context.clip()
  drawGlossAndGradient(context: context, rect: outerRect, 
    startColor: outerTop.cgColor, endColor: outerBottom.cgColor)
  context.restoreGState()
  
  // Inner Path Gloss & Gradient
  let innerTop = UIColor(hue: hue, saturation: saturation, 
    brightness: actualBrightness * 0.9, alpha: 1.0)
  let innerBottom = UIColor(hue: hue, saturation: saturation, 
    brightness: actualBrightness * 0.7, alpha: 1.0)

  let innerMargin: CGFloat = 3.0
  let innerRect = outerRect.insetBy(dx: innerMargin, dy: innerMargin)
  let innerPath = createRoundedRectPath(for: innerRect, radius: 6.0)
  
  context.saveGState()
  context.addPath(innerPath)
  context.clip()
  drawGlossAndGradient(context: context, rect: innerRect, 
    startColor: innerTop.cgColor, endColor: innerBottom.cgColor)
  context.restoreGState()
}

构建并运行;点击它时按钮应该看起来很棒!

后记

本篇主要讲述了如何制作Glossy效果的按钮,感兴趣的给个赞或者关注~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容