commit 4ac732bf11917b9fd2771618f38b5fdcb959581a
parent 094088193bf1ed6c09511d07dbf59c02a47134b2
Author: Marc Stibane <marc@taler.net>
Date: Mon, 29 Jul 2024 06:14:36 +0200
GradientBorder
Diffstat:
2 files changed, 125 insertions(+), 13 deletions(-)
diff --git a/TalerWallet.xcodeproj/project.pbxproj b/TalerWallet.xcodeproj/project.pbxproj
@@ -149,6 +149,8 @@
4E578E942A4822D500F21F1C /* P2pPayURIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E578E932A4822D500F21F1C /* P2pPayURIView.swift */; };
4E5A88F52A38A4FD00072618 /* QRCodeDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E5A88F42A38A4FD00072618 /* QRCodeDetailView.swift */; };
4E5A88F72A3B9E5B00072618 /* WithdrawAcceptDone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E5A88F62A3B9E5B00072618 /* WithdrawAcceptDone.swift */; };
+ 4E5D2C8B2C574CB0003F7A49 /* GradientBorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E5D2C8A2C574CB0003F7A49 /* GradientBorder.swift */; };
+ 4E5D2C8C2C574CB0003F7A49 /* GradientBorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E5D2C8A2C574CB0003F7A49 /* GradientBorder.swift */; };
4E605D902AA8B407002FB9A7 /* Nunito-Black.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 4E605D8E2AA8B407002FB9A7 /* Nunito-Black.ttf */; };
4E605D912AA8B407002FB9A7 /* Nunito-Black.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 4E605D8E2AA8B407002FB9A7 /* Nunito-Black.ttf */; };
4E605D922AA8B407002FB9A7 /* Nunito-BlackItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 4E605D8F2AA8B407002FB9A7 /* Nunito-BlackItalic.ttf */; };
@@ -377,6 +379,7 @@
4E578E932A4822D500F21F1C /* P2pPayURIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = P2pPayURIView.swift; sourceTree = "<group>"; };
4E5A88F42A38A4FD00072618 /* QRCodeDetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QRCodeDetailView.swift; sourceTree = "<group>"; };
4E5A88F62A3B9E5B00072618 /* WithdrawAcceptDone.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WithdrawAcceptDone.swift; sourceTree = "<group>"; };
+ 4E5D2C8A2C574CB0003F7A49 /* GradientBorder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GradientBorder.swift; sourceTree = "<group>"; };
4E605D8E2AA8B407002FB9A7 /* Nunito-Black.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Nunito-Black.ttf"; sourceTree = "<group>"; };
4E605D8F2AA8B407002FB9A7 /* Nunito-BlackItalic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Nunito-BlackItalic.ttf"; sourceTree = "<group>"; };
4E605DAE2AADDD13002FB9A7 /* UIScreen+screenSize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIScreen+screenSize.swift"; sourceTree = "<group>"; };
@@ -832,27 +835,28 @@
isa = PBXGroup;
children = (
4E97968F2A3765ED006F73BC /* AgePicker.swift */,
+ 4EEC118C2B83DE4700146CFF /* AmountInputV.swift */,
+ 4EB095492989CBFE0043A8A1 /* AmountRowV.swift */,
+ 4E2D8DD42B45822A00234039 /* AmountV.swift */,
4E605DB92AB05FB6002FB9A7 /* BarGraph.swift */,
4EB095472989CBFE0043A8A1 /* Buttons.swift */,
4EF840A62A0B85F400EE0D47 /* CopyShare.swift */,
- 4ECB62812A0BB01D004ABBB7 /* SelectDays.swift */,
- 4EA551242A2C923600FEC9A8 /* CurrencyInputView.swift */,
- 4EEC118C2B83DE4700146CFF /* AmountInputV.swift */,
- 4EEC11922B83FB7A00146CFF /* SubjectInputV.swift */,
- 4E2D8DD42B45822A00234039 /* AmountV.swift */,
4E53A33629F50B7B00830EC2 /* CurrencyField.swift */,
- 4EEC157229F8242800D46A03 /* QRGeneratorView.swift */,
- 4E5A88F42A38A4FD00072618 /* QRCodeDetailView.swift */,
- 4E6EDD862A363D8D0031D520 /* ListStyle.swift */,
+ 4EA551242A2C923600FEC9A8 /* CurrencyInputView.swift */,
+ 4E5D2C8A2C574CB0003F7A49 /* GradientBorder.swift */,
4E0A71172C3AB099002485BB /* IconBadge.swift */,
+ 4EB095432989CBFE0043A8A1 /* LaunchAnimationView.swift */,
+ 4E6EDD862A363D8D0031D520 /* ListStyle.swift */,
+ 4EB0954A2989CBFE0043A8A1 /* LoadingView.swift */,
+ 4E5A88F42A38A4FD00072618 /* QRCodeDetailView.swift */,
+ 4EEC157229F8242800D46A03 /* QRGeneratorView.swift */,
+ 4ECB62812A0BB01D004ABBB7 /* SelectDays.swift */,
4E983C282ADBDD3500FA9CC5 /* SingleAxisGeometryReader.swift */,
- 4E983C2B2ADC416800FA9CC5 /* View+fitsSideBySide.swift */,
+ 4EEC11922B83FB7A00146CFF /* SubjectInputV.swift */,
4EB095482989CBFE0043A8A1 /* TextFieldAlert.swift */,
- 4EBA82AA2A3EB2CA00E5F39A /* TransactionButton.swift */,
- 4EB095492989CBFE0043A8A1 /* AmountRowV.swift */,
- 4EB0954A2989CBFE0043A8A1 /* LoadingView.swift */,
- 4EB095432989CBFE0043A8A1 /* LaunchAnimationView.swift */,
4EFA395F2AA7946B00742548 /* ToSButtonView.swift */,
+ 4EBA82AA2A3EB2CA00E5F39A /* TransactionButton.swift */,
+ 4E983C2B2ADC416800FA9CC5 /* View+fitsSideBySide.swift */,
);
path = HelperViews;
sourceTree = "<group>";
@@ -1187,6 +1191,7 @@
4E3EAE292A990778009F1BE8 /* WalletBackendError.swift in Sources */,
4E3EAE2A2A990778009F1BE8 /* PendingRowView.swift in Sources */,
4E3EAE2B2A990778009F1BE8 /* LoadingView.swift in Sources */,
+ 4E5D2C8B2C574CB0003F7A49 /* GradientBorder.swift in Sources */,
4E3EAE8C2AA0933C009F1BE8 /* Font+Taler.swift in Sources */,
4E3327BA2AD1635100BF5AD6 /* AsyncSemaphore.swift in Sources */,
4ED80E8E2B8F6212008BD576 /* QuickDataTask.swift in Sources */,
@@ -1315,6 +1320,7 @@
4EB095212989CBCB0043A8A1 /* WalletBackendError.swift in Sources */,
4EB0955E2989CBFE0043A8A1 /* PendingRowView.swift in Sources */,
4EB0956D2989CBFE0043A8A1 /* LoadingView.swift in Sources */,
+ 4E5D2C8C2C574CB0003F7A49 /* GradientBorder.swift in Sources */,
4E3EAE8D2AA0933C009F1BE8 /* Font+Taler.swift in Sources */,
4E3327BB2AD1635100BF5AD6 /* AsyncSemaphore.swift in Sources */,
4ED80E8F2B8F6212008BD576 /* QuickDataTask.swift in Sources */,
diff --git a/TalerWallet1/Views/HelperViews/GradientBorder.swift b/TalerWallet1/Views/HelperViews/GradientBorder.swift
@@ -0,0 +1,106 @@
+/* MIT License
+ * Copyright (c) 2024 Sucodee
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+/**
+ * @author Marc Stibane
+ */
+import SwiftUI
+
+// Use radius: 0 for rect instead of rounded rect
+struct GradientBorder<Content: View>: View {
+ let size: CGFloat
+ let radius: CGFloat
+ let lineWidth: CGFloat
+ let color: Color
+ let background: Color
+ var content: () -> Content
+
+ @State var rotation: CGFloat = 0
+
+ var body: some View {
+ ZStack {
+ Group {
+ if radius < 2 {
+ Rectangle()
+ } else {
+ RoundedRectangle(cornerRadius: radius, style: .continuous)
+ }
+ }
+ .frame(width: size, height: size).foregroundStyle(background)
+ .shadow(color: background.opacity(0.5), radius: 10, x: 0, y: 10)
+ let gradient = Gradient(colors: [
+ color.opacity(0.01),
+ color,
+ color,
+ color.opacity(0.01)]
+ )
+ let rotatingRect = Rectangle()
+ .frame(width: size*2, height: size/2)
+ .foregroundStyle(LinearGradient(gradient: gradient, startPoint: .top, endPoint: .bottom))
+ .rotationEffect(.degrees(rotation))
+ let border = lineWidth - 0.5
+ if radius < 2 {
+ rotatingRect
+ .mask {
+ Rectangle()
+ .stroke(lineWidth: lineWidth)
+ .frame(width: size - border, height: size - border)
+ }
+ } else {
+ rotatingRect
+ .mask {
+ RoundedRectangle(cornerRadius: radius - lineWidth/2, style: .continuous)
+ .stroke(lineWidth: lineWidth)
+ .frame(width: size - border, height: size - border)
+ }
+ }
+ content()
+ }
+ .onAppear {
+ withAnimation(.linear(duration: 4).repeatForever(autoreverses: false)) {
+ rotation = 360
+ }
+ }
+ }
+}
+extension GradientBorder {
+ init(size: CGFloat, radius: CGFloat = 20.0, lineWidth: CGFloat = 4.0, color: Color, background: Color, content: @escaping () -> Content) {
+ self.size = size
+ self.radius = radius
+ self.lineWidth = lineWidth
+ self.color = color
+ self.background = background
+ self.content = content
+ }
+}
+// MARK: -
+struct GradientBorder_Previews: PreviewProvider {
+ static var previews: some View {
+
+ GradientBorder(size: 260,
+// radius: 1,
+// lineWidth: 2,
+ color: .blue,
+ background: .yellow) {
+ Text("Preview")
+ }
+ }
+}