Skip to content

Commit 88947e4

Browse files
authored
update visualizer
1 parent b383e9c commit 88947e4

File tree

1 file changed

+229
-26
lines changed

1 file changed

+229
-26
lines changed

TouchVisualizer/Visualizer.swift

Lines changed: 229 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,246 @@
11
//
2-
// UIWindow+Swizzle.swift
2+
// TouchVisualizer.swift
33
// TouchVisualizer
44
//
55

66
import UIKit
77

8-
fileprivate var isSwizzled = false
8+
final public class Visualizer:NSObject {
9+
10+
// MARK: - Public Variables
11+
static public let sharedInstance = Visualizer()
12+
fileprivate var enabled = false
13+
fileprivate var config: Configuration!
14+
fileprivate var touchViews = [TouchView]()
15+
fileprivate var previousLog = ""
16+
17+
// MARK: - Object life cycle
18+
private override init() {
19+
super.init()
20+
NotificationCenter
21+
.default
22+
.addObserver(self, selector: #selector(Visualizer.orientationDidChangeNotification(_:)), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
23+
24+
NotificationCenter
25+
.default
26+
.addObserver(self, selector: #selector(Visualizer.applicationDidBecomeActiveNotification(_:)), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
27+
28+
UIDevice
29+
.current
30+
.beginGeneratingDeviceOrientationNotifications()
31+
32+
warnIfSimulator()
33+
}
34+
35+
deinit {
36+
NotificationCenter
37+
.default
38+
.removeObserver(self)
39+
}
40+
41+
// MARK: - Helper Functions
42+
@objc internal func applicationDidBecomeActiveNotification(_ notification: Notification) {
43+
UIApplication.shared.keyWindow?.swizzle()
44+
}
45+
46+
@objc internal func orientationDidChangeNotification(_ notification: Notification) {
47+
let instance = Visualizer.sharedInstance
48+
for touch in instance.touchViews {
49+
touch.removeFromSuperview()
50+
}
51+
}
52+
53+
public func removeAllTouchViews() {
54+
for view in self.touchViews {
55+
view.removeFromSuperview()
56+
}
57+
}
58+
}
959

10-
@available(iOS 8.0, *)
11-
extension UIWindow {
12-
13-
public func swizzle() {
14-
if (isSwizzled) {
15-
return
16-
}
60+
extension Visualizer {
61+
public class func isEnabled() -> Bool {
62+
return sharedInstance.enabled
63+
}
64+
65+
// MARK: - Start and Stop functions
66+
67+
public class func start(_ config: Configuration = Configuration()) {
1768

18-
let sendEvent = class_getInstanceMethod(object_getClass(self), #selector(UIApplication.sendEvent(_:)))
19-
let swizzledSendEvent = class_getInstanceMethod(object_getClass(self), #selector(UIWindow.swizzledSendEvent(_:)))
20-
method_exchangeImplementations(sendEvent!, swizzledSendEvent!)
21-
swizzleNav()
22-
isSwizzled = true
69+
if config.showsLog {
70+
print("Visualizer start...")
71+
}
72+
let instance = sharedInstance
73+
instance.enabled = true
74+
instance.config = config
75+
76+
if let window = UIApplication.shared.keyWindow {
77+
for subview in window.subviews {
78+
if let subview = subview as? TouchView {
79+
subview.removeFromSuperview()
80+
}
81+
}
82+
}
83+
if config.showsLog {
84+
print("started !")
85+
}
2386
}
2487

25-
public func swizzleNav() {
26-
let pushEvent = class_getInstanceMethod(UINavigationController.classForCoder(), #selector(UINavigationController.pushViewController(_:animated:)))
27-
let swizzledPushEvent = class_getInstanceMethod(UINavigationController.classForCoder(), #selector(UINavigationController.swizzledPushViewController(_:animated:)))
28-
method_exchangeImplementations(pushEvent!, swizzledPushEvent!)
88+
public class func stop() {
89+
let instance = sharedInstance
90+
instance.enabled = false
91+
92+
for touch in instance.touchViews {
93+
touch.removeFromSuperview()
94+
}
2995
}
3096

31-
@objc public func swizzledSendEvent(_ event: UIEvent) {
32-
Visualizer.sharedInstance.handleEvent(event)
33-
swizzledSendEvent(event)
97+
public class func getTouches() -> [UITouch] {
98+
let instance = sharedInstance
99+
var touches: [UITouch] = []
100+
for view in instance.touchViews {
101+
guard let touch = view.touch else { continue }
102+
touches.append(touch)
103+
}
104+
return touches
34105
}
35-
}
106+
107+
// MARK: - Dequeue and locating TouchViews and handling events
108+
private func dequeueTouchView() -> TouchView {
109+
var touchView: TouchView?
110+
for view in touchViews {
111+
if view.superview == nil {
112+
touchView = view
113+
break
114+
}
115+
}
116+
117+
if touchView == nil {
118+
touchView = TouchView()
119+
touchViews.append(touchView!)
120+
}
121+
122+
return touchView!
123+
}
124+
125+
private func findTouchView(_ touch: UITouch) -> TouchView? {
126+
for view in touchViews {
127+
if touch == view.touch {
128+
return view
129+
}
130+
}
131+
132+
return nil
133+
}
134+
135+
open func handleEvent(_ event: UIEvent) {
136+
if event.type != .touches {
137+
return
138+
}
139+
140+
if !Visualizer.sharedInstance.enabled {
141+
return
142+
}
36143

37-
extension UINavigationController {
144+
var topWindow = UIApplication.shared.keyWindow!
145+
for window in UIApplication.shared.windows {
146+
if window.isHidden == false && window.windowLevel > topWindow.windowLevel {
147+
topWindow = window
148+
}
149+
}
150+
151+
for touch in event.allTouches! {
152+
let phase = touch.phase
153+
switch phase {
154+
case .began:
155+
let view = dequeueTouchView()
156+
view.config = Visualizer.sharedInstance.config
157+
view.touch = touch
158+
view.beginTouch()
159+
view.center = touch.location(in: topWindow)
160+
topWindow.addSubview(view)
161+
log(touch)
162+
case .moved:
163+
if let view = findTouchView(touch) {
164+
view.center = touch.location(in: topWindow)
165+
}
166+
167+
log(touch)
168+
case .stationary:
169+
log(touch)
170+
case .ended, .cancelled:
171+
if let view = findTouchView(touch) {
172+
UIView.animate(withDuration: 0.2, delay: 0.0, options: .allowUserInteraction, animations: { () -> Void in
173+
view.alpha = 0.0
174+
view.endTouch()
175+
}, completion: { [unowned self] (finished) -> Void in
176+
view.removeFromSuperview()
177+
self.log(touch)
178+
})
179+
}
180+
181+
log(touch)
182+
}
183+
}
184+
}
185+
}
38186

39-
@objc public func swizzledPushViewController(_ viewController: UIViewController, animated: Bool) {
40-
Visualizer.sharedInstance.removeAllTouchViews()
41-
swizzledPushViewController(viewController, animated: animated)
187+
extension Visualizer {
188+
public func warnIfSimulator() {
189+
#if (arch(i386) || arch(x86_64)) && os(iOS)
190+
print("[TouchVisualizer] Warning: TouchRadius doesn't work on the simulator because it is not possible to read touch radius on it.", terminator: "")
191+
#endif
192+
}
193+
194+
// MARK: - Logging
195+
public func log(_ touch: UITouch) {
196+
if !config.showsLog {
197+
return
198+
}
199+
200+
var ti = 0
201+
var viewLogs = [[String:String]]()
202+
for view in touchViews {
203+
var index = ""
204+
205+
index = "\(ti)"
206+
ti += 1
207+
208+
var phase: String!
209+
switch touch.phase {
210+
case .began: phase = "B"
211+
case .moved: phase = "M"
212+
case .stationary: phase = "S"
213+
case .ended: phase = "E"
214+
case .cancelled: phase = "C"
215+
}
216+
217+
let x = String(format: "%.02f", view.center.x)
218+
let y = String(format: "%.02f", view.center.y)
219+
let center = "(\(x), \(y))"
220+
let radius = String(format: "%.02f", touch.majorRadius)
221+
viewLogs.append(["index": index, "center": center, "phase": phase, "radius": radius])
222+
}
223+
224+
var log = ""
225+
226+
for viewLog in viewLogs {
227+
228+
if (viewLog["index"]!).characters.count == 0 {
229+
continue
230+
}
231+
232+
let index = viewLog["index"]!
233+
let center = viewLog["center"]!
234+
let phase = viewLog["phase"]!
235+
let radius = viewLog["radius"]!
236+
log += "Touch: [\(index)]<\(phase)> c:\(center) r:\(radius)\t\n"
237+
}
238+
239+
if log == previousLog {
240+
return
241+
}
242+
243+
previousLog = log
244+
print(log, terminator: "")
42245
}
43246
}

0 commit comments

Comments
 (0)