taler-ios

iOS apps for GNU Taler (wallet)
Log | Files | Refs | README | LICENSE

TabBarView.swift (5749B)


      1 /*
      2  * This file is part of GNU Taler, ©2022-25 Taler Systems S.A.
      3  * See LICENSE.md
      4  */
      5 /**
      6  * @author Marc Stibane
      7  */
      8 import SwiftUI
      9 import SymLog
     10 
     11 struct TabBarView: View {
     12     private let symLog = SymLogV(0)
     13     @Binding var selection: Tab
     14     @Binding var userAction: Int // must be held in main view; used to make Action button jump to indicate it can be dragged
     15     @Binding var hidden: Int
     16     let onActionTab: () -> Void
     17     let onActionDrag: () -> Void
     18 
     19     @Environment(\.keyboardShowing) var keyboardShowing
     20 
     21     @AppStorage("minimalistic") var minimalistic: Bool = false
     22     @AppStorage("tapped") var tapped: Int = 0
     23     @AppStorage("dragged") var dragged: Int = 0
     24 
     25     @State private var offset = CGSize.zero
     26     @State private var didDrag = false
     27 
     28     private func tabBarItem(for tab: Tab) -> some View {
     29         VStack(spacing: 0) {
     30             let isActions = (tab == .actions)
     31             let withText = isActions ? tapped < TAPPED
     32                                      : tapped < TAPPED || !minimalistic
     33             if isActions {
     34                 let width = withText ? 48 : 72.0
     35                 let height = withText ? 36 : 57.6
     36                 tab.image
     37                     .resizable()
     38                     .scaledToFill()
     39                     .frame(width: width, height: height)
     40                     .clipped() // Crop the image to the frame size
     41                     .padding(.bottom, 4)
     42                     .offset(offset)
     43 //                    .opacity(1 - Double(abs(offset.height / 35)))
     44                     .gesture(
     45                         DragGesture(minimumDistance: 10)
     46                             .onChanged { gesture in
     47                                 var trans = gesture.translation
     48                                 trans.width = .zero
     49                                 if abs(trans.height) > 30 {
     50                                     symLog.log(".onChanged: didDrag \(trans.height)")
     51                                     offset = .zero
     52                                     didDrag = true
     53                                     onActionDrag()    // switch to camera
     54                                     if tapped >= TAPPED {
     55                                         dragged += 1
     56                                     }
     57                                 } else {
     58                                     symLog.log(".onChanged: \(trans.height)")
     59                                     offset = trans
     60                                 }
     61                             }
     62                             .onEnded { gesture in
     63                                 var trans = gesture.translation
     64                                 if didDrag {
     65                                     symLog.log(".onEnded: didDrag \(trans.height)")
     66                                     didDrag = false
     67                                 } else {
     68                                     symLog.log(".onActionTab: \(trans.height)")
     69                                     onActionTab()
     70                                 }
     71                                 offset = .zero
     72                             }
     73                     )
     74             } else {
     75                 let size = withText ? 24.0 : 36.0
     76                 tab.image
     77                     .resizable()
     78                     .renderingMode(.template)
     79                     .tint(.black)
     80                     .aspectRatio(contentMode: .fit)
     81                     .frame(width: size, height: size)
     82             }
     83             if withText {
     84                 if selection == tab {
     85                     Text(tab.title)
     86                         .bold()
     87                         .lineLimit(1)
     88                         .talerFont(.picker)
     89                 } else {
     90                     Text(tab.title)
     91                         .lineLimit(1)
     92                         .talerFont(.body)
     93                 }
     94             }
     95         }.id(tab)
     96         .foregroundColor(selection == tab ? .accentColor : .secondary)
     97         .padding(.vertical, 8)
     98         .accessibilityElement(children: .combine)
     99         .accessibility(label: Text(tab.title))
    100         .accessibility(addTraits: [.isButton])
    101         .accessibility(removeTraits: [.isImage])
    102         .frame(maxWidth: .infinity)
    103         .contentShape(Rectangle())
    104     }
    105 
    106     private func userAction(_ newValue: Int) {
    107         if tapped >= TAPPED && dragged < DRAGGED {
    108             withAnimation(Animation.easeOut(duration: DRAGDURATION).delay(DRAGDELAY)) {
    109                 offset.height = -50
    110             }
    111             withAnimation(Animation.easeOut(duration: DRAGSPEED).delay(DRAGDELAY + DRAGDURATION + DRAGSPEED)) {
    112                 offset.height = 10
    113             }
    114             withAnimation(Animation.easeOut(duration: DRAGSPEED/2).delay(DRAGDELAY + DRAGDURATION + 2.5*DRAGSPEED)) {
    115                 offset.height = 0
    116             }
    117         }
    118     }
    119     var body: some View {
    120         Group {
    121             if keyboardShowing || hidden > 0 {
    122                 EmptyView()
    123             } else {
    124                 let actionTab = tabBarItem(for: Tab.actions)
    125                 let balanceTab = tabBarItem(for: Tab.balances)
    126                 let settingsTab = tabBarItem(for: Tab.settings)
    127                 HStack(alignment: .bottom) {
    128                     balanceTab.onTapGesture { selection = .balances; userAction += 1 }
    129                     actionTab.onTapGesture {
    130                         onActionTab()
    131                         tapped += 1
    132                     }
    133                     settingsTab.onTapGesture { selection = .settings; userAction += 1 }
    134                 }
    135                 .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.bottom))
    136                 .onChange(of: userAction) { newValue in
    137                     userAction(newValue)
    138                 }
    139             }
    140         }
    141     }
    142 }
    143 // MARK: -
    144 //#Preview {
    145 //    TabBarView()
    146 //}