diff --git a/Layers/Components/LayerBackground.swift b/Layers/Components/LayerBackground.swift index efd84a8..e4b3271 100644 --- a/Layers/Components/LayerBackground.swift +++ b/Layers/Components/LayerBackground.swift @@ -22,9 +22,3 @@ struct LayerBackground: View { } } } - -#Preview { - LayerBackground { - LayerPlayground() - } -} diff --git a/Layers/Components/LayerButton.swift b/Layers/Components/LayerButton.swift index 3ba6753..2ca5fe4 100644 --- a/Layers/Components/LayerButton.swift +++ b/Layers/Components/LayerButton.swift @@ -104,7 +104,7 @@ struct LayerButton: View { .transition(.scale(scale: 1.0)) .matchedGeometryEffect( id: "layer.button.text.\(id)", - in: LayerConstants.Animations.Namespaces.namespace + in: Constants.Animations.Namespaces.namespace ) .foregroundColor(color.isDark ? Color.white : Color.black) @@ -145,7 +145,7 @@ struct LayerButton: View { } .matchedGeometryEffect( id: "layer.button.select.\(id)", - in: LayerConstants.Animations.Namespaces.namespace + in: Constants.Animations.Namespaces.namespace ) if selected { @@ -166,7 +166,7 @@ struct LayerButton: View { ) .matchedGeometryEffect( id: "layer.button.select.icon.\(id)", - in: LayerConstants.Animations.Namespaces.namespace + in: Constants.Animations.Namespaces.namespace ) } } diff --git a/Layers/Components/LayerStack.swift b/Layers/Components/LayerStack.swift index 21d2c48..f339928 100644 --- a/Layers/Components/LayerStack.swift +++ b/Layers/Components/LayerStack.swift @@ -23,21 +23,21 @@ struct LayerStack: View { VStack(alignment: .leading, spacing: 24) { content } - .padding(32) - .background( - Color(.layerBackground) - .matchedGeometryEffect( - id: LayerConstants.Animations.Namespaces.background, - in: LayerConstants.Animations.Namespaces.namespace - ) - ) - .mask { - RoundedRectangle(cornerRadius: 32, style: .continuous) - .matchedGeometryEffect( - id: LayerConstants.Animations.Namespaces.mask, - in: LayerConstants.Animations.Namespaces.namespace - ) - } + .padding(32) + .background( + Color(Constants.Colors.background) + .matchedGeometryEffect( + id: Constants.Animations.Namespaces.background, + in: Constants.Animations.Namespaces.namespace + ) + ) + .mask { + RoundedRectangle(cornerRadius: 32, style: .continuous) + .matchedGeometryEffect( + id: Constants.Animations.Namespaces.mask, + in: Constants.Animations.Namespaces.namespace + ) + } } } .layerPadding() @@ -49,9 +49,7 @@ struct LayerStack: View { Color(.black.opacity(0.25)).ignoresSafeArea() LayerStack { - VStack { - Text("Hello, World!") - } + VStack {} } } } diff --git a/Layers/Components/LayerText.swift b/Layers/Components/LayerText.swift index a9a88b2..510de44 100644 --- a/Layers/Components/LayerText.swift +++ b/Layers/Components/LayerText.swift @@ -32,8 +32,8 @@ struct LayerTitle: View { Text(title).layerTitleStyle() } .matchedGeometryEffect( - id: LayerConstants.Animations.Namespaces.title, - in: LayerConstants.Animations.Namespaces.namespace + id: Constants.Animations.Namespaces.title, + in: Constants.Animations.Namespaces.namespace ) .transition(.opacity.combined(with: .scale(scale: 1.0))) } @@ -47,8 +47,8 @@ struct LayerDescription: View { Text(description).layerDescriptionStyle() } .matchedGeometryEffect( - id: LayerConstants.Animations.Namespaces.description, - in: LayerConstants.Animations.Namespaces.namespace + id: Constants.Animations.Namespaces.description, + in: Constants.Animations.Namespaces.namespace ) .transition(.opacity.combined(with: .scale(scale: 1.0))) } diff --git a/Layers/ContentView.swift b/Layers/ContentView.swift index 390b4a9..02da23c 100644 --- a/Layers/ContentView.swift +++ b/Layers/ContentView.swift @@ -8,86 +8,84 @@ import SwiftUI struct ContentView: View { + @Namespace private var namespace + + @State var index = 0 + + let headers: [String] = [ + "Heading 1", + "Heading 2", + "Heading 3", + "Heading 4" + ] + + let descriptions: [String] = [ + """ + Ad elit do deserunt elit Lorem consectetur ipsum cillum labore id. + """, + """ + Consequat cillum amet sint elit ut ut exercitation. + """, + """ + Excepteur nostrud anim mollit magna sunt aliqua aliqua do esse. + """, + """ + Eu laborum consequat laboris eiusmod dolor. + """ + ] + + let contents: [LayerContent] = [ + LayerContent(), + LayerContent(), + LayerContent { + LayerImagePicker() + }, + LayerContent() + ] + var body: some View { - @Namespace private var namespace - - @State var index = 0 - - let headers: [String] = [ - "Heading 1", - "Heading 2", - "Heading 3", - "Heading 4" - ] - - let descriptions: [String] = [ - """ - Ad elit do deserunt elit Lorem consectetur ipsum cillum labore id. - """, - """ - Consequat cillum amet sint elit ut ut exercitation. - """, - """ - Excepteur nostrud anim mollit magna sunt aliqua aliqua do esse. - """, - """ - Eu laborum consequat laboris eiusmod dolor. - """ - ] - - let contents: [LayerContent] = [ - LayerContent(), - LayerContent(), - LayerContent { - LayerImagePicker() - }, - LayerContent() - ] - - var body: some View { - LayerStack { - HStack { - LayerTitle(title: headers[index]).id(headers[index]) + LayerStack { + HStack { + LayerTitle(title: headers[index]).id(headers[index]) - LayerIcon(icon: "xmark") - } + LayerIcon(icon: "xmark") + } - LayerDescription(description: descriptions[index]).id(descriptions[index]) + LayerDescription(description: descriptions[index]).id(descriptions[index]) - ForEach(contents.indices) { position in - if position == self.index { - self.contents[index] - .matchedGeometryEffect( - id: LayerConstants.Animations.Namespaces.content + String(index), - in: namespace - ) - .transition( - .asymmetric( - insertion: - .opacity.combined(with: .scale(scale: 0.9)), + ForEach(contents.indices) { position in + if position == self.index { + self.contents[index] + .matchedGeometryEffect( + id: Constants.Animations.Namespaces.content + String(index), + in: namespace + ) + .transition( + .asymmetric( + insertion: + .opacity.combined(with: .scale(scale: 0.9)), - removal: - .opacity.combined(with: .scale(scale: 1.0)) - ) + removal: + .opacity.combined(with: .scale(scale: 1.0)) ) - } + ) } + } - LayerActions { - LayerButton( - id: "cancel", - text: "Cancel", - color: Color(.systemOrange) - ) + LayerActions { + LayerButton( + id: "cancel", + text: "Cancel", + color: Color(.systemOrange) + ) - LayerButton( - id: "continue", - text: "Continue", - color: Color(.systemBlue) - ) { - withAnimation(.snappy) { - index = (index + 1) % 4 - } + LayerButton( + id: "continue", + text: "Continue", + color: Color(.systemBlue) + ) { + withAnimation(.snappy) { + index = (index + 1) % 4 } } } diff --git a/Layers/Utilities/Layer+Constants.swift b/Layers/Utilities/Layer+Constants.swift index 7d2d25c..3616583 100644 --- a/Layers/Utilities/Layer+Constants.swift +++ b/Layers/Utilities/Layer+Constants.swift @@ -7,7 +7,19 @@ import SwiftUI -public enum LayerConstants { +public enum Constants { + enum Luminance { + static let red: CGFloat = 0.2126 + static let green: CGFloat = 0.7152 + static let blue: CGFloat = 0.0722 + static let threshold: CGFloat = 0.7 + } + + enum Scale { + static let pressed: CGFloat = 0.85 + static let brightness: CGFloat = 0.85 + } + enum Styling { static let padding: EdgeInsets = .init(top: 0, leading: 16, bottom: 0, trailing: 16) } @@ -30,4 +42,8 @@ public enum LayerConstants { ) } } + + enum Colors { + static let background: Color = .init(.systemBackground) + } } diff --git a/Layers/Utilities/Layer+Extensions.swift b/Layers/Utilities/Layer+Extensions.swift index 3414d47..bcde7cc 100644 --- a/Layers/Utilities/Layer+Extensions.swift +++ b/Layers/Utilities/Layer+Extensions.swift @@ -7,10 +7,17 @@ import SwiftUI +struct Plain: ViewModifier { + func body(content: Content) -> some View { + content + .transition(.scale(scale: 1.0)) + } +} + struct LayerPadding: ViewModifier { func body(content: Content) -> some View { content - .padding(LayerConstants.Styling.padding) + .padding(Constants.Styling.padding) } } @@ -29,7 +36,48 @@ struct LayerDescriptionStyle: ViewModifier { } } +struct ScaleButtonStyle: ButtonStyle { + public init() {} + + public func makeBody(configuration: Self.Configuration) -> some View { + configuration.label + .scaleEffect(configuration.isPressed ? Constants.Scale.pressed : 1) + // .brightness(configuration.isPressed ? Constants.Scale.brightness : 0) + .animation(.easeOut, value: configuration.isPressed) + } +} + +extension ButtonStyle where Self == ScaleButtonStyle { + static var scale: ScaleButtonStyle { + ScaleButtonStyle() + } +} + +extension Color { + var isDark: Bool { + var red: CGFloat = 0 + var green: CGFloat = 0 + var blue: CGFloat = 0 + + guard UIColor(self).getRed(&red, green: &green, blue: &blue, alpha: nil) + else { + return false + } + + let luminance = + Constants.Luminance.red * red + + Constants.Luminance.green * green + + Constants.Luminance.blue * blue + + return luminance < Constants.Luminance.threshold + } +} + extension View { + func plainTransition() -> some View { + modifier(Plain()) + } + func layerPadding() -> some View { modifier(LayerPadding()) }