Uiview Touch Event in Controller

UIView touch event in controller

You will have to add it through code. First, create the view and add it to the hierarchy:

var myView = UIView(frame: CGRectMake(100, 100, 100, 100))
self.view.addSubview(myView)

After that initialize gesture recognizer. Until Swift 2:

let gesture = UITapGestureRecognizer(target: self, action: "someAction:")

After Swift 2:

let gesture = UITapGestureRecognizer(target: self, action:  #selector (self.someAction (_:)))

Then bind it to the view:

self.myView.addGestureRecognizer(gesture)


Swift 3:

func someAction(_ sender:UITapGestureRecognizer){     
// do other task
}

Swift 4 just add @objc before func:

@objc func someAction(_ sender:UITapGestureRecognizer){     
// do other task
}

Swift UI:

Text("Tap me!").tapAction {
print("Tapped!")
}

How to add a touch event to a UIView?

In iOS 3.2 and higher, you can use gesture recognizers. For example, this is how you would handle a tap event:

//The setup code (in viewDidLoad in your view controller)
UITapGestureRecognizer *singleFingerTap =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(handleSingleTap:)];
[self.view addGestureRecognizer:singleFingerTap];

//The event handling method
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer
{
CGPoint location = [recognizer locationInView:[recognizer.view superview]];

//Do stuff here...
}

There are a bunch of built in gestures as well. Check out the docs for iOS event handling and UIGestureRecognizer. I also have a bunch of sample code up on github that might help.

Touch Event on UIView

A very general way to get touches is to override these methods in a custom UIView subclass:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event

The official docs for these methods is under Responding to Touch Events in the UIResponder class docs (UIView inherits those methods since it's a subclass of UIResponder). Longer and more introductory docs can be found in the Event Handling Guide for iOS.

If you just want to detect a tap (touch-down, then touch-up within your view), it's easiest to add a UIButton as a subview of your view, and add your own custom method as a target/action pair for that button. Custom buttons are invisible by default, so it wouldn't affect the look of your view.

If you're looking for more advanced interactions, it's also good to know about the UIGestureRecognizer class.

How to detect touch event across multiple views on my view controller?

Do it like this

class YourCustomView : UIView {

func addTapGesture(action : @escaping ()->Void ){
self.gestureRecognizers?.forEach({ (gr) in
self.removeGestureRecognizer(gr)
})
let tap = MyTapGestureRecognizer(target: self , action: #selector(self.handleTap(_:)))
tap.action = action
tap.numberOfTapsRequired = 1

self.addGestureRecognizer(tap)
self.isUserInteractionEnabled = true

}
}

or you can use this even in a extension of UIView if you're going to use it in multiple types of UIViews.

And then use it like:

let yourView = YourCustomView()
yourView.addTapGesture{[weak self] in
//implement tap event here
}

Notice that [weak self] is going to avoid getting a strong reference for closure to avoid a retain cycle there.

Preventing unhandled touch events on a child view controller from passing through to container view

I think I understand better what's going on here thanks to this very helpful WWDC video.

Given an incoming touch, first the system associates that touch with the deepest hit-tested view; in my case that's the UIScrollView. Then it apparently walks back up the hierarchy of superviews looking for any other attached recognizers. This behavior is implied by this key bit of documentation:

A gesture recognizer operates on touches hit-tested to a specific view and all of that view’s subviews.

The scroll view has its own internal pan recognizer(s), which either cancel unrecognized touches or possibly fall back on responder methods that don't happen to forward touches up the responder chain. That explains why my responder methods never get called, even when my own recognizers are disabled.

Armed with this information, I can think of a few possible ways to solve my problem, such as:

  • Use gesture delegate methods to ignore touches if/when the associated view is under a child controller.
  • Write a "null" gesture recognizer subclass that captures all touches and ignores them, and attach that to the root view of the child controller.

But what I ended up doing was simply to rearrange my view hierarchy with a new empty view at the top, so that my child controller views can be siblings of the main content view rather than its subviews.

So the view hierarchy changes from this:

"Before" hierarchy

to this:

"After" hierarchy

This solves my problem: my gesture recognizers no longer interact with touches that are hit-tested to the child controller's views. And I think it better captures the conceptual relationships between my app's controllers, without requiring any additional logic.

Xcode & Swift - Detecting user touch of UIView inside of UIScrollView

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

}


Related Topics



Leave a reply



Submit