当前位置:网站首页>1-gradients, shadows, and text
1-gradients, shadows, and text
2022-06-23 08:24:00 【the Summer Palace】
This article is about 《Core Graphics Introductory tutorial 》 The first lecture of . After learning this article , You will learn how to use... In custom controls CG Function to draw a gradient 、 Shadows and text .
ShadowButton
Suppose we want to customize a button control called ShadowButton—— It's not worth the fuss , But the special point is , We are going to use Core Graphics 2D Draw its UI.
First create this class. First , We need to inherit UIControl instead of UIView, This is for the convenience of using it state Property and handle the user's touch .
class ShadowButton: UIControl {
// 1
lazy var outerShadow: NSShadow = {
let outerShadow = NSShadow()
outerShadow.shadowColor = UIColor.black
outerShadow.shadowOffset = CGSize(width: 3.1, height: 3.1)
outerShadow.shadowBlurRadius = 5
return outerShadow
}()
lazy var innerShadow: NSShadow = {
let innerShadow = NSShadow()
innerShadow.shadowColor = UIColor.white
innerShadow.shadowOffset = CGSize(width: 3.1, height: 3.1)
innerShadow.shadowBlurRadius = 9
return innerShadow
}()
lazy var text: Text? = Text(text: "Shadow Button", font: UIFont.systemFont(ofSize: 18), color: .black)
lazy var gradient: CGGradient = {
let deepFillColor = UIColor(red: 0.502, green: 0.502, blue: 0.502, alpha: 1.000)
let lightFillColor = UIColor(red: 0.871, green: 0.871, blue: 0.871, alpha: 1.000)
let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: [lightFillColor.cgColor, deepFillColor.cgColor] as CFArray, locations: [0, 1])!
return gradient
}()
var buttonRadius: CGFloat = 11
// 2
override func draw(_ rect: CGRect) {
if isSelected == true {
selectDraw(rect)
}else {
unselectDraw(rect)
}
}
// 3
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.isSelected = true
setNeedsDisplay()
}
// 4
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.isSelected = false
setNeedsDisplay()
}
// 5
public func selectDraw(_ frame: CGRect) {
let rect = RectanglePainter.insetFrame(frame, delta: outerShadow.shadowBlurRadius)
RectanglePainter.drawGradient(gradient: gradient, frame: rect, cornerRadius: buttonRadius, outerShadow: outerShadow)
RectanglePainter.drawInnerShadow(rect, cornerRadius: buttonRadius, innerShadow: innerShadow)
if text != nil {
TextPainter.drawText(rect, text: text!)
}
}
// 6
public func unselectDraw(_ frame: CGRect) {
let rect = RectanglePainter.insetFrame(frame, delta: outerShadow.shadowBlurRadius)
RectanglePainter.drawGradient(gradient: gradient, frame: rect, cornerRadius: buttonRadius, outerShadow: outerShadow)
if text != nil {
TextPainter.drawText(rect, text: text!)
}
}
}
- Button properties , Including rounded corners 、 Inner shadow 、 Shadow of the vulva 、 Text etc. , Easy to Customize button styles from the outside . The definition of lazy load attribute is used here , So when you first call these properties , These attribute values are initialized automatically . Please note that NSShadow、Text and CGGrandient How these structures are constructed .
- use Core Graphics The main method of drawing is draw(rect:) Method , Here we will judge the button isSelected state , And call the corresponding method UI The draw .
- touchesBegan function , Respond to the user's touch action . In this way, when the user touches the button, there will be a highlighted effect . The normal control state is unselected Of , But when the user clicks on it , We change the control isSelected by true, And then call setNeedsDisplay Redraw control —— This actually triggers draw(rect:) Method .
- touchesEnded function , Respond to the user's release action . This will have a darkening effect when the user releases the button . When the user releases it , We change the control isSelected by false, And call setNeedsDisplay Redraw control , In this way, the button returns to its original state .
- selectDraw Method is responsible for isSelected by true Drawing at . Here we call two Painter class , these Painter Class encapsulates Core Graphics Drawing method . First call RectanglePainter Draw a rectangle ( Draw the outer shadow at the same time ), Then draw the inner shadow of the rectangle , Last call TextPainter Draw text .
- unselectDraw Method is responsible for isSelected by false Drawing at . Same as selectDraw The method is the same , But you don't have to draw inner shadows ( Remove the highlighting effect ).
RectangleButtonPainter and TextPainter Responsible for physical work —— Draw rectangles and text .
ShadowButtonPainter
class RectanglePainter {
// 1
public static func insetFrame(_ frame: CGRect, delta: CGFloat) -> CGRect {
return CGRect(x: delta, y: delta, width: frame.width-delta*2, height: frame.height-delta*2)
}
// 2
public static func drawGradient(gradient: CGGradient?, frame: CGRect, cornerRadius: CGFloat?, outerShadow: NSShadow?) {
// 3
let context = UIGraphicsGetCurrentContext()
if let context = context {
// 4
let path = UIBezierPath(roundedRect: frame, cornerRadius: cornerRadius ?? 0)
// 5
context.saveGState()
// 6
if let shadow = outerShadow, let color = shadow.shadowColor as? UIColor {
context.setShadow(offset: shadow.shadowOffset, blur: shadow.shadowBlurRadius, color: color.cgColor)
}
// 7
context.beginTransparencyLayer(auxiliaryInfo: nil)
// 8
path.addClip()
// 9
if let gradient = gradient {
context.drawLinearGradient(gradient, start: CGPoint(x: frame.midX, y: frame.minY), end: CGPoint(x: frame.midX, y: frame.maxY), options: [])
}else {
path.fill()
}
// 10
context.endTransparencyLayer()
context.restoreGState()
}
}
// 11
public static func drawInnerShadow(_ frame: CGRect, cornerRadius: CGFloat?, innerShadow:NSShadow) {
let context = UIGraphicsGetCurrentContext()
if let context = context, let color = innerShadow.shadowColor as? UIColor {
// 12
let path = UIBezierPath(roundedRect: frame, cornerRadius: cornerRadius ?? 0)
context.saveGState()
// 13
context.clip(to: path.bounds)
// 14
context.setAlpha(color.cgColor.alpha)
// 15
context.beginTransparencyLayer(auxiliaryInfo: nil)
// 16
let rectangleOpaqueShadow = color.withAlphaComponent(1)
// 17
context.setShadow(offset: innerShadow.shadowOffset, blur: innerShadow.shadowBlurRadius, color: rectangleOpaqueShadow.cgColor)
// 18
context.setBlendMode(.sourceOut)
context.beginTransparencyLayer(auxiliaryInfo: nil)
// 19
rectangleOpaqueShadow.setFill()
path.fill()
// 20
context.endTransparencyLayer()
context.endTransparencyLayer()
context.restoreGState()
}
}
}
insetFrame Method , Calculate a “ Smaller ” rectangular , The reduced scope is delta Pixel . This method is often used when drawing control with shadow effect , Because rectangular shadows tend to occupy additional controls , Therefore, when we draw the rectangular space of the control, we can't put the entire control's frame All occupied , Leave enough controls to draw shadows .
drawGradient Method is responsible for drawing a gradient fill + Shadow of the vulva , Need to 4 Parameters : gradient gradient、 Rectangle range frame、 radius cornerRadius、 Shadow of the vulva outerShadow.
First, get the current context context, This is the most important thing , stay CG We always need it in drawing , We can do nothing without it .
Construct a rounded rectangle , As the shape of the button ( No shadows ). Call the construction method of Bezier curve to create a rounded rectangle .
CG Drawing is often concerned with context The state of change . because context The object is global , Before we proceed , The best of context The original status is saved , Wait until the operation is completed , In this way, one painting will not affect the subsequent painting . Save and use the state saveGState function , Recovery of state restoreGState function .
Set up context Shadow , So when we start filling the gradient rectangle , By the way, I will draw the set shadow. Of course , If outerShadow Parameter is nil, We'll do nothing .
Start a new transparent layer , Transparent layers are similar to ps The concept of layers in , And with alpha passageway . It is convenient for you to combine different figures .
Then path clipping . In this way, the filling content will not go beyond the inside of the path .
Draw a gradient inside the path .
End transparent layer , recovery context Graphics status .
drawInnerShadow Responsible for drawing highlights ( Inner shadow ). It needs to 3 Parameters : Rectangle range frame, radius corderRadius, Inner shadow innerShadow.
Construct a rounded rectangle , The shape that is part of the highlight . Call the construction method of Bezier curve to create a rounded rectangle .
Path clipping .
Set up context Of alpha value , Make the currently drawn alpha The value is equivalent to innerShadow Of color Of alpha value ( The previous value was 1).
Then set a transparent layer .
Set the color of the inner shadow , But you need to change the color of the inner shadow alpha Get rid of , No matter how many it is, it is set to 1. Because our inner shadow color alpha The value is also 1, We can 14 One line removed , take 15 The line should be changed to :
rectangleOpaqueShadow = colorThe effect is the same . But here is to avoid the occurrence of alpha Not for 1 The situation of , At this time, we need to manually set the inner shadow alpha Adjusted for 1, Otherwise, the effect will be unexpected .
Use this alpha 1 The inner shadow color setting of context Shadow .
Set the blend mode to source out. The use of mixed mode is complicated , It does not fill the color directly when filling , Instead, the color value is calculated , Calculate the new color value and use it for filling .
Fill the path with the calculated opaque inner shadow color .
End transparent layer , recovery context.
Draw text
The text is simple :
private func drawText(_ rect: CGRect, color: UIColor?) {
if let color = color, let string = text?.text, let font = text?.font {
// 1
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .center
// 2
let fontAttributes = [
.font: font,
.foregroundColor: color,
.paragraphStyle: paragraphStyle,
] as [NSAttributedString.Key: Any]
// 3
let height: CGFloat = string.boundingRect(with: CGSize(width: rect.width, height: CGFloat.infinity), options: .usesLineFragmentOrigin, attributes: fontAttributes, context: nil).height
context?.saveGState()
context?.clip(to: rect)
// 4
string.draw(in: CGRect(x: rect.minX, y: rect.minY + (rect.height - height) / 2, width: rect.width, height: height), withAttributes: fontAttributes)
context?.restoreGState()
}
}
- Set paragraph Center ( Vertical center )
- Set font properties .
- Calculate text height .
- Draw text .
test
stay viewDidLoad Add... To the method :
let button = ShadowButton(frame: CGRect(x: 100,y: 200,width: 200,height: 50))
button.gradient = gradient()
button.buttonRadius = 11
button.innerShadow = innerShadow()
button.outerShadow = outerShadow()
button.text = text("Hello")
button.backgroundColor = .white
addSubview(button)
The effect is as follows :

