aboutsummaryrefslogtreecommitdiffstats
path: root/Sources/WireGuardApp/UI/iOS/View/TunnelListCell.swift
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2020-12-02 12:27:39 +0100
committerAndrej Mihajlov <and@mullvad.net>2020-12-03 13:32:24 +0100
commitec574085703ea1c8b2d4538596961beb910c4382 (patch)
tree73cf8bbdb74fe5575606664bccd0232ffa911803 /Sources/WireGuardApp/UI/iOS/View/TunnelListCell.swift
parentWireGuardKit: Assert that resolutionResults must not contain failures (diff)
downloadwireguard-apple-ec574085703ea1c8b2d4538596961beb910c4382.tar.xz
wireguard-apple-ec574085703ea1c8b2d4538596961beb910c4382.zip
Move all source files to `Sources/` and rename WireGuardKit targets
Signed-off-by: Andrej Mihajlov <and@mullvad.net>
Diffstat (limited to 'Sources/WireGuardApp/UI/iOS/View/TunnelListCell.swift')
-rw-r--r--Sources/WireGuardApp/UI/iOS/View/TunnelListCell.swift122
1 files changed, 122 insertions, 0 deletions
diff --git a/Sources/WireGuardApp/UI/iOS/View/TunnelListCell.swift b/Sources/WireGuardApp/UI/iOS/View/TunnelListCell.swift
new file mode 100644
index 0000000..b2e0ba9
--- /dev/null
+++ b/Sources/WireGuardApp/UI/iOS/View/TunnelListCell.swift
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: MIT
+// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
+
+import UIKit
+
+class TunnelListCell: UITableViewCell {
+ var tunnel: TunnelContainer? {
+ didSet(value) {
+ // Bind to the tunnel's name
+ nameLabel.text = tunnel?.name ?? ""
+ nameObservationToken = tunnel?.observe(\.name) { [weak self] tunnel, _ in
+ self?.nameLabel.text = tunnel.name
+ }
+ // Bind to the tunnel's status
+ update(from: tunnel?.status)
+ statusObservationToken = tunnel?.observe(\.status) { [weak self] tunnel, _ in
+ self?.update(from: tunnel.status)
+ }
+ }
+ }
+ var onSwitchToggled: ((Bool) -> Void)?
+
+ let nameLabel: UILabel = {
+ let nameLabel = UILabel()
+ nameLabel.font = UIFont.preferredFont(forTextStyle: .body)
+ nameLabel.adjustsFontForContentSizeCategory = true
+ nameLabel.numberOfLines = 0
+ return nameLabel
+ }()
+
+ let busyIndicator: UIActivityIndicatorView = {
+ if #available(iOS 13.0, *) {
+ let busyIndicator = UIActivityIndicatorView(style: .medium)
+ busyIndicator.hidesWhenStopped = true
+ return busyIndicator
+ } else {
+ let busyIndicator = UIActivityIndicatorView(style: .gray)
+ busyIndicator.hidesWhenStopped = true
+ return busyIndicator
+ }
+ }()
+
+ let statusSwitch = UISwitch()
+
+ private var statusObservationToken: AnyObject?
+ private var nameObservationToken: AnyObject?
+
+ override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
+ super.init(style: style, reuseIdentifier: reuseIdentifier)
+
+ contentView.addSubview(statusSwitch)
+ statusSwitch.translatesAutoresizingMaskIntoConstraints = false
+ NSLayoutConstraint.activate([
+ statusSwitch.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
+ contentView.trailingAnchor.constraint(equalTo: statusSwitch.trailingAnchor)
+ ])
+
+ contentView.addSubview(busyIndicator)
+ busyIndicator.translatesAutoresizingMaskIntoConstraints = false
+ NSLayoutConstraint.activate([
+ busyIndicator.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
+ statusSwitch.leadingAnchor.constraint(equalToSystemSpacingAfter: busyIndicator.trailingAnchor, multiplier: 1)
+ ])
+
+ contentView.addSubview(nameLabel)
+ nameLabel.translatesAutoresizingMaskIntoConstraints = false
+ nameLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
+ let bottomAnchorConstraint = contentView.layoutMarginsGuide.bottomAnchor.constraint(equalToSystemSpacingBelow: nameLabel.bottomAnchor, multiplier: 1)
+ bottomAnchorConstraint.priority = .defaultLow
+ NSLayoutConstraint.activate([
+ nameLabel.topAnchor.constraint(equalToSystemSpacingBelow: contentView.layoutMarginsGuide.topAnchor, multiplier: 1),
+ nameLabel.leadingAnchor.constraint(equalToSystemSpacingAfter: contentView.layoutMarginsGuide.leadingAnchor, multiplier: 1),
+ busyIndicator.leadingAnchor.constraint(equalToSystemSpacingAfter: nameLabel.trailingAnchor, multiplier: 1),
+ bottomAnchorConstraint
+ ])
+
+ accessoryType = .disclosureIndicator
+
+ statusSwitch.addTarget(self, action: #selector(switchToggled), for: .valueChanged)
+ }
+
+ @objc func switchToggled() {
+ onSwitchToggled?(statusSwitch.isOn)
+ }
+
+ private func update(from status: TunnelStatus?) {
+ guard let status = status else {
+ reset()
+ return
+ }
+ DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(200)) { [weak statusSwitch, weak busyIndicator] in
+ guard let statusSwitch = statusSwitch, let busyIndicator = busyIndicator else { return }
+ statusSwitch.isOn = !(status == .deactivating || status == .inactive)
+ statusSwitch.isUserInteractionEnabled = (status == .inactive || status == .active)
+ if status == .inactive || status == .active {
+ busyIndicator.stopAnimating()
+ } else {
+ busyIndicator.startAnimating()
+ }
+ }
+ }
+
+ required init?(coder aDecoder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ override func setEditing(_ editing: Bool, animated: Bool) {
+ super.setEditing(editing, animated: animated)
+ statusSwitch.isEnabled = !editing
+ }
+
+ private func reset() {
+ statusSwitch.isOn = false
+ statusSwitch.isUserInteractionEnabled = false
+ busyIndicator.stopAnimating()
+ }
+
+ override func prepareForReuse() {
+ super.prepareForReuse()
+ reset()
+ }
+}