Skip to content

Commit

Permalink
Touch Visualiser (#20)
Browse files Browse the repository at this point in the history
* Touch visualiser

* Improve notification receiver

* no message

* no message

* Start tidy up

* no message

* no message

* Improve alpha

* Expose config variables through interfacetoolkit

* Allow setter on touchIndicatorImage

* Improve custom image indicators

* Finalise

* Migrate features into a view controller

* Fix bug
  • Loading branch information
bstillitano authored Sep 26, 2021
1 parent 653bccf commit a6142d1
Show file tree
Hide file tree
Showing 11 changed files with 773 additions and 2 deletions.
16 changes: 16 additions & 0 deletions Sources/Scyther/Extensions/UIImage+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,21 @@ extension UIImage {
}
return UIImage(named: lastIcon)
}

internal static func touchIndicatorImage(withColor color: UIColor, andSize size: CGSize) -> UIImage? {
let rect = CGRect(x: 0.0,
y: 0.0,
width: size.width,
height: size.height)
UIGraphicsBeginImageContextWithOptions(rect.size,
false,
0.0)
let contextRef = UIGraphicsGetCurrentContext()
contextRef?.setFillColor(color.cgColor)
contextRef?.fillEllipse(in: rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image?.withRenderingMode(.alwaysTemplate)
}
}
#endif
5 changes: 5 additions & 0 deletions Sources/Scyther/User Interface/Menu/MenuViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@ internal class MenuViewModel {
actionBlock: { [weak self] in
self?.delegate?.viewModel(viewModel: self, shouldShowViewController: GridOverlayViewController())
}))
uiUxSection.rows.append(actionRow(name: "Touch Visualiser",
icon: UIImage(systemImage: "hand.point.up"),
actionBlock: { [weak self] in
self?.delegate?.viewModel(viewModel: self, shouldShowViewController: TouchVisualiserViewController())
}))
uiUxSection.rows.append(viewFramesSwitch)
uiUxSection.rows.append(slowAnimationsSwitch)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
//
// File.swift
//
//
// Created by Brandon Stillitano on 27/9/21.
//


import UIKit

internal class TouchVisualiserViewController: UIViewController {
// MARK: - Data
private let tableView = UITableView(frame: .zero, style: .insetGroupedSafe)
private var viewModel: TouchVisualiserViewModel = TouchVisualiserViewModel()

// MARK: - Init
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)

setupUI()
setupConstraints()
setupData()
}

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

// MARK: - Setup
private func setupUI() {
//Setup Table View
tableView.delegate = self
tableView.dataSource = self
view.addSubview(tableView)

//Register Table View Cells
tableView.register(DefaultCell.self, forCellReuseIdentifier: RowStyle.default.rawValue)
tableView.register(SwitchCell.self, forCellReuseIdentifier: RowStyle.switchAccessory.rawValue)
tableView.register(ButtonCell.self, forCellReuseIdentifier: RowStyle.button.rawValue)
}

private func setupConstraints() {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|",
options: .directionLeadingToTrailing,
metrics: nil,
views: ["subview": tableView]))
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|",
options: .directionLeadingToTrailing,
metrics: nil,
views: ["subview": tableView]))
}

private func setupData() {
self.viewModel.delegate = self
self.viewModel.prepareObjects()

title = viewModel.title
navigationItem.title = viewModel.title
}

// MARK: - Lifecycle
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

UIView.setAnimationsEnabled(false)
UIDevice.current.setValue(UIDeviceOrientation.portrait.rawValue, forKey: "orientation")
UIView.setAnimationsEnabled(true)
}
}

extension TouchVisualiserViewController: UITableViewDataSource {

func numberOfSections(in tableView: UITableView) -> Int {
return viewModel.numberOfSections
}

func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return viewModel.title(forSection: section)
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.numberOfRows(inSection: section)
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Check for Cell
guard let row = viewModel.row(at: indexPath) else {
return UITableViewCell()
}

// Setup Cell
let cell = tableView.dequeueReusableCell(withIdentifier: row.cellReuseIdentifier,
for: indexPath)
cell.textLabel?.text = viewModel.title(for: row, indexPath: indexPath)
cell.detailTextLabel?.text = row.detailText
cell.accessoryView = row.accessoryView
cell.accessoryType = row.accessoryType ?? .none

return cell
}

}

extension TouchVisualiserViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Deselect Cell
defer { tableView.deselectRow(at: indexPath, animated: true) }

// Check for Cell
guard let row = viewModel.row(at: indexPath) else {
return
}
row.actionBlock?()
}
}

