Is it possible to implement 3D Touch with UITextView?
I tried this example. And this method hadn't called by system when I tapped on any view on the screen. As I read there is no way to use 3D touch with UITapGestureRecognizer. So is there any ways to do this?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("test0")
if let touch = touches.first {
print("test1")
if traitCollection.forceTouchCapability == UIForceTouchCapability.available {
print("test2")
print("FORCE TOUCH VALUE \(touch.force)")
}
}
}
Related
I'm implementing a passThroughView by creating a transparent View on top and override hitTest().
passThroughView should consume touches from Apple Pencil and if touch type is not from pencil, it pass touches to the view underneath.
The problems are:
parameter "event" in hitTest contains no touch, so I can't check touch type in hitTest
I can get touch from touchesBegan and check touch type, but it get called only after hitTest returned true
I subclass UIWindow and override sendEvent() but this function also called after hitTest (and I don't know why)
class WindowAbleToKnowTouchTypes: UIWindow {
override func sendEvent(_ event: UIEvent) {
if event.type == .touches {
// This get called after Hittest
if event.allTouches!.first!.type == .pencil {
print("This touch is from Apple Pencil")
}
}
super.sendEvent(event)
}
}
Is there anyway to check touchType to decide to pass or consume touches?
I ended up using a different approach, it could be useful for many cases: If I can't get touchType in hitTest(), I can still get the touchType with GestureRecognize:
class CustomGestureRecognizer : ImmediatePanGesture {
var beganTouch : UITouch!
var movedTouch : UITouch!
var endedTouch : UITouch!
override func shouldReceive(_ event: UIEvent) -> Bool {
// You can check for touchType here and decide if this gesture should receice the touch or not
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
// Save touch for later use
if let firstTouch = touches.first {
beganTouch = firstTouch
}
super.touchesBegan(touches, with: event)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
// Save touch for later use
if let touch = touches.first {
movedTouch = touch
}
super.touchesMoved(touches, with: event)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
// Save touch for later use
if let touch = touches.first {
endedTouch = touch
}
super.touchesEnded(touches, with: event)
}
}
In the target function of the gestureRecognizer, you can get the UITouch by:
let beganTouch = customGesture.beganTouch
let touchType = beganTouch.touchType
So I've been messing around trying to get the coordinates of touches on the screen. So far I can get the coordinates of one touch with this:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.anyObject()! as UITouch
let location = touch.locationInView(self.view)
println(location)
}
But when touching with two fingers I only get the coordinates of the first touch. Multi-touch works (I tested with this little tutorial: http://www.techotopia.com/index.php/An_Example_Swift_iOS_8_Touch,_Multitouch_and_Tap_Application). So my question is, how do I get the coordinates of the second (and third, fourth...) touch?
** Updated to Swift 4 and Xcode 9 (8 Oct 2017) **
First of all, remember to enable multi-touch events by setting
self.view.isMultipleTouchEnabled = true
in your UIViewController's code, or using the appropriate storyboard option in Xcode:
Otherwise you'll always get a single touch in touchesBegan (see documentation here).
Then, inside touchesBegan, iterate over the set of touches to get their coordinates:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self.view)
print(location)
}
}
the given touches argument is a set of detected touches.
You only see one touch because you select one of the touches with :
touches.anyObject() // Selects a random object (touch) from the set
In order to get all touches iterate the given set
for obj in touches.allObjects {
let touch = obj as UITouch
let location = touch.locationInView(self.view)
println(location)
}
You have to iterate over the different touches. That way you can access every touch.
for touch in touches{
//Handle touch
let touchLocation = touch.locationInView(self.view)
}
In Swift 1.2 this has changed, and touchesBegan now provides a Set of NSObjects.
To iterate through them, cast the touches collection as a Set of UITouch objects as follows:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
var touchSet = touches as! Set<UITouch>
for touch in touchSet{
let location = touch.locationInView(self.view)
println(location)
}
}
For Swift 3, based on #Andrew's answer :
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touchSet = touches
for touch in touchSet{
let location = touch.location(in: self.view)
print(location)
}
}
EDIT, My bad, that's not answering your question, had the same problem and someone linked me to this previous answer :
Anyway, I had to change few things to make it works in swift 3, here is my current code :
var fingers = [String?](repeating: nil, count:5)
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
for touch in touches{
let point = touch.location(in: self.view)
for (index,finger) in fingers.enumerated() {
if finger == nil {
fingers[index] = String(format: "%p", touch)
print("finger \(index+1): x=\(point.x) , y=\(point.y)")
break
}
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
for touch in touches {
let point = touch.location(in: self.view)
for (index,finger) in fingers.enumerated() {
if let finger = finger, finger == String(format: "%p", touch) {
print("finger \(index+1): x=\(point.x) , y=\(point.y)")
break
}
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
for touch in touches {
for (index,finger) in fingers.enumerated() {
if let finger = finger, finger == String(format: "%p", touch) {
fingers[index] = nil
break
}
}
}
}
I still have a little problem but I think it's linked to my GestureRecognizer in my code.
But that should do the trick.
It will print you the coordinate of each point in your consol.
In Swift 3,4
Identify touch pointer by its hash:
// SmallDraw
func pointerHashFromTouch(_ touch:UITouch) -> Int {
return Unmanaged.passUnretained(touch).toOpaque().hashValue
}
I have sprites moving across the screen, and if they are clicked then they disappear (i.e deleted).
I have overridden the touchesBegan func as follows:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("touch")
let touch = touches.first!
let location = touch.location(in: self)
for child in self.children {
if child.position == location {
child.removeFromParent()
}
}
}
This doesn't seem to have any effect, can someone tell me where I am going wrong?
In which class did you implement this method?
If it was in SKNode itself, you simply do:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.removeFromParent()
}
However, if this method is in SKScene, this way that was implemented would probably not work. Because child.position returns a point (x, y) where the touch was made. And you're trying to compare the touch point and position of the SKNode (center point), it's unlikely to work.
Instead of using this way, try using .nodeAtPoint, a method of SKScene.
For this you will need to put a value in the 'name' property of your SKNode:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("touch")
let touch = touches.first!
let positionInScene = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(positionInScene)
if let name = touchedNode.name
{
if name == "your-node-name"
{
touchedNode.removeFromParent()
}
}
}
Font: How do I detect if an SKSpriteNode has been touched
I want to change the color of a UIView based on the number of touches currently on it. I have managed to create this with a single touch:
class colorChangerViewClass: UIView {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
setColor(color: .blue)
print("touchesBegan")
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
setColor(color: .green)
print("touchesEnded")
}
func setColor(color: UIColor) {
self.backgroundColor = color
}
}
But I am struggling with implementing multitouch. I feel like I'm not understanding something here.
Does UIView have a property to check how many fingers are currently touching it?
I could check that each time a new touchesBegan or touchesEnded occurs.
First enable multi touch on your subclass
self.isMultipleTouchEnabled = true
Then inside your touch event functions you can get all touches with event from your UIView class.
let touchCount = event?.touches(for: self)?.count
Make sure the UIView has isMultipleTouchEnabled = true, the default is false.
Then change the touchesBegan method
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
setColor(color: .blue)
let touchCount = event?.touches(for: self)?.count
}
I have subclassed a UITableView in my app so that I can intercept touch events. I am using this to allow me to provide 3D Touch gestures on the entire view (including on top of the table view).
This works great, however the problem is that using 3D Touch on one of the cells and then releasing your finger activates the cell tap.
I need to only activate the cell tap if there is no force exerted. I should explain that I am fading an image in gradually over the entire screen as you apply pressure.
Here is my subclass:
protocol PassTouchesTableViewDelegate {
func touchMoved(touches: Set<UITouch>)
func touchEnded(touches: Set<UITouch>)
}
class PassTouchesTableView: UITableView {
var delegatePass: PassTouchesTableViewDelegate?
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesMoved(touches, withEvent: event)
self.delegatePass?.touchMoved(touches)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesEnded(touches, withEvent: event)
self.delegatePass?.touchEnded(touches)
}
}
And here are the methods I'm calling from my view controller when the touches end and move:
internal func touchMoved(touches: Set<UITouch>) {
let touch = touches.first
self.pressureImageView.alpha = (touch!.force / 2) - 1.0
}
internal func touchEnded(touches: Set<UITouch>) {
UIView.animateWithDuration(0.2, animations: {
self.pressureImageView.alpha = 0.0
})
}
You could create a boolean named isForceTouch which is set to false in touchesBegan, and then once force touch is detected, set it to true. Then in didSelectRowAtIndexPath just return false if isForceTouch is true. It may need tweaking but that should work.