CoreGraphics 专题

CoreGraphic框架解析 (七)—— 基于CoreGrap

2018-10-21  本文已影响43人  刀客传奇

版本记录

版本号 时间
V1.0 2018.10.21 星期日

前言

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的一个简单绘制示例 (二)

开始

在我们的Core Graphics教程的第三部分也是最后一部分中,您将把Flo带到最终形式。 具体来说,你将:

您在本节中的任务是使用UIKit的pattern方法来创建此背景模式:

转到File \ New \ File ...并选择iOS iOS \ Source \ Cocoa Touch Class模板以创建一个名为BackgroundView的类,其为UIView子类。 单击Next,然后单击Create。

转到Main.storyboard,选择ViewController的主视图,并在Identity Inspector中将类更改为BackgroundView

使用Assistant Editor设置BackgroundView.swiftMain.storyboard,使它们并排放置。

BackgroundView.swift中的代码替换为:

import UIKit

@IBDesignable
class BackgroundView: UIView {
  
  //1
  @IBInspectable var lightColor: UIColor = UIColor.orange
  @IBInspectable var darkColor: UIColor = UIColor.yellow
  @IBInspectable var patternSize: CGFloat = 200
  
  override func draw(_ rect: CGRect) {
    //2
    let context = UIGraphicsGetCurrentContext()!
    
    //3
    context.setFillColor(darkColor.cgColor)
    
    //4
    context.fill(rect)
  }
}

您的故事板的背景视图现在应该是黄色的。 以上代码的更多细节:

您现在要使用UIBezierPath()绘制这三个橙色三角形。 这些数字对应于以下代码中的点:

仍然在BackgroundView.swift中,将此代码添加到draw(_ rect:)结束:

   
let drawSize = CGSize(width: patternSize, height: patternSize)
    
//insert code here
        
let trianglePath = UIBezierPath()
//1
trianglePath.move(to: CGPoint(x: drawSize.width/2, y: 0))
//2
trianglePath.addLine(to: CGPoint(x: 0, y: drawSize.height/2))
//3
trianglePath.addLine(to: CGPoint(x: drawSize.width, y: drawSize.height/2))
    
//4
trianglePath.move(to: CGPoint(x: 0,y: drawSize.height/2))
//5
trianglePath.addLine(to: CGPoint(x: drawSize.width/2, y: drawSize.height))
//6
trianglePath.addLine(to: CGPoint(x: 0, y: drawSize.height))
    
//7
trianglePath.move(to: CGPoint(x: drawSize.width, y: drawSize.height/2))
//8
trianglePath.addLine(to: CGPoint(x: drawSize.width/2, y: drawSize.height))
//9
trianglePath.addLine(to: CGPoint(x: drawSize.width, y: drawSize.height))
    
lightColor.setFill()
trianglePath.fill()

请注意您如何使用一条路径绘制三个三角形。move(to:)就像在绘图并将其移动到新位置时从纸上抬起笔。

您的故事板现在应该在背景视图的左上角有一个橙色和黄色的图像。

到目前为止,您已直接绘制到视图的绘图上下文中。 为了能够重复此图案(pattern),您需要在上下文之外创建一个图像,然后将该图像用作上下文中的模式。

找到以下内容。 它接近于draw(_ rect:)的顶部,但是在初始上下文调用之后:

let drawSize = CGSize(width: patternSize, height: patternSize)

添加以下代码,方便地在此处插入代码:

UIGraphicsBeginImageContextWithOptions(drawSize, true, 0.0)
let drawingContext = UIGraphicsGetCurrentContext()!
    
//set the fill color for the new context
darkColor.setFill()
drawingContext.fill(CGRect(x: 0, y: 0, width: drawSize.width, height: drawSize.height))

嘿! 那些橙色三角形从故事板上消失了。 他们去哪儿了?

UIGraphicsBeginImageContextWithOptions()创建一个新的上下文并将其设置为当前绘图上下文,因此您现在正在绘制这个新的上下文。 该方法的参数是:

