Skip to content

Commit 86ed810

Browse files
committed
Implemented VisibleTouchWindow
1 parent e5530e9 commit 86ed810

File tree

6 files changed

+229
-4
lines changed

6 files changed

+229
-4
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
55

66
## [Unreleased]
77
### Added
8-
- Created bare-bones structure
8+
- Created bare-bones structure
9+
- Implemented VisibleTouchWindow
910

1011
[Unreleased]: https://github.com/TrebleFM/VisibleTouchViewSwift/tree/develop

README.md

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,52 @@
11
# VisibleTouchViewSwift
2-
> Show finger touches on the screen using either a UIView or UIWindow. Useful for app preview videos, live product demos, and more.
2+
3+
[![Version](http://img.shields.io/cocoapods/v/VisibleTouchViewSwift.svg)](http://cocoapods.org/?q=VisibleTouchViewSwift)
4+
[![GitHub release](https://img.shields.io/github/release/TrebleFM/VisibleTouchViewSwift.svg)](https://github.com/TrebleFM/VisibleTouchViewSwift/releases)
5+
![Swift 3.0](https://img.shields.io/badge/Swift-3.0-green.svg)
6+
![platforms](https://img.shields.io/badge/platforms-iOS%20-lightgrey.svg)
7+
8+
> Show finger touches on the screen using either a UIView or UIWindow. Useful for app preview videos, live product demos, and more. Based on [EUMTouchPointView](https://github.com/eumlab/EUMTouchPointView).
9+
10+
## Usage
11+
12+
Set `VisibleTouchVWindow` as your Root View Controller
13+
14+
```swift
15+
@UIApplicationMain
16+
class AppDelegate: UIResponder, UIApplicationDelegate {
17+
var window: UIWindow?
18+
var controller = Controller()
19+
20+
override init() {
21+
super.init()
22+
self.window = VisibleTouchVWindow(frame: UIScreen.mainScreen().bounds)
23+
}
24+
25+
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
26+
window!.rootViewController = Controller()
27+
window!.makeKeyAndVisible()
28+
return true
29+
}
30+
}
31+
```
32+
33+
## Installation
34+
35+
VisibleTouchViewSwift is available through [CocoaPods](http://cocoapods.org/). To install it, simply add the following line to your Podfile:
36+
37+
```
38+
pod 'VisibleTouchViewSwift'
39+
```
40+
41+
42+
## Release History
43+
44+
See `CHANGELOG.md`
45+
46+
## Author
47+
48+
Benjamin Chrobot (@bchrobot), [email protected]
49+
50+
## License
51+
52+
VisibleTouchViewSwift is available under the MIT license. See `LICENSE` for more info.

VisibleTouchViewSwift.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
2C05D4681DDD51A400B1CFDB /* VisibleTouchWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C05D4671DDD51A400B1CFDB /* VisibleTouchWindow.swift */; };
1414
2C05D46A1DDD565A00B1CFDB /* VisibleTouchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C05D4691DDD565A00B1CFDB /* VisibleTouchView.swift */; };
1515
2C05D46C1DDD5FDE00B1CFDB /* TouchPointView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C05D46B1DDD5FDE00B1CFDB /* TouchPointView.swift */; };
16+
2C05D46E1DDD71B600B1CFDB /* UITouch+TouchPointView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C05D46D1DDD71B600B1CFDB /* UITouch+TouchPointView.swift */; };
1617
9A68629A4430CDB5FA791F9A /* Pods_VisibleTouchViewSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6D96557FE4ACCB54D5CC4A3B /* Pods_VisibleTouchViewSwift.framework */; };
1718
FD1650214BA06A9329EE6C15 /* Pods_VisibleTouchViewSwiftTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77A7A213C1326DA9239C7F09 /* Pods_VisibleTouchViewSwiftTests.framework */; };
1819
/* End PBXBuildFile section */
@@ -37,6 +38,7 @@
3738
2C05D4671DDD51A400B1CFDB /* VisibleTouchWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VisibleTouchWindow.swift; sourceTree = "<group>"; };
3839
2C05D4691DDD565A00B1CFDB /* VisibleTouchView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VisibleTouchView.swift; sourceTree = "<group>"; };
3940
2C05D46B1DDD5FDE00B1CFDB /* TouchPointView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TouchPointView.swift; sourceTree = "<group>"; };
41+
2C05D46D1DDD71B600B1CFDB /* UITouch+TouchPointView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITouch+TouchPointView.swift"; sourceTree = "<group>"; };
4042
419C58FE1B350595E5647F39 /* Pods-VisibleTouchViewSwift.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VisibleTouchViewSwift.debug.xcconfig"; path = "Pods/Target Support Files/Pods-VisibleTouchViewSwift/Pods-VisibleTouchViewSwift.debug.xcconfig"; sourceTree = "<group>"; };
4143
6D96557FE4ACCB54D5CC4A3B /* Pods_VisibleTouchViewSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_VisibleTouchViewSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4244
77A7A213C1326DA9239C7F09 /* Pods_VisibleTouchViewSwiftTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_VisibleTouchViewSwiftTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -105,6 +107,7 @@
105107
2C05D4671DDD51A400B1CFDB /* VisibleTouchWindow.swift */,
106108
2C05D4691DDD565A00B1CFDB /* VisibleTouchView.swift */,
107109
2C05D46B1DDD5FDE00B1CFDB /* TouchPointView.swift */,
110+
2C05D46D1DDD71B600B1CFDB /* UITouch+TouchPointView.swift */,
108111
);
109112
path = VisibleTouchViewSwift;
110113
sourceTree = "<group>";
@@ -324,6 +327,7 @@
324327
2C05D4681DDD51A400B1CFDB /* VisibleTouchWindow.swift in Sources */,
325328
2C05D46C1DDD5FDE00B1CFDB /* TouchPointView.swift in Sources */,
326329
2C05D46A1DDD565A00B1CFDB /* VisibleTouchView.swift in Sources */,
330+
2C05D46E1DDD71B600B1CFDB /* UITouch+TouchPointView.swift in Sources */,
327331
);
328332
runOnlyForDeploymentPostprocessing = 0;
329333
};
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//
2+
// TouchPointView.swift
3+
// VisibleTouchViewSwift
4+
//
5+
// Created by Benjamin Chrobot on 11/16/16.
6+
// Copyright © 2016 Treble Media, Inc. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
internal class TouchPointView: UIView {
12+
13+
var pointerColor: UIColor = UIColor(red: 0, green: 0.75, blue: 1, alpha: 0.7)
14+
var pointerStockColor: UIColor = UIColor.cyan.withAlphaComponent(0.7)
15+
16+
// MARK: - Lifecycle
17+
18+
override init(frame: CGRect) {
19+
super.init(frame: frame)
20+
21+
self.backgroundColor = .clear
22+
self.isOpaque = false
23+
}
24+
25+
required public init?(coder aDecoder: NSCoder) {
26+
fatalError("init(coder:) has not been implemented")
27+
}
28+
29+
// MARK: - UIView
30+
31+
override func draw(_ rect: CGRect) {
32+
drawPointer(withSize: rect.size, stockWidth: 3)
33+
}
34+
35+
// MARK: - Private
36+
37+
private func drawPointer(withSize size: CGSize, stockWidth: CGFloat) {
38+
// Circle
39+
let circleRect = CGRect(x: 5, y: 5, width: size.width - 10, height: size.height - 10)
40+
let circlePath = UIBezierPath(ovalIn: circleRect)
41+
42+
let shapeLayer = CAShapeLayer()
43+
shapeLayer.path = circlePath.cgPath
44+
shapeLayer.fillColor = pointerColor.cgColor
45+
shapeLayer.strokeColor = pointerStockColor.cgColor
46+
shapeLayer.lineWidth = stockWidth
47+
48+
layer.addSublayer(shapeLayer)
49+
50+
// Shadow
51+
let shadowColor: UIColor = UIColor.black.withAlphaComponent(0.2)
52+
let shadowOffset = CGSize(width: 0.1, height: -0.1)
53+
let shadowBlurRadius: CGFloat = 4;
54+
55+
layer.shadowColor = shadowColor.cgColor
56+
layer.shadowOffset = shadowOffset
57+
layer.shadowRadius = shadowBlurRadius
58+
layer.shadowPath = circlePath.cgPath
59+
}
60+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//
2+
// UITouch+TouchPointView.swift
3+
// VisibleTouchViewSwift
4+
//
5+
// Created by Benjamin Chrobot on 11/17/16.
6+
// Copyright © 2016 Treble Media, Inc. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import ObjectiveC
11+
12+
internal extension UITouch {
13+
14+
private struct AssociationKeys {
15+
static var TouchPointView: UInt8 = 0
16+
}
17+
18+
var touchPointView: TouchPointView! {
19+
get {
20+
return objc_getAssociatedObject(self, &AssociationKeys.TouchPointView) as? TouchPointView
21+
}
22+
set(touchPointView) {
23+
objc_setAssociatedObject(self,
24+
&AssociationKeys.TouchPointView,
25+
touchPointView,
26+
.OBJC_ASSOCIATION_RETAIN)
27+
}
28+
}
29+
}

VisibleTouchViewSwift/VisibleTouchWindow.swift

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,97 @@
88

99
import Foundation
1010

11+
fileprivate let startScale: CGFloat = 2
12+
fileprivate let endScale: CGFloat = 1.2
13+
14+
/// Foobar
1115
public class VisibleTouchWindow: UIWindow {
1216

17+
// MARK: - Properties
18+
19+
public var isShowTouchesEnabled: Bool = true
20+
public var appearAnimationDuration: Double = 0.1
21+
public var movementAnimationDuration: Double = 0.1
22+
public var disappearAnimationDuration: Double = 0.1
23+
private let pointerSize = CGSize(width: 50, height: 50)
24+
25+
// MARK: - Lifecycle
26+
1327
override public init(frame: CGRect) {
1428
super.init(frame: frame)
15-
16-
// stub
1729
}
1830

1931
required public init?(coder aDecoder: NSCoder) {
2032
fatalError("init(coder:) has not been implemented")
2133
}
2234

35+
// MARK: - Event Handling
36+
37+
override public func sendEvent(_ event: UIEvent) {
38+
super.sendEvent(event)
39+
40+
guard event.type == .touches, let allTouches = event.allTouches else {
41+
return
42+
}
43+
44+
for touch in allTouches {
45+
switch touch.phase {
46+
case .began:
47+
// Don't display new touches if showTouches is disabled
48+
guard isShowTouchesEnabled else {
49+
return
50+
}
51+
52+
// Create touch point view
53+
let point = touch.location(in: self)
54+
let origin = CGPoint(x: point.x - pointerSize.width / 2,
55+
y: point.y - pointerSize.height / 2)
56+
let touchPointView = TouchPointView(frame: CGRect(origin: origin, size: pointerSize))
57+
touchPointView.transform = CGAffineTransform(scaleX: startScale, y: startScale)
58+
touchPointView.alpha = 0
59+
60+
// Store view with touch event
61+
touch.touchPointView = touchPointView
62+
63+
// Display touch with animation
64+
addSubview(touchPointView)
65+
UIView.animate(withDuration: appearAnimationDuration) {
66+
touchPointView.transform = CGAffineTransform.identity
67+
touchPointView.alpha = 1
68+
}
69+
70+
case .ended, .cancelled:
71+
// Retrieve touch point view from event
72+
if let touchPointView = touch.touchPointView {
73+
// Animate removal
74+
UIView.animate(withDuration: disappearAnimationDuration, animations: {
75+
touchPointView.transform = CGAffineTransform(scaleX: endScale, y: endScale)
76+
touchPointView.alpha = 0;
77+
}, completion: { (_) in
78+
touchPointView.removeFromSuperview()
79+
})
80+
}
81+
break
82+
83+
case .moved:
84+
// Retrieve touch point view from event
85+
if let touchPointView = touch.touchPointView {
86+
// Determine point's new frame based on touch event
87+
let point = touch.location(in: self)
88+
var touchFrame = touchPointView.frame
89+
touchFrame.origin.x = point.x - pointerSize.width / 2
90+
touchFrame.origin.y = point.y - pointerSize.height / 2
91+
92+
// Animate movement
93+
UIView.animate(withDuration: movementAnimationDuration) {
94+
touchPointView.frame = touchFrame
95+
}
96+
}
97+
break
98+
99+
default:
100+
break
101+
}
102+
}
103+
}
23104
}

0 commit comments

Comments
 (0)