diff --git a/ui/BankingiOSApp/BankingiOSApp.xcodeproj/project.pbxproj b/ui/BankingiOSApp/BankingiOSApp.xcodeproj/project.pbxproj index b0049c9a..14aa2f98 100644 --- a/ui/BankingiOSApp/BankingiOSApp.xcodeproj/project.pbxproj +++ b/ui/BankingiOSApp/BankingiOSApp.xcodeproj/project.pbxproj @@ -21,6 +21,10 @@ 3608D6C624FBAB41006C93A8 /* TanGeneratorPositionMarker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3608D6C524FBAB41006C93A8 /* TanGeneratorPositionMarker.swift */; }; 3642F00A2500F5AE005186FE /* Divider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F0092500F5AE005186FE /* Divider.swift */; }; 3642F00C25010021005186FE /* UIKitActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F00B25010021005186FE /* UIKitActivityIndicator.swift */; }; + 3642F01425018BA9005186FE /* TabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F01325018BA9005186FE /* TabBarController.swift */; }; + 3642F01625018DA1005186FE /* InterceptTabClickViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F01525018DA1005186FE /* InterceptTabClickViewController.swift */; }; + 3642F0182502723A005186FE /* UIKitButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F0172502723A005186FE /* UIKitButton.swift */; }; + 3642F01A2502931F005186FE /* InstantPaymentInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F0192502931F005186FE /* InstantPaymentInfoView.swift */; }; 366744E224FC4E96002B235A /* SectionWithRightAlignedEditButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366744E124FC4E96002B235A /* SectionWithRightAlignedEditButton.swift */; }; 366FA4DA24C472A90094F009 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4D924C472A90094F009 /* Extensions.swift */; }; 366FA4DC24C479120094F009 /* BankInfoListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4DB24C479120094F009 /* BankInfoListItem.swift */; }; @@ -89,7 +93,6 @@ 36FC929C24B39A05002B12E9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36FC929B24B39A05002B12E9 /* AppDelegate.swift */; }; 36FC929E24B39A05002B12E9 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36FC929D24B39A05002B12E9 /* SceneDelegate.swift */; }; 36FC92A124B39A05002B12E9 /* BankingiOSApp.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 36FC929F24B39A05002B12E9 /* BankingiOSApp.xcdatamodeld */; }; - 36FC92A324B39A05002B12E9 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36FC92A224B39A05002B12E9 /* ContentView.swift */; }; 36FC92A524B39A07002B12E9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 36FC92A424B39A07002B12E9 /* Assets.xcassets */; }; 36FC92A824B39A07002B12E9 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 36FC92A724B39A07002B12E9 /* Preview Assets.xcassets */; }; 36FC92AB24B39A07002B12E9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 36FC92A924B39A07002B12E9 /* LaunchScreen.storyboard */; }; @@ -154,6 +157,10 @@ 3608D6C524FBAB41006C93A8 /* TanGeneratorPositionMarker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TanGeneratorPositionMarker.swift; sourceTree = ""; }; 3642F0092500F5AE005186FE /* Divider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Divider.swift; sourceTree = ""; }; 3642F00B25010021005186FE /* UIKitActivityIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitActivityIndicator.swift; sourceTree = ""; }; + 3642F01325018BA9005186FE /* TabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarController.swift; sourceTree = ""; }; + 3642F01525018DA1005186FE /* InterceptTabClickViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterceptTabClickViewController.swift; sourceTree = ""; }; + 3642F0172502723A005186FE /* UIKitButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitButton.swift; sourceTree = ""; }; + 3642F0192502931F005186FE /* InstantPaymentInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantPaymentInfoView.swift; sourceTree = ""; }; 366744E124FC4E96002B235A /* SectionWithRightAlignedEditButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionWithRightAlignedEditButton.swift; sourceTree = ""; }; 366FA4D924C472A90094F009 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; 366FA4DB24C479120094F009 /* BankInfoListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankInfoListItem.swift; sourceTree = ""; }; @@ -218,7 +225,6 @@ 36FC929B24B39A05002B12E9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 36FC929D24B39A05002B12E9 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 36FC92A024B39A05002B12E9 /* BankingiOSApp.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = BankingiOSApp.xcdatamodel; sourceTree = ""; }; - 36FC92A224B39A05002B12E9 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 36FC92A424B39A07002B12E9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 36FC92A724B39A07002B12E9 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 36FC92AA24B39A07002B12E9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; @@ -348,7 +354,8 @@ 36FC92D924B3A479002B12E9 /* ui */, 36FC929B24B39A05002B12E9 /* AppDelegate.swift */, 36FC929D24B39A05002B12E9 /* SceneDelegate.swift */, - 36FC92A224B39A05002B12E9 /* ContentView.swift */, + 3642F01325018BA9005186FE /* TabBarController.swift */, + 3642F01525018DA1005186FE /* InterceptTabClickViewController.swift */, 36FC92A424B39A07002B12E9 /* Assets.xcassets */, 36FC92A924B39A07002B12E9 /* LaunchScreen.storyboard */, 36FC92AC24B39A07002B12E9 /* Info.plist */, @@ -455,6 +462,7 @@ 36BCF88A24C0BD2D005BEC29 /* AccountTransactionsDialog.swift */, 36BE066424CDE62800CBBB68 /* AccountTransactionListItem.swift */, 36BCF88C24C1C1EA005BEC29 /* TransferMoneyDialog.swift */, + 3642F0192502931F005186FE /* InstantPaymentInfoView.swift */, 366FA4DB24C479120094F009 /* BankInfoListItem.swift */, 366FA4DF24C4924A0094F009 /* RemitteeListItem.swift */, 366FA4E124C4ED6C0094F009 /* EnterTanDialog.swift */, @@ -477,6 +485,7 @@ 366744E124FC4E96002B235A /* SectionWithRightAlignedEditButton.swift */, 3642F0092500F5AE005186FE /* Divider.swift */, 3642F00B25010021005186FE /* UIKitActivityIndicator.swift */, + 3642F0172502723A005186FE /* UIKitButton.swift */, ); path = views; sourceTree = ""; @@ -670,6 +679,7 @@ 36BE06B324CF133400CBBB68 /* JsonEncoderSerializer.swift in Sources */, 36BE06BA24D0783900CBBB68 /* FaviconFinder.swift in Sources */, 36BCF89524C31F02005BEC29 /* AppData.swift in Sources */, + 3642F01A2502931F005186FE /* InstantPaymentInfoView.swift in Sources */, 36E21EDD24DCA89100649DC8 /* TanProcedurePicker.swift in Sources */, 3608D6C624FBAB41006C93A8 /* TanGeneratorPositionMarker.swift in Sources */, 36BE065B24CA4B3500CBBB68 /* SelectBankDialog.swift in Sources */, @@ -694,11 +704,11 @@ 36BCF88B24C0BD2D005BEC29 /* AccountTransactionsDialog.swift in Sources */, 36BCF87624BF114F005BEC29 /* UrlSessionWebClient.swift in Sources */, 36BE06C424D0801A00CBBB68 /* Size.swift in Sources */, - 36FC92A324B39A05002B12E9 /* ContentView.swift in Sources */, 366FA4E624C6EBF40094F009 /* EnterTanState.swift in Sources */, 360782CF24F3D6610098FEFE /* InfoLabel.swift in Sources */, 36C4009B24D2F9E4005227AD /* IconedTitleView.swift in Sources */, 36BE065724C9E04800CBBB68 /* UIKitImageView.swift in Sources */, + 3642F01625018DA1005186FE /* InterceptTabClickViewController.swift in Sources */, 36BCF88724C0A310005BEC29 /* PreviewData.swift in Sources */, 36E21ED724DC617200649DC8 /* BankAccountSettingsDialog.swift in Sources */, 366FA4DA24C472A90094F009 /* Extensions.swift in Sources */, @@ -710,6 +720,8 @@ 36FC929E24B39A05002B12E9 /* SceneDelegate.swift in Sources */, 3607829924E148D40098FEFE /* AdaptsToKeyboard.swift in Sources */, 36E21ECF24DA0EEE00649DC8 /* IconView.swift in Sources */, + 3642F0182502723A005186FE /* UIKitButton.swift in Sources */, + 3642F01425018BA9005186FE /* TabBarController.swift in Sources */, 36BCF88524C098C8005BEC29 /* BankAccountListItem.swift in Sources */, 36FC92EF24B3BB81002B12E9 /* AddAccountDialog.swift in Sources */, 36C4009D24D3236B005227AD /* UrlUtil.swift in Sources */, diff --git a/ui/BankingiOSApp/BankingiOSApp/ContentView.swift b/ui/BankingiOSApp/BankingiOSApp/ContentView.swift deleted file mode 100644 index 6721b609..00000000 --- a/ui/BankingiOSApp/BankingiOSApp/ContentView.swift +++ /dev/null @@ -1,182 +0,0 @@ -import SwiftUI -import BankingUiSwift - - -struct ContentView: View { - - static private let OverlayTabIndex = 1 - - - @ObservedObject var data: AppData = AppData() - - @State private var previousSelectedTab: Int = 0 - - @State private var selectedTab = 0 - - private var selectedTabBinding: Binding { - Binding( - get: { self.selectedTab }, - set: { - if $0 == Self.OverlayTabIndex { - self.previousSelectedTab = self.selectedTab - self.showNewOptionsActionSheet = true - } - - self.selectedTab = $0 - }) - } - - @State private var navigationBarTitle = "" - - @State private var leadingNavigationBarItem: AnyView? = nil - @State private var trailingNavigationBarItem: AnyView? = nil - - @State private var showNewOptionsActionSheet = false - - @State private var selectedNewOption: Int? = nil - - - @Inject private var presenter: BankingPresenterSwift - - - @ViewBuilder - var body: some View { - if data.hasAtLeastOneAccountBeenAdded == false { - AccountsTab(data: data) - .hideNavigationBar() - } - else { - TabView(selection: selectedTabBinding) { - - /* First tab: Accounts */ - - AccountsTab(data: data) - .onAppear { - self.savelySetAccountsTabNavigationBar() - } - .tabItem { - VStack { - Image("accounts") - Text("Accounts") - } - } - .tag(0) - - /* Second tab: 'New' action sheet button */ - - VStack { - NavigationLink(destination: LazyView(AddAccountDialog()), tag: 1, selection: self.$selectedNewOption.didSet(self.selectedNewOptionChanged)) { - EmptyView() - } - - NavigationLink(destination: LazyView(TransferMoneyDialog()), tag: 2, selection: self.$selectedNewOption.didSet(self.selectedNewOptionChanged)) { - EmptyView() - } - - .actionSheet(isPresented: self.$showNewOptionsActionSheet, content: { - self.generateNewActionSheet() - }) - } - .onAppear { - self.resetNavigationBar() - } - .tabItem { - VStack { - Image("new") - Text("New") - } - } - .tag(Self.OverlayTabIndex) - - - /* Third tab: Settings dialog */ - - SettingsDialog(data: data) - .onAppear { - self.savelySetSettingsTabNavigationBar() - } - .tabItem { - VStack { - Image("gear.fill") - Text("Settings") - } - } - .tag(2) - - } - .showNavigationBarTitle(LocalizedStringKey(navigationBarTitle)) - .navigationBarItems(leading: leadingNavigationBarItem, trailing: trailingNavigationBarItem) - } - } - - - private func generateNewActionSheet() -> ActionSheet { - var buttons = [ActionSheet.Button]() - - if data.hasAccountsThatSupportTransferringMoney { - buttons.append(.default(Text("Show transfer money dialog")) { self.selectedNewOption = 2 }) - } - - return ActionSheet( - title: Text("New ..."), - buttons: buttons + [ - .default(Text("Add account")) { self.selectedNewOption = 1 }, - .cancel { self.showPreviousSelectedTab() } - ] - ) - } - - private func selectedNewOptionChanged(oldValue: Int?, newValue: Int?) { - if newValue == nil && oldValue != nil { - showPreviousSelectedTab() - } - } - - private func showPreviousSelectedTab() { - self.selectedTab = self.previousSelectedTab - } - - - private func savelySetAccountsTabNavigationBar() { - let leadingItem = data.hasAtLeastOneAccountBeenAdded == false ? nil : AnyView(UpdateButton { _ in - self.presenter.updateAccountsTransactionsAsync { _ in } - }) - - savelySetNavigationBar("Accounts", leadingItem) - } - - private func savelySetSettingsTabNavigationBar() { - savelySetNavigationBar("Settings", nil, nil) - } - - private func savelySetNavigationBar(_ title: String, _ leadingNavigationBarItem: AnyView? = nil, _ trailingNavigationBarItem: AnyView? = nil) { - setNavigationBar(title, leadingNavigationBarItem, trailingNavigationBarItem) - - DispatchQueue.main.async { // when pressing 'Cancel' on ActionSheet navigation bar has to be set asynchronously (why, SwiftUI?) - self.setNavigationBar(title, leadingNavigationBarItem, trailingNavigationBarItem) - } - - DispatchQueue.main.asyncAfter(deadline: .now() + 1) { // can't believe it, sometimes even DispatchQueue.main.async() doesn't work. Ok, so let's do it 1 second later again, then it works - self.setNavigationBar(title, leadingNavigationBarItem, trailingNavigationBarItem) - } - } - - private func setNavigationBar(_ title: String, _ leadingNavigationBarItem: AnyView? = nil, _ trailingNavigationBarItem: AnyView? = nil) { - // due to a SwiftUI bug this cannot be set in AccountsTab directly, so i have to do it via the indirection of navigationBarTitle property - self.navigationBarTitle = title - - self.leadingNavigationBarItem = leadingNavigationBarItem - self.trailingNavigationBarItem = trailingNavigationBarItem - } - - private func resetNavigationBar() { - self.setNavigationBar("", nil, nil) - } - -} - -struct ContentView_Previews: PreviewProvider { - static var previews: some View { - ContentView() - } -} diff --git a/ui/BankingiOSApp/BankingiOSApp/InterceptTabClickViewController.swift b/ui/BankingiOSApp/BankingiOSApp/InterceptTabClickViewController.swift new file mode 100644 index 00000000..dbdecf27 --- /dev/null +++ b/ui/BankingiOSApp/BankingiOSApp/InterceptTabClickViewController.swift @@ -0,0 +1,15 @@ +import SwiftUI + + +class InterceptTabClickViewController : UIViewController { + + var tabClicked: () -> Void = { } + + + convenience init(_ tabClicked: @escaping () -> Void) { + self.init() + + self.tabClicked = tabClicked + } + +} diff --git a/ui/BankingiOSApp/BankingiOSApp/SceneDelegate.swift b/ui/BankingiOSApp/BankingiOSApp/SceneDelegate.swift index 888473b5..f808d3cf 100644 --- a/ui/BankingiOSApp/BankingiOSApp/SceneDelegate.swift +++ b/ui/BankingiOSApp/BankingiOSApp/SceneDelegate.swift @@ -22,13 +22,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // Create the SwiftUI view and set the context as the value for the managedObjectContext environment keyPath. // Add `@Environment(\.managedObjectContext)` in the views that will need the context. - let contentView = ContentView() - .environment(\.managedObjectContext, context) - - // Use a UIHostingController as window root view controller. if let windowScene = scene as? UIWindowScene { let window = UIWindow(windowScene: windowScene) - window.rootViewController = UINavigationController(rootViewController: UIHostingController(rootView: contentView)) + window.rootViewController = UINavigationController(rootViewController: TabBarController()) self.window = window window.makeKeyAndVisible() } diff --git a/ui/BankingiOSApp/BankingiOSApp/TabBarController.swift b/ui/BankingiOSApp/BankingiOSApp/TabBarController.swift new file mode 100644 index 00000000..5a81420f --- /dev/null +++ b/ui/BankingiOSApp/BankingiOSApp/TabBarController.swift @@ -0,0 +1,112 @@ +import SwiftUI + + +class TabBarController : UITabBarController, UITabBarControllerDelegate { + + @ObservedObject var data: AppData = AppData() + + + override func viewDidLoad() { + super.viewDidLoad() + + self.delegate = self + + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + let accountsTab = buildControllerAndTabBarItem("Accounts", "accounts", AccountsTab(data: data)) + + + let newOptionsTab = InterceptTabClickViewController { self.showNewOptionsActionSheet() } + newOptionsTab.tabBarItem = buildTabBarItem("New", "new") + + + let settingsTab = buildControllerAndTabBarItem("Settings", "gear.fill", SettingsDialog(data: data)) + + + self.viewControllers = [accountsTab, newOptionsTab, settingsTab] + + if let firstViewController = viewControllers?.first { + DispatchQueue.main.async { // wait till views are created before setting their title and navigation bar items + self.setNavigationBarForViewController(firstViewController) + } + } + } + + + private func buildControllerAndTabBarItem(_ title: String, _ imageName: String, _ view: Content) -> UIViewController { + return buildControllerAndTabBarItem(title, UIImage(named: imageName), view) + } + + private func buildControllerAndTabBarItem(_ title: String, _ image: UIImage? = nil, _ view: Content) -> UIViewController { + let localizedTitle = title.localize() + + let tabController = UIHostingController(rootView: view) + tabController.title = localizedTitle + + tabController.tabBarItem = buildTabBarItem(localizedTitle, image) + + return tabController + } + + private func buildTabBarItem(_ title: String, _ imageName: String) -> UITabBarItem { + return buildTabBarItem(title.localize(), UIImage(named: imageName)) + } + + private func buildTabBarItem(_ localizedTitle: String, _ image: UIImage? = nil) -> UITabBarItem { + return UITabBarItem(title: localizedTitle, image: image, selectedImage: nil) + } + + + func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) { + setNavigationBarForViewController(viewController) + } + + func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { + if viewController.isKind(of: InterceptTabClickViewController.self) { + (viewController as! InterceptTabClickViewController).tabClicked() + + return false + } + + return true + } + + + private func setNavigationBarForViewController(_ viewController: UIViewController) { + self.title = viewController.title + + self.navigationItem.leftBarButtonItem = viewController.navigationItem.leftBarButtonItem + self.navigationItem.rightBarButtonItem = viewController.navigationItem.rightBarButtonItem + } + + + private func showNewOptionsActionSheet() { + let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) + + if data.hasAccountsThatSupportTransferringMoney { + alert.addAction(UIAlertAction(title: "Show transfer money dialog".localize(), style: .default, handler: { _ in self.showView(TransferMoneyDialog()) })) + } + + alert.addAction(UIAlertAction(title: "Add account".localize(), style: .default, handler: { _ in self.showView(AddAccountDialog()) } )) + alert.addAction(UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: nil)) + + if let popoverController = alert.popoverPresentationController { + popoverController.sourceView = self.tabBar + popoverController.sourceRect = CGRect(x: self.tabBar.bounds.midX, y: 0, width: 0, height: 0) + } + + self.present(alert, animated: true, completion: nil) + } + + private func showView(_ view: Content) { + showViewController(UIHostingController(rootView: view)) + } + + private func showViewController(_ viewController: UIViewController) { + self.navigationController?.pushViewController(viewController, animated: true) + } + +} diff --git a/ui/BankingiOSApp/BankingiOSApp/persistence/Extensions.swift b/ui/BankingiOSApp/BankingiOSApp/persistence/Extensions.swift index 8d3bb84b..ee9cede1 100644 --- a/ui/BankingiOSApp/BankingiOSApp/persistence/Extensions.swift +++ b/ui/BankingiOSApp/BankingiOSApp/persistence/Extensions.swift @@ -25,8 +25,18 @@ extension SceneDelegate { rootViewController as? UINavigationController } + public static var rootTabBarController: UITabBarController? { + rootNavigationController?.viewControllers.first as? UITabBarController + } + public static var currentViewController: UIViewController? { - rootNavigationController?.visibleViewController ?? rootViewController + var currentViewController = rootTabBarController?.selectedViewController ?? rootTabBarController + + while currentViewController?.presentedViewController != nil { + currentViewController = currentViewController?.presentedViewController + } + + return currentViewController } public static var currentNavigationItem: UINavigationItem? { diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/views/AccountsTab.swift b/ui/BankingiOSApp/BankingiOSApp/ui/views/AccountsTab.swift index a94bcf45..2f04579d 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/views/AccountsTab.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/views/AccountsTab.swift @@ -36,6 +36,10 @@ struct AccountsTab: View { } } .systemGroupedBackground() + .navigationBarTitle("Accounts") + .navigationBarItems(leading: data.hasAtLeastOneAccountBeenAdded == false ? nil : UpdateButton { _ in + self.presenter.updateAccountsTransactionsAsync { _ in } + }) } }