For a data analysis project, I want to track the measurements for touch force, size and duration in a log-file for a simple app (I use the Foodtracker app from the Apple Documentation Website).
I know that I can get the force, size and duration from UITouch. But
how do I access UITouch to get these measurements?
and how do I write these measurements into a log-file?
1. How do I access UITouch to get these measurements?
You'll have to override the touch handlers in your view controller.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
let touch = touches.first!
print("\(touch.force)")
print("\(touch.majorRadius)")
print("\(touch.timestamp)")
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
let touch = touches.first!
print("\(touch.force)")
print("\(touch.majorRadius)")
print("\(touch.timestamp)")
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesCancelled(touches, with: event)
let touch = touches.first!
print("\(touch.force)")
print("\(touch.majorRadius)")
print("\(touch.timestamp)")
}
2. How do I write these measurements into a log-file?
For that checkout this post: Read and write a String from text file
Related
I have the following hierarchy, the root view of the controller. Added two subviews, a standard UIView and a UIScrollView. The UIView is on top.
What I want to do is to receive touches on that UIView, meaning touchesBegan, touchesMoved... like this:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Began")
super.touchesBegan(touches, with: event)
next?.touchesBegan(touches, with: event)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Moved")
super.touchesMoved(touches, with: event)
next?.touchesMoved(touches, with: event)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Ended")
super.touchesEnded(touches, with: event)
next?.touchesEnded(touches, with: event)
}
But at the same time I want the scroll to work. The UIScrollView that is below, I want that to keep scrolling as usual.
This seems impossible to do. If my view handles touches, then I can't forward them to the UIScrollView. But I want both things: see the raw touches on the top view while the scrollview to work as usual.
How can I accomplish this?
The opposite would also work. Meaning, a scrollview on top that scrolls as usual but I also receive the raw touches (touchesBegan, touchesMoved...) on the view that is underneath.
Following this answer worked for me, though with some slight changes. Do note that for this solution, the UIScrollView is on top of the UIView. Firstly, you need to create a subclass of UIScrollView and override the touch methods.
protocol CustomScrollViewDelegate {
func scrollViewTouchBegan(_ touches: Set<UITouch>, with event: UIEvent?)
func scrollViewTouchMoved(_ touches: Set<UITouch>, with event: UIEvent?)
func scrollViewTouchEnded(_ touches: Set<UITouch>, with event: UIEvent?)
}
class CustomScrollView: UIScrollView {
var customDelegate: CustomScrollViewDelegate?
override func awakeFromNib() {
super.awakeFromNib()
for gesture in self.gestureRecognizers ?? [] {
gesture.cancelsTouchesInView = false
gesture.delaysTouchesBegan = false
gesture.delaysTouchesEnded = false
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
customDelegate?.scrollViewTouchBegan(touches, with: event)
super.touchesBegan(touches, with: event)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
customDelegate?.scrollViewTouchMoved(touches, with: event)
super.touchesMoved(touches, with: event)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
customDelegate?.scrollViewTouchEnded(touches, with: event)
super.touchesEnded(touches, with: event)
}
}
Take note of the code in awakeFromNib(). UIScrollView has its own set of gestures. So for each gesture, delaysTouchesBegan and delaysTouchesEnded needs to be false to prevent delays for the touch events.
Finally, just assign the delegate to your ViewController and implement the methods like so.
class ViewController: UIViewController {
#IBOutlet weak var scrollView: CustomScrollView!
#IBOutlet weak var touchView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
scrollView.customDelegate = self
}
}
extension ViewController: CustomScrollViewDelegate {
func scrollViewTouchBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// Check if touch location is within the bounds of the UIView
if let touch = touches.first {
let position = touch.location(in: view)
if touchView.bounds.contains(position) {
print("Began")
}
}
}
func scrollViewTouchMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
// Check if touch location is within the bounds of the UIView
if let touch = touches.first {
let position = touch.location(in: view)
if touchView.bounds.contains(position) {
print("Moved")
}
}
}
func scrollViewTouchEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
// Check if touch location is within the bounds of the UIView
if let touch = touches.first {
let position = touch.location(in: view)
if touchView.bounds.contains(position) {
print("Ended")
}
}
}
}
I am making a "slide to confirm" button, the set up is a custom UIButton + a regular UIImageView, the image is passed to the button and the button listen to touches and move the image accordingly.
Everything is working great except for one detail after the button is released everything should go back to their initial state, yet the button stay changed as if its alpha was halved
i commented the alpha because the button was going to hide but i disabled that once i run into this problem, the problem happens regardless of setting the alpha or not.
This is the code for the custom button
class SlideButton: UIButton {
var arrowImage: UIImageView?
var initialLocation: CGPoint?
var initialArrowPosition: CGPoint?
func setArrow(arrow:UIImageView){
arrowImage = arrow
initialArrowPosition = arrow.frame.origin
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
let touch = touches.first!
let location = touch.location(in: self.superview)
initialLocation = location
UIView.animate(withDuration: 0.2) {
//self.alpha = 0.0
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
UIView.animate(withDuration: 0.3) {
self.arrowImage!.frame.origin = self.initialArrowPosition!
//self.alpha = 1.0
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
let currentLocation = touch.location(in: self.superview)
arrowImage!.frame.origin.x = initialArrowPosition!.x - initialLocation!.x + (currentLocation.x)
}
}
You are remembering to call super for one of your touches methods:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
But for the other touches methods you've forgotten to do that, so you've broken the core functionality of the button.
you might have to also add an override to touchesCancelled because you might not always hit the touchesEnded endpoint.
I have attempted to develop a game using swift that leverages the 3D touch hardware of iPhones. However, as I was submitting my app to the App Store, it got rejected because the game wasn't playable on iPads.
My question is, what is the best way of implementing a similar functionality for non 3D touch devices? The way I am doing right now is by implementing the following method
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if self.didStartGame, let touch = touches.first {
let maximumPossibleForce = touch.maximumPossibleForce
let force = touch.force
let normalizedForce = min(force/maximumPossibleForce * 2.5, 1)
// Added game-related code here to modify scene accordingly
}
}
When running the latter on non 3D touch devices, debugging the value of touch.maximumPossibleForce returns 0.
You cannot detect force touch on devices that that don't support it.
But maybe you could use the majorRadius property on UITouch. It gives you the radius of the touch.
With the radius you can allow users that don't have a 3d touch device to control your game with the angle of their finger:
This is the code for the above example:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let pseudoForce = touches.first?.majorRadius else { return }
label.text = "\(pseudoForce)"
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let pseudoForce = touches.first?.majorRadius else { return }
label.text = "\(pseudoForce)"
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
label.text = "-"
}
Ive just started learning programming in general and ran into a problem:
I have read some articles about that the iPhones are only capable of tracking 5 touches or so... But I realised that when I touch the screen with e.g. 7 fingers at once, my program-let stops working.
So, anyone knows, which parts of the code malfunctions when I am touching the screen way too many times at once?
(I am a newbie.)
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first!
touchLocation = touch.locationInNode(self)
nrTouches += touches.count
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
nrTouches -= touches.count
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first!
touchLocation = touch.locationInNode(self)
}
override func update(currentTime: CFTimeInterval) {
if nrTouches > 0 {
touchingLabel.text = "touching"
} else {
touchingLabel.text = "not touching"
}
}
So, in the case I am touching with 7 fingers at once, "touching" will be displayed all the time.
tankyuu
You're missing touchesCancelled, which gets called when the 6th finger is put down:
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
if let touches = touches {
nrTouches -= touches.count // same logic as touchesEnded
}
}
An interesting note: The iPad can handle 11 touch events. More on that here.
I am making a game similar to Fruit Ninja using Swift 2.0 in Xcode v7.0. However, in the touchesBegan, touchesMoved, and touchesCancelled override functions, I am getting the three following errors:
"Value of type 'Set' has no member 'anyObject'" comes up twice and
"Value of optional type 'Set' not unwrapped; did you mean to use "!" or "?"?" comes once.
Here is the code
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.anyObject() as UITouch
let location = touch.locationInNode(self)
activeSlicePoints.append(location)
redrawActiveSlice()
activeSliceBG.removeAllActions()
activeSliceFG.removeAllActions()
activeSliceBG.alpha = 1
activeSliceFG.alpha = 1
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.anyObject() as UITouch
let location = touch.locationInNode(self)
activeSlicePoints.append(location)
redrawActiveSlice()
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
activeSliceBG.runAction(SKAction.fadeOutWithDuration(0.25))
activeSliceFG.runAction(SKAction.fadeOutWithDuration(0.25))
}
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
touchesEnded(touches, withEvent: event)
}
Here is an image of on what lines the errors appear
All help appreciated. Thanks in advance.
let touch = touches.first as? UITouch
This will get you the first touch object.
Use it like:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = touches.first {
// ...
}
super.touchesBegan(touches, withEvent:event)
}