Implemented that displayed value / text in LabelledValue can be selected and copied

This commit is contained in:
dankito 2020-09-28 20:01:22 +02:00
parent 74d30df248
commit 1e812e4b8d
6 changed files with 117 additions and 19 deletions

View File

@ -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 = "<group>"; };
361116A7250562BE00315620 /* FaceIDButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaceIDButton.swift; sourceTree = "<group>"; };
361116A9250562CF00315620 /* TouchIDButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchIDButton.swift; sourceTree = "<group>"; };
361282C125223D7700392A4D /* SelectableText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableText.swift; sourceTree = "<group>"; };
361282C52522438B00392A4D /* SelectableUILabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableUILabel.swift; sourceTree = "<group>"; };
36428CFE251BEC4C009DE1AE /* SectionWithoutBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionWithoutBackground.swift; sourceTree = "<group>"; };
3642F0092500F5AE005186FE /* Divider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Divider.swift; sourceTree = "<group>"; };
3642F00B25010021005186FE /* UIKitActivityIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitActivityIndicator.swift; sourceTree = "<group>"; };
@ -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 = "<group>";
@ -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 */,

View File

@ -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
}
}

View File

@ -14,7 +14,7 @@ struct LabelledObject<Content: View>: View {
}
var body: some View {
HStack {
HStack(alignment: .center) {
Text(label)
Spacer()

View File

@ -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()
}

View File

@ -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
}
}

View File

@ -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
}
})
}