Detect UIImageView Touch in Swift - ios

How would you detect and run an action when a UIImageView is touched?
This is the code that I have so far:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
var touch: UITouch = UITouch()
if touch.view == profileImage {
println("image touched")
}
}

You can detect touches on a UIImageView by adding a UITapGestureRecognizer to it.
It's important to note that by default, a UIImageView has its isUserInteractionEnabled property set to false, so you must set it explicitly in storyboard or programmatically.
override func viewDidLoad() {
super.viewDidLoad()
imageView.isUserInteractionEnabled = true
imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(imageTapped)))
}
#objc private func imageTapped(_ recognizer: UITapGestureRecognizer) {
print("image tapped")
}

You can put an UITapGestureRecognizer inside your UIImageView using Interface Builder or in code (as you want), I prefer the first. Then you can put an #IBAction and handle the tap inside your UIImageView, Don't forget to set the UserInteractionEnabled to true in Interface Builder or in code.
#IBAction func imageTapped(sender: AnyObject) {
println("Image Tapped.")
}
I hope this help you.

Swift 3
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch:UITouch = touches.first!
if touch.view == profileImage {
println("image touched")
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch:UITouch = touches.first!
if touch.view == profileImage {
println("image released")
}
}

Related

I add a UIGestureReconizer to a view to detect Touch Down action, but then the buttons above the view won't work anymore

I have a view which needs to detect "Touch Down" action on the full view, after I found out normal UITapGestureRecognizer can't accomplish it, I customize UIestureRecognizer class to achieve it, using this.
So it does work, but I have several buttons on the view, after I can detect the Touch Down action for the view, those buttons stop to work.
I need both of them to work, the view to detect Touch Down and the buttons can be pressed above the view. How can I achieve it?
To be clear, those buttons just need to detect normal Touch Up Inside action, only the view need to detect Touch Down action. Also, when touch events happen on the buttons, the view doesn't need to do react to the touch event.
Thanks.
import UIKit
import UIKit.UIGestureRecognizerSubclass
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let t = SingleTouchDownGestureRecognizer(target: self, action: #selector(handleTap(sender:)))
self.view.addGestureRecognizer(t)
}
#objc func handleTap(sender: UITapGestureRecognizer? = nil) {
self.view.backgroundColor = #colorLiteral(red: 0.2392156869, green: 0.6745098233, blue: 0.9686274529, alpha: 1)
UIView.animate(withDuration: 1) {
self.view.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
}
}
#IBAction func testBtnPressed(_ sender: Any) {
print("testBtnPressed!")
}
}
class SingleTouchDownGestureRecognizer: UIGestureRecognizer {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
if self.state == .possible {
self.state = .recognized
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
self.state = .failed
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
self.state = .failed
}
}
You could compare that event in touchesBegan has touches for the view that is the same as the view to witch your gesture recognizer was assigned
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
guard let view = self.view else { return }
guard let touches = event.touches(for: view) else { return }
super.touchesBegan(touches, with: event)
if self.state == .possible {
self.state = .recognized
}
}

Change background color of UIView on tap using tap gesture (iOS)

I want to change color of UIView when it's tap and change it back to it's original color after tap event
I already implemented these 2 methods but their behavior is not giving me required results
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
backgroundColor = UIColor.white
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
backgroundColor = UIColor.gray
}
These 2 methods work but after press tap on UIView for 2 seconds then it works. Moreover it doesn't change the color of UIView back to white after pressing it (In short it stays gray until I restart the app)
I am using tap gestures on UIView
Instead of overriding touchesBegan and touchesEnded methods, you could add your own gesture recognizer. Inspired from this answer, you could do something like:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .gray
setupTap()
}
func setupTap() {
let touchDown = UILongPressGestureRecognizer(target:self, action: #selector(didTouchDown))
touchDown.minimumPressDuration = 0
view.addGestureRecognizer(touchDown)
}
#objc func didTouchDown(gesture: UILongPressGestureRecognizer) {
if gesture.state == .began {
view.backgroundColor = .white
} else if gesture.state == .ended || gesture.state == .cancelled {
view.backgroundColor = .gray
}
}
}

