mirror of
https://github.com/passepartoutvpn/wireguard-apple.git
synced 2025-01-17 22:09:06 +00:00
KeyValueCells now share code
Signed-off-by: Eric Kuck <eric@bluelinelabs.com>
This commit is contained in:
parent
cb051f695d
commit
ccd8cfe478
@ -8,8 +8,7 @@
|
|||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
5F45417D21C1B23600994C13 /* UITableViewCell+Reuse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F45417C21C1B23600994C13 /* UITableViewCell+Reuse.swift */; };
|
5F45417D21C1B23600994C13 /* UITableViewCell+Reuse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F45417C21C1B23600994C13 /* UITableViewCell+Reuse.swift */; };
|
||||||
5F45418A21C2D45B00994C13 /* EditableKeyValueCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F45418921C2D45B00994C13 /* EditableKeyValueCell.swift */; };
|
5F45418C21C2D48200994C13 /* TunnelEditKeyValueCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F45418B21C2D48200994C13 /* TunnelEditKeyValueCell.swift */; };
|
||||||
5F45418C21C2D48200994C13 /* TunnelEditReadOnlyKeyValueCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F45418B21C2D48200994C13 /* TunnelEditReadOnlyKeyValueCell.swift */; };
|
|
||||||
5F45419021C2D53800994C13 /* SwitchCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F45418F21C2D53800994C13 /* SwitchCell.swift */; };
|
5F45419021C2D53800994C13 /* SwitchCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F45418F21C2D53800994C13 /* SwitchCell.swift */; };
|
||||||
5F45419221C2D55800994C13 /* CheckmarkCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F45419121C2D55800994C13 /* CheckmarkCell.swift */; };
|
5F45419221C2D55800994C13 /* CheckmarkCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F45419121C2D55800994C13 /* CheckmarkCell.swift */; };
|
||||||
5F45419821C2D60500994C13 /* KeyValueCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F45419721C2D60500994C13 /* KeyValueCell.swift */; };
|
5F45419821C2D60500994C13 /* KeyValueCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F45419721C2D60500994C13 /* KeyValueCell.swift */; };
|
||||||
@ -17,8 +16,6 @@
|
|||||||
5F4541A221C2D6DF00994C13 /* BorderedTextButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F4541A121C2D6DF00994C13 /* BorderedTextButton.swift */; };
|
5F4541A221C2D6DF00994C13 /* BorderedTextButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F4541A121C2D6DF00994C13 /* BorderedTextButton.swift */; };
|
||||||
5F4541A621C4449E00994C13 /* ButtonCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F4541A521C4449E00994C13 /* ButtonCell.swift */; };
|
5F4541A621C4449E00994C13 /* ButtonCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F4541A521C4449E00994C13 /* ButtonCell.swift */; };
|
||||||
5F4541A921C451D100994C13 /* TunnelStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F4541A821C451D100994C13 /* TunnelStatus.swift */; };
|
5F4541A921C451D100994C13 /* TunnelStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F4541A821C451D100994C13 /* TunnelStatus.swift */; };
|
||||||
6BB8400421892C920003598F /* CopyableLabelTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BB8400321892C920003598F /* CopyableLabelTableViewCell.swift */; };
|
|
||||||
6F0068572191AFD200419BE9 /* ScrollableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F0068562191AFD200419BE9 /* ScrollableLabel.swift */; };
|
|
||||||
6F5A2B4621AFDED40081EDD8 /* FileManager+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F5A2B4421AFDE020081EDD8 /* FileManager+Extension.swift */; };
|
6F5A2B4621AFDED40081EDD8 /* FileManager+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F5A2B4421AFDE020081EDD8 /* FileManager+Extension.swift */; };
|
||||||
6F5A2B4821AFF49A0081EDD8 /* FileManager+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F5A2B4421AFDE020081EDD8 /* FileManager+Extension.swift */; };
|
6F5A2B4821AFF49A0081EDD8 /* FileManager+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F5A2B4421AFDE020081EDD8 /* FileManager+Extension.swift */; };
|
||||||
6F5D0C1D218352EF000F85AD /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F5D0C1C218352EF000F85AD /* PacketTunnelProvider.swift */; };
|
6F5D0C1D218352EF000F85AD /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F5D0C1C218352EF000F85AD /* PacketTunnelProvider.swift */; };
|
||||||
@ -105,8 +102,7 @@
|
|||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
5F45417C21C1B23600994C13 /* UITableViewCell+Reuse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableViewCell+Reuse.swift"; sourceTree = "<group>"; };
|
5F45417C21C1B23600994C13 /* UITableViewCell+Reuse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableViewCell+Reuse.swift"; sourceTree = "<group>"; };
|
||||||
5F45418921C2D45B00994C13 /* EditableKeyValueCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditableKeyValueCell.swift; sourceTree = "<group>"; };
|
5F45418B21C2D48200994C13 /* TunnelEditKeyValueCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelEditKeyValueCell.swift; sourceTree = "<group>"; };
|
||||||
5F45418B21C2D48200994C13 /* TunnelEditReadOnlyKeyValueCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelEditReadOnlyKeyValueCell.swift; sourceTree = "<group>"; };
|
|
||||||
5F45418F21C2D53800994C13 /* SwitchCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchCell.swift; sourceTree = "<group>"; };
|
5F45418F21C2D53800994C13 /* SwitchCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchCell.swift; sourceTree = "<group>"; };
|
||||||
5F45419121C2D55800994C13 /* CheckmarkCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckmarkCell.swift; sourceTree = "<group>"; };
|
5F45419121C2D55800994C13 /* CheckmarkCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckmarkCell.swift; sourceTree = "<group>"; };
|
||||||
5F45419721C2D60500994C13 /* KeyValueCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyValueCell.swift; sourceTree = "<group>"; };
|
5F45419721C2D60500994C13 /* KeyValueCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyValueCell.swift; sourceTree = "<group>"; };
|
||||||
@ -114,8 +110,6 @@
|
|||||||
5F4541A121C2D6DF00994C13 /* BorderedTextButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BorderedTextButton.swift; sourceTree = "<group>"; };
|
5F4541A121C2D6DF00994C13 /* BorderedTextButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BorderedTextButton.swift; sourceTree = "<group>"; };
|
||||||
5F4541A521C4449E00994C13 /* ButtonCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonCell.swift; sourceTree = "<group>"; };
|
5F4541A521C4449E00994C13 /* ButtonCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonCell.swift; sourceTree = "<group>"; };
|
||||||
5F4541A821C451D100994C13 /* TunnelStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelStatus.swift; sourceTree = "<group>"; };
|
5F4541A821C451D100994C13 /* TunnelStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelStatus.swift; sourceTree = "<group>"; };
|
||||||
6BB8400321892C920003598F /* CopyableLabelTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CopyableLabelTableViewCell.swift; sourceTree = "<group>"; };
|
|
||||||
6F0068562191AFD200419BE9 /* ScrollableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollableLabel.swift; sourceTree = "<group>"; };
|
|
||||||
6F5A2B4421AFDE020081EDD8 /* FileManager+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+Extension.swift"; sourceTree = "<group>"; };
|
6F5A2B4421AFDE020081EDD8 /* FileManager+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+Extension.swift"; sourceTree = "<group>"; };
|
||||||
6F5D0C1421832391000F85AD /* DNSResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSResolver.swift; sourceTree = "<group>"; };
|
6F5D0C1421832391000F85AD /* DNSResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSResolver.swift; sourceTree = "<group>"; };
|
||||||
6F5D0C1A218352EF000F85AD /* WireGuardNetworkExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WireGuardNetworkExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
6F5D0C1A218352EF000F85AD /* WireGuardNetworkExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WireGuardNetworkExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@ -198,14 +192,11 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
5F45419F21C2D6B700994C13 /* TunnelListCell.swift */,
|
5F45419F21C2D6B700994C13 /* TunnelListCell.swift */,
|
||||||
5F45418B21C2D48200994C13 /* TunnelEditReadOnlyKeyValueCell.swift */,
|
5F45418B21C2D48200994C13 /* TunnelEditKeyValueCell.swift */,
|
||||||
6BB8400321892C920003598F /* CopyableLabelTableViewCell.swift */,
|
|
||||||
6F0068562191AFD200419BE9 /* ScrollableLabel.swift */,
|
|
||||||
5F45418F21C2D53800994C13 /* SwitchCell.swift */,
|
5F45418F21C2D53800994C13 /* SwitchCell.swift */,
|
||||||
5F45419721C2D60500994C13 /* KeyValueCell.swift */,
|
5F45419721C2D60500994C13 /* KeyValueCell.swift */,
|
||||||
5F4541A521C4449E00994C13 /* ButtonCell.swift */,
|
5F4541A521C4449E00994C13 /* ButtonCell.swift */,
|
||||||
5F45419121C2D55800994C13 /* CheckmarkCell.swift */,
|
5F45419121C2D55800994C13 /* CheckmarkCell.swift */,
|
||||||
5F45418921C2D45B00994C13 /* EditableKeyValueCell.swift */,
|
|
||||||
5F4541A121C2D6DF00994C13 /* BorderedTextButton.swift */,
|
5F4541A121C2D6DF00994C13 /* BorderedTextButton.swift */,
|
||||||
);
|
);
|
||||||
path = View;
|
path = View;
|
||||||
@ -662,17 +653,14 @@
|
|||||||
6F6899AC218099F00012E523 /* WgQuickConfigFileParser.swift in Sources */,
|
6F6899AC218099F00012E523 /* WgQuickConfigFileParser.swift in Sources */,
|
||||||
6F7774E421718281006A79B3 /* TunnelsListTableViewController.swift in Sources */,
|
6F7774E421718281006A79B3 /* TunnelsListTableViewController.swift in Sources */,
|
||||||
6F7774EF21722D97006A79B3 /* TunnelsManager.swift in Sources */,
|
6F7774EF21722D97006A79B3 /* TunnelsManager.swift in Sources */,
|
||||||
6BB8400421892C920003598F /* CopyableLabelTableViewCell.swift in Sources */,
|
|
||||||
5F45417D21C1B23600994C13 /* UITableViewCell+Reuse.swift in Sources */,
|
5F45417D21C1B23600994C13 /* UITableViewCell+Reuse.swift in Sources */,
|
||||||
5F45419221C2D55800994C13 /* CheckmarkCell.swift in Sources */,
|
5F45419221C2D55800994C13 /* CheckmarkCell.swift in Sources */,
|
||||||
6FE254FF219C60290028284D /* ZipExporter.swift in Sources */,
|
6FE254FF219C60290028284D /* ZipExporter.swift in Sources */,
|
||||||
6F693A562179E556008551C1 /* Endpoint.swift in Sources */,
|
6F693A562179E556008551C1 /* Endpoint.swift in Sources */,
|
||||||
6F0068572191AFD200419BE9 /* ScrollableLabel.swift in Sources */,
|
|
||||||
6FDEF7E62185EFB200D8FBF6 /* QRScanViewController.swift in Sources */,
|
6FDEF7E62185EFB200D8FBF6 /* QRScanViewController.swift in Sources */,
|
||||||
6FFA5D952194454A0001E2F7 /* NETunnelProviderProtocol+Extension.swift in Sources */,
|
6FFA5D952194454A0001E2F7 /* NETunnelProviderProtocol+Extension.swift in Sources */,
|
||||||
5F4541A921C451D100994C13 /* TunnelStatus.swift in Sources */,
|
5F4541A921C451D100994C13 /* TunnelStatus.swift in Sources */,
|
||||||
6F61F1E921B932F700483816 /* WireGuardAppError.swift in Sources */,
|
6F61F1E921B932F700483816 /* WireGuardAppError.swift in Sources */,
|
||||||
5F45418A21C2D45B00994C13 /* EditableKeyValueCell.swift in Sources */,
|
|
||||||
6F6899A62180447E0012E523 /* x25519.c in Sources */,
|
6F6899A62180447E0012E523 /* x25519.c in Sources */,
|
||||||
6F7774E2217181B1006A79B3 /* AppDelegate.swift in Sources */,
|
6F7774E2217181B1006A79B3 /* AppDelegate.swift in Sources */,
|
||||||
6FDEF80021863C0100D8FBF6 /* ioapi.c in Sources */,
|
6FDEF80021863C0100D8FBF6 /* ioapi.c in Sources */,
|
||||||
@ -683,7 +671,7 @@
|
|||||||
5F45419821C2D60500994C13 /* KeyValueCell.swift in Sources */,
|
5F45419821C2D60500994C13 /* KeyValueCell.swift in Sources */,
|
||||||
6F919EC3218A2AE90023B400 /* ErrorPresenter.swift in Sources */,
|
6F919EC3218A2AE90023B400 /* ErrorPresenter.swift in Sources */,
|
||||||
6F5A2B4821AFF49A0081EDD8 /* FileManager+Extension.swift in Sources */,
|
6F5A2B4821AFF49A0081EDD8 /* FileManager+Extension.swift in Sources */,
|
||||||
5F45418C21C2D48200994C13 /* TunnelEditReadOnlyKeyValueCell.swift in Sources */,
|
5F45418C21C2D48200994C13 /* TunnelEditKeyValueCell.swift in Sources */,
|
||||||
6FDEF8082187442100D8FBF6 /* WgQuickConfigFileWriter.swift in Sources */,
|
6FDEF8082187442100D8FBF6 /* WgQuickConfigFileWriter.swift in Sources */,
|
||||||
6FE254FB219C10800028284D /* ZipImporter.swift in Sources */,
|
6FE254FB219C10800028284D /* ZipImporter.swift in Sources */,
|
||||||
6F7774EA217229DB006A79B3 /* IPAddressRange.swift in Sources */,
|
6F7774EA217229DB006A79B3 /* IPAddressRange.swift in Sources */,
|
||||||
|
@ -40,11 +40,12 @@ class BorderedTextButton: UIView {
|
|||||||
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
|
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
@objc func buttonTapped() {
|
@objc func buttonTapped() {
|
||||||
onTapped?()
|
onTapped?()
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
class CopyableLabelTableViewCell: UITableViewCell {
|
|
||||||
var copyableGesture = true
|
|
||||||
|
|
||||||
var textToCopy: String? {
|
|
||||||
fatalError("textToCopy must be implemented by subclass")
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
|
||||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
|
||||||
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture(_:)))
|
|
||||||
addGestureRecognizer(gestureRecognizer)
|
|
||||||
isUserInteractionEnabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - UIGestureRecognizer
|
|
||||||
@objc func handleTapGesture(_ recognizer: UIGestureRecognizer) {
|
|
||||||
if !copyableGesture {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard recognizer.state == .recognized else { return }
|
|
||||||
|
|
||||||
if let recognizerView = recognizer.view,
|
|
||||||
let recognizerSuperView = recognizerView.superview, recognizerView.becomeFirstResponder() {
|
|
||||||
let menuController = UIMenuController.shared
|
|
||||||
menuController.setTargetRect(detailTextLabel?.frame ?? recognizerView.frame, in: detailTextLabel?.superview ?? recognizerSuperView)
|
|
||||||
menuController.setMenuVisible(true, animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override var canBecomeFirstResponder: Bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
|
|
||||||
return (action == #selector(UIResponderStandardEditActions.copy(_:)))
|
|
||||||
}
|
|
||||||
|
|
||||||
override func copy(_ sender: Any?) {
|
|
||||||
UIPasteboard.general.string = textToCopy
|
|
||||||
}
|
|
||||||
|
|
||||||
override func prepareForReuse() {
|
|
||||||
super.prepareForReuse()
|
|
||||||
copyableGesture = true
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,158 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
class EditableKeyValueCell: UITableViewCell {
|
|
||||||
var key: String {
|
|
||||||
get { return keyLabel.text ?? "" }
|
|
||||||
set(value) { keyLabel.text = value }
|
|
||||||
}
|
|
||||||
var value: String {
|
|
||||||
get { return valueTextField.text ?? "" }
|
|
||||||
set(value) { valueTextField.text = value }
|
|
||||||
}
|
|
||||||
var placeholderText: String {
|
|
||||||
get { return valueTextField.placeholder ?? "" }
|
|
||||||
set(value) { valueTextField.placeholder = value }
|
|
||||||
}
|
|
||||||
var isValueValid = true {
|
|
||||||
didSet {
|
|
||||||
if isValueValid {
|
|
||||||
keyLabel.textColor = .black
|
|
||||||
} else {
|
|
||||||
keyLabel.textColor = .red
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var keyboardType: UIKeyboardType {
|
|
||||||
get { return valueTextField.keyboardType }
|
|
||||||
set(value) { valueTextField.keyboardType = value }
|
|
||||||
}
|
|
||||||
|
|
||||||
var onValueChanged: ((String) -> Void)?
|
|
||||||
var onValueBeingEdited: ((String) -> Void)?
|
|
||||||
|
|
||||||
let keyLabel: UILabel = {
|
|
||||||
let keyLabel = UILabel()
|
|
||||||
keyLabel.font = UIFont.preferredFont(forTextStyle: .body)
|
|
||||||
keyLabel.adjustsFontForContentSizeCategory = true
|
|
||||||
return keyLabel
|
|
||||||
}()
|
|
||||||
|
|
||||||
let valueTextField: UITextField = {
|
|
||||||
let valueTextField = UITextField()
|
|
||||||
valueTextField.font = UIFont.preferredFont(forTextStyle: .body)
|
|
||||||
valueTextField.adjustsFontForContentSizeCategory = true
|
|
||||||
valueTextField.autocapitalizationType = .none
|
|
||||||
valueTextField.autocorrectionType = .no
|
|
||||||
valueTextField.spellCheckingType = .no
|
|
||||||
return valueTextField
|
|
||||||
}()
|
|
||||||
|
|
||||||
var isStackedHorizontally = false
|
|
||||||
var isStackedVertically = false
|
|
||||||
var contentSizeBasedConstraints = [NSLayoutConstraint]()
|
|
||||||
|
|
||||||
private var textFieldValueOnBeginEditing: String = ""
|
|
||||||
|
|
||||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
|
||||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
|
||||||
|
|
||||||
contentView.addSubview(keyLabel)
|
|
||||||
keyLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
keyLabel.textAlignment = .right
|
|
||||||
let widthRatioConstraint = NSLayoutConstraint(item: keyLabel, attribute: .width,
|
|
||||||
relatedBy: .equal,
|
|
||||||
toItem: self, attribute: .width,
|
|
||||||
multiplier: 0.4, constant: 0)
|
|
||||||
// The "Persistent Keepalive" key doesn't fit into 0.4 * width on the iPhone SE,
|
|
||||||
// so set a CR priority > the 0.4-constraint's priority.
|
|
||||||
widthRatioConstraint.priority = .defaultHigh + 1
|
|
||||||
keyLabel.setContentCompressionResistancePriority(.defaultHigh + 2, for: .horizontal)
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
keyLabel.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor),
|
|
||||||
keyLabel.topAnchor.constraint(equalToSystemSpacingBelow: contentView.layoutMarginsGuide.topAnchor, multiplier: 0.5),
|
|
||||||
widthRatioConstraint
|
|
||||||
])
|
|
||||||
|
|
||||||
contentView.addSubview(valueTextField)
|
|
||||||
valueTextField.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
valueTextField.rightAnchor.constraint(equalTo: contentView.layoutMarginsGuide.rightAnchor),
|
|
||||||
contentView.layoutMarginsGuide.bottomAnchor.constraint(equalToSystemSpacingBelow: valueTextField.bottomAnchor, multiplier: 0.5)
|
|
||||||
])
|
|
||||||
valueTextField.delegate = self
|
|
||||||
|
|
||||||
configureForContentSize()
|
|
||||||
}
|
|
||||||
|
|
||||||
func configureForContentSize() {
|
|
||||||
var constraints = [NSLayoutConstraint]()
|
|
||||||
if traitCollection.preferredContentSizeCategory.isAccessibilityCategory {
|
|
||||||
// Stack vertically
|
|
||||||
if !isStackedVertically {
|
|
||||||
constraints = [
|
|
||||||
valueTextField.topAnchor.constraint(equalToSystemSpacingBelow: keyLabel.bottomAnchor, multiplier: 0.5),
|
|
||||||
valueTextField.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor),
|
|
||||||
keyLabel.rightAnchor.constraint(equalTo: contentView.layoutMarginsGuide.rightAnchor)
|
|
||||||
]
|
|
||||||
isStackedVertically = true
|
|
||||||
isStackedHorizontally = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Stack horizontally
|
|
||||||
if !isStackedHorizontally {
|
|
||||||
constraints = [
|
|
||||||
contentView.layoutMarginsGuide.bottomAnchor.constraint(equalToSystemSpacingBelow: keyLabel.bottomAnchor, multiplier: 0.5),
|
|
||||||
valueTextField.leftAnchor.constraint(equalToSystemSpacingAfter: keyLabel.rightAnchor, multiplier: 1),
|
|
||||||
valueTextField.topAnchor.constraint(equalToSystemSpacingBelow: contentView.layoutMarginsGuide.topAnchor, multiplier: 0.5)
|
|
||||||
]
|
|
||||||
isStackedHorizontally = true
|
|
||||||
isStackedVertically = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !constraints.isEmpty {
|
|
||||||
NSLayoutConstraint.deactivate(contentSizeBasedConstraints)
|
|
||||||
NSLayoutConstraint.activate(constraints)
|
|
||||||
contentSizeBasedConstraints = constraints
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override func prepareForReuse() {
|
|
||||||
super.prepareForReuse()
|
|
||||||
key = ""
|
|
||||||
value = ""
|
|
||||||
placeholderText = ""
|
|
||||||
isValueValid = true
|
|
||||||
keyboardType = .default
|
|
||||||
onValueChanged = nil
|
|
||||||
onValueBeingEdited = nil
|
|
||||||
configureForContentSize()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension EditableKeyValueCell: UITextFieldDelegate {
|
|
||||||
func textFieldDidBeginEditing(_ textField: UITextField) {
|
|
||||||
textFieldValueOnBeginEditing = textField.text ?? ""
|
|
||||||
isValueValid = true
|
|
||||||
}
|
|
||||||
func textFieldDidEndEditing(_ textField: UITextField) {
|
|
||||||
let isModified = (textField.text ?? "" != textFieldValueOnBeginEditing)
|
|
||||||
guard isModified else { return }
|
|
||||||
if let onValueChanged = onValueChanged {
|
|
||||||
onValueChanged(textField.text ?? "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
|
||||||
if let onValueBeingEdited = onValueBeingEdited {
|
|
||||||
let modifiedText = ((textField.text ?? "") as NSString).replacingCharacters(in: range, with: string)
|
|
||||||
onValueBeingEdited(modifiedText)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,73 +3,132 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class KeyValueCell: CopyableLabelTableViewCell {
|
class KeyValueCell: UITableViewCell {
|
||||||
var key: String {
|
|
||||||
get { return keyLabel.text ?? "" }
|
|
||||||
set(value) { keyLabel.text = value }
|
|
||||||
}
|
|
||||||
var value: String {
|
|
||||||
get { return valueLabel.text }
|
|
||||||
set(value) { valueLabel.text = value }
|
|
||||||
}
|
|
||||||
|
|
||||||
override var textToCopy: String? {
|
|
||||||
return valueLabel.text
|
|
||||||
}
|
|
||||||
|
|
||||||
let keyLabel: UILabel = {
|
let keyLabel: UILabel = {
|
||||||
let keyLabel = UILabel()
|
let keyLabel = UILabel()
|
||||||
keyLabel.font = UIFont.preferredFont(forTextStyle: .body)
|
keyLabel.font = UIFont.preferredFont(forTextStyle: .body)
|
||||||
keyLabel.adjustsFontForContentSizeCategory = true
|
keyLabel.adjustsFontForContentSizeCategory = true
|
||||||
keyLabel.textColor = .black
|
keyLabel.textColor = .black
|
||||||
|
keyLabel.textAlignment = .left
|
||||||
return keyLabel
|
return keyLabel
|
||||||
}()
|
}()
|
||||||
|
|
||||||
let valueLabel: ScrollableLabel = {
|
let valueLabelScrollView: UIScrollView = {
|
||||||
let valueLabel = ScrollableLabel()
|
let scrollView = UIScrollView(frame: .zero)
|
||||||
valueLabel.label.font = UIFont.preferredFont(forTextStyle: .body)
|
scrollView.isDirectionalLockEnabled = true
|
||||||
valueLabel.label.adjustsFontForContentSizeCategory = true
|
scrollView.showsHorizontalScrollIndicator = false
|
||||||
valueLabel.textColor = .gray
|
scrollView.showsVerticalScrollIndicator = false
|
||||||
return valueLabel
|
return scrollView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
let valueTextField: UITextField = {
|
||||||
|
let valueTextField = UITextField()
|
||||||
|
valueTextField.textAlignment = .right
|
||||||
|
valueTextField.isEnabled = false
|
||||||
|
valueTextField.font = UIFont.preferredFont(forTextStyle: .body)
|
||||||
|
valueTextField.adjustsFontForContentSizeCategory = true
|
||||||
|
valueTextField.autocapitalizationType = .none
|
||||||
|
valueTextField.autocorrectionType = .no
|
||||||
|
valueTextField.spellCheckingType = .no
|
||||||
|
valueTextField.textColor = .gray
|
||||||
|
return valueTextField
|
||||||
|
}()
|
||||||
|
|
||||||
|
var copyableGesture = true
|
||||||
|
|
||||||
|
var key: String {
|
||||||
|
get { return keyLabel.text ?? "" }
|
||||||
|
set(value) { keyLabel.text = value }
|
||||||
|
}
|
||||||
|
var value: String {
|
||||||
|
get { return valueTextField.text ?? "" }
|
||||||
|
set(value) { valueTextField.text = value }
|
||||||
|
}
|
||||||
|
var placeholderText: String {
|
||||||
|
get { return valueTextField.placeholder ?? "" }
|
||||||
|
set(value) { valueTextField.placeholder = value }
|
||||||
|
}
|
||||||
|
var keyboardType: UIKeyboardType {
|
||||||
|
get { return valueTextField.keyboardType }
|
||||||
|
set(value) { valueTextField.keyboardType = value }
|
||||||
|
}
|
||||||
|
|
||||||
|
var isValueValid = true {
|
||||||
|
didSet {
|
||||||
|
if isValueValid {
|
||||||
|
keyLabel.textColor = .black
|
||||||
|
} else {
|
||||||
|
keyLabel.textColor = .red
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var isStackedHorizontally = false
|
var isStackedHorizontally = false
|
||||||
var isStackedVertically = false
|
var isStackedVertically = false
|
||||||
var contentSizeBasedConstraints = [NSLayoutConstraint]()
|
var contentSizeBasedConstraints = [NSLayoutConstraint]()
|
||||||
|
|
||||||
|
var onValueChanged: ((String) -> Void)?
|
||||||
|
var onValueBeingEdited: ((String) -> Void)?
|
||||||
|
|
||||||
|
private var textFieldValueOnBeginEditing: String = ""
|
||||||
|
|
||||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||||
|
|
||||||
contentView.addSubview(keyLabel)
|
contentView.addSubview(keyLabel)
|
||||||
keyLabel.translatesAutoresizingMaskIntoConstraints = false
|
keyLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||||
keyLabel.textAlignment = .left
|
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
keyLabel.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor),
|
keyLabel.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor),
|
||||||
keyLabel.topAnchor.constraint(equalToSystemSpacingBelow: contentView.layoutMarginsGuide.topAnchor, multiplier: 0.5)
|
keyLabel.topAnchor.constraint(equalToSystemSpacingBelow: contentView.layoutMarginsGuide.topAnchor, multiplier: 0.5)
|
||||||
])
|
])
|
||||||
|
|
||||||
contentView.addSubview(valueLabel)
|
valueTextField.delegate = self
|
||||||
valueLabel.translatesAutoresizingMaskIntoConstraints = false
|
valueLabelScrollView.addSubview(valueTextField)
|
||||||
|
valueTextField.translatesAutoresizingMaskIntoConstraints = false
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
valueLabel.rightAnchor.constraint(equalTo: contentView.layoutMarginsGuide.rightAnchor),
|
valueTextField.leftAnchor.constraint(equalTo: valueLabelScrollView.contentLayoutGuide.leftAnchor),
|
||||||
contentView.layoutMarginsGuide.bottomAnchor.constraint(equalToSystemSpacingBelow: valueLabel.bottomAnchor, multiplier: 0.5)
|
valueTextField.topAnchor.constraint(equalTo: valueLabelScrollView.contentLayoutGuide.topAnchor),
|
||||||
|
valueTextField.bottomAnchor.constraint(equalTo: valueLabelScrollView.contentLayoutGuide.bottomAnchor),
|
||||||
|
valueTextField.rightAnchor.constraint(equalTo: valueLabelScrollView.contentLayoutGuide.rightAnchor),
|
||||||
|
valueTextField.heightAnchor.constraint(equalTo: valueLabelScrollView.heightAnchor)
|
||||||
|
])
|
||||||
|
let expandToFitValueLabelConstraint = NSLayoutConstraint(item: valueTextField, attribute: .width, relatedBy: .equal, toItem: valueLabelScrollView, attribute: .width, multiplier: 1, constant: 0)
|
||||||
|
expandToFitValueLabelConstraint.priority = .defaultLow + 1
|
||||||
|
expandToFitValueLabelConstraint.isActive = true
|
||||||
|
|
||||||
|
contentView.addSubview(valueLabelScrollView)
|
||||||
|
|
||||||
|
contentView.addSubview(valueLabelScrollView)
|
||||||
|
valueLabelScrollView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
valueLabelScrollView.rightAnchor.constraint(equalTo: contentView.layoutMarginsGuide.rightAnchor),
|
||||||
|
contentView.layoutMarginsGuide.bottomAnchor.constraint(equalToSystemSpacingBelow: valueLabelScrollView.bottomAnchor, multiplier: 0.5)
|
||||||
])
|
])
|
||||||
|
|
||||||
keyLabel.setContentCompressionResistancePriority(.defaultHigh + 1, for: .horizontal)
|
keyLabel.setContentCompressionResistancePriority(.defaultHigh + 1, for: .horizontal)
|
||||||
keyLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
keyLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
||||||
valueLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
valueLabelScrollView.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
||||||
|
|
||||||
|
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture(_:)))
|
||||||
|
addGestureRecognizer(gestureRecognizer)
|
||||||
|
isUserInteractionEnabled = true
|
||||||
|
|
||||||
configureForContentSize()
|
configureForContentSize()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
func configureForContentSize() {
|
func configureForContentSize() {
|
||||||
var constraints = [NSLayoutConstraint]()
|
var constraints = [NSLayoutConstraint]()
|
||||||
if traitCollection.preferredContentSizeCategory.isAccessibilityCategory {
|
if traitCollection.preferredContentSizeCategory.isAccessibilityCategory {
|
||||||
// Stack vertically
|
// Stack vertically
|
||||||
if !isStackedVertically {
|
if !isStackedVertically {
|
||||||
constraints = [
|
constraints = [
|
||||||
valueLabel.topAnchor.constraint(equalToSystemSpacingBelow: keyLabel.bottomAnchor, multiplier: 0.5),
|
valueLabelScrollView.topAnchor.constraint(equalToSystemSpacingBelow: keyLabel.bottomAnchor, multiplier: 0.5),
|
||||||
valueLabel.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor),
|
valueLabelScrollView.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor),
|
||||||
keyLabel.rightAnchor.constraint(equalTo: contentView.layoutMarginsGuide.rightAnchor)
|
keyLabel.rightAnchor.constraint(equalTo: contentView.layoutMarginsGuide.rightAnchor)
|
||||||
]
|
]
|
||||||
isStackedVertically = true
|
isStackedVertically = true
|
||||||
@ -80,8 +139,8 @@ class KeyValueCell: CopyableLabelTableViewCell {
|
|||||||
if !isStackedHorizontally {
|
if !isStackedHorizontally {
|
||||||
constraints = [
|
constraints = [
|
||||||
contentView.layoutMarginsGuide.bottomAnchor.constraint(equalToSystemSpacingBelow: keyLabel.bottomAnchor, multiplier: 0.5),
|
contentView.layoutMarginsGuide.bottomAnchor.constraint(equalToSystemSpacingBelow: keyLabel.bottomAnchor, multiplier: 0.5),
|
||||||
valueLabel.leftAnchor.constraint(equalToSystemSpacingAfter: keyLabel.rightAnchor, multiplier: 1),
|
valueLabelScrollView.leftAnchor.constraint(equalToSystemSpacingAfter: keyLabel.rightAnchor, multiplier: 1),
|
||||||
valueLabel.topAnchor.constraint(equalToSystemSpacingBelow: contentView.layoutMarginsGuide.topAnchor, multiplier: 0.5)
|
valueLabelScrollView.topAnchor.constraint(equalToSystemSpacingBelow: contentView.layoutMarginsGuide.topAnchor, multiplier: 0.5)
|
||||||
]
|
]
|
||||||
isStackedHorizontally = true
|
isStackedHorizontally = true
|
||||||
isStackedVertically = false
|
isStackedVertically = false
|
||||||
@ -94,14 +153,65 @@ class KeyValueCell: CopyableLabelTableViewCell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
@objc func handleTapGesture(_ recognizer: UIGestureRecognizer) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
if !copyableGesture {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard recognizer.state == .recognized else { return }
|
||||||
|
|
||||||
|
if let recognizerView = recognizer.view,
|
||||||
|
let recognizerSuperView = recognizerView.superview, recognizerView.becomeFirstResponder() {
|
||||||
|
let menuController = UIMenuController.shared
|
||||||
|
menuController.setTargetRect(detailTextLabel?.frame ?? recognizerView.frame, in: detailTextLabel?.superview ?? recognizerSuperView)
|
||||||
|
menuController.setMenuVisible(true, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var canBecomeFirstResponder: Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
|
||||||
|
return (action == #selector(UIResponderStandardEditActions.copy(_:)))
|
||||||
|
}
|
||||||
|
|
||||||
|
override func copy(_ sender: Any?) {
|
||||||
|
UIPasteboard.general.string = valueTextField.text
|
||||||
}
|
}
|
||||||
|
|
||||||
override func prepareForReuse() {
|
override func prepareForReuse() {
|
||||||
super.prepareForReuse()
|
super.prepareForReuse()
|
||||||
|
copyableGesture = true
|
||||||
|
placeholderText = ""
|
||||||
|
isValueValid = true
|
||||||
|
keyboardType = .default
|
||||||
|
onValueChanged = nil
|
||||||
|
onValueBeingEdited = nil
|
||||||
key = ""
|
key = ""
|
||||||
value = ""
|
value = ""
|
||||||
configureForContentSize()
|
configureForContentSize()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension KeyValueCell: UITextFieldDelegate {
|
||||||
|
|
||||||
|
func textFieldDidBeginEditing(_ textField: UITextField) {
|
||||||
|
textFieldValueOnBeginEditing = textField.text ?? ""
|
||||||
|
isValueValid = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func textFieldDidEndEditing(_ textField: UITextField) {
|
||||||
|
let isModified = textField.text ?? "" != textFieldValueOnBeginEditing
|
||||||
|
guard isModified else { return }
|
||||||
|
onValueChanged?(textField.text ?? "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
||||||
|
if let onValueBeingEdited = onValueBeingEdited {
|
||||||
|
let modifiedText = ((textField.text ?? "") as NSString).replacingCharacters(in: range, with: string)
|
||||||
|
onValueBeingEdited(modifiedText)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
class ScrollableLabel: UIScrollView {
|
|
||||||
var text: String {
|
|
||||||
get { return label.text ?? "" }
|
|
||||||
set(value) { label.text = value }
|
|
||||||
}
|
|
||||||
var textColor: UIColor {
|
|
||||||
get { return label.textColor }
|
|
||||||
set(value) { label.textColor = value }
|
|
||||||
}
|
|
||||||
|
|
||||||
let label: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
label.textAlignment = .right
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
init() {
|
|
||||||
super.init(frame: CGRect.zero)
|
|
||||||
|
|
||||||
isDirectionalLockEnabled = true
|
|
||||||
showsHorizontalScrollIndicator = false
|
|
||||||
showsVerticalScrollIndicator = false
|
|
||||||
|
|
||||||
addSubview(label)
|
|
||||||
label.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
label.leftAnchor.constraint(equalTo: contentLayoutGuide.leftAnchor),
|
|
||||||
label.topAnchor.constraint(equalTo: contentLayoutGuide.topAnchor),
|
|
||||||
label.bottomAnchor.constraint(equalTo: contentLayoutGuide.bottomAnchor),
|
|
||||||
label.rightAnchor.constraint(equalTo: contentLayoutGuide.rightAnchor),
|
|
||||||
label.heightAnchor.constraint(equalTo: heightAnchor)
|
|
||||||
])
|
|
||||||
|
|
||||||
let expandToFitValueLabelConstraint = NSLayoutConstraint(item: label, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 1, constant: 0)
|
|
||||||
expandToFitValueLabelConstraint.priority = .defaultLow + 1
|
|
||||||
expandToFitValueLabelConstraint.isActive = true
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
}
|
|
42
WireGuard/WireGuard/UI/iOS/View/TunnelEditKeyValueCell.swift
Normal file
42
WireGuard/WireGuard/UI/iOS/View/TunnelEditKeyValueCell.swift
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class TunnelEditKeyValueCell: KeyValueCell {
|
||||||
|
|
||||||
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||||
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||||
|
|
||||||
|
keyLabel.textAlignment = .right
|
||||||
|
valueTextField.textAlignment = .left
|
||||||
|
|
||||||
|
let widthRatioConstraint = NSLayoutConstraint(item: keyLabel, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 0.4, constant: 0)
|
||||||
|
// In case the key doesn't fit into 0.4 * width,
|
||||||
|
// so set a CR priority > the 0.4-constraint's priority.
|
||||||
|
widthRatioConstraint.priority = .defaultHigh + 1
|
||||||
|
widthRatioConstraint.isActive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class TunnelEditEditableKeyValueCell: TunnelEditKeyValueCell {
|
||||||
|
|
||||||
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||||
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||||
|
|
||||||
|
valueTextField.textColor = .black
|
||||||
|
valueTextField.isEnabled = true
|
||||||
|
valueLabelScrollView.isScrollEnabled = false
|
||||||
|
valueTextField.widthAnchor.constraint(equalTo: valueLabelScrollView.widthAnchor).isActive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,74 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
class TunnelEditReadOnlyKeyValueCell: CopyableLabelTableViewCell {
|
|
||||||
var key: String {
|
|
||||||
get { return keyLabel.text ?? "" }
|
|
||||||
set(value) { keyLabel.text = value }
|
|
||||||
}
|
|
||||||
var value: String {
|
|
||||||
get { return valueLabel.text }
|
|
||||||
set(value) { valueLabel.text = value }
|
|
||||||
}
|
|
||||||
|
|
||||||
override var textToCopy: String? {
|
|
||||||
return valueLabel.text
|
|
||||||
}
|
|
||||||
|
|
||||||
let keyLabel: UILabel = {
|
|
||||||
let keyLabel = UILabel()
|
|
||||||
keyLabel.font = UIFont.preferredFont(forTextStyle: .body)
|
|
||||||
keyLabel.adjustsFontForContentSizeCategory = true
|
|
||||||
keyLabel.textColor = .gray
|
|
||||||
return keyLabel
|
|
||||||
}()
|
|
||||||
|
|
||||||
let valueLabel: ScrollableLabel = {
|
|
||||||
let valueLabel = ScrollableLabel()
|
|
||||||
valueLabel.label.font = UIFont.preferredFont(forTextStyle: .body)
|
|
||||||
valueLabel.label.adjustsFontForContentSizeCategory = true
|
|
||||||
valueLabel.textColor = .gray
|
|
||||||
return valueLabel
|
|
||||||
}()
|
|
||||||
|
|
||||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
|
||||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
|
||||||
|
|
||||||
contentView.addSubview(keyLabel)
|
|
||||||
keyLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
keyLabel.textAlignment = .right
|
|
||||||
let widthRatioConstraint = NSLayoutConstraint(item: keyLabel, attribute: .width,
|
|
||||||
relatedBy: .equal,
|
|
||||||
toItem: self, attribute: .width,
|
|
||||||
multiplier: 0.4, constant: 0)
|
|
||||||
// In case the key doesn't fit into 0.4 * width,
|
|
||||||
// so set a CR priority > the 0.4-constraint's priority.
|
|
||||||
widthRatioConstraint.priority = .defaultHigh + 1
|
|
||||||
keyLabel.setContentCompressionResistancePriority(.defaultHigh + 2, for: .horizontal)
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
keyLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
|
|
||||||
keyLabel.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor),
|
|
||||||
widthRatioConstraint
|
|
||||||
])
|
|
||||||
|
|
||||||
contentView.addSubview(valueLabel)
|
|
||||||
valueLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
valueLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
|
|
||||||
valueLabel.leftAnchor.constraint(equalToSystemSpacingAfter: keyLabel.rightAnchor, multiplier: 1),
|
|
||||||
valueLabel.rightAnchor.constraint(equalTo: contentView.layoutMarginsGuide.rightAnchor)
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override func prepareForReuse() {
|
|
||||||
super.prepareForReuse()
|
|
||||||
key = ""
|
|
||||||
value = ""
|
|
||||||
}
|
|
||||||
}
|
|
@ -79,8 +79,8 @@ class TunnelEditTableViewController: UITableViewController {
|
|||||||
tableView.estimatedRowHeight = 44
|
tableView.estimatedRowHeight = 44
|
||||||
tableView.rowHeight = UITableView.automaticDimension
|
tableView.rowHeight = UITableView.automaticDimension
|
||||||
|
|
||||||
tableView.register(EditableKeyValueCell.self)
|
tableView.register(TunnelEditKeyValueCell.self)
|
||||||
tableView.register(TunnelEditReadOnlyKeyValueCell.self)
|
tableView.register(TunnelEditEditableKeyValueCell.self)
|
||||||
tableView.register(ButtonCell.self)
|
tableView.register(ButtonCell.self)
|
||||||
tableView.register(SwitchCell.self)
|
tableView.register(SwitchCell.self)
|
||||||
tableView.register(CheckmarkCell.self)
|
tableView.register(CheckmarkCell.self)
|
||||||
@ -218,14 +218,14 @@ extension TunnelEditTableViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func publicKeyCell(for tableView: UITableView, at indexPath: IndexPath, with field: TunnelViewModel.InterfaceField) -> UITableViewCell {
|
private func publicKeyCell(for tableView: UITableView, at indexPath: IndexPath, with field: TunnelViewModel.InterfaceField) -> UITableViewCell {
|
||||||
let cell: TunnelEditReadOnlyKeyValueCell = tableView.dequeueReusableCell(for: indexPath)
|
let cell: TunnelEditKeyValueCell = tableView.dequeueReusableCell(for: indexPath)
|
||||||
cell.key = field.rawValue
|
cell.key = field.rawValue
|
||||||
cell.value = tunnelViewModel.interfaceData[field]
|
cell.value = tunnelViewModel.interfaceData[field]
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
|
||||||
private func interfaceFieldKeyValueCell(for tableView: UITableView, at indexPath: IndexPath, with field: TunnelViewModel.InterfaceField) -> UITableViewCell {
|
private func interfaceFieldKeyValueCell(for tableView: UITableView, at indexPath: IndexPath, with field: TunnelViewModel.InterfaceField) -> UITableViewCell {
|
||||||
let cell: EditableKeyValueCell = tableView.dequeueReusableCell(for: indexPath)
|
let cell: TunnelEditEditableKeyValueCell = tableView.dequeueReusableCell(for: indexPath)
|
||||||
cell.key = field.rawValue
|
cell.key = field.rawValue
|
||||||
|
|
||||||
switch field {
|
switch field {
|
||||||
@ -328,7 +328,7 @@ extension TunnelEditTableViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func peerFieldKeyValueCell(for tableView: UITableView, at indexPath: IndexPath, peerData: TunnelViewModel.PeerData, field: TunnelViewModel.PeerField) -> UITableViewCell {
|
private func peerFieldKeyValueCell(for tableView: UITableView, at indexPath: IndexPath, peerData: TunnelViewModel.PeerData, field: TunnelViewModel.PeerField) -> UITableViewCell {
|
||||||
let cell: EditableKeyValueCell = tableView.dequeueReusableCell(for: indexPath)
|
let cell: TunnelEditEditableKeyValueCell = tableView.dequeueReusableCell(for: indexPath)
|
||||||
cell.key = field.rawValue
|
cell.key = field.rawValue
|
||||||
|
|
||||||
switch field {
|
switch field {
|
||||||
|
Loading…
Reference in New Issue
Block a user