UIScrollView programming seems one of the hardest task to me :(
I have subviews in UIScrollView.
I want to let the subviews to take touch event first (and be dragged).
When subviews think it should relinquish touches and scroll should begin, it should be able to tell so to the scrollView.
I've experimented with touchesShouldCancelInContentView but this method only gets called once when scrollView thinks that user actually intended 'scroll'.
I need to take control of when to cancel touch event for subviews not depend on internal uiscrollview's implementation.
My best strategy so far is
subclass ScrollView
- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event
{
SYSLOG(LOG_DEBUG, "MyScrollView touchesBegan");
if (!self.dragging && self.isSubviewTouchable)
[self.nextResponder touchesBegan: touches withEvent:event];
[super touchesBegan: touches withEvent: event];
}
and from subView of scrollView
when subview needs to release the touch and let the scrollView to take touch events so on.
self.myScrollView.isSubviewTouchable = false;
[self touchesEnded: touches withEvent: event];
and in scrollView's delegate class, I set the bool value back.
- (void) scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
self.myScrollView.isSubviewTouchable = true;
}
Problem is, calling touchesEnded from the subview doesn't actually release the touch event it seems.(the subview keeps getting dragged even after the touchesEnded call)
Also, I don't see scrollView getting any touch event after the touchesEnded call which I hoped it would.
Any ideas?
Thank you
I once tried solving this problem by using the PanGesture. To all the subviews you want to be draggable, you can probably add a UIPanGestureRegognizer and do whatever you want to do in their selector. That works well even inside ScrollView.
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(doSomething:)];
[panGesture setDelegate:self];
[subviewtoMove addGestureRecognizer:panGesture];
Related
I have a UIScrollView with subviews and a UITapGestureRecognizer.
I create the recognizer like this:
UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGestureRecognized:)];
[self addGestureRecognizer:tgr];
The view property of UITapGestureRecognizer points to the scroll view itself even if the user touched a different view. I need to know if touch went down on the scroll view directly.
Paul's suggestions are good, but if you don't want (or can't) subclass or become the delegate of the recognizer, there's another way.
You can ask the gesture recognizer for its locationInView: and then retrieve the view which that point is on top of with your scrollView's hitTest:withEvent: method (defined on UIView). Something like:
CGPoint location = [recognizer locationInView:scrollView];
UIView *touchedView = [scrollView hitTest:location withEvent:nil];
You can become either subclass UITapGestureRecognizer and add a new ivar to hold this info by overriding the touchesBegan:withEvent: method something like this
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
/*
* If you wanted you could store a set of all the views to allow
* for multiple touches
*/
self.touchedView = [touches.anyObject view];
[super touchesBegan:touches withEvent:event];
}
Or if you like you can become the delegate for UITapGestureRecognizer and store the tapped view as a property in your class by implementing gestureRecognizer:shouldReceiveTouch:
Note: I already read some questions about the UIScrollView sending touches to the subviews (this included and although I have up voted, it's not working as I intended anymore).
What I have: I have a UIScrollView with a Custom UIView (let's call it A) inside which covers the entire UIScrollView. I am also allowed to put other custom UIViews inside the A.
On the code I am doing this:
[scrollView setDelaysContentTouches:YES];
scrollView.canCancelContentTouches = NO;
What is happening: At the moment my only issue is that, if I want to move a subview inside A, I have to touch it, wait, and then move it. Exactly as stated here:
Now, the behaviour changes depending on the "length in time" of the
first touch on the UIView. If it's short, then the relative dragging
is managed as it was a scroll for the UIScrollView. If it's long, then
I'm getting the touchesMoved: events inside my UIView.
What I want: The subviews inside A should always receive priority and I shouldn't have to touch and wait. If I touch A and not a subview of it, I want the UIScrollView to receive the touches, like panning and moving around (the contentSize is bigger than the frame).
Edit 1.0
The only reason for me to have this A view inside a generic UIScrollView, is because I want to be able to zoom in/out on the A view. So I am doing the following:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return customView; // this is the A view
}
In the beginning I didn't had the A view inside the UIScrollView and the only thing I did was adding the A as a subView of my UIViewController's root view and everything went well. If there is another way to enable zoom in/out I will gladly accept the answer.
Note: Thank you all for your contributions, specially to Aaron Hayman.
I was able to figure it out by doing the following on the UIScrollView sub-class I had:
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
CGPoint pointOfContact = [gestureRecognizer locationInView:self];
// The view with a tag of 200 is my A view.
return (![[self hitTest:pointOfContact withEvent:nil] isEqual:[self viewWithTag:200]]);
}
I haven't tested this, but I believe how you are handling the touch events in View A (or it's subviews) will determine how touch events are passed on. Specifically, if you're trying to use the methods: touchesBegan, touchesMoves, touchesEnded, etc instead of a UIGestureRecognizer you won't receive the touches in the way you want. Apple design the UIGestureRecognizer to handle problems like the one you're facing. Specifically, the UIScrollView uses UIPanGestureRecognizer to handle the scrolling. If you add a UIPanGestureRecognizer to each of the subviews of View A any "panning" that occurs on one of those subviews should be sent to that subview instead of the UIScrollView. However, if you're simply using the "raw" touches methods, the UIPanGestureRecognizer in UIScrollView will never be cancelled.
In general, it's almost always best to use a UIGestureRecognizer instead of processing the touches directly in the view. If you need touches processed in a way that no standard UIGestureRecognizer can provide, subclass UIGestureRecognizer and process the touches there. That way you get all the the functionality of a UIGestureRecognizer along with your own custom touch processing. I really think Apple intended for UIGestureRecognizer to replace most (if not all) of the custom touch processing code that developers use on UIView. It allows for code-reuse and it's a lot easier to deal with when mitigating what code processes what touch event.
Jacky, I needed a similar thing: Within a building plan (your A, in my case a subclass of UIScrollView), let the user place and resize objects (call them Bs). Here's a sketch of what it took me to get at this behavior:
In the superview's (A) initWithFrame: method, set these two:
self.canCancelContentTouches = YES;
self.delaysContentTouches = NO;
This will ensure taps on B are immediately routed to the Bs.
In the embedded B, stop the superview A from cancelling taps, so it does not interfere with a gesture started on the B.
In the touchesBegan: method, search the view hierarchy upwards (using superview property of the views) until you find a UIScrollView, and set its canCancelContentTouches to NO. Remember the superview you changed, and restore this property in the touchesEnded and touchesCancelled methods of B.
I'd be interested whether this works for you as well. Good Luck!
nobi
I think you had better use "touchesBegan,touchesMoved,touchesEnded" to pass the event.
you can do like this:
you should make a mainView . It has 2 property. One is yourScrollView A , and One is yourCustomView.
`[yourScrollView addSubviews:yourCustomView];
[mainView addSubviews:yourScrollView];`
and then write your touches method in the mainView.m like this (ignor the scrollView statment)
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
if ([[touches allObjects] isKindOfClass:[yourCustomView class]])
{
//do whatever you want
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
if ([[touches allObjects] isKindOfClass:[yourCustomView class]])
{
//do whatever you want
}
}
The last step: pass the event to the subview of the scrollView(your A).
#import "yourScrollView.h"
#implementation yourScrollView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code.
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesBegan:touches withEvent:event];
if(!self.dragging)
[[self nextResponder] touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesMoved:touches withEvent:event];
if(!self.dragging)
[[self nextResponder] touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesEnded:touches withEvent:event];
if(!self.dragging)
[[self nextResponder] touchesEnded:touches withEvent:event];
}
- (void)dealloc {
[super dealloc];
}
#end
wish to help you
I have a UIView with 4 buttons on it and another UIView on top of the buttons view. The top most view contains a UIImageView with a UITapGestureRecognizer on it.
The behavoir I am trying to create is that when the user taps the UIImageView it toggles between being small in the bottom right hand corner of the screen and animating to become larger. When it is large I want the buttons on the bottom view to be disabled and when it is small and in the bottom right hand corner I want the touches to be passed through to the buttons and for them to work as normal. I am almost there but I cannot get the touches to pass through to the buttons unless I disable the UserInteractions of the top view.
I have this in my initWithFrame: of the top view:
// Add a gesture recognizer to the image view
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imageTapped:)];
tapGestureRecognizer.cancelsTouchesInView = NO;
[imageView addGestureRecognizer:tapGestureRecognizer];
[tapGestureRecognizer release];
and I this is my imageTapped: method:
- (void) imageTapped:(UITapGestureRecognizer *) gestureRecognizer {
// Toggle between expanding and contracting the image
if (expanded) {
[self contractAnimated:YES];
expanded = NO;
gestureRecognizer.cancelsTouchesInView = NO;
self.userInteractionEnabled = NO;
self.exclusiveTouch = NO;
}
else {
[self expandAnimated:YES];
expanded = YES;
gestureRecognizer.cancelsTouchesInView = NO;
self.userInteractionEnabled = YES;
self.exclusiveTouch = YES;
}
}
With the above code, when the image is large the buttons are inactive, when I touch the image it shrinks and the buttons become active. However, the small image doesn't receive the touches and therefore wont expand.
If I set self.userInteractionEnabled = YES in both cases, then the image expands and contracts when touched but the buttons never receive touches and act as though disabled.
Is there away to get the image to expand and contract when touched but for the buttons underneath to only receive touches if the image is in its contracted state? Am I doing something stupid here and missing something obvious?
I am going absolutely mad trying to get this to work so any help would be appreciated,
Dave
UPDATE:
For further testing I overrode the touchesBegan: and touchesCancelled: methods and called their super implementations on my view containing the UIImageView. With the code above, the touchesCancelled: is never called and the touchesBegan: is always called.
So it would appear that the view is getting the touches, they are just not passed to the view underneath.
UPDATE
Is this because of the way the responder chain works? My view hierarchy looks like this:
VC - View1
-View2
-imageView1 (has tapGestureRecogniser)
-imageView2
-View3
-button1
-button2
I think the OS first does a hitTest as says View2 is in front so should get all the touches and these are never passed on to View3 unless userInteractions is set to NO for View2, in which case the imageView1 is also prevented from receiving touches. Is this how it works and is there a way for View2 to pass through it's touches to View3?
The UIGestureRecognizer is a red herring I think. In the end to solve this I overrode the pointInside:withEvent: method of my UIView:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
BOOL pointInside = NO;
if (CGRectContainsPoint(imageView.frame, point) || expanded) pointInside = YES;
return pointInside;
}
This causes the view to trap all touches if you touch either the imageView or if its expanded flag is set. If it is not expanded then only trap the touches if they are on the imageView.
By returning NO, the top level VC's View queries the rest of its view hierarchy looking for a hit.
Select your View in Storyboard or XIB and...
Or in Swift
view.isUserInteractionEnabled = false
Look into the UIGestureRecognizerDelegate Protocol. Specifically, gestureRecognizer:shouldReceiveTouch:
You'll want to make each UIGestureRecognizer a property of your UIViewController,
// .h
#property (nonatomic, strong) UITapGestureRecognizer *lowerTap;
// .m
#synthesize lowerTap;
// When you are adding the gesture recognizer to the image view
self.lowerTap = tapGestureRecognizer
Make sure you make your UIViewController a delegate,
[self.lowerTap setDelegate: self];
Then, you'd have something like this,
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (expanded && gestureRecognizer == self.lowerTap) {
return NO;
}
else {
return YES;
}
}
Of course, this isn't exact code. But this is the general pattern you'd want to follow.
I have a another solution. I have two views, let's call them CustomSubView that were overlapping and they should both receive the touches. So I have a view controller and a custom UIView class, lets call it ViewControllerView that I set in interface builder, then I added the two views that should receive the touches to that view.
So I intercepted the touches in ViewControllerView by overwriting hitTest:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
return self;
}
Then I overwrote in ViewControllerView:
- (void)touchesBegan:(NSSet *)touches
withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
for (UIView *subview in [self.subviews reverseObjectEnumerator])
{
if ([subview isKindOfClass:[CustomSubView class]])
{
[subview touchesBegan:touches withEvent:event];
}
}
}
Do the exact same with touchesMoved touchesEnded and touchesCancelled.
#Magic Bullet Dave's solution but in Swift
Swift 3
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
var pointInside = false
if commentTextField.frame.contains(point) {
pointInside = true
} else {
commentTextField.resignFirstResponder()
}
return pointInside
}
I use it in my CameraOverlayView for ImagePickerViewController.cameraOverlay to give user ability to comment while taking new photo
I have the main wiev. I have placed a UIScrollView object as a subview of the main view.
I have an extended class from UIView, which manage touch events. This class models a sort of transparent blackboard, in which the user can draw a path.
I create an object out of my UIVIew extended class and I add it as a subview of the UIScrollView.
I carry out this process by using the Inspector and .xib file.
Then, no touch event gets to the UIScrollView object. No matter if I uncheck "User Interaction Enabled" checkbox for my UIView extended class in the Inspector.
I've also tried with :
[[self nextResponder] touchesBegan:touches withevent:event];
or
[[self.superview nextResponder] touchesBegan:touches withevent:event];
or
[super touchesBegan:touches withEvent:event];
or
[super touchesBegan:touches withEvent:event];
in the touchEvents methods of my extended UIView class.
I haven't succeeded in getting touch events to the UIScrollView.
On the other hand, if I add scrollView and my extended UIView class programatically, maintaining the same hierarchy, UIScrolView get touch events, but not its subview (my UIView extended class).
I don't want both UIScrollView and my UIView extended class to manage touch events at the same time. I just want to toggle between UIScrollView and my UIView extended class for listening to touch evebnts, by using a button for example.
Is it possible, in this case I am explaining, to select which, UIScrollView or my UIView extended class, should listen to touch events?
A UIScrollView has a scrollEnabled property. If you set this to YES, the scroll view will be scrollable. If not, the events will be forwarded up the responder chain. So try changing that property.
Read the docs for UIScrollView!
As for the difference between setting up in a nib versus XCode, your code setup and your nib version aren't quite matching some important way. I can't see your nib or your programmatic setup so can't help you much more with that.
You need to enable user interaction for scroll view:
[self.scrollView setUserInteractionEnabled:YES];
Add gestureRecognizer to subview:
for (int i = 0; i < pos.count; i++) {
SCVerticalBarsChartBarView *bar = [[SCVerticalBarsChartBarView alloc]initWithBlaBlaBla];
[bar addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(someAction:)]];
bar.tag = i;
[self.scrollView addSubview:bar];
}
And detect the tapped view:
-(void)someAction:(UITapGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateEnded) {
if ([recognizer.view isKindOfClass:[SCVerticalBarsChartBarView class]]) {
SCVerticalBarsChartBarView *tmpButt=(SCVerticalBarsChartBarView *)recognizer.view;
NSLog(#"%d", tmpButt.tag);
}
}
}
I'm trying to handle touches on a iPhone's UITextView. I successfully managed to handle taps and other touch events by creating a subclass of UIImageViews for example and implementing the touchesBegan method...however that doesn't work with the UITextView apparently :(
The UITextView has user interaction and multi touch enabled, just to be sure...no no joy. Anyone managed to handle this?
UITextView (subclass of UIScrollView) includes a lot of event processing. It handles copy and paste and data detectors. That said, it is probably a bug that it does not pass unhandled events on.
There is a simple solution: you can subclass UITextView and impement your own touchesEnded (and other event handling messages) in your own versions, you should call[super touchesBegan:touches withEvent:event]; inside every touch handling method.
#import "MyTextView.h" //MyTextView:UITextView
#implementation MyTextView
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"touchesBegan");
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesBegan:touches withEvent:event];
NSLog(#"touchesMoved");
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"****touchesEnded");
[self.nextResponder touchesEnded: touches withEvent:event];
NSLog(#"****touchesEnded");
[super touchesEnded:touches withEvent:event];
NSLog(#"****touchesEnded");
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
[super touches... etc];
NSLog(#"touchesCancelled");
}
If you want to handle single/double/triple tap on UITextView, you can delegate UIGestureRecongnizer and add gesture recognizers on your textview.
Heres sameple code (in viewDidLoad):
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap)];
//modify this number to recognizer number of tap
[singleTap setNumberOfTapsRequired:1];
[self.textView addGestureRecognizer:singleTap];
[singleTap release];
and
-(void)handleSingleTap{
//handle tap in here
NSLog(#"Single tap on view");
}
Hope this help :D
Better solution (Without swizzling anything or using any Private API :D )
As explained below, adding new UITapGestureRecognizers to the textview does not have the expected results, handler methods are never called. That is because the UITextView has some tap gesture recognizer setup already and I think their delegate does not allow my gesture recognizer to work properly and changing their delegate could lead to even worse results, I believe.
Luckily the UITextView has the gesture recognizer I want already setup, the problem is that it changes according to the state of the view (i.e.: set of gesture recognizers are different when inputing Japanese than when inputing English and also when not being in editing mode).
I solved this by overriding these in a subclass of UITextView:
- (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
[super addGestureRecognizer:gestureRecognizer];
// Check the new gesture recognizer is the same kind as the one we want to implement
// Note:
// This works because `UITextTapRecognizer` is a subclass of `UITapGestureRecognizer`
// and the text view has some `UITextTapRecognizer` added :)
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
UITapGestureRecognizer *tgr = (UITapGestureRecognizer *)gestureRecognizer;
if ([tgr numberOfTapsRequired] == 1 &&
[tgr numberOfTouchesRequired] == 1) {
// If found then add self to its targets/actions
[tgr addTarget:self action:#selector(_handleOneFingerTap:)];
}
}
}
- (void)removeGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
// Check the new gesture recognizer is the same kind as the one we want to implement
// Read above note
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
UITapGestureRecognizer *tgr = (UITapGestureRecognizer *)gestureRecognizer;
if ([tgr numberOfTapsRequired] == 1 &&
[tgr numberOfTouchesRequired] == 1) {
// If found then remove self from its targets/actions
[tgr removeTarget:self action:#selector(_handleOneFingerTap:)];
}
}
[super removeGestureRecognizer:gestureRecognizer];
}
- (void)_handleOneFingerTap:(UITapGestureRecognizer *)tgr
{
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:tgr forKey:#"UITapGestureRecognizer"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"TextViewOneFingerTapNotification" object:self userInfo:userInfo];
// Or I could have handled the action here directly ...
}
By doing this way, no matter when the textview changes its gesture recognizers, we will always catch the tap gesture recognizer we want → Hence, our handler method will be called accordingly :)
Conclusion:
If you want to add a gesture recognizers to the UITextView, you have to check the text view does not have it already.
If it does not have it, just do the regular way. (Create your gesture recognizer, set it up, and add it to the text view) and you are done!.
If it does have it, then you probably need to do something similar as above.
Old Answer
I came up with this answer by swizzling a private method because previous answers have cons and they don't work as expected. Here, rather than modifying the tapping behavior of the UITextView, I just intercept the called method and then call the original method.
Further Explanation
UITextView has a bunch of specialized UIGestureRecognizers, each of these has a target and a action but their target is not the UITextView itself, it's an object of the forward class UITextInteractionAssistant. (This assistant is a #package ivar of UITextView but is forward definition is in the public header: UITextField.h).
UITextTapRecognizer recognizes taps and calls oneFingerTap: on the UITextInteractionAssistant so we want to intercept that call :)
#import <objc/runtime.h>
// Prototype and declaration of method that is going be swizzled
// When called: self and sender are supposed to be UITextInteractionAssistant and UITextTapRecognizer objects respectively
void proxy_oneFingerTap(id self, SEL _cmd, id sender);
void proxy_oneFingerTap(id self, SEL _cmd, id sender){
[[NSNotificationCenter defaultCenter] postNotificationName:#"TextViewOneFinderTap" object:self userInfo:nil];
if ([self respondsToSelector:#selector(proxy_oneFingerTap:)]) {
[self performSelector:#selector(proxy_oneFingerTap:) withObject:sender];
}
}
...
// subclass of UITextView
// Add above method and swizzle it with.
- (void)doTrickForCatchingTaps
{
Class class = [UITextInteractionAssistant class]; // or below line to avoid ugly warnings
//Class class = NSClassFromString(#"UITextInteractionAssistant");
SEL new_selector = #selector(proxy_oneFingerTap:);
SEL orig_selector = #selector(oneFingerTap:);
// Add method dynamically because UITextInteractionAssistant is a private class
BOOL success = class_addMethod(class, new_selector, (IMP)proxy_oneFingerTap, "v#:#");
if (success) {
Method originalMethod = class_getInstanceMethod(class, orig_selector);
Method newMethod = class_getInstanceMethod(class, new_selector);
if ((originalMethod != nil) && (newMethod != nil)){
method_exchangeImplementations(originalMethod, newMethod); // Method swizzle
}
}
}
//... And in the UIViewController, let's say
[textView doTrickForCatchingTaps];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(textViewWasTapped:) name:#"TextViewOneFinderTap" object:nil];
- (void)textViewWasTapped:(NSNotification *)noti{
NSLog(#"%#", NSStringFromSelector:#selector(_cmd));
}
You need to assign the UITextView instance.delegate = self (assuming you want to take care of the events in the same controller)
And make sure to implement the UITextViewDelegate protocol in the interface... ex:
#interface myController : UIViewController <UITextViewDelegate>{
}
Then you can implement any of the following
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView;
- (BOOL)textViewShouldEndEditing:(UITextView *)textView;
- (void)textViewDidBeginEditing:(UITextView *)textView;
- (void)textViewDidEndEditing:(UITextView *)textView;
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
- (void)textViewDidChange:(UITextView *)textView;
- (void)textViewDidChangeSelection:(UITextView *)textView;
I'm using a textview as a subview of a larger view. I need the user to be able to scroll the textview, but not edit it. I want to detect a single tap on the textview's superview, including on the textview itself.
Of course, I ran into the problem that the textview swallows up the touches that begin on it. Disabling user interaction would fix this, but then the user won't be able to scroll the textview.
My solution was to make the textview editable and use the textview's shouldBeginEditing delegate method to detect a tap in the textview. I simply return NO, thereby preventing editing, but now I know that the textview (and thus the superview) has been tapped. Between this method and the superview's touchesEnded method I have what I need.
I know that this won't work for people who want to get access to the actual touches, but if all you want to do is detect a tap, this approach works!
How about make a UIScrollView and [scrollView addSubview: textview] which makes it possible to scroll textview?
You can also send a Touch Down event. Wire-up this event through the Interface Builder.
Then add code in your event handler
- (IBAction)onAppIDTap:(id)sender {
//Your code
}