Implemented SwiftUI FlickerCodeTanView; extracted ScaleImageView from ImageTanView

This commit is contained in:
dankito 2020-08-30 16:32:05 +02:00
parent b3dd944ce5
commit b28f6ae68f
14 changed files with 554 additions and 47 deletions

View File

@ -1,5 +1,6 @@
package net.dankito.banking.ui.util package net.dankito.banking.ui.util
import net.dankito.utils.multiplatform.Freezer
import net.dankito.utils.multiplatform.ObjectHolder import net.dankito.utils.multiplatform.ObjectHolder
@ -56,7 +57,7 @@ open class FlickerCodeStepsCalculator {
steps.add(calculateStep(halfbyteid, clock, bitarray)) steps.add(calculateStep(halfbyteid, clock, bitarray))
} while (halfbyteid.value > 0 || clock.value == Bit.Low) } while (halfbyteid.value > 0 || clock.value == Bit.Low)
return steps return Freezer.freeze(steps)
} }
protected open fun calculateStep(halfbyteid: ObjectHolder<Int>, clock: ObjectHolder<Bit>, bitarray: List<Step>): Step { protected open fun calculateStep(halfbyteid: ObjectHolder<Int>, clock: ObjectHolder<Bit>, bitarray: List<Step>): Step {

View File

@ -9,6 +9,16 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
3607829924E148D40098FEFE /* AdaptsToKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3607829824E148D40098FEFE /* AdaptsToKeyboard.swift */; }; 3607829924E148D40098FEFE /* AdaptsToKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3607829824E148D40098FEFE /* AdaptsToKeyboard.swift */; };
360782C124E18D5E0098FEFE /* AddAccountButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 360782C024E18D5E0098FEFE /* AddAccountButtonView.swift */; }; 360782C124E18D5E0098FEFE /* AddAccountButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 360782C024E18D5E0098FEFE /* AddAccountButtonView.swift */; };
360782C324E49FF70098FEFE /* ValidationLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 360782C224E49FF70098FEFE /* ValidationLabel.swift */; };
360782C524E541970098FEFE /* ScaleImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 360782C424E541970098FEFE /* ScaleImageView.swift */; };
360782C724E544170098FEFE /* FlickerCodeTanView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 360782C624E544170098FEFE /* FlickerCodeTanView.swift */; };
360782CB24E5746B0098FEFE /* FlickerCodeAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 360782CA24E5746A0098FEFE /* FlickerCodeAnimator.swift */; };
360782CD24F1A57F0098FEFE /* LabelledUIKitTextFieldWithValidationLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 360782CC24F1A57E0098FEFE /* LabelledUIKitTextFieldWithValidationLabel.swift */; };
360782CF24F3D6610098FEFE /* InfoLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 360782CE24F3D6610098FEFE /* InfoLabel.swift */; };
360782D124F3F4120098FEFE /* SelectorWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 360782D024F3F4120098FEFE /* SelectorWrapper.swift */; };
360782D324F429F80098FEFE /* FlickerCodeStripe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 360782D224F429F70098FEFE /* FlickerCodeStripe.swift */; };
3608D6C224FBA9C6006C93A8 /* TrianglePointingDown.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3608D6C124FBA9C6006C93A8 /* TrianglePointingDown.swift */; };
3608D6C624FBAB41006C93A8 /* TanGeneratorPositionMarker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3608D6C524FBAB41006C93A8 /* TanGeneratorPositionMarker.swift */; };
366FA4DA24C472A90094F009 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4D924C472A90094F009 /* Extensions.swift */; }; 366FA4DA24C472A90094F009 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4D924C472A90094F009 /* Extensions.swift */; };
366FA4DC24C479120094F009 /* BankInfoListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4DB24C479120094F009 /* BankInfoListItem.swift */; }; 366FA4DC24C479120094F009 /* BankInfoListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4DB24C479120094F009 /* BankInfoListItem.swift */; };
366FA4E024C4924A0094F009 /* RemitteeListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4DF24C4924A0094F009 /* RemitteeListItem.swift */; }; 366FA4E024C4924A0094F009 /* RemitteeListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4DF24C4924A0094F009 /* RemitteeListItem.swift */; };
@ -128,6 +138,16 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
3607829824E148D40098FEFE /* AdaptsToKeyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdaptsToKeyboard.swift; sourceTree = "<group>"; }; 3607829824E148D40098FEFE /* AdaptsToKeyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdaptsToKeyboard.swift; sourceTree = "<group>"; };
360782C024E18D5E0098FEFE /* AddAccountButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAccountButtonView.swift; sourceTree = "<group>"; }; 360782C024E18D5E0098FEFE /* AddAccountButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAccountButtonView.swift; sourceTree = "<group>"; };
360782C224E49FF70098FEFE /* ValidationLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidationLabel.swift; sourceTree = "<group>"; };
360782C424E541970098FEFE /* ScaleImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScaleImageView.swift; sourceTree = "<group>"; };
360782C624E544170098FEFE /* FlickerCodeTanView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlickerCodeTanView.swift; sourceTree = "<group>"; };
360782CA24E5746A0098FEFE /* FlickerCodeAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlickerCodeAnimator.swift; sourceTree = "<group>"; };
360782CC24F1A57E0098FEFE /* LabelledUIKitTextFieldWithValidationLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelledUIKitTextFieldWithValidationLabel.swift; sourceTree = "<group>"; };
360782CE24F3D6610098FEFE /* InfoLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoLabel.swift; sourceTree = "<group>"; };
360782D024F3F4120098FEFE /* SelectorWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectorWrapper.swift; sourceTree = "<group>"; };
360782D224F429F70098FEFE /* FlickerCodeStripe.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlickerCodeStripe.swift; sourceTree = "<group>"; };
3608D6C124FBA9C6006C93A8 /* TrianglePointingDown.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrianglePointingDown.swift; sourceTree = "<group>"; };
3608D6C524FBAB41006C93A8 /* TanGeneratorPositionMarker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TanGeneratorPositionMarker.swift; sourceTree = "<group>"; };
366FA4D924C472A90094F009 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; }; 366FA4D924C472A90094F009 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
366FA4DB24C479120094F009 /* BankInfoListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankInfoListItem.swift; sourceTree = "<group>"; }; 366FA4DB24C479120094F009 /* BankInfoListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankInfoListItem.swift; sourceTree = "<group>"; };
366FA4DF24C4924A0094F009 /* RemitteeListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemitteeListItem.swift; sourceTree = "<group>"; }; 366FA4DF24C4924A0094F009 /* RemitteeListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemitteeListItem.swift; sourceTree = "<group>"; };
@ -403,8 +423,13 @@
36BE06B424CF85A300CBBB68 /* AmountLabel.swift */, 36BE06B424CF85A300CBBB68 /* AmountLabel.swift */,
36BE06C724D0DE7400CBBB68 /* UIKitTextField.swift */, 36BE06C724D0DE7400CBBB68 /* UIKitTextField.swift */,
36E21EDA24DC990300649DC8 /* LabelledUIKitTextField.swift */, 36E21EDA24DC990300649DC8 /* LabelledUIKitTextField.swift */,
360782CE24F3D6610098FEFE /* InfoLabel.swift */,
360782C224E49FF70098FEFE /* ValidationLabel.swift */,
360782CC24F1A57E0098FEFE /* LabelledUIKitTextFieldWithValidationLabel.swift */,
36E21EDC24DCA89100649DC8 /* TanProcedurePicker.swift */, 36E21EDC24DCA89100649DC8 /* TanProcedurePicker.swift */,
3607829824E148D40098FEFE /* AdaptsToKeyboard.swift */, 3607829824E148D40098FEFE /* AdaptsToKeyboard.swift */,
360782CA24E5746A0098FEFE /* FlickerCodeAnimator.swift */,
360782D024F3F4120098FEFE /* SelectorWrapper.swift */,
); );
path = ui; path = ui;
sourceTree = "<group>"; sourceTree = "<group>";
@ -424,7 +449,10 @@
366FA4DB24C479120094F009 /* BankInfoListItem.swift */, 366FA4DB24C479120094F009 /* BankInfoListItem.swift */,
366FA4DF24C4924A0094F009 /* RemitteeListItem.swift */, 366FA4DF24C4924A0094F009 /* RemitteeListItem.swift */,
366FA4E124C4ED6C0094F009 /* EnterTanDialog.swift */, 366FA4E124C4ED6C0094F009 /* EnterTanDialog.swift */,
360782C424E541970098FEFE /* ScaleImageView.swift */,
36BE064E24C9A17F00CBBB68 /* ImageTanView.swift */, 36BE064E24C9A17F00CBBB68 /* ImageTanView.swift */,
360782C624E544170098FEFE /* FlickerCodeTanView.swift */,
360782D224F429F70098FEFE /* FlickerCodeStripe.swift */,
36E21ED024DC540400649DC8 /* SettingsDialog.swift */, 36E21ED024DC540400649DC8 /* SettingsDialog.swift */,
36E21ED424DC549800649DC8 /* BankSettingsDialog.swift */, 36E21ED424DC549800649DC8 /* BankSettingsDialog.swift */,
36E21ED624DC617200649DC8 /* BankAccountSettingsDialog.swift */, 36E21ED624DC617200649DC8 /* BankAccountSettingsDialog.swift */,
@ -435,6 +463,8 @@
36C4009A24D2F9E4005227AD /* IconedTitleView.swift */, 36C4009A24D2F9E4005227AD /* IconedTitleView.swift */,
36E21ECE24DA0EEE00649DC8 /* IconView.swift */, 36E21ECE24DA0EEE00649DC8 /* IconView.swift */,
36E21EDE24DCCC2700649DC8 /* CheckmarkListItem.swift */, 36E21EDE24DCCC2700649DC8 /* CheckmarkListItem.swift */,
3608D6C124FBA9C6006C93A8 /* TrianglePointingDown.swift */,
3608D6C524FBAB41006C93A8 /* TanGeneratorPositionMarker.swift */,
); );
path = views; path = views;
sourceTree = "<group>"; sourceTree = "<group>";
@ -607,6 +637,7 @@
36E21EDB24DC990300649DC8 /* LabelledUIKitTextField.swift in Sources */, 36E21EDB24DC990300649DC8 /* LabelledUIKitTextField.swift in Sources */,
36BE068D24CE41E700CBBB68 /* Styles.swift in Sources */, 36BE068D24CE41E700CBBB68 /* Styles.swift in Sources */,
36E21ECB24D88DF000649DC8 /* UIKitExtensions.swift in Sources */, 36E21ECB24D88DF000649DC8 /* UIKitExtensions.swift in Sources */,
360782C524E541970098FEFE /* ScaleImageView.swift in Sources */,
366FA4E224C4ED6C0094F009 /* EnterTanDialog.swift in Sources */, 366FA4E224C4ED6C0094F009 /* EnterTanDialog.swift in Sources */,
36FC92DC24B3A4A0002B12E9 /* AccountsTab.swift in Sources */, 36FC92DC24B3A4A0002B12E9 /* AccountsTab.swift in Sources */,
36BCF86E24BA691B005BEC29 /* DependencyInjector.swift in Sources */, 36BCF86E24BA691B005BEC29 /* DependencyInjector.swift in Sources */,
@ -614,24 +645,31 @@
36BE06B824D077EC00CBBB68 /* SwiftBankIconFinder.swift in Sources */, 36BE06B824D077EC00CBBB68 /* SwiftBankIconFinder.swift in Sources */,
36BCF89124C25971005BEC29 /* CoreDataBankingPersistence.swift in Sources */, 36BCF89124C25971005BEC29 /* CoreDataBankingPersistence.swift in Sources */,
36FC92A124B39A05002B12E9 /* BankingiOSApp.xcdatamodeld in Sources */, 36FC92A124B39A05002B12E9 /* BankingiOSApp.xcdatamodeld in Sources */,
360782D324F429F80098FEFE /* FlickerCodeStripe.swift in Sources */,
36BE06C624D080C900CBBB68 /* FaviconType.swift in Sources */, 36BE06C624D080C900CBBB68 /* FaviconType.swift in Sources */,
360782CD24F1A57F0098FEFE /* LabelledUIKitTextFieldWithValidationLabel.swift in Sources */,
36BCF89324C25BC3005BEC29 /* Mapper.swift in Sources */, 36BCF89324C25BC3005BEC29 /* Mapper.swift in Sources */,
36FC92D724B3A3BA002B12E9 /* NSUrlWebClient.swift in Sources */, 36FC92D724B3A3BA002B12E9 /* NSUrlWebClient.swift in Sources */,
360782D124F3F4120098FEFE /* SelectorWrapper.swift in Sources */,
36BE06B324CF133400CBBB68 /* JsonEncoderSerializer.swift in Sources */, 36BE06B324CF133400CBBB68 /* JsonEncoderSerializer.swift in Sources */,
36BE06BA24D0783900CBBB68 /* FaviconFinder.swift in Sources */, 36BE06BA24D0783900CBBB68 /* FaviconFinder.swift in Sources */,
36BCF89524C31F02005BEC29 /* AppData.swift in Sources */, 36BCF89524C31F02005BEC29 /* AppData.swift in Sources */,
36E21EDD24DCA89100649DC8 /* TanProcedurePicker.swift in Sources */, 36E21EDD24DCA89100649DC8 /* TanProcedurePicker.swift in Sources */,
3608D6C624FBAB41006C93A8 /* TanGeneratorPositionMarker.swift in Sources */,
36BE065B24CA4B3500CBBB68 /* SelectBankDialog.swift in Sources */, 36BE065B24CA4B3500CBBB68 /* SelectBankDialog.swift in Sources */,
36E21ED524DC549800649DC8 /* BankSettingsDialog.swift in Sources */, 36E21ED524DC549800649DC8 /* BankSettingsDialog.swift in Sources */,
36BE068924CE288800CBBB68 /* CollapsibleText.swift in Sources */, 36BE068924CE288800CBBB68 /* CollapsibleText.swift in Sources */,
36BE06B524CF85A300CBBB68 /* AmountLabel.swift in Sources */, 36BE06B524CF85A300CBBB68 /* AmountLabel.swift in Sources */,
36BCF88324C098BB005BEC29 /* BankListItem.swift in Sources */, 36BCF88324C098BB005BEC29 /* BankListItem.swift in Sources */,
360782CB24E5746B0098FEFE /* FlickerCodeAnimator.swift in Sources */,
36BCF88D24C1C1EA005BEC29 /* TransferMoneyDialog.swift in Sources */, 36BCF88D24C1C1EA005BEC29 /* TransferMoneyDialog.swift in Sources */,
36E21EDF24DCCC2700649DC8 /* CheckmarkListItem.swift in Sources */, 36E21EDF24DCCC2700649DC8 /* CheckmarkListItem.swift in Sources */,
36E7BA1424B3D05C00757859 /* ViewExtensions.swift in Sources */, 36E7BA1424B3D05C00757859 /* ViewExtensions.swift in Sources */,
36BCF88924C0A7D7005BEC29 /* Message.swift in Sources */, 36BCF88924C0A7D7005BEC29 /* Message.swift in Sources */,
366FA4E024C4924A0094F009 /* RemitteeListItem.swift in Sources */, 366FA4E024C4924A0094F009 /* RemitteeListItem.swift in Sources */,
3608D6C224FBA9C6006C93A8 /* TrianglePointingDown.swift in Sources */,
36BE068B24CE3B0400CBBB68 /* SwiftExtensions.swift in Sources */, 36BE068B24CE3B0400CBBB68 /* SwiftExtensions.swift in Sources */,
360782C724E544170098FEFE /* FlickerCodeTanView.swift in Sources */,
36BE065D24CB08FC00CBBB68 /* LazyView.swift in Sources */, 36BE065D24CB08FC00CBBB68 /* LazyView.swift in Sources */,
360782C124E18D5E0098FEFE /* AddAccountButtonView.swift in Sources */, 360782C124E18D5E0098FEFE /* AddAccountButtonView.swift in Sources */,
36BCF86C24BA5E72005BEC29 /* DispatchQueueAsyncRunner.swift in Sources */, 36BCF86C24BA5E72005BEC29 /* DispatchQueueAsyncRunner.swift in Sources */,
@ -642,12 +680,14 @@
36BE06C424D0801A00CBBB68 /* Size.swift in Sources */, 36BE06C424D0801A00CBBB68 /* Size.swift in Sources */,
36FC92A324B39A05002B12E9 /* ContentView.swift in Sources */, 36FC92A324B39A05002B12E9 /* ContentView.swift in Sources */,
366FA4E624C6EBF40094F009 /* EnterTanState.swift in Sources */, 366FA4E624C6EBF40094F009 /* EnterTanState.swift in Sources */,
360782CF24F3D6610098FEFE /* InfoLabel.swift in Sources */,
36C4009B24D2F9E4005227AD /* IconedTitleView.swift in Sources */, 36C4009B24D2F9E4005227AD /* IconedTitleView.swift in Sources */,
36BE065724C9E04800CBBB68 /* UIKitImageView.swift in Sources */, 36BE065724C9E04800CBBB68 /* UIKitImageView.swift in Sources */,
36BCF88724C0A310005BEC29 /* PreviewData.swift in Sources */, 36BCF88724C0A310005BEC29 /* PreviewData.swift in Sources */,
36E21ED724DC617200649DC8 /* BankAccountSettingsDialog.swift in Sources */, 36E21ED724DC617200649DC8 /* BankAccountSettingsDialog.swift in Sources */,
366FA4DA24C472A90094F009 /* Extensions.swift in Sources */, 366FA4DA24C472A90094F009 /* Extensions.swift in Sources */,
36BE068F24CEE1BD00CBBB68 /* AllBanksListItem.swift in Sources */, 36BE068F24CEE1BD00CBBB68 /* AllBanksListItem.swift in Sources */,
360782C324E49FF70098FEFE /* ValidationLabel.swift in Sources */,
36BE069124CEF52800CBBB68 /* UpdateButton.swift in Sources */, 36BE069124CEF52800CBBB68 /* UpdateButton.swift in Sources */,
36E21ED124DC540400649DC8 /* SettingsDialog.swift in Sources */, 36E21ED124DC540400649DC8 /* SettingsDialog.swift in Sources */,
366FA4DC24C479120094F009 /* BankInfoListItem.swift in Sources */, 366FA4DC24C479120094F009 /* BankInfoListItem.swift in Sources */,
@ -767,7 +807,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.5; IPHONEOS_DEPLOYMENT_TARGET = 13.6;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
@ -822,7 +862,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.5; IPHONEOS_DEPLOYMENT_TARGET = 13.6;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -849,6 +889,7 @@
"../../ui/BankingUiNativeIntegration/build/xcode-frameworks/", "../../ui/BankingUiNativeIntegration/build/xcode-frameworks/",
); );
INFOPLIST_FILE = BankingiOSApp/Info.plist; INFOPLIST_FILE = BankingiOSApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@ -857,7 +898,7 @@
PRODUCT_BUNDLE_IDENTIFIER = net.dankito.banking.ios.BankingiOSApp.freeprovisioning; PRODUCT_BUNDLE_IDENTIFIER = net.dankito.banking.ios.BankingiOSApp.freeprovisioning;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Debug; name = Debug;
}; };
@ -878,6 +919,7 @@
"../../ui/BankingUiNativeIntegration/build/xcode-frameworks/", "../../ui/BankingUiNativeIntegration/build/xcode-frameworks/",
); );
INFOPLIST_FILE = BankingiOSApp/Info.plist; INFOPLIST_FILE = BankingiOSApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@ -886,7 +928,7 @@
PRODUCT_BUNDLE_IDENTIFIER = net.dankito.banking.ios.BankingiOSApp.freeprovisioning; PRODUCT_BUNDLE_IDENTIFIER = net.dankito.banking.ios.BankingiOSApp.freeprovisioning;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Release; name = Release;
}; };

