First batch of UI updates

The focus was on neumorphic buttons that work for both light and dark as well as state of the button (pressed)
This commit is contained in:
Lukas Korba 2022-02-21 21:17:49 +01:00
parent 23126eb97b
commit 81ccc2b283
19 changed files with 272 additions and 39 deletions

View File

@ -82,6 +82,7 @@
66A0807B271993C500118B79 /* OnboardingProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66A0807A271993C500118B79 /* OnboardingProgressIndicator.swift */; };
66D50668271D9B6100E51F0D /* NavigationButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D50667271D9B6100E51F0D /* NavigationButtonStyle.swift */; };
66DC733F271D88CC0053CBB6 /* StandardButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66DC733E271D88CC0053CBB6 /* StandardButtonStyle.swift */; };
9E4DC6E027C409A100E657F4 /* NeumorphicDesignModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4DC6DF27C409A100E657F4 /* NeumorphicDesignModifier.swift */; };
F9322DC0273B555C00C105B5 /* NavigationLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9322DBF273B555C00C105B5 /* NavigationLinks.swift */; };
F93673D62742CB840099C6AF /* Previews.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93673D52742CB840099C6AF /* Previews.swift */; };
F93874F0273C4DE200F0E875 /* HomeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93874ED273C4DE200F0E875 /* HomeStore.swift */; };
@ -209,6 +210,7 @@
66A0807A271993C500118B79 /* OnboardingProgressIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingProgressIndicator.swift; sourceTree = "<group>"; };
66D50667271D9B6100E51F0D /* NavigationButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationButtonStyle.swift; sourceTree = "<group>"; };
66DC733E271D88CC0053CBB6 /* StandardButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardButtonStyle.swift; sourceTree = "<group>"; };
9E4DC6DF27C409A100E657F4 /* NeumorphicDesignModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NeumorphicDesignModifier.swift; sourceTree = "<group>"; };
F9322DBF273B555C00C105B5 /* NavigationLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationLinks.swift; sourceTree = "<group>"; };
F93673D52742CB840099C6AF /* Previews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Previews.swift; sourceTree = "<group>"; };
F93874ED273C4DE200F0E875 /* HomeStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeStore.swift; sourceTree = "<group>"; };
@ -562,6 +564,7 @@
663FAB9F271D876200E495F8 /* PrimaryButton.swift */,
663FABA1271D876C00E495F8 /* SecondaryButton.swift */,
66D50667271D9B6100E51F0D /* NavigationButtonStyle.swift */,
9E4DC6DF27C409A100E657F4 /* NeumorphicDesignModifier.swift */,
);
path = Buttons;
sourceTree = "<group>";
@ -1000,6 +1003,7 @@
0DC487C32772574C00BE6A63 /* ValidationSucceededView.swift in Sources */,
0D8A43C4272AEEDE005A6414 /* SecantTextStyles.swift in Sources */,
0D1922F226BDE29300052649 /* ZcashSDKStubs.swift in Sources */,
9E4DC6E027C409A100E657F4 /* NeumorphicDesignModifier.swift in Sources */,
0DACFA7F27208CE00039EEA5 /* Clamped.swift in Sources */,
0DFE93E3272CA1AA000FCCA5 /* RecoveryPhraseValidation.swift in Sources */,
0D354A0B26D5A9D000315F45 /* MnemonicSeedPhraseHandling.swift in Sources */,

View File

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

View File

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

View File

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

View File

@ -5,9 +5,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.937",
"green" : "0.863",
"red" : "0.784"
"blue" : "0xFC",
"green" : "0xF8",
"red" : "0xF5"
}
},
"idiom" : "universal"
@ -23,9 +23,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.937",
"green" : "0.863",
"red" : "0.784"
"blue" : "0xD9",
"green" : "0xC0",
"red" : "0xA7"
}
},
"idiom" : "universal"

View File

@ -5,9 +5,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xD9",
"green" : "0xC0",
"red" : "0xA7"
"blue" : "0xFC",
"green" : "0xF8",
"red" : "0xF5"
}
},
"idiom" : "universal"
@ -23,9 +23,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xD9",
"green" : "0xC0",
"red" : "0xA7"
"blue" : "0xEF",
"green" : "0xDC",
"red" : "0xC8"
}
},
"idiom" : "universal"

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xFC",
"green" : "0xF8",
"red" : "0xF5"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.514",
"green" : "0.486",
"red" : "0.443"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

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

View File

@ -20,12 +20,12 @@
}
],
"color" : {
"color-space" : "srgb",
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.333",
"green" : "0.192",
"red" : "0.141"
"blue" : "0x52",
"green" : "0x31",
"red" : "0x26"
}
},
"idiom" : "universal"

