From 1e812e4b8d2581bf24ea91e5111edc1ddc32285f Mon Sep 17 00:00:00 2001 From: dankito Date: Mon, 28 Sep 2020 20:01:22 +0200 Subject: [PATCH] Implemented that displayed value / text in LabelledValue can be selected and copied --- .../BankingiOSApp.xcodeproj/project.pbxproj | 8 +++ .../ui/UIKit/SelectableUILabel.swift | 51 +++++++++++++++++++ .../ui/views/LabelledObject.swift | 2 +- .../ui/views/LabelledValue.swift | 12 ++--- .../ui/views/SelectableText.swift | 47 +++++++++++++++++ .../ui/views/TextWithScrollView.swift | 16 +++--- 6 files changed, 117 insertions(+), 19 deletions(-) create mode 100644 ui/BankingiOSApp/BankingiOSApp/ui/UIKit/SelectableUILabel.swift create mode 100644 ui/BankingiOSApp/BankingiOSApp/ui/views/SelectableText.swift diff --git a/ui/BankingiOSApp/BankingiOSApp.xcodeproj/project.pbxproj b/ui/BankingiOSApp/BankingiOSApp.xcodeproj/project.pbxproj index ce31fa6c..c8c6ffdc 100644 --- a/ui/BankingiOSApp/BankingiOSApp.xcodeproj/project.pbxproj +++ b/ui/BankingiOSApp/BankingiOSApp.xcodeproj/project.pbxproj @@ -22,6 +22,8 @@ 361116A62505430500315620 /* KeychainPasswordItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 361116A52505430400315620 /* KeychainPasswordItem.swift */; }; 361116A8250562BE00315620 /* FaceIDButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 361116A7250562BE00315620 /* FaceIDButton.swift */; }; 361116AA250562CF00315620 /* TouchIDButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 361116A9250562CF00315620 /* TouchIDButton.swift */; }; + 361282C225223D7700392A4D /* SelectableText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 361282C125223D7700392A4D /* SelectableText.swift */; }; + 361282C62522438B00392A4D /* SelectableUILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 361282C52522438B00392A4D /* SelectableUILabel.swift */; }; 36428CFF251BEC4C009DE1AE /* SectionWithoutBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36428CFE251BEC4C009DE1AE /* SectionWithoutBackground.swift */; }; 3642F00A2500F5AE005186FE /* Divider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F0092500F5AE005186FE /* Divider.swift */; }; 3642F00C25010021005186FE /* UIKitActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F00B25010021005186FE /* UIKitActivityIndicator.swift */; }; @@ -174,6 +176,8 @@ 361116A52505430400315620 /* KeychainPasswordItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainPasswordItem.swift; sourceTree = ""; }; 361116A7250562BE00315620 /* FaceIDButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaceIDButton.swift; sourceTree = ""; }; 361116A9250562CF00315620 /* TouchIDButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchIDButton.swift; sourceTree = ""; }; + 361282C125223D7700392A4D /* SelectableText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableText.swift; sourceTree = ""; }; + 361282C52522438B00392A4D /* SelectableUILabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableUILabel.swift; sourceTree = ""; }; 36428CFE251BEC4C009DE1AE /* SectionWithoutBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionWithoutBackground.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 = ""; }; @@ -407,6 +411,7 @@ 36B8A4552503E9B200C15359 /* UIAlertBase.swift */, 36B8A4532503E93B00C15359 /* UIAlert.swift */, 36B8A4572503EEB600C15359 /* ActionSheet.swift */, + 361282C52522438B00392A4D /* SelectableUILabel.swift */, 3684EB93250FD75B0001139E /* CustomUITextField.swift */, ); path = UIKit; @@ -578,6 +583,7 @@ 3675054125201F0C006B13A0 /* VerticalLabelledValue.swift */, 36750531252006C9006B13A0 /* TextWithScrollView.swift */, 36428CFE251BEC4C009DE1AE /* SectionWithoutBackground.swift */, + 361282C125223D7700392A4D /* SelectableText.swift */, ); path = views; sourceTree = ""; @@ -769,6 +775,7 @@ 360782CD24F1A57F0098FEFE /* LabelledUIKitTextFieldWithValidationLabel.swift in Sources */, 3642F00A2500F5AE005186FE /* Divider.swift in Sources */, 36BCF89324C25BC3005BEC29 /* Mapper.swift in Sources */, + 361282C62522438B00392A4D /* SelectableUILabel.swift in Sources */, 360782D124F3F4120098FEFE /* SelectorWrapper.swift in Sources */, 36BE06BA24D0783900CBBB68 /* FaviconFinder.swift in Sources */, 36BCF89524C31F02005BEC29 /* AppData.swift in Sources */, @@ -801,6 +808,7 @@ 36B8A4512503DE1800C15359 /* LoginDialog.swift in Sources */, 36FC929C24B39A05002B12E9 /* AppDelegate.swift in Sources */, 36BCF88B24C0BD2D005BEC29 /* AccountTransactionsDialog.swift in Sources */, + 361282C225223D7700392A4D /* SelectableText.swift in Sources */, 3675054225201F0C006B13A0 /* VerticalLabelledValue.swift in Sources */, 36BCF87624BF114F005BEC29 /* UrlSessionWebClient.swift in Sources */, 36BE06C424D0801A00CBBB68 /* Size.swift in Sources */, diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/UIKit/SelectableUILabel.swift b/ui/BankingiOSApp/BankingiOSApp/ui/UIKit/SelectableUILabel.swift new file mode 100644 index 00000000..64d6dc71 --- /dev/null +++ b/ui/BankingiOSApp/BankingiOSApp/ui/UIKit/SelectableUILabel.swift @@ -0,0 +1,51 @@ +import SwiftUI + + +/// Partially copied from https://stackoverflow.com/a/59667107 +class SelectableUILabel: UITextField, UITextFieldDelegate { + + // change the cursor to have zero size + override func caretRect(for position: UITextPosition) -> CGRect { + return .zero + } + + override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { + + // disable 'cut', 'delete', 'paste','_promptForReplace:' + // if it is not editable + switch action { + case #selector(cut(_:)), + #selector(delete(_:)), + #selector(paste(_:)): + return false + default: + return super.canPerformAction(action, withSender: sender) + } + } + + + public func showCopyContextMenuOnLongPress() { + let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressed)) + self.addGestureRecognizer(longPressRecognizer) + } + + @objc func longPressed(sender: UILongPressGestureRecognizer) { + showMenu() + } + + public func showMenu() { + becomeFirstResponder() + + let menu = UIMenuController.shared + menu.showMenu(from: self, rect: bounds) + } + + + func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + return false + } + +} + + + diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/views/LabelledObject.swift b/ui/BankingiOSApp/BankingiOSApp/ui/views/LabelledObject.swift index 0b940f0b..3c5558c9 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/views/LabelledObject.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/views/LabelledObject.swift @@ -14,7 +14,7 @@ struct LabelledObject: View { } var body: some View { - HStack { + HStack(alignment: .center) { Text(label) Spacer() diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/views/LabelledValue.swift b/ui/BankingiOSApp/BankingiOSApp/ui/views/LabelledValue.swift index 6c8096d8..e1e720c5 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/views/LabelledValue.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/views/LabelledValue.swift @@ -5,16 +5,12 @@ struct LabelledValue: View { private let label: LocalizedStringKey - private let value: LocalizedStringKey - - - init(_ label: LocalizedStringKey, _ value: LocalizedStringKey) { - self.label = label - self.value = value - } + private let value: String + init(_ label: LocalizedStringKey, _ value: String) { - self.init(label, LocalizedStringKey(value)) + self.label = label + self.value = value.localize() } diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/views/SelectableText.swift b/ui/BankingiOSApp/BankingiOSApp/ui/views/SelectableText.swift new file mode 100644 index 00000000..6046ce25 --- /dev/null +++ b/ui/BankingiOSApp/BankingiOSApp/ui/views/SelectableText.swift @@ -0,0 +1,47 @@ +import SwiftUI + + +struct SelectableText: UIViewRepresentable { + + private let text: String + + private let textColor: UIColor? + + private let textAlignment: NSTextAlignment + + private var selectable: Bool + + + init(_ text: String, _ textColor: UIColor? = nil, _ textAlignment: NSTextAlignment = .natural, selectable: Bool = true) { + self.text = text + self.textColor = textColor + self.textAlignment = textAlignment + self.selectable = selectable + } + + func makeUIView(context: Context) -> SelectableUILabel { + let label = SelectableUILabel() + + label.text = self.text + label.textAlignment = textAlignment + + if let textColor = textColor { + label.textColor = textColor + } + + + // set the input view so the keyboard does not show up + label.inputView = UIView() + label.autocorrectionType = .no + + label.showCopyContextMenuOnLongPress() + + return label + } + + func updateUIView(_ uiView: SelectableUILabel, context: Context) { + uiView.text = self.text + uiView.isEnabled = self.selectable + } + +} diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/views/TextWithScrollView.swift b/ui/BankingiOSApp/BankingiOSApp/ui/views/TextWithScrollView.swift index fb56ca20..d56dca61 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/views/TextWithScrollView.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/views/TextWithScrollView.swift @@ -3,17 +3,13 @@ import SwiftUI struct TextWithScrollView: View { - private let value: LocalizedStringKey + private let value: String @State private var textFitsIntoAvailableSpace = true - - - init( _ value: LocalizedStringKey) { - self.value = value - } + init(_ value: String) { - self.init(LocalizedStringKey(value)) + self.value = value } @@ -29,8 +25,7 @@ struct TextWithScrollView: View { } private var valueText: some View { - return Text(value) - .detailForegroundColor() + return SelectableText(value, UIColor.secondaryLabel, .right) .background( @@ -46,7 +41,8 @@ struct TextWithScrollView: View { // And compare the two Color.clear.onAppear { - self.textFitsIntoAvailableSpace = self.textFitsIntoAvailableSpace == false ? false : availableSpace.size.width >= requiredSpace.size.width + let textsFrame = availableSpace.frame(in: .global) + self.textFitsIntoAvailableSpace = self.textFitsIntoAvailableSpace == false ? false : textsFrame.maxX <= UIScreen.main.bounds.width } }) }