【问题标题】:Xcode & Swift - Detecting user touch of UIView inside of UIScrollViewXcode & Swift - 在 UIScrollView 内检测用户对 UIView 的触摸
【发布时间】:2014-09-23 13:34:01
【问题描述】:

我正在创建一个类似于 Flappy Bird 的游戏,但用户将手指放在屏幕上并躲避障碍物,而不是点击让小鸟飞起来。

我通过一个 UIScrollView 来做到这一点,其中 UIView 被用作障碍物。当用户触摸 UIView 时,游戏结束。

如何从 UIScrollView 中检测用户对 UIView 的触摸?我正在使用带有 Xcode Beta 4 的 Swift。

编辑:这是游戏截图

如您所见,用户在向上滚动时在灰色块 (UIView) 之间移动手指。

【问题讨论】:

    标签: ios xcode uiview uiscrollview swift


    【解决方案1】:

    通过将滚动视图的userInteractionEnabled 设置为NO,视图控制器将开始接收触摸事件,因为UIViewControllerUIResponder 的子类。您可以在视图控制器中覆盖这些方法中的一个或多个以响应这些触摸:

    • touchesBegan: withEvent:
    • touchesMoved: withEvent:
    • touchesEnded: withEvent:
    • touchesCancelled: withEvent:

    我创建了一些示例代码来演示如何做到这一点:

    class ViewController: UIViewController {
        @IBOutlet weak var scrollView: UIScrollView!
    
        // This array keeps track of all obstacle views
        var obstacleViews : [UIView] = []
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // Create an obstacle view and add it to the scroll view for testing purposes
            let obstacleView = UIView(frame: CGRectMake(100,100,100,100))
            obstacleView.backgroundColor = UIColor.redColor()
            scrollView.addSubview(obstacleView)
    
            // Add the obstacle view to the array
            obstacleViews += obstacleView
        }
    
        override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!) {
            testTouches(touches)
        }
    
        override func touchesMoved(touches: NSSet!, withEvent event: UIEvent!) {
            testTouches(touches)
        }
    
        func testTouches(touches: NSSet!) {
            // Get the first touch and its location in this view controller's view coordinate system
            let touch = touches.allObjects[0] as UITouch
            let touchLocation = touch.locationInView(self.view)
    
            for obstacleView in obstacleViews {
                // Convert the location of the obstacle view to this view controller's view coordinate system
                let obstacleViewFrame = self.view.convertRect(obstacleView.frame, fromView: obstacleView.superview)
    
                // Check if the touch is inside the obstacle view
                if CGRectContainsPoint(obstacleViewFrame, touchLocation) {
                    println("Game over!")
                }
            }
        }
    
    }
    

    【讨论】:

    • 这是个好方法。但是,它不是在调用 touchesMoved 时重新定义触摸,这不是必需的吗?效率可能会有所提高。 CGRectContainsPoint 是查找位置的方法,但您需要为每一侧的 UIView 调用两次
    • 是的,只要用户不断移动手指,它确实会调用 testTouches。但是OP可能会用一些代码替换println("Game over!"),在第一次接触时用游戏结束屏幕替换当前屏幕,所以我认为这不会是一个问题。你说得对,CGRectContainsPoint 至少需要调用两次。但是障碍视图数组包含所有障碍视图,包括两个“关键”视图,所以没关系。如果性能低于标准,OP 始终可以优化代码以仅检查两个“关键”视图或当前可见的视图。
    • 公平。所有代码都在这里,也许可以创建另一个函数来检查初始触摸。
    • 你能发布一个 swift 3 版本吗?
    【解决方案2】:

    您可以按如下方式以编程方式添加手势识别器

    var touch = UITapGestureRecognizer(target:self, action:"action")
    scrollView.addGestureRecognizer(touch)
    

    但是,此手势识别器不适用于您。 UITapGestureRecognizer 只会在点击时返回,而不是点击并按住,并且 UILongPressGestureRecognizer 不会提供有关位置的信息,因此您想使用 UIPanGestureRecognizer。它不断地告诉您触摸移动了多远。

    var touch = UIPanGestureRecognizer(target:self, action:"handlePan")
    scrollView.addGestureRecognizer(touch)
    
    @IBAction func handlePan(recognizer:UIPanGestureRecognizer) {
        let translation = recognizer.translationInView(self.view)
        recognizer.setTranslation(CGPointZero, inView: self.view)
    }
    

    您可以使用常量“平移”来移动对象,它表示人滑动手指的距离。使用它加上你的鸟的位置将鸟移动到一个新的点。调用此函数后,您必须将翻译重置为零。

    编辑:根据你的游戏格式,这段代码应该是最好的方法。

    所以,总而言之,找到手指位置的代码应该如下。

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
        for touch: AnyObject in touches {
            let location = touch.locationInView(yourScrollView)
        }
    }
    
    
    @IBAction func handlePan(recognizer:UIPanGestureRecognizer) {
        let translation = recognizer.translationInView(self.view)
        var currentLocation : CGPoint = CGPointMake(location.x+translation.x, location.y+translation.y)
        recognizer.setTranslation(CGPointZero, inView: self.view)
    }
    

    currentLocation 是一个包含当前触摸位置的 CGPoint,即手指滑动到的位置。由于我不知道您是如何创建要避免的视图,因此您必须使用 currentLocation 的 y 坐标来确定要在该 y 处避免的视图的 x 边界,并使用 比较器来确定如果触摸的 x 边界在其中任何一个视图内。

    注意:您必须声明位置,以便可以在 handlePan 中访问它

    var location : CGPoint = CGPointZero
    

    【讨论】:

    • 这看起来不错,但是当用户手指触摸其中一个 UIView 时,我需要做什么才能让应用程序发送消息或其他内容?我不需要移动鸟或任何东西,因为只有用户的手指在交互和躲避障碍物。当用户触摸多个 UIView 之一时,游戏将结束。
    • 你误解了问题:没有鸟:游戏是关于将手指放在屏幕上并避开障碍物。
    • 注明。你应该做的是使用touchesBegan方法找到触摸的初始y值,然后在我给出的代码中添加常量翻译的y值,然后使用一些逻辑来找出这些值是否与边界重叠你的视图控制器。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-01
    • 2012-10-12
    • 1970-01-01
    • 1970-01-01
    • 2013-04-12
    • 1970-01-01
    相关资源
    最近更新 更多