Merge pull request #162 from zcash/feature/welcome-screen

Issue #61 - Zcash Logo and welcome screen
This commit is contained in:
Francisco Gindre 2022-02-18 11:10:12 -03:00 committed by GitHub
commit 3e1a8f59eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 892 additions and 7 deletions

View File

@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
0D0781C4278750E30083ACD7 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D0781C3278750E30083ACD7 /* WelcomeView.swift */; };
0D0781C9278776D20083ACD7 /* ZcashSymbol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D0781C7278776D20083ACD7 /* ZcashSymbol.swift */; };
0D185819272723FF0046B928 /* ColoredChip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D185818272723FF0046B928 /* ColoredChip.swift */; };
0D18581B272728D60046B928 /* PhraseChip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D18581A272728D60046B928 /* PhraseChip.swift */; };
0D1922F226BDE29300052649 /* ZcashSDKStubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D1922F126BDE29300052649 /* ZcashSDKStubs.swift */; };
@ -54,6 +56,7 @@
0DDB6A5127737D4A0012A410 /* ValidationFailedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DDB6A5027737D4A0012A410 /* ValidationFailedView.swift */; };
0DF2DC51272344E400FA31E2 /* EmptyChip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DF2DC50272344E400FA31E2 /* EmptyChip.swift */; };
0DF2DC5427235E3E00FA31E2 /* View+InnerShadow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DF2DC5327235E3E00FA31E2 /* View+InnerShadow.swift */; };
0DF482BA2787ADA800EB37D6 /* ConditionalModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DF482B92787ADA800EB37D6 /* ConditionalModifier.swift */; };
0DFE93DF272C6D4B000FCCA5 /* RecoveryPhraseBackupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DFE93DE272C6D4B000FCCA5 /* RecoveryPhraseBackupTests.swift */; };
0DFE93E1272C9ECB000FCCA5 /* RecoveryPhraseBackupValidationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DFE93E0272C9ECB000FCCA5 /* RecoveryPhraseBackupValidationView.swift */; };
0DFE93E3272CA1AA000FCCA5 /* RecoveryPhraseValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DFE93E2272CA1AA000FCCA5 /* RecoveryPhraseValidation.swift */; };
@ -126,6 +129,8 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
0D0781C3278750E30083ACD7 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
0D0781C7278776D20083ACD7 /* ZcashSymbol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZcashSymbol.swift; sourceTree = "<group>"; };
0D185818272723FF0046B928 /* ColoredChip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColoredChip.swift; sourceTree = "<group>"; };
0D18581A272728D60046B928 /* PhraseChip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhraseChip.swift; sourceTree = "<group>"; };
0D1922F126BDE29300052649 /* ZcashSDKStubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZcashSDKStubs.swift; sourceTree = "<group>"; };
@ -179,6 +184,7 @@
0DDB6A5027737D4A0012A410 /* ValidationFailedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidationFailedView.swift; sourceTree = "<group>"; };
0DF2DC50272344E400FA31E2 /* EmptyChip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyChip.swift; sourceTree = "<group>"; };
0DF2DC5327235E3E00FA31E2 /* View+InnerShadow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+InnerShadow.swift"; sourceTree = "<group>"; };
0DF482B92787ADA800EB37D6 /* ConditionalModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConditionalModifier.swift; sourceTree = "<group>"; };
0DFE93DE272C6D4B000FCCA5 /* RecoveryPhraseBackupTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseBackupTests.swift; sourceTree = "<group>"; };
0DFE93E0272C9ECB000FCCA5 /* RecoveryPhraseBackupValidationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseBackupValidationView.swift; sourceTree = "<group>"; };
0DFE93E2272CA1AA000FCCA5 /* RecoveryPhraseValidation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseValidation.swift; sourceTree = "<group>"; };
@ -258,6 +264,22 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
0D0781C2278750C00083ACD7 /* Welcome */ = {
isa = PBXGroup;
children = (
0D0781C3278750E30083ACD7 /* WelcomeView.swift */,
);
path = Welcome;
sourceTree = "<group>";
};
0D0781C5278776B90083ACD7 /* Shapes */ = {
isa = PBXGroup;
children = (
0D0781C7278776D20083ACD7 /* ZcashSymbol.swift */,
);
path = Shapes;
sourceTree = "<group>";
};
0D170A7426BC9B7500EB6A46 /* MockedDependencies */ = {
isa = PBXGroup;
children = (
@ -430,6 +452,7 @@
0DA13C9126C15E1900E3B610 /* UIComponents */ = {
isa = PBXGroup;
children = (
0D0781C5278776B90083ACD7 /* Shapes */,
0D8A43C2272AEEA7005A6414 /* FontStyles */,
669FDAE7272C239D007B9422 /* CircularFrame */,
0DB8AA80271DC7520035BC9D /* DesignGuide.swift */,
@ -488,6 +511,7 @@
isa = PBXGroup;
children = (
0DF2DC5327235E3E00FA31E2 /* View+InnerShadow.swift */,
0DF482B92787ADA800EB37D6 /* ConditionalModifier.swift */,
);
path = Extensions;
sourceTree = "<group>";
@ -545,6 +569,7 @@
6654C73B2715A3F000901167 /* Features */ = {
isa = PBXGroup;
children = (
0D0781C2278750C00083ACD7 /* Welcome */,
F9971A4927680DC400A2DB75 /* App */,
F93874EC273C4DE200F0E875 /* Home */,
F9971A4F27680DD000A2DB75 /* Profile */,
@ -990,6 +1015,7 @@
F93673D62742CB840099C6AF /* Previews.swift in Sources */,
0D5D16F526E24CCF00AD33D1 /* AppError.swift in Sources */,
0D18581B272728D60046B928 /* PhraseChip.swift in Sources */,
0DF482BA2787ADA800EB37D6 /* ConditionalModifier.swift in Sources */,
665C963F272C26E600BC04FB /* CircularFrameBackground.swift in Sources */,
0DB8AA81271DC7520035BC9D /* DesignGuide.swift in Sources */,
F9971A4D27680DC400A2DB75 /* App.swift in Sources */,
@ -1035,7 +1061,9 @@
F9971A6027680DF600A2DB75 /* Scan.swift in Sources */,
0DFE93E1272C9ECB000FCCA5 /* RecoveryPhraseBackupValidationView.swift in Sources */,
F9C165CB2741AB5D00592F76 /* SendView.swift in Sources */,
0D0781C4278750E30083ACD7 /* WelcomeView.swift in Sources */,
F9971A6527680DFE00A2DB75 /* Settings.swift in Sources */,
0D0781C9278776D20083ACD7 /* ZcashSymbol.swift in Sources */,
6654C7412715A47300901167 /* Onboarding.swift in Sources */,
F9C165C42740403600592F76 /* SentView.swift in Sources */,
F9971A5927680DDE00A2DB75 /* Request.swift in Sources */,
@ -1200,7 +1228,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 12;
CURRENT_PROJECT_VERSION = 14;
DEVELOPMENT_ASSET_PATHS = "\"secant/Preview Content\"";
DEVELOPMENT_TEAM = RLPRR8CPQG;
ENABLE_PREVIEWS = YES;
@ -1224,7 +1252,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 12;
CURRENT_PROJECT_VERSION = 14;
DEVELOPMENT_ASSET_PATHS = "\"secant/Preview Content\"";
DEVELOPMENT_TEAM = RLPRR8CPQG;
ENABLE_PREVIEWS = YES;

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "0.530",
"blue" : "0.278",
"green" : "0.216",
"red" : "0.176"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.745",
"green" : "0.643",
"red" : "0.576"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "0.200",
"blue" : "0x36",
"green" : "0x2C",
"red" : "0x25"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "0.200",
"blue" : "0.212",
"green" : "0.173",
"red" : "0.145"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x4E",
"green" : "0x2A",
"red" : "0x1D"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x00",
"green" : "0x00",
"red" : "0x00"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x28",
"green" : "0xB7",
"red" : "0xF4"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xBF",
"green" : "0xB0",
"red" : "0x9C"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x44",
"green" : "0x23",
"red" : "0x17"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xFC",
"green" : "0xF6",
"red" : "0xF0"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x74",
"green" : "0x50",
"red" : "0x43"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xD2",
"green" : "0xC3",
"red" : "0xB0"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "0.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x6A",
"green" : "0x3E",
"red" : "0x2D"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xBB",
"green" : "0xA4",
"red" : "0x99"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -2,6 +2,7 @@ import ComposableArchitecture
struct AppState: Equatable {
enum Route: Equatable {
case welcome
case startup
case onboarding
case home
@ -13,7 +14,7 @@ struct AppState: Equatable {
var onboardingState: OnboardingState
var phraseValidationState: RecoveryPhraseValidationState
var phraseDisplayState: RecoveryPhraseDisplayState
var route: Route = .startup
var route: Route = .welcome
}
enum AppAction: Equatable {

View File

@ -30,6 +30,7 @@ struct AppView: View {
case .startup:
ZStack(alignment: .topTrailing) {
StartupView(sendAction: viewStore.send)
.transition(.opacity)
}
case .phraseValidation:
@ -65,6 +66,16 @@ struct AppView: View {
)
)
}
case .welcome:
WelcomeView()
.transition(.opacity)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
withAnimation(.easeInOut(duration: 1)) {
viewStore.send(.updateRoute(.startup))
}
}
}
}
}
}
@ -88,8 +99,8 @@ private struct StartupView: View {
sendAction(.updateRoute(.phraseValidation))
}
Button("Go To Phrase Display Demo") {
sendAction(.updateRoute(.phraseDisplay))
Button("Go To Welcome Screen") {
sendAction(.updateRoute(.welcome))
}
}
}

View File

@ -0,0 +1,128 @@
//
// WelcomeView.swift
// secant-testnet
//
// Created by Francisco Gindre on 1/6/22.
//
import SwiftUI
struct WelcomeView: View {
let topPaddingRatio: Double = 0.18
let horizontalPaddingRatio: Double = 0.07
var body: some View {
GeometryReader { proxy in
ZStack(alignment: .top) {
VStack(alignment: .center, spacing: 80) {
let diameter = proxy.size.width - 40
ZcashBadge()
.frame(
width: diameter,
height: diameter
)
VStack {
Text("Welcome!")
.titleText()
Text("Just Loading, one sec")
.captionText()
}
}
}
.frame(alignment: .center)
.applyScreenBackground()
.animation(.easeInOut, value: 3)
}
}
}
struct ZcashBadge: View {
@Environment(\.colorScheme) var colorScheme
var body: some View {
ZStack {
GeometryReader { proxy in
let outterPadding = proxy.size.height * 0.015
let firstPadding = proxy.size.height * 0.075 + outterPadding
let secondRingPadding = firstPadding * 1.5
let outerShadowDrop = proxy.size.height * 0.14
let outerShadowOffset = proxy.size.height * 0.055
Circle()
.fill(
LinearGradient(
colors: [
Asset.Colors.ZcashBadge.outerRingGradientStart.color,
Asset.Colors.ZcashBadge.outerRingGradientEnd.color
],
startPoint: UnitPoint(x: 0.5, y: 0),
endPoint: UnitPoint(x: 0.5, y: 1)
)
)
.if(colorScheme == .light) { view in
view.shadow(
color: Asset.Colors.ZcashBadge.shadowColor.color,
radius: outerShadowDrop,
x: outerShadowOffset,
y: outerShadowOffset
)
}
Circle()
.foregroundColor(Asset.Colors.ZcashBadge.thickRing.color)
.padding(outterPadding)
Circle()
.foregroundColor(Asset.Colors.ZcashBadge.thinRing.color)
.padding(firstPadding)
Circle()
.foregroundColor(Asset.Colors.ZcashBadge.innerCircle.color)
.padding(secondRingPadding)
ZcashSymbol()
.fill(Asset.Colors.ZcashBadge.zcashLogoFill.color)
.padding(firstPadding + secondRingPadding)
}
}
}
}
struct WelcomeView_Previews: PreviewProvider {
static let squarePreviewSize: CGFloat = 360
static var previews: some View {
ZcashBadge()
.applyScreenBackground()
.previewLayout(
.fixed(
width: squarePreviewSize,
height: squarePreviewSize
)
)
.preferredColorScheme(.dark)
ZStack {
ZcashBadge()
}
.padding()
.applyScreenBackground()
.previewLayout(
.fixed(
width: squarePreviewSize,
height: squarePreviewSize
)
)
.preferredColorScheme(.light)
Group {
WelcomeView()
.preferredColorScheme(.dark)
WelcomeView()
.previewDevice("iPhone SE (2nd generation)")
}
}
}

View File

@ -84,8 +84,19 @@ internal enum Asset {
internal static let regular = ColorAsset(name: "Regular")
internal static let secondaryButtonText = ColorAsset(name: "SecondaryButtonText")
internal static let titleText = ColorAsset(name: "TitleText")
internal static let captionText = ColorAsset(name: "captionText")
internal static let captionTextShadow = ColorAsset(name: "captionTextShadow")
internal static let highlightedSuperscriptText = ColorAsset(name: "highlightedSuperscriptText")
}
internal enum ZcashBadge {
internal static let zcashLogoFill = ColorAsset(name: "ZcashLogoFill")
internal static let innerCircle = ColorAsset(name: "innerCircle")
internal static let outerRingGradientEnd = ColorAsset(name: "outerRingGradientEnd")
internal static let outerRingGradientStart = ColorAsset(name: "outerRingGradientStart")
internal static let shadowColor = ColorAsset(name: "shadowColor")
internal static let thickRing = ColorAsset(name: "thickRing")
internal static let thinRing = ColorAsset(name: "thinRing")
}
}
}
// swiftlint:enable identifier_name line_length nesting type_body_length type_name

