无法触摸在Swift中移动的UIImageView

时间:2018-03-28 07:33:28

标签: ios swift uiimageview

我制作掉落瓷砖游戏,瓷砖从屏幕顶部落到一个底部。 这个游戏系统是当你触摸一个瓷砖时,瓷砖将被隐藏。

tile是自定义类(GameTile类),但GameViewController中的Touches开始不起作用。 我该如何解决?

GameTile.swift

class GameTile: UIImageView {

    init(named: String, frame: CGRect) {
        super.init(frame: frame)
        super.image = (UIImage(named: named))
        super.isUserInteractionEnabled = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

class GameTileNormal: GameTile {
    let namedDefault: String
    var frameDefault: CGRect
    let isHiddenDefault: Bool
    var isUserInteractionEnabledDefault: Bool
    let colorName: UIColor

    init(
        named: String,
        frame: CGRect,
        isHidden: Bool = false,
        isUserInteractionEnabled: Bool = true,
        color: UIColor = UIColor.blue) {
        namedDefault = named
        isHiddenDefault = isHidden
        frameDefault = frame
        isUserInteractionEnabledDefault = isUserInteractionEnabled
        colorName = color

        super.init(named: named, frame: frame)
        super.isHidden = isHiddenDefault
        super.isUserInteractionEnabled = isUserInteractionEnabledDefault
        super.backgroundColor = colorName

    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

GameView.swift

class GameView: UIView {

    override init(frame: CGRect) {
        super.init(frame: frame)

        self.isUserInteractionEnabled = true

        self.backgroundColor = (UIColor.white)

        self.frame = CGRect(x:0, y:0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height)


            //make tiles
            let tileNormal = GameTileNormal.init(named: "clear",
                                                 frame: CGRect(x:0), y:-60, width:60, height:60),isUserInteractionEnabled: true)
            self.addSubview(tileNormal)

            //move tiles
            moveTile(tile: tileNormal, lane: 1)
    }
}

    func moveTile(tile: GameTile, lane: Int) {

        UIImageView.animate(withDuration: TimeInterval(2.0),
                            delay: 0.0,
                            options: .curveLinear,
                            animations: {
                                tile.frame.origin.y = UIScreen.main.bounds.size.height
        }, completion: {finished in
            tile.removeFromSuperview()

            //make new tile
            self.makeTiles(lane: lane)

        })
    }

GameViewController.swift

class GameViewController: UIViewController {

var gameView: GameView!

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    self.view.isUserInteractionEnabled = true

    gameView = GameView.init(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: 568))
    self.view.addSubview(trapView)

}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    let touchEvent = touches.first!

    if let gameView = self.gameView {

        // touchEvent.view is "gameView", not the view whose kind of class is GameTileNormal...
        if let touchedGameTile = touchEvent.view as? GameTileNormal {
            print("Touched normal tile")
            touchEvent.view?.isHidden = true
            touchEvent.view?.isUserInteractionEnabled = false

        }else{
            // other view
        }
    }
}

更新

我改变了如何将磁贴从UIImageView.animation移动到Timer。 然后,如果我触摸了瓷砖,它在touchesBegan,GameViewController ..... {/ p>之后的if (tile.layer.presentation()?.hitTest(location)) != nil {之后没有通过

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let touchEvent = touches.first!
        let location = touchEvent.location(in: touchEvent.view)

        if let standardView = self.standardView {

            for tile in standardView.tiles {

             //breakpoint stops here

                if (tile.layer.presentation()?.hitTest(location)) != nil {
                    //breakpoint doesn't through here
                    if tile is GameTileNormal {

                        //normal tile touched

                    }else{

                    }
                    break
                }
            }
        }
    }

moveTiles

makeTileTimer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(updateTilesPositionY(timer:)), userInfo: sendArray, repeats: true)

更新平铺位置(放置平铺)

@objc func updateTilesPositionY(timer: Timer){

//tile info
let timerInfo:[Any] = timer.userInfo as! [Any]
let tile:GameTile =  timerInfo[0] as! GameTile
let lane: Int = timerInfo[1] as! Int

//drop tile
tile.frame.origin.y = tile.frame.origin.y+1

//if tile reached on the bottom
if tile.frame.origin.y >= UIScreen.main.bounds.size.height {
    if tile is GameTileNormal {
        self.showGameOverView()
    }
}else{
    //drop tile
}

2 个答案:

答案 0 :(得分:2)

UIImageView.animate添加选项.allowUserInteraction

UIImageView.animate(withDuration: TimeInterval(2.0),
                    delay: 0.0,
                    options: [.curveLinear, .allowUserInteraction],
                    animations: {
                        tile.frame.origin.y = UIScreen.main.bounds.size.height
}, completion: {finished in
    ...

默认情况下,动画期间不允许用户互动。

<强>更新

但是,要测试用户是否击中了移动物体,您将会有更难的时间。例如,见SO question。基本上,UIView对象并没有真正移动,您可以在触发动画后轻松测试,动画对象的frame直接设置到结束位置。只是表示层绘制移动视图。

你必须经常浏览游戏中所有移动的牌,并测试每一个牌是否有任何一个被触及(这里我假设你有一个对游戏中所有tiles的引用): / p>

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    let touchEvent = touches.first!
    let location = touchEvent.location(in: touchEvent.view)

    if let gameView = self.gameView {
        for tile in tiles {
            // go over all the moving objects in your scene and hit test all of them
            if let touchedLayer = tile.layer.presentation()?.hitTest(location) {
                // if a hittest returns a layer, it means that this tile was touched, we can handle it and break out of the loop

                tile.isHidden = true
                tile.isUserInteractionEnabled = false
                tile.removeFromSuperview()
                break
            }
        }
    }
}

答案 1 :(得分:0)

使用 layer.presentationLayer 运行hitTest,如果hitTest返回CALayer,那么您正在触摸该titleView,事实上这仅在您的标题为userInteractionEnabled = false时才有效

完整代码

import UIKit

class GameTile: UIImageView {

    init(named: String, frame: CGRect) {
        super.init(frame: frame)
        super.image = (UIImage(named: named))
        super.isUserInteractionEnabled = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

class GameTileNormal: GameTile {
    let namedDefault: String
    var frameDefault: CGRect
    let isHiddenDefault: Bool
    var isUserInteractionEnabledDefault: Bool
    let colorName: UIColor

    init(
        named: String,
        frame: CGRect,
        isHidden: Bool = false,
        isUserInteractionEnabled: Bool = false,
        color: UIColor = UIColor.blue) {
        namedDefault = named
        isHiddenDefault = isHidden
        frameDefault = frame
        isUserInteractionEnabledDefault = isUserInteractionEnabled
        colorName = color

        super.init(named: named, frame: frame)
        super.isHidden = isHiddenDefault
        super.isUserInteractionEnabled = isUserInteractionEnabledDefault
        super.backgroundColor = colorName

    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

class GameView: UIView {

    override init(frame: CGRect) {
        super.init(frame: frame)

        self.isUserInteractionEnabled = true

        self.backgroundColor = (UIColor.white)

        self.frame = CGRect(x:0, y:0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height)


        //make tiles
        let tileNormal = GameTileNormal.init(named: "clear",
                                             frame: CGRect(x:0, y:-60, width:60, height:60),isUserInteractionEnabled: false)
        self.addSubview(tileNormal)
        //move tiles
        moveTile(tile: tileNormal, lane: 1)

        self.layer.borderWidth = 1
        self.layer.borderColor = UIColor.blue.cgColor
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func moveTile(tile: GameTile, lane: Int) {

        UIImageView.animate(withDuration: TimeInterval(10),
                            delay: 0.0,
                            options: .curveLinear,
                            animations: {
                                tile.frame.origin.y = UIScreen.main.bounds.size.height
        }, completion: {finished in
            tile.removeFromSuperview()

            //make new tile
            //self.makeTiles(lane: lane)

        })
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let touchEvent = touches.first!
        let location = touchEvent.location(in: touchEvent.view)

        for tile in self.subviews {
            // go over all the moving objects in your scene and hit test all of them
            if tile.layer.presentation()?.hitTest(location) != nil {
                // if a hittest returns a layer, it means that this tile was touched, we can handle it and break out of the loop
                tile.isHidden = true
                break
            }
        }
    }

}

class ViewController: UIViewController {

    var gameView: GameView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        self.view.isUserInteractionEnabled = true

        gameView = GameView.init(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: 568))
        self.view.addSubview(gameView)

    }

}

enter image description here