边栏推荐
- usb peripheral 驱动 - debug
- After reading five books, I summarized these theories of wealth freedom
- Open source technology exchange batch stream integrated data synchronization engine Chunjun data restore DDL function module analysis
- Odoo project sends information to wechat official account or enterprise wechat
- How to mine keywords and improve user experience before website construction?
- 实战监听Eureka client的缓存更新
- GTEST死亡测试
- 值得反复回味的81句高人高语
- 谈谈 @Autowired 的实现原理
- 鸿蒙读取资源文件
猜你喜欢

Data assets are king, analyzing the relationship between enterprise digital transformation and data asset management

十多年前的入职第一天
![[paper notes] catching both gray and black swans: open set supervised analog detection*](/img/52/787b25a9818cfc6a1897af81d41ab2.png)
[paper notes] catching both gray and black swans: open set supervised analog detection*

观察者模式

What are the PCB characteristics inspection items?

开源软件、自由软件、Copyleft、CC都是啥,傻傻分不清楚?

Imperva- method of finding regular match timeout

The first day of employment more than ten years ago

Image segmentation - improved network structure

The rtsp/onvif protocol video platform easynvr startup service reports an error "service not found". How to solve it?
随机推荐
aquatone工具 中的2个bug修复
List接口三个子实现类
Structure and usage of transform
jmeter压测结果分析
GTEST死亡测试
How can easycvr access the Dahua CVS video recorder and download a video file with an empty name?
Map interface and its sub implementation classes
Go language basic conditional statement if
Regular expression use cases
Fillet the tabbar with the flutter
Observer mode
ThreadPoolExecutor线程池实现原理与源码解析
Deep learning ----- different methods to realize vgg16
Vulnhub | dc: 3 | [actual combat]
Deep learning ----- convolution (conv2d) bottom layer
MySQL common skills
Vulnhub | DC: 3 |【实战】
论文阅读【Quo Vadis, Action Recognition? A New Model and the Kinetics Dataset】
PHP serialization and deserialization CTF
Sequence table Curriculum