From 52fa05b0fc3bd1e499e6494d7deb44a00c532c4a Mon Sep 17 00:00:00 2001 From: Chase Carroll Date: Mon, 5 Dec 2022 10:21:29 -0500 Subject: [PATCH 1/9] Add UIWindow subclass to display all touches. --- .../View/Window/TouchesVisibleWindow.swift | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift diff --git a/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift b/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift new file mode 100644 index 000000000..26971ef1e --- /dev/null +++ b/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift @@ -0,0 +1,134 @@ +// +// File.swift +// +// +// Created by Chase Carroll on 12/5/22. +// + +#if DEBUG + +import UIKit + +fileprivate final class TouchView: UIView { + + fileprivate lazy var blurView: UIVisualEffectView = { + let blurEffect = UIBlurEffect(style: .systemUltraThinMaterialLight) + return UIVisualEffectView(effect: blurEffect) + }() + + override var frame: CGRect { + didSet { + layer.cornerRadius = frame.height / 2.0 + } + } + + override init(frame: CGRect) { + super.init(frame: frame) + + backgroundColor = .clear + layer.masksToBounds = true + layer.cornerCurve = .circular + layer.borderColor = UIColor.white.cgColor + layer.borderWidth = 2.0 + + addSubview(blurView) + } + + @available(iOS, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layoutSubviews() { + super.layoutSubviews() + blurView.frame = bounds + } + +} + + +public final class TouchesVisibleWindow: UIWindow { + + var touchesVisible = false { + didSet { + if !touchesVisible { + cleanUpAllTouches() + } + } + } + + fileprivate var touchViews: [UITouch : TouchView] = [:] + + fileprivate func newTouchView() -> TouchView { + let touchSize = 44.0 + return TouchView(frame: CGRect( + origin: .zero, + size: CGSize( + width: touchSize, + height: touchSize + ) + )) + } + + fileprivate func cleanupTouch(_ touch: UITouch) { + guard let touchView = touchViews[touch] else { + return + } + + touchView.removeFromSuperview() + touchViews.removeValue(forKey: touch) + } + + fileprivate func cleanUpAllTouches() { + for (_, touchView) in touchViews { + touchView.removeFromSuperview() + } + + touchViews.removeAll() + } + + public override func sendEvent(_ event: UIEvent) { + if !touchesVisible { + super.sendEvent(event) + return + } + + let touches = event.allTouches + + guard + let touches = touches, + touches.count > 0 + else { + cleanUpAllTouches() + super.sendEvent(event) + return + } + + for touch in touches { + let touchLocation = touch.location(in: self) + switch touch.phase { + case .began: + let touchView = newTouchView() + touchView.center = touchLocation + addSubview(touchView) + touchViews[touch] = touchView + + case .moved: + guard let touchView = touchViews[touch] else { + return + } + touchView.center = touchLocation + + case .ended, .cancelled: + cleanupTouch(touch) + + default: + break + } + } + + super.sendEvent(event) + } +} + +#endif From 12791ddf284ade317f0901ce7e6dc16f41dcffea Mon Sep 17 00:00:00 2001 From: Chase Carroll Date: Mon, 5 Dec 2022 10:22:14 -0500 Subject: [PATCH 2/9] Use UIWindow subclass in DEBUG builds --- Mastodon/Supporting Files/SceneDelegate.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Mastodon/Supporting Files/SceneDelegate.swift b/Mastodon/Supporting Files/SceneDelegate.swift index 15c4069cb..e3256d260 100644 --- a/Mastodon/Supporting Files/SceneDelegate.swift +++ b/Mastodon/Supporting Files/SceneDelegate.swift @@ -11,6 +11,7 @@ import Combine import CoreDataStack import MastodonCore import MastodonExtension +import MastodonUI #if PROFILE import FPSIndicator @@ -35,8 +36,13 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = scene as? UIWindowScene else { return } + #if DEBUG + let window = TouchesVisibleWindow(windowScene: windowScene) + self.window = window + #else let window = UIWindow(windowScene: windowScene) self.window = window + #endif // set tint color window.tintColor = UIColor.label From 7f58422900e6b1a98f9c0d90c1d3cae24acab7af Mon Sep 17 00:00:00 2001 From: Chase Carroll Date: Mon, 5 Dec 2022 10:32:10 -0500 Subject: [PATCH 3/9] Add debug menu option Puts an option in the debug menu for toggling on/off visible touches. --- .../HomeTimeline/HomeTimelineViewController+DebugAction.swift | 4 ++++ .../Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DebugAction.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DebugAction.swift index ff90775ff..6bded8c37 100644 --- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DebugAction.swift +++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DebugAction.swift @@ -122,6 +122,10 @@ extension HomeTimelineViewController { identifier: nil, options: [], children: [ + UIAction(title: "Toggle Visible Touches", image: UIImage(systemName: "hand.tap"), attributes: []) { [weak self] action in + guard let window = UIApplication.shared.keyWindow as? TouchesVisibleWindow else { return } + window.touchesVisible = !window.touchesVisible + }, UIAction(title: "Toggle EmptyView", image: UIImage(systemName: "clear"), attributes: []) { [weak self] action in guard let self = self else { return } if self.emptyView.superview != nil { diff --git a/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift b/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift index 26971ef1e..7a8a37976 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift @@ -49,7 +49,7 @@ fileprivate final class TouchView: UIView { public final class TouchesVisibleWindow: UIWindow { - var touchesVisible = false { + public var touchesVisible = false { didSet { if !touchesVisible { cleanUpAllTouches() From 4689c8e78fc9e59c0cf1d6cac516e5d13dba5e0e Mon Sep 17 00:00:00 2001 From: Chase Carroll Date: Mon, 5 Dec 2022 16:01:41 -0500 Subject: [PATCH 4/9] Use modern APIs for accessing key window Shouldn't be relying on deprecated API. Tsk. Tsk. --- Mastodon/Extension/UIApplication.swift | 8 ++++++++ .../HomeTimelineViewController+DebugAction.swift | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Mastodon/Extension/UIApplication.swift b/Mastodon/Extension/UIApplication.swift index 38080fdab..74019b0ae 100644 --- a/Mastodon/Extension/UIApplication.swift +++ b/Mastodon/Extension/UIApplication.swift @@ -22,5 +22,13 @@ extension UIApplication { return version == build ? "v\(version)" : "v\(version) (\(build))" } + + func getKeyWindow() -> UIWindow? { + return UIApplication + .shared + .connectedScenes + .flatMap { ($0 as? UIWindowScene)?.windows ?? [] } + .first { $0.isKeyWindow } + } } diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DebugAction.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DebugAction.swift index 6bded8c37..8ad798165 100644 --- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DebugAction.swift +++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DebugAction.swift @@ -122,8 +122,8 @@ extension HomeTimelineViewController { identifier: nil, options: [], children: [ - UIAction(title: "Toggle Visible Touches", image: UIImage(systemName: "hand.tap"), attributes: []) { [weak self] action in - guard let window = UIApplication.shared.keyWindow as? TouchesVisibleWindow else { return } + UIAction(title: "Toggle Visible Touches", image: UIImage(systemName: "hand.tap"), attributes: []) { _ in + guard let window = UIApplication.shared.getKeyWindow() as? TouchesVisibleWindow else { return } window.touchesVisible = !window.touchesVisible }, UIAction(title: "Toggle EmptyView", image: UIImage(systemName: "clear"), attributes: []) { [weak self] action in From 5648b13517bb3eb3ad04b928583c319892462d99 Mon Sep 17 00:00:00 2001 From: Chase Carroll Date: Mon, 5 Dec 2022 20:22:50 -0500 Subject: [PATCH 5/9] Tidy up. --- .../View/Window/TouchesVisibleWindow.swift | 85 +++++++++---------- 1 file changed, 42 insertions(+), 43 deletions(-) diff --git a/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift b/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift index 7a8a37976..bdde38ed0 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift @@ -9,12 +9,10 @@ import UIKit +/// View that represents a single touch from the user. fileprivate final class TouchView: UIView { - fileprivate lazy var blurView: UIVisualEffectView = { - let blurEffect = UIBlurEffect(style: .systemUltraThinMaterialLight) - return UIVisualEffectView(effect: blurEffect) - }() + private let blurView: UIVisualEffectView override var frame: CGRect { didSet { @@ -23,6 +21,9 @@ fileprivate final class TouchView: UIView { } override init(frame: CGRect) { + let blurEffect = UIBlurEffect(style: .systemUltraThinMaterialLight) + blurView = UIVisualEffectView(effect: blurEffect) + super.init(frame: frame) backgroundColor = .clear @@ -47,6 +48,7 @@ fileprivate final class TouchView: UIView { } +/// `UIWindow` subclass that renders visual representations of the user's touches. public final class TouchesVisibleWindow: UIWindow { public var touchesVisible = false { @@ -57,9 +59,9 @@ public final class TouchesVisibleWindow: UIWindow { } } - fileprivate var touchViews: [UITouch : TouchView] = [:] + private var touchViews: [UITouch : TouchView] = [:] - fileprivate func newTouchView() -> TouchView { + private func newTouchView() -> TouchView { let touchSize = 44.0 return TouchView(frame: CGRect( origin: .zero, @@ -70,7 +72,7 @@ public final class TouchesVisibleWindow: UIWindow { )) } - fileprivate func cleanupTouch(_ touch: UITouch) { + private func cleanupTouch(_ touch: UITouch) { guard let touchView = touchViews[touch] else { return } @@ -79,7 +81,7 @@ public final class TouchesVisibleWindow: UIWindow { touchViews.removeValue(forKey: touch) } - fileprivate func cleanUpAllTouches() { + private func cleanUpAllTouches() { for (_, touchView) in touchViews { touchView.removeFromSuperview() } @@ -88,42 +90,39 @@ public final class TouchesVisibleWindow: UIWindow { } public override func sendEvent(_ event: UIEvent) { - if !touchesVisible { - super.sendEvent(event) - return - } - - let touches = event.allTouches - - guard - let touches = touches, - touches.count > 0 - else { - cleanUpAllTouches() - super.sendEvent(event) - return - } - - for touch in touches { - let touchLocation = touch.location(in: self) - switch touch.phase { - case .began: - let touchView = newTouchView() - touchView.center = touchLocation - addSubview(touchView) - touchViews[touch] = touchView - - case .moved: - guard let touchView = touchViews[touch] else { - return + if touchesVisible { + let touches = event.allTouches + + guard + let touches = touches, + touches.count > 0 + else { + cleanUpAllTouches() + super.sendEvent(event) + return + } + + for touch in touches { + let touchLocation = touch.location(in: self) + switch touch.phase { + case .began: + let touchView = newTouchView() + touchView.center = touchLocation + addSubview(touchView) + touchViews[touch] = touchView + + case .moved: + guard let touchView = touchViews[touch] else { + return + } + touchView.center = touchLocation + + case .ended, .cancelled: + cleanupTouch(touch) + + default: + break } - touchView.center = touchLocation - - case .ended, .cancelled: - cleanupTouch(touch) - - default: - break } } From b634d2b844cdb4c024e505a6bfcff07e2ce0923c Mon Sep 17 00:00:00 2001 From: Chase Carroll Date: Tue, 6 Dec 2022 11:23:04 -0500 Subject: [PATCH 6/9] Add extra contrast on the touch views Felt like they needed to stand out just a bit more in light mode. --- .../MastodonUI/View/Window/TouchesVisibleWindow.swift | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift b/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift index bdde38ed0..586501f3b 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift @@ -10,9 +10,9 @@ import UIKit /// View that represents a single touch from the user. -fileprivate final class TouchView: UIView { +private final class TouchView: UIView { - private let blurView: UIVisualEffectView + private let blurView = UIVisualEffectView(effect: nil) override var frame: CGRect { didSet { @@ -21,9 +21,6 @@ fileprivate final class TouchView: UIView { } override init(frame: CGRect) { - let blurEffect = UIBlurEffect(style: .systemUltraThinMaterialLight) - blurView = UIVisualEffectView(effect: blurEffect) - super.init(frame: frame) backgroundColor = .clear @@ -32,6 +29,10 @@ fileprivate final class TouchView: UIView { layer.borderColor = UIColor.white.cgColor layer.borderWidth = 2.0 + let blurEffect = traitCollection.userInterfaceStyle == .light ? + UIBlurEffect(style: .systemUltraThinMaterialDark) : + UIBlurEffect(style: .systemUltraThinMaterialLight) + blurView.effect = blurEffect addSubview(blurView) } From 6f0903466842835782610d0d720b00688f6475cb Mon Sep 17 00:00:00 2001 From: Chase Carroll Date: Tue, 6 Dec 2022 14:42:25 -0500 Subject: [PATCH 7/9] Tweak touch view border color --- .../MastodonUI/View/Window/TouchesVisibleWindow.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift b/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift index 586501f3b..6eb7a4feb 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift @@ -23,13 +23,15 @@ private final class TouchView: UIView { override init(frame: CGRect) { super.init(frame: frame) + let isLightMode = traitCollection.userInterfaceStyle == .light + backgroundColor = .clear layer.masksToBounds = true layer.cornerCurve = .circular - layer.borderColor = UIColor.white.cgColor + layer.borderColor = isLightMode ? UIColor.gray.cgColor : UIColor.white.cgColor layer.borderWidth = 2.0 - let blurEffect = traitCollection.userInterfaceStyle == .light ? + let blurEffect = isLightMode ? UIBlurEffect(style: .systemUltraThinMaterialDark) : UIBlurEffect(style: .systemUltraThinMaterialLight) blurView.effect = blurEffect From 08ec662e221f9330ea6cd0c0abdcd37f71a53eea Mon Sep 17 00:00:00 2001 From: Chase Carroll Date: Wed, 7 Dec 2022 07:19:48 -0500 Subject: [PATCH 8/9] Fixing case where events aren't forwarded --- .../MastodonUI/View/Window/TouchesVisibleWindow.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift b/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift index 6eb7a4feb..79888220b 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift @@ -115,10 +115,9 @@ public final class TouchesVisibleWindow: UIWindow { touchViews[touch] = touchView case .moved: - guard let touchView = touchViews[touch] else { - return + if let touchView = touchViews[touch] { + touchView.center = touchLocation } - touchView.center = touchLocation case .ended, .cancelled: cleanupTouch(touch) From 34b3d7d559e0fd7439d806a7a46e299d1ea32749 Mon Sep 17 00:00:00 2001 From: Chase Carroll Date: Wed, 7 Dec 2022 07:20:08 -0500 Subject: [PATCH 9/9] Fix file header --- .../Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift b/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift index 79888220b..8779e1ce7 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Window/TouchesVisibleWindow.swift @@ -1,5 +1,5 @@ // -// File.swift +// TouchesVisibleWindow.swift // // // Created by Chase Carroll on 12/5/22.