第四篇 swiftUI Landmarks 绘画

453 阅读6分钟

绘制路径和形状

每当用户访问列表中的地标时,都会收到徽章。当然,用户要获得徽章,您需要创建一个。本教程将您完成通过组合路径和形状来创建徽章的过程,然后用另一个代表位置的形状叠加。

如果您想为不同类型的地标创建多个徽章,请尝试使用叠加符号,改变重复的量,或更改各种角度和比例。 起始工程

第一节 为徽章视图创建绘图数据

要创建徽章,您需要定义为徽章背景绘制六边形形状的数据。

第一步 创建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视图以旋转和显示徽章符号的副本。

image.png

完整的360°旋转分为八个部分,通过重复山符号来创建太阳般的图案。

第六步 为了保持项目井然有序,在进入下一个教程之前,请将您在本教程中添加的所有新文件收集到徽章组中。