Gesture recognizer with delaysTouchesBegan stops all calls to touchesMoved?

The view controller code below demonstrates my question. It prints some debugging statements in response to touch events and a pinch gesture.
If the gesture recognizer is disabled, and I drag a finger on the screen, everything works as I expected: the controller gets a touchesBegan, a bunch of touchesMoved, and a touchesEnded. But if I enable the gesture recognizer and set its delaysTouchesBegan property to true, and drag a single finger, the controller receives only touchesBegan and touchesEnded calls - no touchesMoved.
The docs for delaysTouchesBegan suggest that touch events will pile up and be delivered after the gesture recognizer fails. Is this the expected behavior - to lose all the touchesMoved events?
class ViewController: UIViewController {
var gr: UIPinchGestureRecognizer!
override func viewDidLoad() {
super.viewDidLoad()
gr = UIPinchGestureRecognizer(target: self, action: #selector(pinched(_:)))
gr.delaysTouchesBegan = true
view.addGestureRecognizer(gr)
}
#IBAction func buttonTapped(_ sender: Any) {
gr.isEnabled = !gr.isEnabled
print("pinch recognizer enabled: \(gr.isEnabled)")
}
#objc func pinched(_ gr: Any?) {
print("pinched")
}
var i = 0
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
i = 0
print("touchesBegan")
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
print("touchesMoved \(i)")
i += 1
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
print("touchesEnded")
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
print("touchesCancelled")
}
}
A pinch gesture recognizer reports changes each time the distance between the fingers change. The distance between the fingers is reported as a scale factor. So you will want to look at the scale factor, not touchesMoved. Was that helpful?
#objc func pinched(_ gr: UIPinchGestureRecognizer) {
if gr.state == .began {
print("began")
}
if gr.state == .changed {
print("scale: \(gr.scale)")
}
if gr.state == .ended {
print("ended")
}
}
Handling Pinch Gestures
Handling Pinch Gestures ( Swift 4 - 2018 )

Detecting UITouches while UIViewController is being shown as 3DTouch preview

