【问题标题】:How to make a UIImageView tappable and cause it to do something? (Swift)如何使 UIImageView 可点击并使其执行某些操作? (迅速)
【发布时间】:2015-02-15 01:18:05
【问题描述】:

(我几天前才开始使用 Swift,对编程比较陌生,所以请多多包涵。)我正在尝试让随机块出现在屏幕上,用户必须点击它们才能使它们消失。我已经能够创建块,但我不知道如何真正使它们可点击。有人可以帮帮我吗?到目前为止,这是我的代码:

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()


    })



}

编辑:这是我正在尝试的新代码:

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()


    })



}

【问题讨论】:

    标签: xcode swift uiimage touch


    【解决方案1】:

    创建视图后,您需要将其 userInteractionEnabled 属性设置为 true。然后你需要给它附加一个手势。

    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)
    

    现在您仍然需要该函数,在本例中为 imageTapped:,这是识别手势时发生动作的地方。识别的手势将作为参数发送,您可以从手势视图属性中找出点击了哪个 imageView。

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

    【讨论】:

    • @JeremyPope 这有点尴尬,但我还是想不通!所以我添加了你建议的代码,在函数“imageTapped”中我只是从超级视图中删除了“tappedimageview”。当我实际运行程序时,当我点击块时没有任何反应):
    • 你能在函数中放置断点或打印以确保它被调用吗?您如何从超级视图中删除点击的图像?
    • @JeremyPope 我发布了新代码,以便您可以看到我在函数中添加的内容。我认为它不起作用。当按下某些东西时,它应该将分数增加 10,我有一个 NSTimer,它运行一个每秒更新分数的函数,以便玩家可以看到分数。
    • 两薄。 Target: self 指的是类(可能是你的 ViewController 类),所以它在类中寻找方法。看起来您在 createBlock 方法中有该方法。将它移到类本身之外。
    • 此外,由于视图正在动画化,因此手势识别器正在寻找从哪里开始或结束的点击。解决方案将变得更加棘手。我很快就会更新我的答案,必须通过 Xcode 运行它来验证。
    【解决方案2】:

    在 Swift 3.0 中:

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

    以及目标方法:

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

    【讨论】:

    • 而在 swift 4.x 中,imageTapped 函数需要在它之前声明@objc
    【解决方案3】:

    我之前的回答对于向 UIImageView 添加点击识别器仍然是准确的,但是对于动画视图来说这还不够。如果您实际上是在创建游戏,您可能会想要研究 SpriteKit,因为它确实是为此量身定制的。然而,这可以通过普通的 UIKit 来完成。我已经测试了两种方法,它们都有效。

    这两种方法在实际设备上的效果都比在模拟器上好得多

    方法 1,更简单但精度稍差 在我的测试中慢了十分之一秒。

    不是向 UIImageView 添加轻击手势,而是向超级视图添加手势。在数组属性中保留对块的引用,并检查是否有任何块被点击拦截。

    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)
            }
        }
    
    }
    

    方法 2,最佳性能 SpriteKit 之外

    在方法二中,您将 UIView 子类化,并将您的主视图设置为它而不是 UIView。您只需要覆盖一个方法。为了方便起见,我将块数组存储在视图中。

    首先是 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)
                    }
                }
            }
        }
    }
    

    现在 ViewController 看起来像:

    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
        }
    }
    

    在我的测试中,这似乎工作得很好,即使是快速动画“块”。同样,如果您打算制作一个完整的游戏,您应该真正看看 SpriteKit。有一个非常简单的教程here 应该可以帮助您入门。如果您可以从我之前的答案中删除已接受的答案,这将有助于其他人不会走错路,除非他们只是想向非移动的 UIImageView 添加手势。

    【讨论】:

      【解决方案4】:

      您可以尝试使用自定义 UIButton 并将默认按钮图像替换为您的而不是使用 UIImageView。因此,您不需要为每个 UIImageView 添加UITapGestureRecognizer。 @JeremyPope 的方法实际上比这种方法更有效。

      处理UIButton 非常简单,它是UIControl 的子类,所以有hideenabled 之类的属性,您可以按照自己的方式操作它们。

      您需要做的是以编程方式创建UIButton,就像您创建UIImageView 的方式一样。创建它们的方法类似于UIImageView,更多信息请阅读create UIButton programmaticly in Swift

      下图是自定义UIButton 的样子。

      如果您需要更多信息,请离开 cmets。

      【讨论】:

      • 这似乎是一种严厉的做法。该按钮内部仍然有一个 UIImageView ,以及提问者当前实现不需要的许多其他东西。如果他要创造无数个,为什么要让这些物体比它们需要的更重?
      • @JeremyPope 是的,你是对的。这是我解决这个问题的方法。你的方法肯定比我的更有效。
      【解决方案5】:

      默认情况下,图像视图用户交互未启用,​​因此请务必通过将其设置为 true 来启用它。

      let myImageView = UIImageView()
      myImageView.isUserInteractionEnabled = true
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-07-13
        • 2015-07-25
        • 1970-01-01
        • 2022-01-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多