View File

@ -5,9 +5,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x59",
"green" : "0x35",
"red" : "0x28"
"blue" : "0x47",
"green" : "0x37",
"red" : "0x2D"
}
},
"idiom" : "universal"
@ -23,9 +23,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
"blue" : "0x00",
"green" : "0x00",
"red" : "0x00"
}
},
"idiom" : "universal"

View File

@ -44,9 +44,12 @@ internal enum Asset {
}
internal enum Buttons {
internal static let activeButton = ColorAsset(name: "ActiveButton")
internal static let buttonsTitleShadow = ColorAsset(name: "ButtonsTitleShadow")
internal static let createButton = ColorAsset(name: "CreateButton")
internal static let createButtonDisabled = ColorAsset(name: "CreateButtonDisabled")
internal static let createButtonPressed = ColorAsset(name: "CreateButtonPressed")
internal static let neumorphicDarkSide = ColorAsset(name: "NeumorphicDarkSide")
internal static let neumorphicLightSide = ColorAsset(name: "NeumorphicLightSide")
internal static let onboardingNavigation = ColorAsset(name: "OnboardingNavigation")
internal static let onboardingNavigationPressed = ColorAsset(name: "OnboardingNavigationPressed")
internal static let primaryButton = ColorAsset(name: "PrimaryButton")
@ -56,7 +59,8 @@ internal enum Asset {
internal static let secondaryButtonPressed = ColorAsset(name: "SecondaryButtonPressed")
}
internal enum Onboarding {
internal static let circularFrame = ColorAsset(name: "CircularFrame")
internal static let circularFrameGradientEnd = ColorAsset(name: "CircularFrameGradientEnd")
internal static let circularFrameGradientStart = ColorAsset(name: "CircularFrameGradientStart")
internal static let navigationButtonDisabled = ColorAsset(name: "NavigationButtonDisabled")
internal static let navigationButtonEnabled = ColorAsset(name: "NavigationButtonEnabled")
}

View File

@ -41,7 +41,7 @@ struct OnboardingContentView: View {
}
)
)
.frame(width: width * 0.85, height: width * 0.85)
.frame(width: width * 0.82, height: width * 0.82)
.badgeIcons(
store.actionless.scope(
state: { state in
@ -61,16 +61,15 @@ struct OnboardingContentView: View {
VStack(spacing: viewStore.isFinalStep ? 50 : 15) {
HStack {
Text(viewStore.steps[stepIndex].title)
.font(.custom(FontFamily.Roboto.bold.name, size: 30))
.fontWeight(.regular)
.titleText()
if !viewStore.isFinalStep {
Spacer()
}
}
Text(viewStore.steps[stepIndex].description)
.font(.custom(FontFamily.Roboto.regular.name, size: 15))
.lineSpacing(5)
.bodyText()
.opacity(0.53)
}
.opacity(stepIndex == viewStore.index ? 1: 0)
.padding(.horizontal, 35)
@ -85,7 +84,7 @@ struct OnboardingContentView: View {
struct OnboardingContentView_Previews: PreviewProvider {
static var previews: some View {
let store = Store(
initialState: OnboardingState(index: 2),
initialState: OnboardingState(index: 0),
reducer: OnboardingReducer.default,
environment: ()
)
@ -114,8 +113,9 @@ struct OnboardingContentView_Previews: PreviewProvider {
width: proxy.size.width,
height: proxy.size.height
)
.preferredColorScheme(.light)
}
}
.applyScreenBackground()
.preferredColorScheme(.light)
}
}

View File

@ -35,18 +35,18 @@ struct OnboardingFooterView: View {
}
}
.primaryButtonStyle
.frame(height: proxy.size.height / 12)
.padding(.horizontal, 15)
.frame(height: 69)
.padding(.horizontal, 28)
.transition(.opacity)
}
ProgressView(
"\(viewStore.index + 1)/\(viewStore.steps.count)",
"0\(viewStore.index + 1)",
value: Double(viewStore.index + 1),
total: Double(viewStore.steps.count)
)
.onboardingProgressStyle
.padding(.horizontal, 30)
.padding(.horizontal, 28)
.padding([.vertical], 20)
}
}
@ -64,10 +64,17 @@ struct OnboardingFooterView_Previews: PreviewProvider {
Group {
OnboardingFooterView(store: store)
.applyScreenBackground()
.preferredColorScheme(.light)
.previewDevice("iPhone 13 Pro Max")
OnboardingFooterView(store: store)
.applyScreenBackground()
.preferredColorScheme(.dark)
.previewDevice("iPhone 13 Pro Max")
OnboardingFooterView(store: store)
.applyScreenBackground()
.preferredColorScheme(.dark)
.previewDevice("iPhone 13 mini")
}

View File

@ -8,6 +8,8 @@
import SwiftUI
struct NavigationButtonStyle: ButtonStyle {
@Environment(\.colorScheme) var colorScheme
func makeBody(configuration: Configuration) -> some View {
configuration.label
.frame(
@ -23,6 +25,7 @@ struct NavigationButtonStyle: ButtonStyle {
Asset.Colors.Buttons.onboardingNavigation.color
)
.cornerRadius(.infinity)
.neumorphicDesign(configuration.isPressed)
}
}
@ -39,12 +42,14 @@ struct NavigationModifier_Previews: PreviewProvider {
Button("Back") { dump("Example button") }
.navigationButtonStyle
.frame(width: 80, height: 40)
.applyScreenBackground()
.previewLayout(.fixed(width: 300, height: 100))
.preferredColorScheme(.dark)
Button("Skip") { dump("Example button") }
.navigationButtonStyle
.frame(width: 80, height: 40)
.applyScreenBackground()
.previewLayout(.fixed(width: 300, height: 100))
.preferredColorScheme(.light)
}

View File

@ -0,0 +1,44 @@
//
// NeumorphicDesignModifier.swift
// secant-testnet
//
// Created by Lukáš Korba on 21.02.2022.
//
import SwiftUI
/// Neumorphic design is charasterictical with two shadows (light & dark) around the button
/// Appereance in our case is influenced by two parameters:
/// - Parameters:
/// - colorScheme: The light is using full neumorphic design while dark is limited to soft shadow only
/// - isPressed: When the button is pressed, there are different behaviours for light vs. dark colorScheme
struct NeumorphicDesign: ViewModifier {
@Environment(\.colorScheme) var colorScheme
let isPressed: Bool
init(_ isPressed: Bool) {
self.isPressed = isPressed
}
func body(content: Content) -> some View {
content
.shadow(
color: Asset.Colors.Buttons.neumorphicDarkSide.color,
radius: 10,
x: colorScheme == .light && !isPressed ? 10 : 0,
y: colorScheme == .light && !isPressed ? 10 : 0
)
.shadow(
color: Asset.Colors.Buttons.neumorphicLightSide.color,
radius: 10,
x: colorScheme == .light && !isPressed ? -5 : 0,
y: colorScheme == .light && !isPressed ? -5 : 0
)
}
}
extension View {
func neumorphicDesign(_ isPressed: Bool = false) -> some View {
self.modifier(NeumorphicDesign(isPressed))
}
}

View File

@ -26,11 +26,13 @@ struct PrimaryButton_Previews: PreviewProvider {
.frame(width: 250, height: 50)
.previewLayout(.fixed(width: 300, height: 100))
.preferredColorScheme(.light)
.applyScreenBackground()
Button("Primary Button") { dump("Primary button") }
.primaryButtonStyle
.frame(width: 250, height: 50)
.previewLayout(.fixed(width: 300, height: 100))
.preferredColorScheme(.dark)
.applyScreenBackground()
}
}

View File

@ -14,6 +14,7 @@ struct StandardButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.shadow(color: Asset.Colors.Buttons.buttonsTitleShadow.color, radius: 2, x: 0, y: 2)
.frame(
minWidth: 0,
maxWidth: .infinity,
@ -25,6 +26,7 @@ struct StandardButtonStyle: ButtonStyle {
configuration.isPressed ? pressedBackgroundColor : background
)
.cornerRadius(12)
.neumorphicDesign(configuration.isPressed)
}
}

View File

@ -8,20 +8,27 @@
import SwiftUI
struct CircularFrame: View {
private let gradient = LinearGradient(
gradient: Gradient(colors: [
Asset.Colors.Onboarding.circularFrameGradientStart.color, Asset.Colors.Onboarding.circularFrameGradientEnd.color
]),
startPoint: .leading,
endPoint: .trailing
)
var body: some View {
GeometryReader { proxy in
let lineWidth = proxy.size.width * 0.05
Circle()
.stroke(lineWidth: lineWidth)
.foregroundColor(Asset.Colors.Onboarding.circularFrame.color)
// Add two points to the frame to properly mask edges
.stroke(gradient, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round))
// Add two points to the frame to properly mask edges
.frame(
width: proxy.size.width - lineWidth + 2,
height: proxy.size.height - lineWidth + 2,
alignment: .center
)
// Update the offset to account for the 2 extra points
// Update the offset to account for the 2 extra points
.offset(x: lineWidth / 2 - 1, y: lineWidth / 2 - 1)
.shadow(radius: 10)
}