Is it possible to detect touches and get the location of a touch from a UIViewController which is being currently used as previewingContext view controller for 3D Touch? (I want to change the image of within the preview controller when the touch moves from left to right)
I've tried both touchesBegan and touchesMoved none of them are fired.
class ThreeDTouchPreviewController: UIViewController {
func getLocationFromTouch(touches: Set<UITouch>) -> CGPoint?{
guard let touch = touches.first else { return nil }
return touch.location(in: self.view)
}
//Not fired
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let location = getLocationFromTouch(touches: touches)
print("LOCATION", location)
}
//Not fired
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let location = getLocationFromTouch(touches: touches)
print("LOCATION", location)
}
}
Even tried adding a UIPanGesture.
Attempting to replicate FaceBook's 3D Touch feature where a user can move finger from left to right to change the current image being displayed.
Video for context: https://streamable.com/ilnln
If you want to achive result same as in FaceBook's 3D Touch feature you need to create your own 3D Touch gesture class
import UIKit.UIGestureRecognizerSubclass
class ForceTouchGestureRecognizer: UIGestureRecognizer {
var forceValue: CGFloat = 0
var isForceTouch: Bool = false
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesBegan(touches, with: event)
handleForceWithTouches(touches: touches)
state = .began
self.isForceTouch = false
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
handleForceWithTouches(touches: touches)
if self.forceValue > 6.0 {
state = .changed
self.isForceTouch = true
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesEnded(touches, with: event)
state = .ended
handleForceWithTouches(touches: touches)
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesCancelled(touches, with: event)
state = .cancelled
handleForceWithTouches(touches: touches)
}
func handleForceWithTouches(touches: Set<UITouch>) {
if touches.count != 1 {
state = .failed
return
}
guard let touch = touches.first else {
state = .failed
return
}
forceValue = touch.force
}
}
and now you can add this gesture in your ViewController in viewDidLoad method
override func viewDidLoad() {
super.viewDidLoad()
let gesture = ForceTouchGestureRecognizer(target: self, action: #selector(imagePressed(sender:)))
self.view.addGestureRecognizer(gesture)
}
Now you can manage your controller UI in Storyboard. Add cover view above UICollectionView and UIImageView in a center and connect it with IBOutlets in code.
Now you can add handler methods for gesture
func imagePressed(sender: ForceTouchGestureRecognizer) {
let location = sender.location(in: self.view)
guard let indexPath = collectionView?.indexPathForItem(
at: location) else { return }
let image = self.images[indexPath.row]
switch sender.state {
case .changed:
if sender.isForceTouch {
self.coverView?.isHidden = false
self.selectedImageView?.isHidden = false
self.selectedImageView?.image = image
}
case .ended:
print("force: \(sender.forceValue)")
if sender.isForceTouch {
self.coverView?.isHidden = true
self.selectedImageView?.isHidden = true
self.selectedImageView?.image = nil
} else {
//TODO: handle selecting items of UICollectionView here,
//you can refer to this SO question for more info: https://stackoverflow.com/questions/42372609/collectionview-didnt-call-didselectitematindexpath-when-superview-has-gesture
print("Did select row at indexPath: \(indexPath)")
self.collectionView?.selectItem(at: indexPath, animated: true, scrollPosition: .centeredVertically)
}
default: break
}
}
From this point you need to customize your view to make it look in same way as Facebook do.
Also I created small example project on GitHub https://github.com/ChernyshenkoTaras/3DTouchExample to demonstrate it

Function to detect if there's any touch occurring on the screen

I'm trying to create a simple function, similar to the touchesBegan, that detects if there's any touch occurring on the screen.
I've hit a brick wall trying it out myself because I'm not comfortable with UITouch class, but I really need some self made function, outside the touchesBegan default one.
I was trying to do something like this 'pseudo-code/swift'
func isTouchingTheScreen() -> Bool {
let someTouchHandleConstant: uitouch ???
if imTouchingTheScreen {
return true
} else {
return false
}
}
Do you have any hints?
PS: I know that code doesn't work, don't call that out, it was just to give you some 'image' of what I was trying to do (:
The idea
You can simply keep track of every touch begun, ended or cancelled by the user.
class GameScene: SKScene {
private var activeTouches = Set<UITouch>()
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
activeTouches.unionInPlace(touches)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
activeTouches.subtractInPlace(touches)
}
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
if let touches = touches {
activeTouches.subtractInPlace(touches)
}
}
var isTouchingTheScreen: Bool { return !activeTouches.isEmpty }
}
Keeping activeTouches updated
As you can see I am keeping updated the activeTouches Set.
Every time a touch does begin I add it to activeTouches. And every time a touch does end or is cancelled I remove it from activeTouches.
The isTouchingTheScreen computed variable
This allows me to define the isTouchingTheScreen computed property that simply returns true when the Set contains some element.
You can implement UITapGestureRecognizer as below:
var tapGesture :UITapGestureRecognizer!
override func didMoveToNode() {
// Add UITapGestureRecognizer to view
self.tapGesture = UITapGestureRecognizer(target: self, action: #selector(ViewController.touchedView(_:)))
view.addGestureRecognizer(tapGesture)
}
func touchedView(sender: UITapGestureRecognizer) {
print("view touched")
}
You could implement UITapGestureRecognizer:
// global var
var tapGesture :UITapGestureRecognizer!
override func didMoveToView() {
// Add tap gesture recognizer to view
self.tapGesture = UITapGestureRecognizer(target: self, action: #selector(GameScene.handleTap(_:)))
self.tapGesture.cancelsTouchesInView = false
view.addGestureRecognizer(tapGesture)
}
func handleTap(sender: UITapGestureRecognizer) {
print("GameScene tap")
if sender.state == .Ended {
var positionInScene: CGPoint = sender.locationInView(sender.view)
positionInScene = self.scene!.convertPointFromView(positionInScene)
let touchedNode = self.nodeAtPoint(positionInScene)
if touchedNode.name != "myHero" {
print("The SKSpriteNode myHero was tapped")
}
}
}
You can find more details in Apple docs here.

Resources