ios path绘制形状
Today we will try to draw custom shapes and views and try to find out more about drawings in iOS (Swift) and how that can be done through CODE.
今天,我们将尝试绘制自定义形状和视图,并尝试找到有关iOS(Swift)中图纸的更多信息,以及如何通过CODE来实现。
For all the frontend developers, one of the most important parts of the development process is the implementation of User Interface. We can create a simple UI with a combination of pre-made graphics and in-built views in the Interface Builder, but this is not enough, there are often cases when devs need to draw custom shaped views programmatically to meet UI requirements, and if we are not able to draw those, it creates a problem.
对于所有前端开发人员来说,开发过程中最重要的部分之一就是用户界面的实现。 我们可以在Interface Builder中结合预制图形和内置视图来创建一个简单的UI,但这还不够,在很多情况下,开发人员需要以编程方式绘制自定义形状的视图来满足UI要求,如果我们无法绘制这些图形,这会产生问题。
This is where UIBezierPath comes in, it’s Apple’s way of helping devs draw on-screen programmatically. It may seem complex at first but it is quite simple to draw custom shapes.
这就是UIBezierPath出现的地方,这是Apple帮助开发人员以编程方式在屏幕上绘制的方式。 乍一看似乎很复杂,但是绘制自定义形状非常简单。
To make it more familiar, we will draw a custom shaped view and define it in such a way that we can use it like any inbuilt UI element.
为了使它更加熟悉,我们将绘制一个自定义形状的视图并对其进行定义,以使我们可以像使用任何内置UI元素一样使用它。
In this write-up we will touch upon the following topics:
在本文中,我们将涉及以下主题:
Analysis of Custom View
自定义视图分析
UIBezierPath class
UIBezierPath类
Drawing view programmatically
以编程方式绘制视图
Use it in your storyboard/code
在故事板/代码中使用它
To begin with, the aim will be to make a custom Card View that will have rounded corners and a button slot in the bottom-center
首先,我们的目标是制作一个自定义的Card View,该View的圆角和位于底部中心的按钮槽
![Image for post](https://miro.medium.com/max/9999/1*4Tqn9dhF9TZCSIO9SmCWmQ.png)
Note that here we are also going to draw round edges programmatically, which can be normally done using cornerRadius
property of CAShapeLayer for almost any view. We do this to make concepts clearer by working on common properties, which will be interesting and will give a peek into how Apple might be doing things under the hood!
请注意,这里我们还将以编程方式绘制圆形边缘,通常可以使用CAShapeLayer的cornerRadius
属性对几乎所有视图进行绘制。 我们这样做的目的是通过研究通用属性来使概念更清晰,这将很有趣,并且可以窥视苹果公司如何在后台进行操作!
When drawing custom views, it is always beneficial to first analyze and divide it into sections that we can easily draw as individual components and later merge them to create a complete view. Once this is done only then should we dive into coding the view. So we will do the same for our custom Card View.
绘制自定义视图时,首先将其分析并将其划分为易于作为单个组件轻松绘制的部分,然后将它们合并以创建完整的视图始终是有益的。 一旦完成此操作,我们就应该开始对视图进行编码了。 因此,我们将对自定义卡片视图执行相同的操作。
分析自定义视图 (Analyzing custom view)
Before analyzing the view it is important that we understand how the views are drawn on the screen. Consider the screen to be a Cartesian Plane with origin — (0, 0) at the top-left corner of the screen. Along with —
在分析视图之前,重要的是我们了解如何在屏幕上绘制视图。 将屏幕视为笛卡尔平面,其原点为(0,0)在屏幕的左上角。 随着 -
- Positive X-axis is along the width of the screen in the right direction 正X轴沿屏幕的宽度沿正确方向
- Positive Y-axis is along the height of the screen in the downward direction Y轴正值沿屏幕高度在向下方向上
- Negative X-axis is along the left direction from the left edge of the screen 负X轴沿屏幕左边缘的左侧方向
- Negative Y-axis is in the upward direction from the top edge of the screen 负Y轴位于屏幕顶部边缘的上方
So for our analysis, we will consider the same with origin — (0, 0) at the top-left corner of the view frame, and positive x in the right direction along the width of the view and positive y in the downward direction along with the height of the view.
因此,对于我们的分析,我们将考虑原点-(0,0)在视图框架的左上角,在视图的右侧沿宽度方向为正x,在视图的向下沿宽度为正y与视图的高度。
![Image for post](https://miro.medium.com/max/9999/1*kgElIu3O2nKrx9_J67Ns6w.png)
Card View — Width = N, Height = H
卡片视图-宽度= N,高度= H
Button Slot — Width = N/2, Height = 30
按钮插槽—宽度= N / 2,高度= 30
Thus the rectangular frame of our custom UI is from (0,0) to (N, H)
因此,我们的自定义用户界面的矩形框是从(0,0)到(N,H)
Broadly there are three types of sections in the UI drawing
UI绘图中大致分为三种类型的部分
1. Corner Arcs2. Button Slot Arcs3. Straight Lines
1.角弧2。 按钮槽弧3。 直线
We will now traverse the view starting from the top-right corner and move in the clockwise direction and investigate various sections and define their origin and length.
现在,我们将从右上角开始遍历该视图,并沿顺时针方向移动,并研究各个部分并定义其原点和长度。
Topline — from (30, 0) to (N-30, 0)
顶线 -从( 30,0 )到(N-30,0)
Top-right arc — the origin of arc at (N-30, 30) and radius 30
右上角圆弧 -在(N-30,30)和半径30处的圆弧原点
Right line — from (N, 30) to (N, H-30–30)
右行 -从(N,30)到(N,H-30–30)
Bottom-right arc — the origin of arc at (N-30, H-30–30) and radius 30
右下角的弧线 -在(N-30,H-30–30)和半径30处的弧线原点
Bottom-right line — from (N-30, H-30) to (3N/4, H-30)
右下角 -从(N-30,H-30)到(3N / 4,H-30)
Right-buttonSlot arc — the origin of arc at (3N/4–30, H-30) and radius 30
右按钮槽弧 —在(3N / 4–30,H-30)和半径30处的弧的原点
Bottom-buttonSlot line — from (3N/4 –30, H) to (N/4+30, H)
底部按钮插槽线 -从(3N / 4 –30,H)到(N / 4 + 30,H)
Left-buttonSlot arc — the origin of arc at (N/4+30, H-30) and radius 30
左键槽弧 -在(N / 4 + 30,H-30)和半径30处的弧的原点
Bottom-left line — from (N/4, H-30) to (30, H-30)
左下线 -从(N / 4,H-30)到(30,H-30)
Bottom-left arc — the origin of arc at (30, H-30–30) and radius 30
左下圆弧 -在(30,H-30–30)和半径30处的圆弧原点
Left line — from (0, H-30–30) to (0, 30)
左线 -从(0,H-30–30)到(0,30)
Top-left arc — the origin of arc at (30, 30) and radius 30
左上圆弧 -(30,30)和半径30处的圆弧原点
From the above traversal, it is clear that we can divide our card view into 12 individual sections and then combine them to form the complete view.We can do this by using UIBezierPath, where we can create 12 subpaths and later combine them to form the complete CardView.
通过以上遍历,很明显我们可以将卡片视图分为12个单独的部分,然后将它们组合以形成完整的视图。我们可以使用UIBezierPath做到这一点,我们可以创建12个子路径,然后将它们组合以形成完整的CardView。
Thus it is time to take a deeper look into UIBezierPath
因此,现在该深入了解UIBezierPath
UIBezierPath (UIBezierPath)
Before we start to code a pre-requisite that needs to be addressed is UIBezierPath class. Apple says-
在开始编写代码之前,需要解决的先决条件是UIBezierPath类。 苹果说-
A
UIBezierPath
object is a wrapper for aCGPathRef
data type. Paths are vector-based shapes that are built using line and curve segments. You can use line segments to create rectangles and polygons, and you can use curve segments to create arcs, circles, and complex curved shapes.
UIBezierPath
对象是CGPathRef
数据类型的包装。 路径是使用线段和曲线段构建的基于矢量的形状。 您可以使用线段创建矩形和多边形,也可以使用曲线段创建圆弧,圆形和复杂的曲线形状。
This is what we need to create vector-based paths. With this class, we can define custom paths that describe any shape, and use those paths to achieve any custom result we want — simple or complex.
这就是我们创建基于矢量的路径所需要的。 通过此类,我们可以定义描述任何形状的自定义路径,并使用这些路径来实现我们想要的任何自定义结果-简单或复杂。
Two different ways this can be done are —
可以通过两种不同的方式来完成此操作-
Using the shape initializers that let us draw pre-defined shapes —
init(rect:), init(ovalIn:)
etc使用形状初始值设定项让我们绘制预定义的形状
init(rect:), init(ovalIn:)
等Using methods to add paths of different geometry as subpaths —
addLine(to:), addArc(_:), append(:)
etc使用方法将不同几何形状的路径添加为子路径
addLine(to:), addArc(_:), append(:)
等
Note — A bezier path cannot stand on its own, it needs a Core Graphics context where it can be rendered. Context can be passed on in different ways. Here we will pass context by subclassing our custom view from UIView.
注意—贝塞尔曲线路径不能独立存在,它需要可在其中呈现的Core Graphics上下文。 上下文可以以不同的方式传递。 在这里,我们将通过从UIView继承自定义视图的子类来传递上下文。
是时候学习代码了 (Time to dive into code)
Since we now have the required knowledge about the UIBezierPath and have done some fair analysis of our card view we are now ready to dive into code and start defining our new custom view. For this
由于我们现在已经具备有关UIBezierPath的必需知识,并且已经对卡片视图进行了一些公平的分析,因此我们现在准备深入研究代码并开始定义新的自定义视图。 为了这
- Start a new Xcode project. 启动一个新的Xcode项目。
- Define the custom class CardView that will inherit from UIView in ViewController.swift as in the below code snippet. 定义自定义类CardView,该类将从ViewController.swift中的UIView继承,如下面的代码片段所示。
Draw a custom shape for the CardView by overriding the
draw(_:)
method of the UIView class.通过覆盖UIView类的
draw(_:)
方法为CardView绘制自定义形状。In the
draw(_:)
method, create a UIBezierPath and add subpaths to it one by one in the same order as discussed in the above analysis.在
draw(_:)
方法中,创建一个UIBezierPath并以与上述分析中讨论的顺序相同的顺序逐一添加子路径。
///
/// Custom card view class
///
class CardView : UIView
{
// init the view with a rectangular frame
override init(frame: CGRect)
{
super.init(frame: frame)
backgroundColor = UIColor.clear
} // init the view by deserialisation
required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
backgroundColor = UIColor.clear
} /// override the draw(_:) to draw your own view
///
/// Default implementation - `rectangular view`
///
override func draw(_ rect: CGRect)
{
// Card view corner radius
let cardRadius = CGFloat(30)
// Button slot arc radius
let buttonSlotRadius = CGFloat(30)
// Card view frame dimensions
let viewSize = self.bounds.size
// Effective height of the view
let effectiveViewHeight = viewSize.height - buttonSlotRadius // Get a path to define and traverse
let path = UIBezierPath() // Shift origin to left corner of top straight line
path.move(to: CGPoint(x: cardRadius, y: 0))
// top line
path.addLine(to: CGPoint(x: viewSize.width - cardRadius, y: 0)) // top-right corner arc
path.addArc(
withCenter: CGPoint(
x: viewSize.width - cardRadius,
y: cardRadius
),
radius: cardRadius,
startAngle: CGFloat(Double.pi * 3 / 2),
endAngle: CGFloat(0),
clockwise: true
) // right line
path.addLine(
to: CGPoint(x: viewSize.width, y: effectiveViewHeight)
)
// bottom-right corner arc
path.addArc(
withCenter: CGPoint(
x: viewSize.width - cardRadius,
y: effectiveViewHeight - cardRadius
),
radius: cardRadius,
startAngle: CGFloat(0),
endAngle: CGFloat(Double.pi / 2),
clockwise: true
) // right half of bottom line
path.addLine(
to: CGPoint(x: viewSize.width / 4 * 3, y: effectiveViewHeight)
) // button-slot right arc
path.addArc(
withCenter: CGPoint(
x: viewSize.width / 4 * 3 - buttonSlotRadius,
y: effectiveViewHeight
),
radius: buttonSlotRadius,
startAngle: CGFloat(0),
endAngle: CGFloat(Double.pi / 2),
clockwise: true
)
// button-slot line
path.addLine(
to: CGPoint(
x: viewSize.width / 4 + buttonSlotRadius,
y: effectiveViewHeight + buttonSlotRadius
)
) // button left arc
path.addArc(
withCenter: CGPoint(
x: viewSize.width / 4 + buttonSlotRadius,
y: effectiveViewHeight
),
radius: buttonSlotRadius,
startAngle: CGFloat(Double.pi / 2),
endAngle: CGFloat(Double.pi),
clockwise: true
) // left half of bottom line
path.addLine(
to: CGPoint(x: cardRadius, y: effectiveViewHeight)
) // bottom-left corner arc
path.addArc(
withCenter: CGPoint(
x: cardRadius,
y: effectiveViewHeight - cardRadius
),
radius: cardRadius,
startAngle: CGFloat(Double.pi / 2),
endAngle: CGFloat(Double.pi),
clockwise: true
) // left line
path.addLine(to: CGPoint(x: 0, y: cardRadius)) // top-left corner arc
path.addArc(
withCenter: CGPoint(x: cardRadius, y: cardRadius),
radius: cardRadius,
startAngle: CGFloat(Double.pi),
endAngle: CGFloat(Double.pi / 2 * 3),
clockwise: true
)
// close path join to origin
path.close() // Set the background color of the view
UIColor.gray.set()
path.fill()
}
}
In the above code snippet, we have used —
在上述代码段中,我们使用了-
path.move(to:) — to move to a new position without drawing on the canvas, takes in — a destination point.
path.move(to :) —移至新位置而不在画布上绘图,则进入—目标点。
path.addLine(to:) — to draw a straight line, takes in — a destination point.
path.addLine(to :) -绘制一条直线,并接受-一个目的地点。
path.addArc(:) — to draw an arc, takes in — a center for the arc, a radius of the arc, a start angle in radians(π), an end angle in radians(π) and a direction of translation(clockwise/anti-clockwise).
path.addArc(:) —绘制弧,取入—弧的中心 ,弧的半径 ,以弧度(π)为单位的起始角度,以弧度(π)为单位的终止角度和平移方向 (顺时针/逆时针)。
path.close() — to join the current position to the point of origin and close the figure by drawing a straight line(by default).
path.close() —将当前位置连接到原点,并通过绘制一条直线(默认情况下)来关闭图形。
Since now we have successfully defined our custom view class — CardView, it is now time to use it.
从现在开始,我们已经成功定义了自定义视图类CardCard,现在该使用它了。
使用自定义视图类 (Using the custom view class)
To use our custom view, for this we will now move to storyboard —
要使用我们的自定义视图,为此,我们现在转到情节提要-
- Open Main.storyboard 打开主板
- Remove the default NavigationView and UITableViewController and add a new UIViewController to the canvas. 删除默认的NavigationView和UITableViewController并将新的UIViewController添加到画布。
Make it the
Root view
by clicking on theIs initial view controller
in Attribute Inspector.通过单击“属性检查
Is initial view controller
中的“Is initial view controller
,使其成为“Root view
。
![Image for post](https://miro.medium.com/max/9999/1*LZk04UcA77oTk7L727FH_Q.png)
Once that is done we can start adding UI elements into our UIViewController.
一旦完成,我们就可以开始将UI元素添加到我们的UIViewController中。
Start by adding a new view — UIView to the canvas and rename it to
cardView
首先添加一个新视图— UIView到画布,然后将其重命名为
cardView
- Set the constraints for the UIView as — 将UIView的约束设置为—
1. Safe Area.trailing = cardView.trailing + 16 //Trailing constraint
2. cardView.centerY = Safe Area.centerY //Vertical constraint
3. cardView.leading = Safe Area.leading + 16 //Leading constraint
4. cardView.height = 0.5 × height //Height constraint
![Image for post](https://miro.medium.com/max/9999/1*OPMVz9TxlDw8IFvHswde2Q.png)
Now we have our view ready and setup
现在我们准备好视图并进行设置
In the property inspector on the right panel add the class of our UIView to CardView.
在右侧面板的属性检查器中,将UIView的类添加到CardView。
![Image for post](https://miro.medium.com/max/9999/1*_x_OTLdlYn_i4qv0Okz19w.png)
With this, the storyboard setup is done.
这样,情节提要设置就完成了。
We are Done! And that is all we had to do to make our custom view show on the screen. Now run your app and see our custom view. Similar to this —
我们完了! 这就是我们要做的,以便在屏幕上显示我们的自定义视图。 现在运行您的应用程序并查看我们的自定义视图。 与此类似-
![Image for post](https://miro.medium.com/max/9999/1*dvqX7GfmB7z2Oa5DIM8reA.png)
在卡片视图中添加按钮 (Adding a button to the card view)
To add a UIButton to the view
向视图添加UIButton
- Open Main.storyboard file and add a UIButton to the card view 打开Main.storyboard文件,然后向卡视图添加UIButton
- Set up the constraints for the button as — 将按钮的约束设置为-
1. bottom = Button.bottom + 5 //Bottom constraint
2. Button.centerX = centerX //Horizontal constrain
3. Button.width = 0.47 × width //button width constraint
4. height = 50 //button height constraint
Change the button appearance by changing the background color/tint color
通过更改背景色/淡色来更改按钮外观
![Image for post](https://miro.medium.com/max/9999/1*5VThW9sXOLFDCqGrpaxEUQ.png)
To make the corners of the button curve, create an IBOutlet of the button.
要绘制按钮曲线的角,请创建按钮的IBOutlet。
@IBOutlet private weak var button: UIButton!
and add the following line to viewDidLoad()
in the ViewController class.
并将以下行添加到ViewController类的viewDidLoad()
中。
button.layer.cornerRadius = button.bounds.height / 2
This will curve the corner with a radius equal to half the height.
这将使拐角弯曲的半径等于高度的一半。
Run the app, we will have our desired view.
运行该应用程序,我们将获得所需的视图。
![Image for post](https://miro.medium.com/max/9999/1*eQaPEg68Dq0FtmtnaBJ38w.png)
So our custom Card View is now ready and it looks awesome! We can use it anywhere we want and play around with it to see what more can be done.
因此,我们的自定义Card View现已准备就绪,看起来很棒! 我们可以在任何需要的地方使用它,并尝试使用它,看看还有什么可以做的。
There is so much more to cover, as this is a huge topic but for the time being thank you for following along, I have tried to keep this as informational as possible.
涉及的内容太多,因为这是一个巨大的话题,但是暂时感谢您的关注,我已尽力使此信息尽可能多。
Suggestions to look for further study and enhancement-
寻求进一步研究和改进的建议-
Modify the CardView class to contain open properties that can be used to customize appearance such as —
cornerRadius, backgroundColor, buttonSlotHeight, buttonPadding etc.
修改CardView类以包含可用于自定义外观的开放属性,例如—
cornerRadius, backgroundColor, buttonSlotHeight, buttonPadding etc.
- Add the CardView programmatically into the view hierarchy as a subview. 以编程方式将CardView作为子视图添加到视图层次结构中。
For reading references — I found this very informational and helpful — https://developer.apple.com/library/archive/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/BezierPaths/BezierPaths.html
为了阅读参考资料-我发现这非常有用,也很有帮助-https: //developer.apple.com/library/archive/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/BezierPaths/BezierPaths.html
翻译自: https://medium.com/@ayusinghi96/draw-custom-shapes-and-views-with-uiberzierpath-ios-1737f5cb975
ios path绘制形状