然后,您使用UIGraphicsGetCurrentContext()来获取对此新上下文的引用。

然后用黄色填充新的上下文。 您可以通过将上下文不透明度设置为false来让原始背景显示,但绘制不透明上下文比绘制透明更快,并且该参数足以变为不透明。

将此代码添加到draw(_ rect:)结束:

let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()

这从当前上下文中提取UIImage。 当您使用UIGraphicsEndImageContext()结束当前上下文时,绘图上下文将恢复为视图的上下文,因此draw(_ rect:)中的任何进一步绘图都会出现在视图中。

要将图像绘制为重复模式,请将此代码添加到draw(_ rect:)结束:

UIColor(patternImage: image).setFill()
context.fill(rect)

这通过将图像用作颜色而不是纯色来创建新的UIColor

构建并运行应用程序。 您现在应该拥有相当明亮的应用背景。

转到Main.storyboard,选择背景视图,然后在Attributes Inspector中将@IBInspectable值更改为以下内容:

使用绘制背景图案进行更多实验。 看看你是否可以将波尔卡圆点图案作为背景而不是三角形。

当然,您可以将自己的非矢量图像替换为重复样式。


Drawing Images - 绘图图像

在本教程的最后一段,你将获得一枚奖章,以奖励用户喝足够的水。 当计数器达到八个眼镜的目标时,将出现此奖章。

您将在Swift Playground中绘制它,而不是使用@IBDesignable,然后将代码复制到UIImageView子类。虽然交互式故事板通常很有用,但它们有局限性;他们只绘制简单的代码,当您创建复杂的设计时,故事板通常会过时。

在这种特殊情况下,当用户喝八杯水时,您只需要绘制一次图像。如果用户永远不会达到目标,则无需制作奖牌。

一旦绘制,它也不需要使用draw(_ rect :)setNeedsDisplay()重绘。

是时候把画笔放到画布上了。您将使用Swift playground构建奖牌视图,然后在完成后将代码复制到Flo项目中。

转到File \ New \ Playground ....选择Blank模板,单击Next,将操场命名为MedalDrawing,然后单击Create。

在新的playground窗口中,将playground代码替换为:

import UIKit

let size = CGSize(width: 120, height: 200)

UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
let context = UIGraphicsGetCurrentContext()!

//This code must always be at the end of the playground
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

这将创建绘图上下文,就像您对图案图像所做的那样。

记下最后两行;你总是需要在playground的底部,所以你可以在playground上预览图像。

接下来,在灰色结果列中单击此代码右侧的方形按钮:

let image = UIGraphicsGetImageFromCurrentImageContext()

这将在代码下方放置预览图像。 图像将随着您对代码所做的每次更改而更新。

通常最好做一个草图来围绕绘制元素所需的顺序 - 看看我在构思本教程时所做的“杰作”:

这是绘制奖牌的顺序:

请记住保留playground的最后两行(从最后的上下文中提取图像的位置),并在这些行之前将此绘图代码添加到playground

首先,设置您需要的非标准颜色。

//Gold colors
let darkGoldColor = UIColor(red: 0.6, green: 0.5, blue: 0.15, alpha: 1.0)
let midGoldColor = UIColor(red: 0.86, green: 0.73, blue: 0.3, alpha: 1.0)
let lightGoldColor = UIColor(red: 1.0, green: 0.98, blue: 0.9, alpha: 1.0)

这一切现在都应该很熟悉了。 请注意,当您声明颜色时,颜色会显示在playground的右边缘。

添加丝带红色部分的绘图代码:

//Lower Ribbon
let lowerRibbonPath = UIBezierPath()
lowerRibbonPath.move(to: CGPoint(x: 0, y: 0))
lowerRibbonPath.addLine(to: CGPoint(x: 40, y: 0))
lowerRibbonPath.addLine(to: CGPoint(x: 78, y: 70))
lowerRibbonPath.addLine(to: CGPoint(x: 38, y: 70))
lowerRibbonPath.close()
UIColor.red.setFill()
lowerRibbonPath.fill()