View File

@ -89,6 +89,7 @@
"Enter TAN Dialog Title" = "Enter TAN"; "Enter TAN Dialog Title" = "Enter TAN";
"TAN procedure" = "TAN procedure"; "TAN procedure" = "TAN procedure";
"TAN medium" = "TAN medium"; "TAN medium" = "TAN medium";
"Tan Generator Frequency" = "Frequency";
"Size" = "Size"; "Size" = "Size";
"TAN hint from your bank:" = "Hint from your bank:"; "TAN hint from your bank:" = "Hint from your bank:";
"Enter TAN:" = "TAN"; "Enter TAN:" = "TAN";

View File

@ -89,6 +89,7 @@
"Enter TAN Dialog Title" = "TAN-Abfrage"; "Enter TAN Dialog Title" = "TAN-Abfrage";
"TAN procedure" = "TAN Verfahren"; "TAN procedure" = "TAN Verfahren";
"TAN medium" = "TAN Medium"; "TAN medium" = "TAN Medium";
"Tan Generator Frequency" = "Geschw.";
"Size" = "Größe"; "Size" = "Größe";
"TAN hint from your bank:" = "Hinweis Ihrer Bank:"; "TAN hint from your bank:" = "Hinweis Ihrer Bank:";
"Enter TAN:" = "TAN"; "Enter TAN:" = "TAN";

