Skip to content

Commit

Permalink
Merge pull request #3 from p-x9/feature/uikit
Browse files Browse the repository at this point in the history
Support UIKit
  • Loading branch information
p-x9 committed Apr 13, 2023
2 parents a7a5fe6 + 9e528e8 commit 646e71e
Show file tree
Hide file tree
Showing 6 changed files with 306 additions and 5 deletions.
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,31 @@ Text("Hello")
.touchPointImage(Image(systemName: "swift").resizable())
```
![Example2](https://user-images.githubusercontent.com/50244599/231510854-c1669ba5-2071-446d-8cda-5131bce14511.PNG)

### UIKit
If you want to adapt it to the entire app created with UIKit, write the following.
Use a view named `TouchTrackingUIView`
```swift
import TouchTracker

class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

let v = TouchTrackingUIView(isShowLocation: true)

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if let window {
window.addSubview(v)
v.translatesAutoresizingMaskIntoConstraints = false

NSLayoutConstraint.activate([
v.topAnchor.constraint(equalTo: window.topAnchor),
v.bottomAnchor.constraint(equalTo: window.bottomAnchor),
v.leftAnchor.constraint(equalTo: window.leftAnchor),
v.rightAnchor.constraint(equalTo: window.rightAnchor),
])
}
return true
}
```
147 changes: 147 additions & 0 deletions Sources/TouchTracker/Cocoa/TouchPointUIView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
//
// TouchPointUIView.swift
//
//
// Created by p-x9 on 2023/04/14.
//
//

#if canImport(UIKit)
import UIKit

class TouchPointUIView: UIWindow {
var location: CGPoint {
didSet {
let x = String(format: "%.1f", location.x)
let y = String(format: "%.1f", location.y)
locationLabel.text = "x: \(x)\ny: \(y)"
}
}
var radius: CGFloat

var color: UIColor = .red

var isBordered: Bool = true
var borderColor: UIColor = .black
var borderWidth: CGFloat = 1

var isDropShadow: Bool = true
var shadowColor: UIColor = .black
var shadowRadius: CGFloat = 3

var image: UIImage?

var isShowLocation: Bool = false

lazy var imageView: UIImageView = {
let view = UIImageView()
view.contentMode = .scaleAspectFit
view.translatesAutoresizingMaskIntoConstraints = false

return view
}()

lazy var locationLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 2
label.font = .systemFont(ofSize: 10)
label.translatesAutoresizingMaskIntoConstraints = false

return label
}()

init(
location: CGPoint,
radius: CGFloat,
color: UIColor,
isBordered: Bool,
borderColor: UIColor,
borderWidth: CGFloat,
isDropShadow: Bool,
shadowColor: UIColor,
shadowRadius: CGFloat,
image: UIImage? = nil,
isShowLocation: Bool
) {
self.location = location
self.radius = radius
self.color = color
self.isBordered = isBordered
self.borderColor = borderColor
self.borderWidth = borderWidth
self.isDropShadow = isDropShadow
self.shadowColor = shadowColor
self.shadowRadius = shadowRadius
self.image = image
self.isShowLocation = isShowLocation

let frame = CGRect(origin: location,
size: .init(width: radius * 2, height: radius * 2))
super.init(frame: frame)

setupViews()
setupViewConstraints()

windowLevel = .statusBar
isUserInteractionEnabled = false
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

func setupViews() {
layer.cornerRadius = radius
backgroundColor = color

if isBordered {
layer.borderWidth = borderWidth
layer.borderColor = borderColor.cgColor
}

if isDropShadow {
layer.shadowColor = shadowColor.cgColor
layer.shadowRadius = shadowRadius
}

if let image {
imageView.image = image
addSubview(imageView)
}

if isShowLocation {
addSubview(locationLabel)
}
}

func setupViewConstraints() {
translatesAutoresizingMaskIntoConstraints = false

NSLayoutConstraint.activate([
self.heightAnchor.constraint(equalToConstant: radius * 2),
self.widthAnchor.constraint(equalToConstant: radius * 2),
])

if let _ = image {
NSLayoutConstraint.activate([
imageView.topAnchor.constraint(equalTo: topAnchor),
imageView.bottomAnchor.constraint(equalTo: bottomAnchor),
imageView.leftAnchor.constraint(equalTo: leftAnchor),
imageView.rightAnchor.constraint(equalTo: rightAnchor),
])
}

if isShowLocation {
NSLayoutConstraint.activate([
locationLabel.centerXAnchor.constraint(equalTo: centerXAnchor),
locationLabel.bottomAnchor.constraint(equalTo: topAnchor, constant: -shadowRadius)
])
}
}

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
false
}
}
#endif

126 changes: 126 additions & 0 deletions Sources/TouchTracker/Cocoa/TouchTrackingUIView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
//
// CocoaTrackPointManager.swift
//
//
// Created by p-x9 on 2023/04/14.
//
//

#if canImport(UIKit)
import UIKit

public class TouchTrackingUIView: UIView {
let radius: CGFloat

var color: UIColor = .red

var isBordered: Bool = false
var borderColor: UIColor = .black
var borderWidth: CGFloat = 1

var isDropShadow: Bool = true
var shadowColor: UIColor = .black
var shadowRadius: CGFloat = 3

var image: UIImage?

var isShowLocation: Bool = false


var touches: Set<UITouch> = []
var locations: [CGPoint] = [] {
didSet {
updatePoints()
}
}

var pointViews = [TouchPointUIView]()

public init(
radius: CGFloat = 20,
color: UIColor = .red,
isBordered: Bool = false,
borderColor: UIColor = .black,
borderWidth: CGFloat = 1,
isDropShadow: Bool = true,
shadowColor: UIColor = .black,
shadowRadius: CGFloat = 3,
image: UIImage? = nil,
isShowLocation: Bool = false
) {
self.radius = radius
self.color = color
self.isBordered = isBordered
self.borderColor = borderColor
self.borderWidth = borderWidth
self.isDropShadow = isDropShadow
self.shadowColor = shadowColor
self.shadowRadius = shadowRadius
self.image = image
self.isShowLocation = isShowLocation

super.init(frame: .null)

isUserInteractionEnabled = false

UIWindow.hook()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.touches.formUnion(touches)
self.locations = self.touches.map { $0.location(in: self) }
}

public override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
self.locations = self.touches.map { $0.location(in: self) }
}

public override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.touches.subtract(touches)
self.locations = self.touches.map { $0.location(in: self) }
}

public override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
self.touches.subtract(touches)
self.locations = self.touches.map { $0.location(in: self) }
}

func updatePoints() {
if pointViews.count > touches.count {
pointViews[touches.count..<pointViews.count].forEach {
$0.isHidden = true
$0.windowScene = nil
}
pointViews = Array(pointViews[0..<touches.count])
}
if pointViews.count < touches.count {
let diff = touches.count - pointViews.count
pointViews += (0..<diff).map { _ in
TouchPointUIView(
location: .zero,
radius: radius,
color: color,
isBordered: isBordered,
borderColor: borderColor,
borderWidth: borderWidth,
isDropShadow: isDropShadow,
shadowColor: shadowColor,
shadowRadius: shadowRadius,
isShowLocation: isShowLocation
)
}
}

zip(pointViews, locations).forEach { view, location in
view.location = location
view.center = location
view.makeKeyAndVisible()
}
}
}

#endif
2 changes: 1 addition & 1 deletion Sources/TouchTracker/Extension/UIWindow+.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ extension UIWindow {
let moved = touches.filter { $0.phase == .moved }
let ended = touches.filter { $0.phase == .cancelled || $0.phase == .ended }

let touchLocationViews = find(for: TouchLocationUIView.self)
let touchLocationViews: [UIView] = find(for: TouchLocationCocoaView.self) + find(for: TouchTrackingUIView.self)

touchLocationViews
.filter {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// TouchLocationUIView.swift
// TouchLocationCocoaView.swift
//
//
// Created by p-x9 on 2023/04/12.
Expand All @@ -9,7 +9,7 @@
#if canImport(UIKit)
import UIKit

class TouchLocationUIView: UIView {
class TouchLocationCocoaView: UIView {
var touchesChangeHandler: (([CGPoint]) -> Void)?

var touches: Set<UITouch> = []
Expand Down
4 changes: 2 additions & 2 deletions Sources/TouchTracker/TouchLocationView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ class TouchLocationWrapView<Content: View>: UIHostingController<Content> {
}
}

lazy var trackLocationUIView: TouchLocationUIView = {
let view = TouchLocationUIView(frame: .null)
lazy var trackLocationUIView: TouchLocationCocoaView = {
let view = TouchLocationCocoaView(frame: .null)
view.isHidden = true
view.translatesAutoresizingMaskIntoConstraints = false
view.touchesChangeHandler = { [weak self] locations in
Expand Down

0 comments on commit 646e71e

Please sign in to comment.