反馈请联系hertz@hertzwang.com,谢谢
本文参考
简要说明
- iOS 在运行循环中会整合所有的绘图请求,并一次性将它们绘制出来
- 不能在子线程中绘制,也不能进行复杂操作,否则会造成主线程卡顿
- 调用 UIView 的 drawRect: / draw(_:) 方法进行绘制
setNeedsDisplay
该方法标记为整个视图重新绘制,并且会在下一次绘制周期中重新绘制,自动调用 drawRect: / draw(_:)方法setNeedsDisplayInRect:
该方法标记为视图部分区域重新绘制,周期同上。从绘制带来的开销角度,不推荐整个视图的重新绘制。
UIKit部分
UIKit是高级的图形接口,它的 API 都是基于 Objective-C的。它能够访问绘图、动画、字体、图片等内容。
填充、描边
代码示例:
override func draw(_ rect: CGRect) {
// Drawing code
// 填充
/// 方式一
UIColor.brown.setFill() // UIKit,将当前的图形上下文设置要填充的颜色
UIRectFill(rect) // UIKit,填充矩形
/// 方式二:路径形式填充
let p = UIBezierPath.init(roundedRect: CGRect.init(x: 120, y: 120, width: 40, height: 40), cornerRadius: 5)
UIColor.red.setFill()
p.fill()
// 描边
/// 方式一
UIColor.white.setStroke() // UIKit,描边
let frame = CGRect.init(x: 20, y: 30, width: 100, height: 300)
UIRectFrame(frame)
/// 方式二:路径形式描边
let b = UIBezierPath.init(roundedRect: CGRect.init(x: 120, y: 170, width: 40, height: 40), cornerRadius: 5)
UIColor.red.setStroke()
b.stroke()
// 画横线
let space: CGFloat = 44.0
var lineFrame = CGRect.init(x: 0, y: 350, width: rect.width, height: 1)
UIColor.green.setFill()
for _ in 1...4 {
UIRectFill(lineFrame)
//UIRectFrame(lineFrame)
lineFrame.origin.y += space
}
}
仅供参考的个人理解:
- 简单矩形填充可用
setFill
和UIRectFill
结合使用 - 简单矩形描边可用
setStroke
和UIRectFrame
结合使用 - 复杂图像可以用
UIBezierPath
设置路径来进行填充、描边 UIBezierPath
比较灵活一些
UIImage绘制图像、NSString绘制文本
代码示例:
override func draw(_ rect: CGRect) {
// Drawing code
// UIImage绘制图像
if let image = UIImage.init(named: "dog") {
let imageReact = CGRect.init(origin: CGPoint.init(x: 150, y: 150), size: image.size)
image.draw(in: imageReact) // 注意图片的原始size
//image.draw(in: imageReact, blendMode: .normal, alpha: 0.9)
//image.draw(at: imagePoint) // 从某个点开始
//image.draw(at: imagePoint, blendMode: .normal, alpha: 1.0)
}
// NSString绘制文字
let dogString = NSString.init(string: "小狗")
let font = UIFont.systemFont(ofSize: 34)
let attr = [NSAttributedStringKey.font : font]
dogString.draw(at: CGPoint.init(x: 150, y: 150), withAttributes: attr)
//dogString.draw(in: <#T##CGRect#>, withAttributes: <#T##[NSAttributedStringKey : Any]?#>)
}
仅供参考的个人理解:
- 绘制图片、文本,可以设置起点,也可以设置size
- 图片绘制需要考虑原始的size
实战
九九乘法表
参考代码如下,如有更好的代码请联系我,非常感谢!
class MultiplicationTableView: UIView {
override func draw(_ rect: CGRect) {
// Drawing code
let width = UIScreen.main.bounds.width
let height = width * 3.0 / 5.0 // 5:3
let itemWidth = (width - 20.0) / 9.0
let itemHeight = height / 9.0
// 设置背景
UIColor.white.setFill()
UIRectFill(rect)
// 乘法表内容
var strRect = CGRect.zero
var strAttr = [NSAttributedStringKey:Any]()
strAttr[NSAttributedStringKey.font] = UIFont.systemFont(ofSize: 7)
let style = NSMutableParagraphStyle()
style.alignment = .center
strAttr[NSAttributedStringKey.paragraphStyle] = style
for x in 1...9 {
for y in 1...9 {
guard y <= x else {
break
}
let stringChinese = NSString.init(string: "\(y.chinese)\(x.chinese)\((x*y).resultChinese)")
let string = NSString.init(string: "\(y)x\(x)=\(x*y)\n\(stringChinese)")
debugPrint("\(string)")
strRect = CGRect.init(x: CGFloat(y-1) * itemWidth - CGFloat(y), y: CGFloat(x-1) * itemHeight - CGFloat(x), width: itemWidth, height: itemHeight)
// 背景
UIColor.init(red: CGFloat(150+y*10)/255.0, green: CGFloat(100+y*10)/255.0, blue: CGFloat(60+y*10)/255.0, alpha: 1.0).setFill()
UIRectFill(strRect)
// 内容
let strSize = string.size(withAttributes: strAttr)
let topMargin = (itemHeight - strSize.height) * 0.5
let stringRect = CGRect.init(x: strRect.minX, y: strRect.minY + topMargin, width: itemWidth, height: itemHeight)
string.draw(in: stringRect, withAttributes: strAttr)
// 边框
UIRectFrame(strRect)
}
}
}
}
extension Int {
var chinese: String {
get {
switch self {
case 1:
return "一"
case 2:
return "二"
case 3:
return "三"
case 4:
return "四"
case 5:
return "五"
case 6:
return "六"
case 7:
return "七"
case 8:
return "八"
case 9:
return "九"
default:
return ""
}
}
}
var resultChinese: String {
get {
if self < 10 {
// 小于10: 得X
return "得\(self.chinese)"
} else {
if self%10 == 0 {
// 个位为0: X十
return "\((self/10).chinese)十"
} else if self < 20 {
// 十位为1:十X
return "十\((self-10).chinese)"
} else {
// X十Y
let unit = self%10
let decade = (self-unit)/10
return "\(decade.chinese)十\(unit.chinese)"
}
}
}
}
}
随机验证码
参考代码如下,如有更好的代码请联系我,非常感谢!
//
// VerificationCodeView.swift
// CoreGraphicsDemo01
//
// Created by HertzWang on 2018/1/19.
// Copyright © 2018年 HertzWang. All rights reserved.
//
import UIKit
class VerificationCodeView: UIView {
private let items = ["0","1","2","3","4","5","6","7","8","9"]
private let count: Int = 4
override func draw(_ rect: CGRect) {
// Drawing code
// 设置背景
UIColor.init(red: CGFloat(arc4random_uniform(UInt32(80)))/255.0, green: CGFloat(arc4random_uniform(UInt32(125)))/255.0, blue: CGFloat(arc4random_uniform(UInt32(255)))/255.0, alpha: 1.0).setFill()
UIRectFill(rect)
// 设置内容
let itemWidth = rect.width / CGFloat(count)
for index in 0..<count {
let random = Int(arc4random_uniform(UInt32(self.items.count)))
let item = NSString.init(string: self.items[random])
let attr = [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 18),
NSAttributedStringKey.foregroundColor: UIColor.init(red: CGFloat(arc4random_uniform(UInt32(80)))/255.0, green: CGFloat(arc4random_uniform(UInt32(125)))/255.0, blue: CGFloat(arc4random_uniform(UInt32(255)))/255.0, alpha: 1.0) ]
var point = CGPoint.init(x: itemWidth * CGFloat(index), y: 0)
point.x += CGFloat(arc4random_uniform(UInt32(itemWidth-18)))
point.y += CGFloat(arc4random_uniform(UInt32(rect.height-18)))
item.draw(at: point, withAttributes: attr)
}
}
override init(frame: CGRect) {
super.init(frame: frame)
addGestureRecognizer(UITapGestureRecognizer.init(target: self, action: #selector(refreshCode)))
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func refreshCode() {
setNeedsDisplay()
}
}
进阶
上述的操作只能在 UIView 的 drawRect: / draw(_:) 中绘制,现在说下在其它位置的写法
图片绘制
示例代码:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// 绘制图片
/// 1. 获取图片上下文
UIGraphicsBeginImageContextWithOptions(CGSize.init(width: 100, height: 100), false, 0) // size 可用理解为画布
/// 2. 绘制
let p = UIBezierPath.init(ovalIn: CGRect.init(x: 0, y: 0, width: 100, height: 100)) // 设置路径,在画布中的frame
UIColor.red.setFill() // 设置填充颜色
p.fill() // 填充
/// 3.1. 从上下文中获取绘制的图片
if let image = UIGraphicsGetImageFromCurrentImageContext() {
/// 3.2. 添加到视图上
let imageView = UIImageView.init(image: image)
imageView.frame = CGRect.init(origin: CGPoint.zero, size: image.size)
self.view.addSubview(imageView)
}
/// 4. 关闭上下文
UIGraphicsEndImageContext()
}
Core Graphics/Quartz 2D 部分
drawRect:
示例代码:
override func draw(_ rect: CGRect) {
// Drawing code
// Core Graphics
if let con = UIGraphicsGetCurrentContext() { // 获取上下文(画布)
/// 矩形填充
con.addRect(CGRect.init(x: 120, y: 100, width: 100, height: 100))
con.setFillColor(UIColor.green.cgColor)
con.fillPath()
/// 圆形描边
con.addEllipse(in:CGRect.init(x: 120, y: 300, width: 100, height: 100))
con.setStrokeColor(red: 255, green: 0, blue: 0, alpha: 1)
con.strokePath()
}
}
图片上下文
示例代码:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// 使用 Core Graphics 绘制图片
/// 1. 获取上下文
UIGraphicsBeginImageContext(CGSize.init(width: 100, height: 100)) // 画布size,超出部分不显示
/// 2. 绘制图片
if let con = UIGraphicsGetCurrentContext() {
//con.addEllipse(in: CGRect.init(x: 0, y: 0, width: 100, height: 100)) // 圆形
con.addRect(CGRect.init(x: 0, y: 0, width: 100, height: 100)) // 矩形
con.setFillColor(UIColor.green.cgColor)
con.fillPath()
/// 3.1 获取图片
if let image = UIGraphicsGetImageFromCurrentImageContext() {
/// 3.2 添加到视图
let imageView = UIImageView.init(image: image)
imageView.frame = CGRect.init(origin: CGPoint.zero, size: image.size)
self.view.addSubview(imageView)
}
}
/// 4. 关闭图片上下文
UIGraphicsEndImageContext()
}