View File

@ -0,0 +1,89 @@
import SwiftUI
import BankingUiSwift
class FlickerCodeAnimator {
static let MinFrequency = 2
static let MaxFrequency = 40
static let DefaultFrequency = 30
private var currentFrequency: Int = DefaultFrequency
private var isPaused = false
private var currentStepIndex = 0
private var steps: [Step] = []
private var showStep: ((Step) -> Void)? = nil
private var timer: Timer? = nil
func animate(_ flickerCode: String, _ showStep: @escaping (Step) -> Void) {
self.stop() // stop may still running previous animation
self.steps = FlickerCodeStepsCalculator().calculateSteps(flickerCode: flickerCode)
self.showStep = showStep
self.start()
}
private func start() {
currentStepIndex = 0
let interval = 1.0 / Double(currentFrequency)
timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { timer in
self.calculateAndShowNextStep()
}
}
private func calculateAndShowNextStep() {
if isPaused == false {
let nextStep = steps[currentStepIndex]
self.showStep?(nextStep)
currentStepIndex = currentStepIndex + 1
if (currentStepIndex >= steps.count) {
currentStepIndex = 0 // all steps shown, start again from beginning
}
}
}
func stop() {
timer?.invalidate()
timer = nil
}
func pause() {
self.isPaused = true
}
func resume() {
self.isPaused = false
}
func setFrequency(frequency: Int) {
if frequency >= Self.MinFrequency && frequency <= Self.MaxFrequency {
let isRunning = timer != nil
stop()
currentFrequency = frequency
if isRunning {
start()
}
}
}
}

