How to make a UIImageView tappable and cause it to do something? (Swift)

(I just started using Swift a few days ago and am relatively new to programming, so please bear with me.) I am trying to make random blocks appear on the screen, and the user must tap them to make them disappear. I have been able to create the blocks, but I have no idea how to actually make them tappable. Can someone please help me? This is my code so far:

func createBlock(){

    let imageName = "block.png"
    let image = UIImage(named: imageName)
    let imageView = UIImageView(image: image!)

    imageView.frame = CGRect(x: xPosition, y: -50, width: size, height: size)
    self.view.addSubview(imageView)



    UIView.animateWithDuration(duration, delay: delay, options: options, animations: {

        imageView.backgroundColor = UIColor.redColor()

        imageView.frame = CGRect(x: self.xPosition, y: 590, width: self.size, height: self.size)

        }, completion: { animationFinished in


            imageView.removeFromSuperview()


    })



}

This is the new code:

  • Deserialize a JSON array to a Swift array of objects
  • Move button when keyboard appears swift
  • How to handle multiple date formats?
  • Swift extension for ?
  • Animation in core graphics
  • Parse JSON using Swift / SwiftyJSON
  • func createBlock(){
    
    
        let imageName = "block.png"
        let image = UIImage(named: imageName)
        let imageView = UIImageView(image: image!)
    
        imageView.frame = CGRect(x: xPosition, y: -50, width: size, height: size)
        self.view.addSubview(imageView)
    
        imageView.userInteractionEnabled = true
        let tapRecognizer = UITapGestureRecognizer(target: self, action: Selector("imageTapped"))
        imageView.addGestureRecognizer(tapRecognizer)
    
        func imageTapped(gestureRecognizer: UITapGestureRecognizer) {
            let tappedImageView = gestureRecognizer.view!
            tappedImageView.removeFromSuperview()
            score += 10
        }
    
    
    
        UIView.animateWithDuration(duration, delay: delay, options: options, animations: {
    
            imageView.backgroundColor = UIColor.redColor()
            imageView.frame = CGRect(x: self.xPosition, y: 590, width: self.size, height: self.size)
    
            }, completion: { animationFinished in
    
    
                imageView.removeFromSuperview()
    
    
        })
    
    
    
    }
    

    5 Solutions Collect From Internet About “How to make a UIImageView tappable and cause it to do something? (Swift)”

    Once you’ve create the view, you need to set it’s userInteractionEnabled property to true. Then you need to attach a gesture to it.

    imageView.userInteractionEnabled = true
    //now you need a tap gesture recognizer
    //note that target and action point to what happens when the action is recognized.
    let tapRecognizer = UITapGestureRecognizer(target: self, action: Selector("imageTapped:"))
    //Add the recognizer to your view.
    imageView.addGestureRecognizer(tapRecognizer)
    

    Now you still need the function, in this case imageTapped:, which is where the action happens when the gesture is recognized. The gesture that was recognized will be sent as an argument, and you can find out which imageView was tapped from they gestures view property.

    func imageTapped(gestureRecognizer: UITapGestureRecognizer) {
        //tappedImageView will be the image view that was tapped.
        //dismiss it, animate it off screen, whatever.
        let tappedImageView = gestureRecognizer.view!
    }
    

    In swift 3.0:

            imvEditName.isUserInteractionEnabled = true
            let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageTapped))
            imvEditName.addGestureRecognizer(tapRecognizer)
    

    And the target method:

    func imageTapped(sender: UIImageView) {
        print("image tapped")
    }
    

    Instead using a UIImageView, you could try use a Custome UIButton and replace the default button image with yours. Hence, you do not need to add UITapGestureRecognizer for each UIImageView. @JeremyPope ‘s approach is actually more efficient compare to this approach.

    Handling UIButton is pretty simple, its subclass of UIControl so there are properties like hide and enabled and you can manipulate them in the way you want.

    What you need to do is to create UIButton programmaticly, just like you they way you creating UIImageView. The way to create them is similar to UIImageView, read more on create UIButton programmaticly in Swift.

    The image below is how a custom UIButton looks like.

    enter image description here

    Leave comments if you need more info.

    My previous answer is still accurate for adding a tap recognizer to a UIImageView, however it is not sufficient for the purposes of a view that is animating. If you are actually creating a game, you’ll likely want to look into SpriteKit, as it is literally tailor made for this. However, this can be accomplished with normal UIKit. I’ve tested two approaches, which both work.

    Both approaches worked far better on an actual device than in the simulator

    Approach 1, Simpler yet slightly less precise up to a tenth of second slower in my tests.

    Instead of adding a tap gesture to the UIImageView, add the gesture to the superview. Keep a reference to your blocks in an array property, and check if any blocks are intercepted by the tap.

    class ViewController: UIViewController {
        let size = CGFloat(40)
        let xPosition = CGFloat(14)
        let options = UIViewAnimationOptions.Autoreverse
        var blocks = [UIImageView]()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Adding recognizer to the whole view.
            let tapRecognizer = UITapGestureRecognizer(target: self, action: Selector("screenTapped:"))
            view.addGestureRecognizer(tapRecognizer)
            blocks.append(createBlock())
        }
    
        //changed to return the created block so it can be stored in an array.
        func createBlock() -> UIImageView {
            let imageName = "block.png"
            let image = UIImage(named: imageName)
            let imageView = UIImageView(image: image!)
            imageView.frame = CGRect(x: xPosition, y: -40, width: size, height: size)
            self.view.addSubview(imageView)
            UIView.animateWithDuration(2, delay: 0.0, options: options, animations: {
                imageView.backgroundColor = UIColor.redColor()
                imageView.frame = CGRect(x: self.xPosition, y: 590, width: self.size, height: self.size)
                }, completion: { animationFinished in
                    imageView.removeFromSuperview()
                    self.blocks.append(self.createBlock())
            })
            return imageView
        }
    
        func screenTapped(gestureRecognizer: UITapGestureRecognizer) {
            let tapLocation = gestureRecognizer.locationInView(view)
            //This is to keep track of the blocks to remove from the array.
            //If you remove them as you iterate the array, and you have more than 
            //one block to remove, you may end up removing the wrong block
            //or with an index out of bounds.
            var removeBlocks = [Int]()
            for (index, block) in enumerate(blocks) {
                //this is the frame of the view as we see it on screen.
                //unfortunately block.frame is not where we see it making a recognizer
                //on the block itself not work.
                if block.layer.presentationLayer().frame.contains(tapLocation) {
                    //Then this block was clicked.
                    block.removeFromSuperview()
                    removeBlocks.append(index)
                }
            }
            //get the indexes ro remove backwards so we are removing from
            //back to front.
            for index in removeBlocks.reverse() {
                blocks.removeAtIndex(index)
            }
        }
    
    }
    

    Approach 2, Best Performance outside of SpriteKit

    In approach two, you subclass UIView, and set your main view to it instead of the UIView. You only need to override a single method. I’m storing the block array in the view for convenience.

    First the TouchView.

    import UIKit
    
    class TouchView: UIView {
        var blocks = [UIImageView]()
        override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
            // ignoring multiple touches for now
            if touches.count == 1 {
                if let touch = touches.first as? UITouch {
                    let touchLocation = touch.locationInView(self)
                    //This is to keep track of the blocks to remove from the array.
                    //If you remove them as you iterate the array, and you have more than
                    //one block to remove, you may end up removing the wrong block
                    //or with an index out of bounds.
                    var removeBlocks = [Int]()
                    for (index, block) in enumerate(blocks) {
                        //this is the frame of the view as we see it on screen.
                        //unfortunately block.frame is not where we see it making a recognizer
                        //on the block itself not work.
                        if block.layer.presentationLayer().frame.contains(touchLocation) {
                            //Then this block was clicked.
                            block.removeFromSuperview()
                            removeBlocks.append(index)
                        }
                    }
                    //get the indexes ro remove backwards so we are removing from
                    //back to front.
                    for index in removeBlocks.reverse() {
                        blocks.removeAtIndex(index)
                    }
                }
            }
        }
    }
    

    Now the ViewController would look like:

    import UIKit
    
    class ViewController: UIViewController {
        let size = CGFloat(40)
        let xPosition = CGFloat(14)
        let options = UIViewAnimationOptions.Autoreverse
        var blocks = [UIImageView]()
        // computed get only property for conveniece of accessing block array
        var touchView:TouchView {
            get {
                return self.view as! TouchView
            }
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
            touchView.blocks.append(createBlock())
            // Do any additional setup after loading the view, typically from a nib.
        }
    
        func createBlock() -> UIImageView {
            let imageName = "block.png"
            let image = UIImage(named: imageName)
            let imageView = UIImageView(image: image!)
            imageView.frame = CGRect(x: xPosition, y: -40, width: size, height: size)
            self.view.addSubview(imageView)
            UIView.animateWithDuration(2, delay: 0.0, options: options, animations: {
                imageView.backgroundColor = UIColor.redColor()
                imageView.frame = CGRect(x: self.xPosition, y: 590, width: self.size, height: self.size)
                }, completion: { animationFinished in
                    imageView.removeFromSuperview()
                    self.touchView.blocks.append(self.createBlock())
            })
            return imageView
        }
    }
    

    In my tests, this seems to work pretty good, even with quickly animated “blocks”. Again, if you are planning on making a full fledged game, you should really look at SpriteKit. There is a really simple tutorial here that should get you started. If you can remove the accepted answer from my previous answer, that would help others not be lead down the wrong path, unless they only are looking to add a gesture to a non-moving UIImageView.

    By Default, image view user interaction is not enabled, so be sure to turn it off.

    let myImageView = UIImageView()
    myImageView.isUserInteractionEnabled = true