绘制路径和形状
每当用户访问列表中的地标时,都会收到徽章。当然,用户要获得徽章,您需要创建一个。本教程将您完成通过组合路径和形状来创建徽章的过程,然后用另一个代表位置的形状叠加。
如果您想为不同类型的地标创建多个徽章,请尝试使用叠加符号,改变重复的量,或更改各种角度和比例。 起始工程
第一节 为徽章视图创建绘图数据
要创建徽章,您需要定义为徽章背景绘制六边形形状的数据。
第一步 创建HexgonParameters.swift
在导航窗格中选择视图组后,选择文件>新建>文件,从iOS模板表中选择Swift文件,然后单击下一步,命名HexgonParameters。 您将使用此结构来定义六边形的形状
第二步 定义Segment结构体
定义一个Segment结构来保存代表六边形一侧的三个点;导入CoreGraphics,以便您可以使用CGPoint。
每边从前一端开始,以直线移动到第一个点,然后在拐角处的贝塞尔曲线移动到第二个点。第三点控制曲线的形状。
struct Segment {
let line: CGPoint
let curve: CGPoint
let control: CGPoint
}
第三步 创建一个数组来保存段。
添加六个段的数据,六边形每边一个。
这些值存储为单位正方形的分数,其起源在左上角,正x在右边,正y向下。稍后,您将使用这些分数来查找给定大小的六边形的实际点。
添加一个调整值,允许您调整六边形的形状。
struct HexagonParameters {
struct Segment {
let line: CGPoint
let curve: CGPoint
let control: CGPoint
}
static let adjustment: CGFloat = 0.085
static let segments = [
Segment(
line: CGPoint(x: 0.60, y: 0.05),
curve: CGPoint(x: 0.40, y: 0.05),
control: CGPoint(x: 0.50, y: 0.00)
),
Segment(
line: CGPoint(x: 0.05, y: 0.20 + adjustment),
curve: CGPoint(x: 0.00, y: 0.30 + adjustment),
control: CGPoint(x: 0.00, y: 0.25 + adjustment)
),
Segment(
line: CGPoint(x: 0.00, y: 0.70 - adjustment),
curve: CGPoint(x: 0.05, y: 0.80 - adjustment),
control: CGPoint(x: 0.00, y: 0.75 - adjustment)
),
Segment(
line: CGPoint(x: 0.40, y: 0.95),
curve: CGPoint(x: 0.60, y: 0.95),
control: CGPoint(x: 0.50, y: 1.00)
),
Segment(
line: CGPoint(x: 0.95, y: 0.80 - adjustment),
curve: CGPoint(x: 1.00, y: 0.70 - adjustment),
control: CGPoint(x: 1.00, y: 0.75 - adjustment)
),
Segment(
line: CGPoint(x: 1.00, y: 0.30 + adjustment),
curve: CGPoint(x: 0.95, y: 0.20 + adjustment),
control: CGPoint(x: 1.00, y: 0.25 + adjustment)
)
]
}
第二节 绘制徽章背景
使用SwiftUI中的图形API绘制自定义徽章形状。
第一步 创建BadgeBackground.swift
使用文件>新建>文件创建另一个新文件,这次从iOS模板表中选择SwiftUI视图。单击下一步,然后将文件命名为BadgeBackground.swift。
在body中添加一个路径,使用fill来填充。
您可以使用路径组合线条、曲线和其他绘图原语,以形成更复杂的形状,如徽章的六边形背景。
第二步 绘制起点
假设一个大小为100 x 100 px的容器,在路径中添加一个起点。
move(to:)方法将绘图光标移动到形状的边界内,就像一根假想的钢笔或铅笔悬停在该区域上,等待开始绘图。
第三步 为形状数据的每个点绘制线条,以创建一个粗糙的六边形形状。
addLine(to:)方法取一个点并绘制它。对addLine(to:)的连续调用在上一个点开始一行,然后继续到新点。
如果你的六边形看起来有点不寻常,不要担心;那是因为你忽略了形状角落每个段的弯曲部分。接下来你会对此负责。
第四步 处理拐角和切面
使用addCurve(to:control:)方法为徽章的角落绘制贝塞尔曲线。
第五步 适应父视图
在Geometry中包装路径,以便徽章可以使用其包含视图的大小,该视图定义了大小,而不是对值(100)进行硬编码。
当包含的视图不是正方形时,使用几何两个维度中最小的一个可以保留徽章的宽高比。
第六步 x轴大小缩放
使用x在x轴上缩放形状,然后添加x以更新其几何形状。
第七步 背景渐变
将纯黑色背景替换为渐变,以匹配设计。
第八步
通过保持1:1的宽高比,徽章保持其在视图中心的位置,即使其祖先视图不是正方形。
import SwiftUI
struct BadgeBackground: View {
var body: some View {
GeometryReader { geometry in
Path { path in
var width: CGFloat = min(geometry.size.width, geometry.size.height)
let height = width
let xScale: CGFloat = 0.832
let xOffset: CGFloat = (width * (1 - xScale))/2.0
width *= xScale
path.move(
to: CGPoint(
x: width * 0.95 + xOffset,
y: height * (0.20 + HexagonParameters.adjustment)
)
)
HexagonParameters.segments.forEach { segment in
path.addLine(
to: CGPoint(
x: width * segment.line.x + xOffset,
y: height * segment.line.y
)
)
path.addQuadCurve(
to: CGPoint(
x: width * segment.curve.x + xOffset,
y: height * segment.curve.y
),
control: CGPoint(
x: width * segment.control.x + xOffset,
y: height * segment.control.y
)
)
}
}
.fill(.linearGradient(
Gradient(colors: [Self.gradientStart, Self.gradientEnd]),
startPoint: UnitPoint(x: 0.5, y: 0),
endPoint: UnitPoint(x: 0.5, y: 0.6)
))
.aspectRatio(1, contentMode: .fit)
}
}
static let gradientStart = Color(red: 239.0 / 255, green: 120.0 / 255, blue: 221.0 / 255)
static let gradientEnd = Color(red: 239.0 / 255, green: 172.0 / 255, blue: 120.0 / 255)
}
struct BadgeBackground_Previews: PreviewProvider {
static var previews: some View {
BadgeBackground()
}
}
第三节 绘制徽章符号
地标徽章的中心有一个自定义徽章,该徽章基于地标应用程序图标中显示的山。
山符号由两种形状组成:一种代表山顶的雪帽,另一种代表沿途的植被。您将使用两个由小缝隙分开的部分三角形形状来绘制它们。
首先,您将为您的应用程序提供一个图标,以建立徽章的外观。
第一步 替换AppIcon
从项目的资产目录中删除空的App项目,然后将AppIcon.appiconset文件夹从下载的项目的Resources文件夹拖到资产目录中。
Xcode将该文件夹识别为包含应用程序图标的所有大小变体,并在目录中创建相应的项目。
第二步 冲压的山地形状
为徽章设计中以旋转图案冲压的山地形状创建一个名为BadgeSymbol的新自定义视图。
import SwiftUI
struct BadgeSymbol: View {
static let symbolColor = Color(red: 79.0 / 255, green: 79.0 / 255, blue: 191.0 / 255)
var body: some View {
GeometryReader { geometry in
Path { path in
let width = min(geometry.size.width, geometry.size.height)
let height = width * 0.75
let spacing = width * 0.030
let middle = width * 0.5
let topWidth = width * 0.226
let topHeight = height * 0.488
path.addLines([
CGPoint(x: middle, y: spacing),
CGPoint(x: middle - topWidth, y: topHeight - spacing),
CGPoint(x: middle, y: topHeight / 2 + spacing),
CGPoint(x: middle + topWidth, y: topHeight - spacing),
CGPoint(x: middle, y: spacing)
])
path.move(to: CGPoint(x: middle, y: topHeight / 2 + spacing * 3))
path.addLines([
CGPoint(x: middle - topWidth, y: topHeight + spacing),
CGPoint(x: spacing, y: height - spacing),
CGPoint(x: width - spacing, y: height - spacing),
CGPoint(x: middle + topWidth, y: topHeight + spacing),
CGPoint(x: middle, y: topHeight / 2 + spacing * 3)
])
}
.fill(Self.symbolColor)
}
}
}
struct BadgeSymbol_Previews: PreviewProvider {
static var previews: some View {
BadgeSymbol()
}
}
第三步 旋转符号
创建一个新的RotatedBadgeSymbol视图,以封装旋转符号的概念。
import SwiftUI
struct RotatedBadgeSymbol: View {
let angle: Angle
var body: some View {
BadgeSymbol()
.padding(-60)
.rotationEffect(angle, anchor: .bottom)
}
}
struct RotatedBadgeSymbol_Previews: PreviewProvider {
static var previews: some View {
RotatedBadgeSymbol(angle: Angle(degrees: 5))
}
}
第四节 结合徽章前景和背景
徽章设计要求在徽章背景上旋转和重复多次山的形状。
定义新的旋转类型,并利用For视图对山形状的多个副本进行相同的调整。
第一步 创建一个名为Badge的新SwiftUI视图。
第二步 将BadgeBackground放在Badge的正文中。
第三步 通过将徽章的符号放在ZStack中,将其放在徽章背景上。
第四步 通过读取周围的几何图形和缩放符号来纠正徽章符号的大小。
第五步 添加For视图以旋转和显示徽章符号的副本。
完整的360°旋转分为八个部分,通过重复山符号来创建太阳般的图案。