View File

@ -21,6 +21,14 @@ struct ScreenBackground: View {
}
}
extension ScreenBackground {
static let `default` = ScreenBackground(
colors: [
Asset.Colors.ScreenBackground.gradientStart.color,
Asset.Colors.ScreenBackground.gradientEnd.color
]
)
}
struct ScreenBackgroundModifier: ViewModifier {
var colors: [Color]

View File

@ -0,0 +1,23 @@
//
// ConditionalModifier.swift
// secant-testnet
//
// Created by ANTOINE VAN DER LEE
//
// Credits: https://www.avanderlee.com/swiftui/conditional-view-modifier/
import SwiftUI
extension View {
/// Applies the given transform if the given condition evaluates to `true`.
/// - Parameters:
/// - condition: The condition to evaluate.
/// - transform: The transform to apply to the source `View`.
/// - Returns: Either the original `View` or the modified `View` if the condition is `true`.
@ViewBuilder func `if`<Content: View>(_ condition: @autoclosure () -> Bool, transform: (Self) -> Content) -> some View {
if condition() {
transform(self)
} else {
self
}
}
}

View File

@ -9,6 +9,10 @@ import Foundation
import SwiftUI
extension Text {
func captionText() -> some View {
self.modifier(CaptionTextStyle())
}
func bodyText() -> some View {
self.modifier(BodyTextStyle())
}
@ -16,6 +20,7 @@ extension Text {
func titleText() -> some View {
self.modifier(TitleTextStyle())
}
/// Body text style. Used for content. Roboto-Regular 18pt
private struct BodyTextStyle: ViewModifier {
func body(content: Content) -> some View {
@ -28,8 +33,18 @@ extension Text {
private struct TitleTextStyle: ViewModifier {
func body(content: Content) -> some View {
content
.foregroundColor(Asset.Colors.Text.body.color)
.font(.custom(FontFamily.Roboto.medium.name, size: 24))
.foregroundColor(Asset.Colors.Text.heading.color)
.font(.custom(FontFamily.Rubik.regular.name, size: 33))
.shadow(color: Asset.Colors.Text.captionTextShadow.color, radius: 1, x: 0, y: 1)
}
}
private struct CaptionTextStyle: ViewModifier {
func body(content: Content) -> some View {
content
.foregroundColor(Asset.Colors.Text.captionText.color)
.font(.custom(FontFamily.Rubik.regular.name, size: 16))
.shadow(color: Asset.Colors.Text.captionTextShadow.color, radius: 1, x: 0, y: 1)
}
}
}

View File

@ -0,0 +1,312 @@
//
// ZcashSymbol.swift
// wallet
//
// Created by Francisco Gindre on 12/30/19.
// Copyright © 2019 Francisco Gindre. All rights reserved.
//
import SwiftUI
// swiftlint:disable indentation_width
/**
_
__| |__
|_____ |
/ /
/ /
/ /
/ /
/ /___
|__ __|
|_|
*/
struct ZcashSymbol: Shape {
static let ratio: CGFloat = 0.56
func path(in rect: CGRect) -> Path {
Path { path in
let width = rect.height * Self.ratio
let origin = CGPoint(x: rect.midX - width / 2, y: rect.origin.y)
let height = rect.height
let middle = origin.x + width / 2
let endX = origin.x + width
let yOffset = height / 8
let caretWidth = yOffset
let caretheight = yOffset
let caretStartX = middle - caretWidth / 2
let caretEndX = caretStartX + caretWidth
let strokeHeight = caretheight * 1.25
path.move(
to: CGPoint(
x: origin.x,
y: caretheight
)
)
// __
path.addLine(
to: CGPoint(
x: caretStartX,
y: yOffset
)
)
// __|
path.addLine(
to: CGPoint(
x: caretStartX,
y: origin.y
)
)
/*
_
__|
*/
path.addLine(
to: CGPoint(
x: caretEndX,
y: origin.y
)
)
/*
_
__| |
*/
path.addLine(
to: CGPoint(
x: caretEndX,
y: caretheight
)
)
/*
_
__| |__
*/
path.addLine(
to: CGPoint(
x: endX,
y: caretheight
)
)
/*
_
__| |__
|
*/
path.addLine(
to: CGPoint(
x: endX,
y: caretheight + strokeHeight * 0.75
)
)
/*
_
__| |__
|_____ |
/
/
/
/
/
*/
path.addLine(
to: CGPoint(
x: caretStartX,
y: caretheight + strokeHeight + strokeHeight * 3
)
)
/*
_
__| |__
|_____ |
/
/
/
/
/____
*/
path.addLine(
to: CGPoint(
x: endX,
y: caretheight + strokeHeight + strokeHeight * 3
)
)
/*
_
__| |__
|_____ |
/
/
/
/
/____
|
*/
path.addLine(
to: CGPoint(
x: endX,
y: caretheight + strokeHeight + strokeHeight * 3 + strokeHeight
)
)
/*
_
__| |__
|_____ |
/
/
/
/
/____
___|
*/
path.addLine(
to: CGPoint(
x: caretEndX,
y: caretheight + strokeHeight + strokeHeight * 3 + strokeHeight
)
)
/*
_
__| |__
|_____ |
/
/
/
/
/____
___|
_|
*/
path.addLine(
to: CGPoint(
x: caretEndX,
y: caretheight + strokeHeight + strokeHeight * 3 + strokeHeight + caretheight
)
)
/*
_
__| |__
|_____ |
/
/
/
/
/____
___|
_|
*/
path.addLine(
to: CGPoint(
x: caretStartX,
y: caretheight + strokeHeight + strokeHeight * 3 + strokeHeight + caretheight
)
)
/*
_
__| |__
|_____ |
/
/
/
/
/____
___|
|_|
*/
path.addLine(
to: CGPoint(
x: caretStartX,
y: caretheight + strokeHeight + strokeHeight * 3 + strokeHeight
)
)
/*
_
__| |__
|
/
/
/
/
/____
|___ ___|
|_|
*/
path.addLine(
to: CGPoint(
x: origin.x,
y: caretheight + strokeHeight + strokeHeight * 3 + strokeHeight
)
)
/*
_
__| |__
|
/
/
/
/
/____
|___ ___|
|_|
*/
path.addLine(
to: CGPoint(
x: origin.x,
y: caretheight + strokeHeight + strokeHeight * 3 + strokeHeight * 0.25
)
)
/*
_
__| |__
____ |
/ /
/ /
/ /
/ /
/ /____
|___ ___|
|_|
*/
path.addLine(
to: CGPoint(
x: caretEndX,
y: caretheight + strokeHeight
)
)
/*
_
___| |__
______ |
/ /
/ /
/ /
/ /
/ /____
|___ ___|
|_|
*/
path.addLine(
to: CGPoint(
x: origin.x,
y: caretheight + strokeHeight
)
)
path.closeSubpath()
}
}
}