commit c0e8da621faea384b45f194bf151b0b289ee9a5f
parent 06639e32ddaf19ba1c735ac9279c2045051ff1d4
Author: Marc Stibane <marc@taler.net>
Date: Mon, 31 Mar 2025 21:05:59 +0200
flying
Diffstat:
4 files changed, 127 insertions(+), 91 deletions(-)
diff --git a/TalerWallet1/Views/OIM/OIMView.swift b/TalerWallet1/Views/OIM/OIMView.swift
@@ -92,11 +92,15 @@ struct OIMnavBack<Content: View>: View {
content()
VStack {
HStack {
- BackButton() { dismiss() }
+ BackButton() {
+ print("dismiss")
+ dismiss()
+ }.padding(.leading, 4)
Spacer()
+ let inset = UIScreen.horzInsets
ForwardButton(enabled: !amount.isZero) {
buttonSelected = true
- }.padding(.trailing, UIScreen.horzInsets)
+ }.padding(.trailing, inset > 0 ? inset - 12 : 0)
}
}
.ignoresSafeArea(edges: .horizontal)
@@ -117,14 +121,12 @@ struct OIMView: View {
@State private var amountVal: Int = 0
@Namespace var namespace
- @State private var tappedVal: Int = 0
- @State private var isFlyingToDeck = false
+ @State private var tappedVal = 0
+ @State private var flying = 0
@State private var shake = false
var body: some View {
-#if PRINT_CHANGES || true
- let _ = Self._printChanges()
-#endif
+// let _ = Self._printChanges()
let currency = sierraLeone ? OIMleones : OIMeuros
let actions = HStack(spacing: 30) {
@@ -146,7 +148,7 @@ struct OIMView: View {
amountVal: $amountVal,
namespace: namespace,
tappedVal: tappedVal,
- isFlyingToDeck: $isFlyingToDeck,
+ flying: $flying,
shake: shake,
canEdit: false)
Spacer()
@@ -167,14 +169,12 @@ struct OIMPayView: View {
@State private var amountVal: Int = 0
@Namespace var namespace
- @State private var tappedVal: Int = 0
- @State private var isFlyingToDeck = false
+ @State private var tappedVal = 0
+ @State private var flying = 0
@State private var shake = false
var body: some View {
-#if PRINT_CHANGES || true
- let _ = Self._printChanges()
-#endif
+// let _ = Self._printChanges()
let currency = sierraLeone ? OIMleones : OIMeuros
OIMbackground(amount: amount, currStr: currency.noteBase) {
@@ -184,7 +184,7 @@ struct OIMPayView: View {
amountVal: $amountVal,
namespace: namespace,
tappedVal: tappedVal,
- isFlyingToDeck: $isFlyingToDeck,
+ flying: $flying,
shake: shake,
canEdit: true)
Spacer()
@@ -210,14 +210,12 @@ struct OIMEditView: View {
@State private var availableVal: Int = 0
@Namespace var namespace
- @State private var tappedVal: Int = 0
- @State private var isFlyingToDeck = false
+ @State private var tappedVal = 0
+ @State private var flying = 0
@State private var shake = false
var body: some View {
-#if PRINT_CHANGES || true
- let _ = Self._printChanges()
-#endif
+// let _ = Self._printChanges()
let currency = sierraLeone ? OIMleones : OIMeuros
OIMnavBack(stack: stack.push(),
@@ -226,15 +224,17 @@ struct OIMEditView: View {
amount: $amount,
buttonSelected: $buttonSelected
) {
- VStack {
+ ZStack { // without this, money would fly below the scroller
+ VStack { // even though this is the only item in the ZStack
Spacer()
OIMlineView(currency: currency,
amountVal: $amountVal,
namespace: namespace,
tappedVal: tappedVal,
- isFlyingToDeck: $isFlyingToDeck,
+ flying: $flying,
shake: shake,
canEdit: true)
+ .zIndex(1) // make notes fly from topZ
.onChange(of: amountVal) { newVal in
let currencyStr = amount.currencyStr
amount = Amount(currency: currencyStr, cent: UInt64(newVal))
@@ -246,13 +246,14 @@ struct OIMEditView: View {
amountVal: $amountVal,
namespace: namespace,
tappedVal: $tappedVal,
- isFlyingToDeck: $isFlyingToDeck,
+ flying: $flying,
shake: $shake)
.onChange(of: available) { newVal in
availableVal = intValue(newVal) - intValue(amount)
}
+ }
+ .border(.red)
}
- .border(.red)
}.task {
amountVal = intValue(amount)
availableVal = intValue(available) - intValue(amount)
@@ -270,9 +271,7 @@ struct OIMSubjectView: View {
@AppStorage("sierraLeone") var sierraLeone: Bool = false
var body: some View {
-#if PRINT_CHANGES || true
- let _ = Self._printChanges()
-#endif
+// let _ = Self._printChanges()
let currency = sierraLeone ? OIMleones : OIMeuros
OIMnavBack(stack: stack.push(),
diff --git a/TalerWallet1/Views/OIM/OIMcurrencyScroller.swift b/TalerWallet1/Views/OIM/OIMcurrencyScroller.swift
@@ -15,28 +15,34 @@ struct OIMcurrencyScroller: View {
@Binding var amountVal: Int
let namespace: Namespace.ID
@Binding var tappedVal: Int
- @Binding var isFlyingToDeck: Bool
+ @Binding var flying: Int
@Binding var shake: Bool
- func tap(value: Int, _ delay: Int = 300) {
- isFlyingToDeck = false
- tappedVal = value
+ func tap(value: Int, _ delay: Int = 250) {
+ withAnimation(.shake1) {
+ flying = 0
+ tappedVal = value
+ }
let ms = debugAnimations ? 1500 : delay
- print("tapped \(value)")
-// DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(ms)) {
+// print("tapped \(value)")
+ DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(ms)) {
// print("shake start")
-// withAnimation(.shake) {
-// shake = true
-// }
+ withAnimation(.shake1) {
+ shake = true
+ }
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(ms)) {
- print("shake end")
- withAnimation(. shake1) {
+// print("shake end")
+ withAnimation(.shake1) {
shake = false
- amountVal += value
- tappedVal = 0
+ }
+ DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(ms)) {
+ withAnimation(.basic1) {
+ amountVal += value
+ tappedVal = 0
+ }
}
}
-// }
+ }
}
var body: some View {
ScrollView(.horizontal) {
@@ -56,6 +62,7 @@ struct OIMcurrencyScroller: View {
currency: currency,
availableVal: availableVal,
canEdit: true,
+ pct: 0.0,
action: { tap(value: value) }
)
.matchedGeometryEffect(id: value, in: namespace, isSource: true)
diff --git a/TalerWallet1/Views/OIM/OIMcurrencyViews.swift b/TalerWallet1/Views/OIM/OIMcurrencyViews.swift
@@ -32,9 +32,7 @@ struct OIMnoteV: View {
let action: () -> Void
var body: some View {
-#if PRINT_CHANGES || true
- let _ = Self._printChanges()
-#endif
+// let _ = Self._printChanges()
// Use EmptyView, because the modifier actually ignores
// the value passed to its body() function.
EmptyView().modifier(OIMmod(value: value,
@@ -93,9 +91,11 @@ struct OIMcoinV: View {
let currency: OIMcurrency
let availableVal: Int
let canEdit: Bool
+ var pct: CGFloat
let action: () -> Void
var body: some View {
+ let shadow = (3 - 8) * pct + 8
if let name = currency.coinName(value), let size = currency.coinSize(value) {
let image = Image(name)
.resizable()
@@ -112,6 +112,7 @@ struct OIMcoinV: View {
}
}
.frame(width: size / 4, height: size / 4)
+ .shadow(radius: shadow)
.accessibilityLabel(Text("\(value)", comment: "VoiceOver"))
} else {
EmptyView()
@@ -132,50 +133,51 @@ struct OIMsingleV: View {
if value > currency.bankCoins[0] {
OIMnoteV(value: value, currency: currency, availableVal: availableVal, canEdit: canEdit, pct: pct, action: action)
} else {
- OIMcoinV(value: value, currency: currency, availableVal: availableVal, canEdit: canEdit, action: action)
+ OIMcoinV(value: value, currency: currency, availableVal: availableVal, canEdit: canEdit, pct: pct, action: action)
}
}
}
// MARK: -
-// renders a stack of (identical) banknotes with offset 20
+// renders a stack of (identical) banknotes with offset 10,20
struct OIMnoteStackV: View {
let value: Int
let count: Int
let currency: OIMcurrency
let namespace: Namespace.ID
let tappedVal: Int
- @Binding var isFlyingToDeck: Bool
+ @Binding var flying: Int
let shake: Bool
let canEdit: Bool
let action: () -> Void
var body: some View {
-#if PRINT_CHANGES || true
- let _ = Self._printChanges()
-#endif
+// let _ = Self._printChanges()
let maxIndex = count - 1
ZStack {
ForEach(0...maxIndex, id: \.self) { index in
let match = tappedVal == value && index == maxIndex
- let id = match && !isFlyingToDeck ? value : -value
+ let isFlying = flying > 0
+ let id = match && !isFlying ? value : -value
let xOffset = CGFloat(10 * index)
let yOffset = CGFloat(20 * index)
- let shakeOffset: CGSize = shake ? .random(width: 10...40, height: 5...10)
- : .zero
- let _ = print("id \(id), flying \(isFlyingToDeck), shaking \(shakeOffset)")
+ let shakeOffset: CGFloat = shake && !match ? .random(in: 10...25)
+ : .zero
+ let direction = value < tappedVal
+// let _ = print("id \(id), flying \(flying), shaking \(shakeOffset)")
OIMnoteV(value: value,
currency: currency,
availableVal: value,
canEdit: canEdit,
pct: match ? 0.0 : 1.0,
action: action)
- .offset(x: xOffset + shakeOffset.width, y: yOffset - shakeOffset.height)
+ .offset(x: xOffset + (direction ? shakeOffset : -shakeOffset),
+ y: yOffset)
.matchedGeometryEffect(id: id, in: namespace, isSource: false)
.onAppear {
- print("start flying \(id), shaking \(shakeOffset)")
+// print("start flying \(id), shaking \(shakeOffset)")
withAnimation(.fly1) {
- isFlyingToDeck = true
+ flying = value // start flying
}
}
}
@@ -190,21 +192,46 @@ struct OIMcoinStackV: View {
let value: Int
let count: Int
let currency: OIMcurrency
+ let namespace: Namespace.ID
+ let tappedVal: Int
+ @Binding var flying: Int
+ let shake: Bool
let canEdit: Bool
let action: () -> Void
var body: some View {
- let number = count - 1
+ let maxIndex = count - 1
if let size = currency.coinSize(value) {
let offset = size / 16
ZStack {
- ForEach(0...number, id: \.self) { index in
- OIMcoinV(value: value, currency: currency, availableVal: value, canEdit: canEdit, action: action)
- .offset(x: offset * CGFloat(index), y: offset * CGFloat(index))
+ ForEach(0...maxIndex, id: \.self) { index in
+ let match = tappedVal == value && index == maxIndex
+ let isFlying = flying > 0
+ let id = match && !isFlying ? value : -value
+ let yOffset = offset * CGFloat(index)
+ let xOffset = yOffset / 2
+ let shakeOffset: CGFloat = shake && !match ? .random(in: 5...10)
+ : .zero
+ let direction = value < tappedVal
+ OIMcoinV(value: value,
+ currency: currency,
+ availableVal: value,
+ canEdit: canEdit,
+ pct: match ? 0.0 : 1.0,
+ action: action)
+ .offset(x: xOffset + (direction ? shakeOffset : -shakeOffset),
+ y: yOffset)
+ .matchedGeometryEffect(id: id, in: namespace, isSource: false)
+ .onAppear {
+// print("start flying \(id), shaking \(shakeOffset)")
+ withAnimation(.fly1) {
+ flying = value // start flying
+ }
+ }
}
}
- .padding(.trailing, offset * CGFloat(number))
- .padding(.bottom, offset * CGFloat(number))
+ .padding(.trailing, offset * CGFloat(maxIndex))
+ .padding(.bottom, offset * CGFloat(maxIndex))
}
}
}
diff --git a/TalerWallet1/Views/OIM/OIMlineViews.swift b/TalerWallet1/Views/OIM/OIMlineViews.swift
@@ -15,38 +15,33 @@ struct OIMnotesView1: View {
@Binding var amountVal: Int
let namespace: Namespace.ID
let tappedVal: Int
- @Binding var isFlyingToDeck: Bool
+ @Binding var flying: Int
let shake: Bool
let canEdit: Bool
var body: some View {
-#if PRINT_CHANGES || true
- let _ = Self._printChanges()
-#endif
+// let _ = Self._printChanges()
let nrOfNotes = currency.bankNotes.count - 1
HStack(alignment: .top, spacing: 10) {
ForEach(0...nrOfNotes, id: \.self) { index in
- let count = spread[index]
let value = currency.bankNotes[index]
- let flyingVal = tappedVal == value
- if count > 0 || flyingVal {
+ let shouldFly = tappedVal == value
+ let count = spread[index] + (shouldFly ? 1 : 0)
+ if count > 0 {
OIMnoteStackV(value: value,
- count: count + (flyingVal ? 1 : 0),
+ count: count,
currency: currency,
namespace: namespace,
tappedVal: tappedVal,
- isFlyingToDeck: $isFlyingToDeck,
+ flying: $flying,
shake: shake,
canEdit: canEdit
) {
withAnimation(.basic1) {
amountVal -= value // remove on button press
- }
- }
- .matchedGeometryEffect(id: tappedVal, in: namespace, isSource: false)
- }
- }
- }
+ } } }
+ } // ForEach
+ } // HStack
}
}
@@ -57,26 +52,33 @@ struct OIMcoinsView1: View {
let currency: OIMcurrency
@Binding var amountVal: Int
let namespace: Namespace.ID
+ let tappedVal: Int
+ @Binding var flying: Int
+ let shake: Bool
let canEdit: Bool
var body: some View {
let nrOfCoins = currency.bankCoins.count - 1
HStack(alignment: .top, spacing: 10) {
ForEach(0...nrOfCoins, id: \.self) { index in
- let count = spread[index]
+ let value = currency.bankCoins[index]
+ let shouldFly = tappedVal == value
+ let count = spread[index] + (shouldFly ? 1 : 0)
if count > 0 {
- let value = currency.bankCoins[index]
OIMcoinStackV(value: value,
count: count,
currency: currency,
- canEdit: canEdit) {
+ namespace: namespace,
+ tappedVal: tappedVal,
+ flying: $flying,
+ shake: shake,
+ canEdit: canEdit
+ ) {
withAnimation(Animation.easeIn1) {
amountVal -= value
- }
- }
- }
- }
- }
+ } } }
+ } // ForEach
+ } // HStack
}
}
@@ -89,16 +91,14 @@ struct OIMlineView: View {
@Binding var amountVal: Int
let namespace: Namespace.ID
let tappedVal: Int
- @Binding var isFlyingToDeck: Bool
+ @Binding var flying: Int
let shake: Bool
let canEdit: Bool
@AppStorage("oimTwoRows") var oimTwoRows: Bool = false
var body: some View {
-#if PRINT_CHANGES || true
- let _ = Self._printChanges()
-#endif
+// let _ = Self._printChanges()
#if DEBUG
let debug = 1==0
let red = debug ? Color.red : Color.clear
@@ -113,7 +113,7 @@ struct OIMlineView: View {
amountVal: $amountVal,
namespace: namespace,
tappedVal: tappedVal,
- isFlyingToDeck: $isFlyingToDeck,
+ flying: $flying,
shake: shake,
canEdit: canEdit
).id("notes")
@@ -125,6 +125,9 @@ struct OIMlineView: View {
currency: currency,
amountVal: $amountVal,
namespace: namespace,
+ tappedVal: tappedVal,
+ flying: $flying,
+ shake: shake,
canEdit: canEdit
).id("coins")
.matchedGeometryEffect(id: "coins", in: namespace)
@@ -176,7 +179,7 @@ struct OIMlineView: View {
}
}
} else {
- LayoutThatFits([HStackLayout(), VStackLayout()]) {
+ LayoutThatFits([HStackLayout(alignment: .top), VStackLayout()]) {
notes
#if DEBUG
.padding(1)