View File

@ -12,3 +12,27 @@ extension UIResponder {
} }
} }
extension UserDefaults {
func string(forKey key: String, defaultValue: String) -> String {
return UserDefaults.standard.object(forKey: key) as? String ?? defaultValue
}
func integer(forKey key: String, defaultValue: Int) -> Int {
return UserDefaults.standard.object(forKey: key) as? Int ?? defaultValue
}
func float(forKey key: String, defaultValue: Float) -> Float {
return UserDefaults.standard.object(forKey: key) as? Float ?? defaultValue
}
func double(forKey key: String, defaultValue: Double) -> Double {
return UserDefaults.standard.object(forKey: key) as? Double ?? defaultValue
}
func bool(forKey key: String, defaultValue: Bool) -> Bool {
return UserDefaults.standard.object(forKey: key) as? Bool ?? defaultValue
}
}

View File

@ -85,6 +85,11 @@ extension View {
.foregroundColor(amountColor) .foregroundColor(amountColor)
} }
func turnAnimationOff() -> some View {
return self.animation(nil)
}
} }

View File

@ -31,9 +31,9 @@ struct EnterTanDialog: View {
private var showSelectTanMediumView = false private var showSelectTanMediumView = false
private var showFlickerCodeTanView = false private let flickerCodeTanChallenge: FlickerCodeTanChallenge?
private var showImageTanView = false private let imageTanChallenge: ImageTanChallenge?
@State private var enteredTan = "" @State private var enteredTan = ""
@ -54,8 +54,8 @@ struct EnterTanDialog: View {
self.showSelectTanMediumView = self.customersTanMedia.count > 1 // TODO: use isOpticalTanProcedure && tanMedia.count > 1 self.showSelectTanMediumView = self.customersTanMedia.count > 1 // TODO: use isOpticalTanProcedure && tanMedia.count > 1
self.showFlickerCodeTanView = tanChallenge is FlickerCodeTanChallenge self.flickerCodeTanChallenge = tanChallenge as? FlickerCodeTanChallenge
self.showImageTanView = tanChallenge is ImageTanChallenge self.imageTanChallenge = tanChallenge as? ImageTanChallenge
if let decodingError = (tanChallenge as? FlickerCodeTanChallenge)?.flickerCode.decodingError { if let decodingError = (tanChallenge as? FlickerCodeTanChallenge)?.flickerCode.decodingError {
showDecodingTanChallengeFailedErrorDelayed(decodingError) showDecodingTanChallengeFailedErrorDelayed(decodingError)
@ -82,12 +82,12 @@ struct EnterTanDialog: View {
} }
} }
if showFlickerCodeTanView { flickerCodeTanChallenge.map { flickerCodeTanChallenge in
Text("Entschuldigen Sie, aber die Darstellung von Flicker Codes wird gegenwärtig noch nicht unterstützt (fauler Programmierer). Bitte wählen Sie ein anderes TAN Verfahren, im Notfall chipTAN manuell.") FlickerCodeTanView(flickerCodeTanChallenge)
} }
if showImageTanView { imageTanChallenge.map { imageTanChallenge in
ImageTanView(self.tanChallenge as! ImageTanChallenge) ImageTanView(imageTanChallenge)
} }
VStack { VStack {

View File

@ -0,0 +1,69 @@
import SwiftUI
struct FlickerCodeStripe: View {
private static let Height = CGFloat(100)
@Binding private var showBit: Bool
@Binding private var width: CGFloat
init(_ showBit: Binding<Bool>, _ width: Binding<CGFloat>) {
_showBit = showBit
_width = width
}
var body: some View {
Rectangle()
.fill(showBit ? Color.white : Color.black)
.frame(width: width, height: Self.Height)
.turnAnimationOff() // it's very important to turn animation off otherwise stripes get displayed in gray instead of white which TAN generator doesn't recognize
}
}
struct FlickerCodeBit_Previews: PreviewProvider {
static var previews: some View {
let bitWidth: CGFloat = 40
let spaceWidth: CGFloat = 15
return HStack {
Spacer()
HStack {
FlickerCodeStripe(.constant(true), .constant(bitWidth))
Spacer()
.frame(width: spaceWidth)
FlickerCodeStripe(.constant(true), .constant(bitWidth))
Spacer()
.frame(width: spaceWidth)
FlickerCodeStripe(.constant(true), .constant(bitWidth))
Spacer()
.frame(width: spaceWidth)
FlickerCodeStripe(.constant(false), .constant(bitWidth))
Spacer()
.frame(width: spaceWidth)
FlickerCodeStripe(.constant(true), .constant(bitWidth))
}
Spacer()
}
.frame(width: UIScreen.main.bounds.width)
.background(Color.black)
}
}

View File

@ -0,0 +1,188 @@
import SwiftUI
import BankingUiSwift
struct FlickerCodeTanView: View {
private static let FlickerCodeScaleFactorUserDefaultsKey = "FlickerCodeScaleFactor"
private static let FlickerCodeFrequencyDefaultsKey = "FlickerCodeFrequency"
private static let SpaceBetweenStripesStepSize: CGFloat = 0.5
private static let StripesWidthStepSize: CGFloat = 2.35 * Self.SpaceBetweenStripesStepSize
private let MinScaleFactor: CGFloat = 10
private let MaxScaleFactor: CGFloat
private var tanChallenge: BankingUiSwift.FlickerCodeTanChallenge
@State private var showBit1: Bool = true
@State private var showBit2: Bool = true
@State private var showBit3: Bool = true
@State private var showBit4: Bool = true
@State private var showBit5: Bool = true
private let animator: FlickerCodeAnimator = FlickerCodeAnimator()
private let timer = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect()
@State private var frequency = CGFloat(UserDefaults.standard.float(forKey: Self.FlickerCodeFrequencyDefaultsKey, defaultValue: Float(FlickerCodeAnimator.DefaultFrequency)))
private var frequencyBinding: Binding<CGFloat> {
Binding<CGFloat>(
get: { self.frequency },
set: {
if (self.frequency != $0) {
let newFrequency = $0
self.animator.setFrequency(frequency: Int(newFrequency))
UserDefaults.standard.set(newFrequency, forKey: Self.FlickerCodeFrequencyDefaultsKey)
DispatchQueue.main.async {
self.frequency = newFrequency
}
}
})
}
@State private var stripeWidth: CGFloat = 24
@State private var spaceBetweenStripes: CGFloat = 10
@State private var spaceBetweenTanGeneratorPositionMarker: CGFloat = 4 * 40 + 4 * 15 - TanGeneratorPositionMarker.Width
@State private var scaleFactor = CGFloat(UserDefaults.standard.float(forKey: Self.FlickerCodeScaleFactorUserDefaultsKey, defaultValue: 20.0))
private var scaleFactorBinding: Binding<CGFloat> {
Binding<CGFloat>(
get: { self.scaleFactor },
set: {
if (self.scaleFactor != $0) {
let newFlickerScaleFactor = $0
UserDefaults.standard.set(newFlickerScaleFactor, forKey: Self.FlickerCodeScaleFactorUserDefaultsKey)
DispatchQueue.main.async {
self.scaleFactor = newFlickerScaleFactor
self.calculateStripeWidth()
}
}
})
}
init(_ tanChallenge: BankingUiSwift.FlickerCodeTanChallenge) {
self.tanChallenge = tanChallenge
let oneStepDiff = 5 * Self.StripesWidthStepSize + 4 * Self.SpaceBetweenStripesStepSize
MaxScaleFactor = CGFloat(Int(UIScreen.main.bounds.width / oneStepDiff))
animator.setFrequency(frequency: Int(frequency))
}
var body: some View {
Section {
HStack {
Text("Tan Generator Frequency")
Spacer()
Image(systemName: "tortoise")
Slider(value: frequencyBinding, in: CGFloat(FlickerCodeAnimator.MinFrequency)...CGFloat(FlickerCodeAnimator.MaxFrequency), step: 1)
Image(systemName: "hare")
}
ScaleImageView(scaleFactorBinding, imageMinWidth: MinScaleFactor, imageMaxWidth: MaxScaleFactor, step: 1)
VStack {
HStack {
Spacer()
TanGeneratorPositionMarker()
Spacer()
.frame(width: spaceBetweenTanGeneratorPositionMarker)
TanGeneratorPositionMarker()
Spacer()
}
.padding(.bottom, -4)
HStack {
Spacer()
HStack {
FlickerCodeStripe($showBit1, $stripeWidth)
Spacer()
.frame(width: spaceBetweenStripes)
FlickerCodeStripe($showBit2, $stripeWidth)
Spacer()
.frame(width: spaceBetweenStripes)
FlickerCodeStripe($showBit3, $stripeWidth)
Spacer()
.frame(width: spaceBetweenStripes)
FlickerCodeStripe($showBit4, $stripeWidth)
Spacer()
.frame(width: spaceBetweenStripes)
FlickerCodeStripe($showBit5, $stripeWidth)
}
Spacer()
}
.padding(.bottom, 12)
}
.background(Color.black)
.listRowInsets(EdgeInsets())
}
// what a hack to be able to call animator.animate() (otherwise compiler would throw 'use of immutable self in closure' error)
.onReceive(timer) { timer in
self.timer.upstream.connect().cancel()
self.calculateStripeWidth()
self.animator.animate(self.tanChallenge.flickerCode.parsedDataSet, self.showStep)
}
}
private func showStep(_ step: Step) {
self.showBit1 = step.bit1.isHigh
self.showBit2 = step.bit2.isHigh
self.showBit3 = step.bit3.isHigh
self.showBit4 = step.bit4.isHigh
self.showBit5 = step.bit5.isHigh
}
private func calculateStripeWidth() {
stripeWidth = scaleFactor * Self.StripesWidthStepSize
spaceBetweenStripes = scaleFactor * Self.SpaceBetweenStripesStepSize
spaceBetweenTanGeneratorPositionMarker = 4 * stripeWidth + 4 * spaceBetweenStripes - TanGeneratorPositionMarker.Width
}
}
struct FlickerCodeTanView_Previews: PreviewProvider {
static var previews: some View {
FlickerCodeTanView(previewFlickerCodeTanChallenge)
}
}

View File

@ -8,51 +8,19 @@ struct ImageTanView: View {
private var imageData: Data private var imageData: Data
@State private var imageWidth: CGFloat @State private var imageWidth: CGFloat = UIScreen.main.bounds.width / 2
private let imageMinWidth: CGFloat
private let imageMaxWidth: CGFloat
private let step: CGFloat
init(_ tanChallenge: ImageTanChallenge) { init(_ tanChallenge: ImageTanChallenge) {
self.tanChallenge = tanChallenge self.tanChallenge = tanChallenge
self.imageData = tanChallenge.image.imageBytesAsNSData() self.imageData = tanChallenge.image.imageBytesAsNSData()
let screenWidth = UIScreen.main.bounds.width
let screenWidthQuarter = screenWidth / 4
self.imageMinWidth = screenWidthQuarter < 150 ? 150 : screenWidthQuarter // don't know whey but iOS seems that it doesn't scale image smaller than 150
self.imageMaxWidth = screenWidth
let range = imageMaxWidth - imageMinWidth
self._imageWidth = State(initialValue: imageMinWidth + range / 2)
self.step = range / 20
} }
var body: some View { var body: some View {
Section { Section {
HStack { ScaleImageView($imageWidth)
Text("Size")
Spacer()
Rectangle()
.fill(Color.gray)
.frame(width: 6, height: 9)
Slider(value: $imageWidth, in: imageMinWidth...imageMaxWidth, step: step)
Rectangle()
.fill(Color.gray)
.frame(width: 16, height: 19)
}
HStack { HStack {
Spacer() Spacer()

View File

@ -0,0 +1,66 @@
import SwiftUI
struct ScaleImageView: View {
@Binding private var imageWidth: CGFloat
private let imageMinWidth: CGFloat
private let imageMaxWidth: CGFloat
private let step: CGFloat
init(_ imageWidth: Binding<CGFloat>, _ initialImageWidth: CGFloat? = nil) {
let screenWidth = UIScreen.main.bounds.width
let screenWidthQuarter = screenWidth / 4
let imageMinWidth = screenWidthQuarter < 150 ? 150 : screenWidthQuarter // don't know whey but iOS seems that it doesn't scale image smaller than 150
let imageMaxWidth = screenWidth
let range = imageMaxWidth - imageMinWidth
self.init(imageWidth, imageMinWidth: imageMinWidth, imageMaxWidth: imageMaxWidth, step: range / 20)
self.$imageWidth.wrappedValue = initialImageWidth ?? (imageMinWidth + range / 2)
}
init(_ imageWidth: Binding<CGFloat>, imageMinWidth: CGFloat, imageMaxWidth: CGFloat, step: CGFloat) {
_imageWidth = imageWidth
self.imageMinWidth = imageMinWidth
self.imageMaxWidth = imageMaxWidth
self.step = step
}
var body: some View {
HStack {
Text("Size")
Spacer()
Rectangle()
.fill(Color.gray) // TODO: use a system color
.frame(width: 6, height: 9)
Slider(value: $imageWidth, in: imageMinWidth...imageMaxWidth, step: step)
Rectangle()
.fill(Color.gray) // TODO: use a system color
.frame(width: 16, height: 19)
}
}
}
struct ScaleImageView_Previews: PreviewProvider {
static var previews: some View {
ScaleImageView(.constant(250))
}
}

View File

@ -0,0 +1,26 @@
import SwiftUI
struct TanGeneratorPositionMarker: View {
static let Width = CGFloat(24)
static let Height = CGFloat(24)
var body: some View {
TrianglePointingDown()
.fill(Color.gray)
.frame(width: Self.Width, height: Self.Height)
}
}
struct TanGeneratorPositionMarker_Previews: PreviewProvider {
static var previews: some View {
TanGeneratorPositionMarker()
}
}

View File

@ -0,0 +1,27 @@
import SwiftUI
struct TrianglePointingDown: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: rect.minX, y: rect.minY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
path.addLine(to: CGPoint(x: rect.midX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.minX, y: rect.minY))
return path
}
}
struct TrianglePointingDown_Previews: PreviewProvider {
static var previews: some View {
TrianglePointingDown()
.frame(width: 300, height: 300)
}
}