这里没有什么新东西,只是创建一条路径并填充它。 您应该会在右侧窗格中看到红色路径。

添加clasp的代码:

//Clasp
let claspPath = UIBezierPath(roundedRect: CGRect(x: 36, y: 62, width: 43, height: 20), cornerRadius: 5)
claspPath.lineWidth = 5
darkGoldColor.setStroke()
claspPath.stroke()

在这里,您可以使用带有圆角的UIBezierPath(roundedRect :),方法是使用cornerRadius参数。 扣环应在右侧窗格中绘制。

添加纪念章的代码:

//Medallion
let medallionPath = UIBezierPath(ovalIn: CGRect(x: 8, y: 72, width: 100, height: 100))
//context.saveGState()
//medallionPath.addClip()

let colors = [darkGoldColor.cgColor, midGoldColor.cgColor, lightGoldColor.cgColor] as CFArray
let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: colors, locations: [0, 0.51, 1])!
context.drawLinearGradient(gradient, start: CGPoint(x: 40, y: 40), end: CGPoint(x: 40, y: 162), options: [])
//context.restoreGState()

请注意注释掉的行。 这些是暂时显示如何绘制渐变:

要将渐变放在一个角度上,使其从左上角到右下角,请更改渐变的结束x坐标。 将drawLinearGradient()代码更改为:

context.drawLinearGradient(gradient, start: CGPoint(x: 40, y: 40), end: CGPoint(x: 100, y: 160), options: [])

现在取消注释奖章绘图代码中的这三行,以创建一个剪切路径来约束徽章圆圈内的渐变。

就像在本系列的第2部分中绘制图形时所做的那样,在添加剪切路径之前保存上下文的绘制状态,并在绘制渐变之后恢复它,以便不再剪切上下文。

这会将路径缩小到原始大小的80%,然后变换路径以使其在渐变视图中居中。

在内部行代码后添加上部丝带绘图代码:

//Upper Ribbon
let upperRibbonPath = UIBezierPath()
upperRibbonPath.move(to: CGPoint(x: 68, y: 0))
upperRibbonPath.addLine(to: CGPoint(x: 108, y: 0))
upperRibbonPath.addLine(to: CGPoint(x: 78, y: 70))
upperRibbonPath.addLine(to: CGPoint(x: 38, y: 70))
upperRibbonPath.close()

UIColor.blue.setFill()
upperRibbonPath.fill()

这与您为下部丝带添加的代码非常相似:创建贝塞尔路径并填充它。

看起来不错!

你越来越近了,但看起来有点二维。 有一些阴影会很好。


Shadows - 阴影

要创建阴影,您需要三个元素:颜色,偏移(阴影的距离和方向)和模糊。

playground的顶部,在定义金色之后但在// Lower Ribbon线之前,插入此阴影代码:

//Add Shadow
let shadow: UIColor = UIColor.black.withAlphaComponent(0.80)
let shadowOffset = CGSize(width: 2.0, height: 2.0)
let shadowBlurRadius: CGFloat = 5

context.setShadow(offset: shadowOffset, blur: shadowBlurRadius, color: shadow.cgColor)

这会产生影子,但结果可能不是你想象的那样。 这是为什么?

将对象绘制到上下文中时,此代码会为每个对象创建一个阴影。

添加代码以在阴影代码之后创建组。 从这开始:

context.beginTransparencyLayer(auxiliaryInfo: nil)

当你开始一个组时,你还需要结束它,所以在playground的末尾添加下一个块,但是在你检索最终图像的点之前:

context.endTransparencyLayer()

现在,您将获得一个完整的奖牌图像,其中包含干净整洁的阴影:

Size Inspector中,为Image View提供坐标X = 76,Y = 147,宽度= 80和高度= 80:

上一篇下一篇

猜你喜欢

热点阅读