I am creating a game, similar to Flappy Bird but the user holds their finger on the screen and dodges the obstacles, rather than tapping to make the bird fly.
I am doing this by having a UIScrollView, in which UIView's are used as obstacles. When the user touches a UIView, the game is over.
How do I detect the users touch of a UIView from within a UIScrollView? I am using Swift with Xcode Beta 4.
EDIT: This is screenshot of the game
As you can see, the user moves their finger between the grey blocks (UIViews) as they scroll up.
By setting userInteractionEnabled
to NO
for your scroll view, the view controller will start receiving touch events since UIViewController
is a subclass of UIResponder
. You can override one or more of these methods in your view controller to respond to these touches:
- touchesBegan: withEvent:
- touchesMoved: withEvent:
- touchesEnded: withEvent:
- touchesCancelled: withEvent:
I created some example code to demonstrate how you could do this:
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!")
}
}
}
}
You can programmatically add a gesture recognizer as follows
var touch = UITapGestureRecognizer(target:self, action:"action")
scrollView.addGestureRecognizer(touch)
However, this gesture recognizer won't work for you. UITapGestureRecognizer will only return for a tap, not a tap and hold, and UILongPressGestureRecognizer doesn't give information about location, so you want to use a UIPanGestureRecognizer. It continually tells you how far the touch has moved.
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)
}
You can use the constant "translation" to move your object, it represents the distance the person has slid their finger. Use that plus the location of your bird to move the bird to a new point. You have to reset the translation to zero after this function is called.
Edit: With the format of your game, this code should be the best method.
So, all together, the code to find the location of your finger should be as follows.
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 is a CGPoint containing the location of the current touch, wherever the finger is slid to. As I do not know how you are creating the views to be avoided, you will have to use the y coordinate of currentLocation to determine the x boundaries of the views that are to be avoided at that y, and use < or > comparators to determine if the x boundary of the touch is inside either of those views.
Note: you have to declare location so it can be accessed in handlePan
var location : CGPoint = CGPointZero