当前位置:网站首页>6-shining laser application of calayer
6-shining laser application of calayer
2022-06-23 08:24:00 【the Summer Palace】
## Start
Recently, a business card scanning function needs to be implemented in the project , This will use an interface that allows users to wait for business card recognition results . The interface is very simple , Let a green laser constantly move up and down the business card , Simulate how the scanner is working .
It's so simple UI, It's quite possible to put aside the art and cut pictures , For one person Core Grahics To achieve ! And this time we have to test our previous learning achievements !
draw 4 Corner
First, we are at the window 4 Draw a right angle figure on each corner , The viewfinder used to represent the lens . First draw the first corner . Just draw the first corner , Other 3 An angle is nothing more than a certain angle of rotation on its basis .
First declare the variable :
var borderColor: UIColor = .white // 4 The color of the corners
var borderWidth: CGFloat = 3.5 // 4 Width of corners
lazy private var cornerLayer: CALayer = {
// 1
let cornerLayer = CALayer()
// 2
cornerLayer.frame = bounds
// 3
cornerLayer.position = center
// 4
cornerLayer.addSublayer(leftTopCornerLayer())
// 5
return cornerLayer
}()
cornerLayer Used to draw 4 Right angle , It is a lazy load property , This means that when you first access this property , Use the code in the code block to initialize its value . Here we are not going to draw(_ The function draws , Because this figure is static , Nothing will change from beginning to end , We draw it in a CALayer On , Then add it to the view .
About CALayer, You can simply think of it as UIView, It has and UIView The same tree structure as the view tree , But you can't add anything to it UIKit Components , You can only add other CALayer, such as CAShapeLayer.
Now let's take a closer look at this lazy load to property :
- Initialize a CALayer.
- Set it up frame Same size as the view hosting it .
- Align it to the center of the view .
- Add the first subLayer. This subLayer Draw on 4 The first of the three corners .leftTopCornerLayer Method is responsible for creating the subLayer.
- Go back to this CALayer, As cornerLayer The initial value of the .
Then come to see leftTopCornerLayer function :
private func leftTopCornerLayer() -> CALayer {
// 1
let cornerLayer = CAShapeLayer()
cornerLayer.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
cornerLayer.lineWidth = borderWidth
cornerLayer.strokeColor = borderColor.cgColor
cornerLayer.fillColor = UIColor.clear.cgColor
// 2
var points: [CGPoint] = []
points.append(CGPoint(x: 36, y: 4))
points.append(CGPoint(x: 4, y: 4))
points.append(CGPoint(x: 4, y: 36))
// 3
let cornerPath = LinePainter.polyLines(points)
// 4
cornerPath.lineCapStyle = .round
cornerPath.lineJoinStyle = .round
// 5
cornerLayer.path = cornerPath.cgPath
return cornerLayer
}
- Construct a CAShapeLayer, It is CALayer Subclasses of , You can use the Bezier curve in CAShapeLayer Draw various geometric figures on the . meanwhile , Set its line width 、stokeColor Equal attribute , In this way, these attributes can be used when drawing graphics .
- Because you want to draw line segments , We need to construct vertices on the polyline .
- Use the LinePainter Path is generated .
- lineCapStyle Properties and lineJoinStyle Attribute is used to set the style of curve ends and curve bends respectively . The rounded corners here will look better .
- Assign a path to CAShapeLayer Of path, Complete the drawing of the graph .
Draw the remaining right angles . It's just a rotation based on the above figure , It's easy to understand , There is no need for too much explanation :
private func rightTopCornerLayer() -> CALayer {
let cornerLayer = leftTopCornerLayer()
// 1
cornerLayer.position = CGPoint(x: frame.maxX-20, y: 20)
// 2
cornerLayer.transform = CATransform3DRotate(CATransform3DIdentity, -CGFloat.pi/2, 0, 0, -1.0)
return cornerLayer
}
private func leftBottomoCornerLayer() -> CALayer {
let cornerLayer = leftTopCornerLayer()
cornerLayer.position = CGPoint(x: 20, y: frame.maxY-20)
cornerLayer.transform = CATransform3DRotate(CATransform3DIdentity, CGFloat.pi/2, 0, 0, -1.0)
return cornerLayer
}
private func rightBottomoCornerLayer() -> CALayer {
let cornerLayer = leftTopCornerLayer()
cornerLayer.position = CGPoint(x: frame.maxX-20, y: frame.maxY-20)
cornerLayer.transform = CATransform3DRotate(CATransform3DIdentity, CGFloat.pi, 0, 0, -1.0)
return cornerLayer
}
CALayer Of position and anchor In fact, they all refer to the origin of coordinates . It's just that the former is super layer The coordinate system of , and anchor It uses layer The coordinate system of ( x, y All values 0-1 Between ).
CATransform3DRotate The first parameter of the method is the original transformation matrix ( That is, the matrix before the rotation ), Here, of course, we use identity Matrix ( This matrix does not go through any 3d The original matrix of the transformation ). The first 2 The first parameter is the rotation angle , Remember it's a radian . The first 3、4、5 Parameters specify which axis to rotate on , What we do is rotate the plane (z Axis ), therefore x, y The axes are 0,z The axis is set to 1 It means to rotate clockwise ,-1 For counter clockwise rotation .
And then in cornerLayer Property :
lazy private var cornerLayer: CALayer = {
... ...
cornerLayer.addSublayer(rightTopCornerLayer())
cornerLayer.addSublayer(leftBottomoCornerLayer())
cornerLayer.addSublayer(rightBottomoCornerLayer())
return cornerLayer
}()
Constructors :
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
fileprivate func commonInit() {
layer.addSublayer(cornerLayer)
}
stay commonInit in , use layer.addSublayer() Methods will cornerLayer Add to view .
Draw laser
First draw a stationary laser . This is very simple :
var laserWidth:CGFloat = 4 // Laser thickness
var laserColor: UIColor = .green // Laser color
var gap: CGFloat = 16 // The distance between the laser and the frame
var laserLayerHeight:CGFloat = 20 // laserLayer Height , Leave enough room for shadows
// shadow
lazy var laserShadow: NSShadow! = {
let shadow = NSShadow()
shadow.shadowColor = UIColor(red: 0, green: 1, blue: 1, alpha: 1)
shadow.shadowOffset = CGSize(width: 0, height: 0)
shadow.shadowBlurRadius = 4
return shadow
}()
lazy private var laserLayer: CAShapeLayer = {
let lineLayer = CAShapeLayer()
lineLayer.frame = CGRect(x: 0, y: 0, width: frame.width, height: laserLayerHeight)
lineLayer.lineWidth = laserWidth
lineLayer.strokeColor = laserColor.cgColor
layer.addSublayer(lineLayer)
let points = [CGPoint(x: gap, y: laserLayerHeight/2), CGPoint(x:frame.width-gap, y: laserLayerHeight/2)]
let linePath = LinePainter.polyLines(points)
lineLayer.path = linePath.cgPath
lineLayer.shadowOffset = laserShadow.shadowOffset
lineLayer.shadowColor = (laserShadow.shadowColor as! UIColor).cgColor
lineLayer.shadowRadius = laserShadow.shadowBlurRadius
lineLayer.shadowOpacity = 1
return lineLayer
}()
Also use CAShapeLayer Draw line segments , But this time it's just a straight line , We added a little shadow .
stay commonInit() In the method , add to laserLayer Layers .
layer.addSublayer(laserLayer)
Use a timer to animate
First, you need to define some variables :
private var direction = 1 // Direction of laser movement ,1 Down ,-1 Up
private var currentY:CGFloat = 0 // Laser current y Location
var speed:CGFloat = 75 // Movement speed , Unit pixel / second
var timeInterval: CGFloat = 0.03 // clock frequency , Unit second
private var isAnimating = false // Indicates whether the laser is moving
private var jiggleColor: UIColor! // The color when the laser flashes
This time use the regular timer :
lazy private var timer: Timer = {
let timer = Timer(timeInterval: Double(timeInterval), target: self, selector: #selector(timerUpdate), userInfo: nil, repeats: true)
return timer
}()
Notice we didn't use it scheduledTimer Function to build a timer , So it doesn't automatically trigger .
When the laser works, it will have a flickering effect of alternating light and dark . We can simulate this effect by switching the laser color regularly .
The first color we have is the original green of laser ( namely laserColor). The second color is a variable jiggleColor To store .
For the sake of simplicity , We decided to reduce the brightness of the first color as the second laser color .
stay commonInit In the method , initialization jiggleColor Color :
// 1
var h:CGFloat=0, s:CGFloat=0, b:CGFloat=0, a:CGFloat=0
laserColor.getHue(&h, saturation: &s, brightness: &b, alpha: &a)
// 2
jiggleColor = UIColor(hue: h, saturation: s, brightness: 0.7, alpha: a)
- take laserColor The grayscale of 、 saturation 、 Brightness and transparency are read out separately , Save the corresponding variable .
- modify laserColor The brightness of is 70% Generate new colors , And give it to jiggleColor. As the second color of the laser .
Then the timer starts / stop it :
func startAnimation() {
if isAnimating == false {
isAnimating = true
// 1
RunLoop.current.add(timer, forMode: .common)
}
}
func stopAnimation() {
if isAnimating == true {
timer.invalidate()
isAnimating = false
}
}
- We constructed a conventional timer before ( Lazy loading form ), But it was not added to runloop. Here we do this action . Be careful ,timer.fire() Method will only trigger a timer call , It cannot be run repeatedly . We must add the timer to the current runloop In order to work properly .
Then there is the timer callback function :
@objc func timerUpdate() {
// 1
if direction == 1 && currentY > frame.height-laserLayerHeight {
direction = -1
}
// 2
if direction == -1 && currentY < 0 {
direction = 1
}
// 3
moveLaser()
}
- The laser beam moves in two directions : Up , Or down . By timer , We let the laser move up and down . We use it direction To indicate the moving direction of the laser beam . by 1 Indicates downward movement ,-1 Indicates upward movement . When the laser moves down to the bottom of the view ( Leave a certain distance ) when , Change direction , Let it move up .
- When the laser moves up to the top of the view ( Leave a gap ) when , Change direction , Let it move down .
- The realization method of real mobile laser is moveLaser() in .
Look at moveLaser() Method :
private func moveLaser() {
// 1
currentY = currentY+(speed*timeInterval)*CGFloat(direction)
// 2
let r = laserLayer.frame
laserLayer.frame = CGRect(x: r.minX, y: currentY, width: r.width, height: r.height)
// 3
if round(currentY).truncatingRemainder(dividingBy: 2) > 0 {
laserLayer.strokeColor = jiggleColor.cgColor
laserLayer.shadowOpacity = 0
}else {
laserLayer.strokeColor = laserColor.cgColor
laserLayer.shadowOpacity = 1
}
}
stay viewDidLoad In the method :
let view = LaserAnimationView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
view.backgroundColor = .lightGray
view.center = self.view.center
view.startAnimation()
The operation effect is shown below :

边栏推荐
- 5-旋转的小菊-旋转画布和定时器
- Go 数据类型篇(三)之整型及运算符
- Leetcode 173 Binary search tree iterator (2022.06.22)
- APM performance monitoring practice of jubasha app
- Code quality level 3 - readable code
- Production environment server environment setup + project release process
- Ignore overlength parameter violation
- Fillet the tabbar with the flutter
- 自组织映射神经网络(SOM)
- Map interface and its sub implementation classes
猜你喜欢

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

如何在conda虚拟环境开启jupyter-notebook

给你的win10装一个wget

Imperva- method of finding regular match timeout

Vulnhub | DC: 3 |【实战】

PCB电路板特性检查项目都有哪些?

PHP file contains -ctf

【论文笔记】Catching Both Gray and Black Swans: Open-set Supervised Anomaly Detection*

Multi-scale feature combination in target detection

渲染效果图哪家好?2022最新实测(四)
随机推荐
aquatone工具 中的2個bug修複
Tensorboard的使用
APM performance monitoring practice of jubasha app
C RichTextBox controls the maximum number of rows
Configuration asmx not accessible
黄蓉真的存在吗?
顺序表课设
Ad object of Active Directory
After reading five books, I summarized these theories of wealth freedom
Implementation of AVL tree
Capturing packets to find repeated acks and a large number of TCP retransmissions in TCP sessions -- sack (selective acknowledgement) technology
C print zoom
实战监听Eureka client的缓存更新
Captain Abu's soul torture
Go data types (II) overview of data types supported by go and Boolean types
Deep learning ----- different methods to implement lenet-5 model
论文阅读【Quo Vadis, Action Recognition? A New Model and the Kinetics Dataset】
Production environment server environment setup + project release process
typeScript的介绍与变量定义的基本类型
Ignore overlength parameter violation