extension TouchVisualiserViewController: TouchVisualiserViewModelProtocol {
func viewModelShouldReloadData() {
self.tableView.reloadData()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
//
// File.swift
//
//
// Created by Brandon Stillitano on 27/9/21.
//

import UIKit

internal protocol TouchVisualiserViewModelProtocol: AnyObject {
func viewModelShouldReloadData()
}

internal class TouchVisualiserViewModel {
// MARK: - Data
private var sections: [Section] = []

// MARK: - Delegate
weak var delegate: TouchVisualiserViewModelProtocol?

/// Switch to enable/disable touch visualiser
var visualiseTouchesSwitch: SwitchAccessoryRow {
var row: SwitchAccessoryRow = SwitchAccessoryRow()
row.text = "Show screen touches"

//Setup Accessory
let switchView = UIActionSwitch()
switchView.isOn = InterfaceToolkit.instance.visualiseTouches
switchView.actionBlock = { [weak self] in
InterfaceToolkit.instance.visualiseTouches = switchView.isOn
self?.delegate?.viewModelShouldReloadData()
}
switchView.addTarget(self, action: #selector(switchToggled(_:)), for: .valueChanged)
row.accessoryView = switchView
return row
}

var touchDurationSwitch: SwitchAccessoryRow {
var row: SwitchAccessoryRow = SwitchAccessoryRow()
row.text = "Show touch duration"

//Setup Accessory
let switchView = UIActionSwitch()
switchView.isOn = InterfaceToolkit.instance.touchVisualiser.config.showsTouchDuration
switchView.actionBlock = {
InterfaceToolkit.instance.touchVisualiser.config.showsTouchDuration = switchView.isOn
}
switchView.addTarget(self, action: #selector(switchToggled(_:)), for: .valueChanged)
row.accessoryView = switchView
return row
}

var touchRadiusSwitch: SwitchAccessoryRow {
var row: SwitchAccessoryRow = SwitchAccessoryRow()
row.text = "Show touch radius"

//Setup Accessory
let switchView = UIActionSwitch()
switchView.isOn = InterfaceToolkit.instance.touchVisualiser.config.showsTouchRadius
switchView.actionBlock = {
InterfaceToolkit.instance.touchVisualiser.config.showsTouchRadius = switchView.isOn
}
switchView.addTarget(self, action: #selector(switchToggled(_:)), for: .valueChanged)
row.accessoryView = switchView
return row
}

var touchLoggingSwitch: SwitchAccessoryRow {
var row: SwitchAccessoryRow = SwitchAccessoryRow()
row.text = "Log screen touches"

//Setup Accessory
let switchView = UIActionSwitch()
switchView.isOn = InterfaceToolkit.instance.touchVisualiser.config.loggingEnabled
switchView.actionBlock = {
InterfaceToolkit.instance.touchVisualiser.config.loggingEnabled = switchView.isOn
}
switchView.addTarget(self, action: #selector(switchToggled(_:)), for: .valueChanged)
row.accessoryView = switchView
return row
}

func prepareObjects() {
//Clear Data
sections.removeAll()

//Setup Section
var section: Section = Section()
section.rows.append(visualiseTouchesSwitch)
section.rows.append(touchDurationSwitch)
section.rows.append(touchRadiusSwitch)
section.rows.append(touchLoggingSwitch)

//Setup Data
sections.append(section)

//Call Delegate
delegate?.viewModelShouldReloadData()
}
}

// MARK: - Public data accessors
extension TouchVisualiserViewModel {
var title: String {
return "Visualise Touches"
}

var numberOfSections: Int {
return sections.count
}

func title(forSection index: Int) -> String? {
return sections[index].title
}

func numberOfRows(inSection index: Int) -> Int {
return rows(inSection: index)?.count ?? 0
}

internal func row(at indexPath: IndexPath) -> Row? {
guard let rows = rows(inSection: indexPath.section) else { return nil }
guard rows.indices.contains(indexPath.row) else { return nil }
return rows[indexPath.row]
}

func title(for row: Row, indexPath: IndexPath) -> String? {
return row.text
}

func performAction(for row: Row, indexPath: IndexPath) {
row.actionBlock?()
}
}

// MARK: - Private data accessors
extension TouchVisualiserViewModel {
private func section(for index: Int) -> Section? {
return sections[index]
}

private func rows(inSection index: Int) -> [Row]? {
guard let section = section(for: index) else { return nil }
return section.rows.filter { !$0.isHidden }
}
}

extension TouchVisualiserViewModel {
@objc
func switchToggled(_ sender: UIActionSwitch?) {
sender?.actionBlock?()
}
}
Loading

0 comments on commit a6142d1

Please sign in to comment.