diff --git a/xdrip.xcodeproj/project.pbxproj b/xdrip.xcodeproj/project.pbxproj index 284cac87..849376c5 100644 --- a/xdrip.xcodeproj/project.pbxproj +++ b/xdrip.xcodeproj/project.pbxproj @@ -35,6 +35,7 @@ D4028CC02774A50600341476 /* TreatmentsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4028CBF2774A50600341476 /* TreatmentsViewController.swift */; }; D40C3DA4277542C400111B73 /* TreatmentEntry+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = D40C3DA3277542C400111B73 /* TreatmentEntry+CoreDataClass.swift */; }; D40C3DA62775438F00111B73 /* TreatmentEntry+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = D40C3DA52775438F00111B73 /* TreatmentEntry+CoreDataProperties.swift */; }; + D417E51C282EC8DB008DC467 /* ActivityIndicatorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D417E51B282EC8DB008DC467 /* ActivityIndicatorViewController.swift */; }; D41F32922827240E00861B3D /* SettingsViewHousekeeperSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D41F32912827240E00861B3D /* SettingsViewHousekeeperSettingsViewModel.swift */; }; D41F32942827332000861B3D /* DataExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D41F32932827332000861B3D /* DataExporter.swift */; }; D482BD942776153F003C4FB2 /* TreatmentsNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D482BD932776153F003C4FB2 /* TreatmentsNavigationController.swift */; }; @@ -728,6 +729,7 @@ D4028CBF2774A50600341476 /* TreatmentsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TreatmentsViewController.swift; sourceTree = ""; }; D40C3DA3277542C400111B73 /* TreatmentEntry+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TreatmentEntry+CoreDataClass.swift"; sourceTree = ""; }; D40C3DA52775438F00111B73 /* TreatmentEntry+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TreatmentEntry+CoreDataProperties.swift"; sourceTree = ""; }; + D417E51B282EC8DB008DC467 /* ActivityIndicatorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityIndicatorViewController.swift; sourceTree = ""; }; D41F32912827240E00861B3D /* SettingsViewHousekeeperSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewHousekeeperSettingsViewModel.swift; sourceTree = ""; }; D41F32932827332000861B3D /* DataExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataExporter.swift; sourceTree = ""; }; D482BD932776153F003C4FB2 /* TreatmentsNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TreatmentsNavigationController.swift; sourceTree = ""; }; @@ -2406,6 +2408,7 @@ F8B3A82E227F085A004BA588 /* SettingsTableViewCell */, F8B3A7F22278E0E7004BA588 /* SettingsViewModelProtocol.swift */, F8B3A807227A2933004BA588 /* SettingsSelectedRowAction.swift */, + D417E51B282EC8DB008DC467 /* ActivityIndicatorViewController.swift */, ); path = Helpers; sourceTree = ""; @@ -3662,6 +3665,7 @@ F8B3A830227F085A004BA588 /* SettingsTableViewCell.swift in Sources */, F82436FC24BE014000BED341 /* TextsLibreStates.swift in Sources */, F8F7B8EB259A7B1C00C47B04 /* SavitzkyGolaySmoothableArray.swift in Sources */, + D417E51C282EC8DB008DC467 /* ActivityIndicatorViewController.swift in Sources */, F830991C23C2909E005741DF /* Watlaa+CoreDataClass.swift in Sources */, F808D2CC240328FA0084B5DB /* Bubble+CoreDataClass.swift in Sources */, F8A1586122EDB844007F5B5D /* ConstantsNotifications.swift in Sources */, diff --git a/xdrip/View Controllers/Helpers/ActivityIndicatorViewController.swift b/xdrip/View Controllers/Helpers/ActivityIndicatorViewController.swift new file mode 100644 index 00000000..adbc320c --- /dev/null +++ b/xdrip/View Controllers/Helpers/ActivityIndicatorViewController.swift @@ -0,0 +1,56 @@ +// +// ActivityIndicatorViewController.swift +// xdrip +// +// Created by Eduardo Pietre on 13/05/22. +// Copyright © 2022 Johan Degraeve. All rights reserved. +// + +import Foundation + + +/// ActivityIndicatorViewController is an utility UIViewController +/// that creates a spinner loading indicator. +public class ActivityIndicatorViewController : UIViewController { + + /// Reference to the loading indicator. + private var indicator = UIActivityIndicatorView(style: .whiteLarge) + + /// loadView will ofuscate the background and centralize the indicator, starting it. + public override func loadView() { + view = UIView() + /// Change the backgroundColor to ofuscate the stuff behind. + view.backgroundColor = UIColor(white: 0, alpha: 0.7) + + /// Centralizes the indicator + self.indicator.translatesAutoresizingMaskIntoConstraints = false + self.indicator.startAnimating() + view.addSubview(self.indicator) + + self.indicator.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true + self.indicator.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true + } + + /// Starts the ActivityIndicator. + /// This method can be called from any thread in a safe way. + /// - parameters: + /// - onParent: UIViewController: the parent UIViewController. + public func start(onParent: UIViewController) { + DispatchQueue.main.async { + onParent.addChild(self) + self.view.frame = onParent.view.frame + onParent.view.addSubview(self.view) + self.didMove(toParent: onParent) + } + } + + /// Ends the ActivityIndicator. + /// This method can be called from any thread in a safe way. + public func end() { + DispatchQueue.main.async { + self.willMove(toParent: nil) + self.view.removeFromSuperview() + self.removeFromParent() + } + } +} diff --git a/xdrip/View Controllers/Helpers/SettingsSelectedRowAction.swift b/xdrip/View Controllers/Helpers/SettingsSelectedRowAction.swift index f7a639f6..4d57140c 100644 --- a/xdrip/View Controllers/Helpers/SettingsSelectedRowAction.swift +++ b/xdrip/View Controllers/Helpers/SettingsSelectedRowAction.swift @@ -38,6 +38,8 @@ enum SettingsSelectedRowAction { /// when clicked, the function parameter needs to be called /// but takes as argument a callback that when called returns an optional URL, /// that if not nil the user should be presented with a share menu. + /// Important: Displays a loading indicator, so all routies MUST call the callback or will + /// result in an endless loading indicator. case callFunctionAndShareFile(function: ((_ callback: @escaping ((_ file: URL?) -> Void)) -> Void)) /// when clicked a list of items must be presented form which the user needs to pick one, for example transmitter type diff --git a/xdrip/View Controllers/SettingsNavigationController/SettingsViewController/SettingsViewModels/SettingsViewHousekeeperSettingsViewModel.swift b/xdrip/View Controllers/SettingsNavigationController/SettingsViewController/SettingsViewModels/SettingsViewHousekeeperSettingsViewModel.swift index 1fc3c89c..bd3b3523 100644 --- a/xdrip/View Controllers/SettingsNavigationController/SettingsViewController/SettingsViewModels/SettingsViewHousekeeperSettingsViewModel.swift +++ b/xdrip/View Controllers/SettingsNavigationController/SettingsViewController/SettingsViewModels/SettingsViewHousekeeperSettingsViewModel.swift @@ -103,6 +103,9 @@ struct SettingsViewHousekeeperSettingsViewModel: SettingsViewModelProtocol { /// coreDataManager must not be nil. if let coreDataManager = coreDataManager { DataExporter(coreDataManager: coreDataManager).exportAllData(callback: callback) + } else { + /// All routines MUST call callback + callback(nil) } } } diff --git a/xdrip/View Controllers/SettingsNavigationController/SettingsViewController/SettingsViewUtilities.swift b/xdrip/View Controllers/SettingsNavigationController/SettingsViewController/SettingsViewUtilities.swift index 9917e797..92fad391 100644 --- a/xdrip/View Controllers/SettingsNavigationController/SettingsViewController/SettingsViewUtilities.swift +++ b/xdrip/View Controllers/SettingsNavigationController/SettingsViewController/SettingsViewUtilities.swift @@ -103,8 +103,16 @@ class SettingsViewUtilities { case let .callFunctionAndShareFile(function): + // Start loading indicator + let loadingIndicator = ActivityIndicatorViewController() + loadingIndicator.start(onParent: uIViewController) + // call function and in the callback present the share file menu. function({ fileURL in + + // Stop loading indicator + loadingIndicator.end() + if let fileURL = fileURL { // UI Code must be done at main thread. DispatchQueue.main.async {