I have a grouped table view with custom cells in it, and it contains many cells (i.e. scrollable), there is a scroll view in every cell (which contains a UILabel), I've set up that scroll view properly (made its content size larger than its frame size for the scroll to work properly and then added it as a sub-view on the custom cell), however, the text on the label that is inside this scroll view appears but not scrollable (no scroll bars, no scrolling ...), the only scrollable object on the screen is the default scroll view of the grouped table view.
how can i get the mini scroll view to scroll properly ?
thank you in advance.
Are you implementing touchesBegan and touchesEnded in your scrollview?
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
[[self nextResponder] touchesBegan:touches withEvent:event];
}
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
[[self nextResponder] touchesEnded:touches withEvent:event];
}
If not, make a class for your scrollview (scrollview subclass) and implement them therein.
Here's the full code of what should be written in the subclass of the scroll view.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if (self.dragging) {
[super touchesBegan:touches withEvent:event];
} else {
[self.superview touchesBegan:touches withEvent:event];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if (self.dragging) {
[super touchesMoved:touches withEvent:event];
} else {
if ([self.delegate isKindOfClass:[UITableViewCell class]]) {
[(UITableViewCell *)self.delegate touchesCancelled:touches withEvent:event];
}
[self.superview touchesMoved:touches withEvent:event];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if (self.dragging) {
[super touchesEnded:touches withEvent:event];
} else {
[self.superview touchesEnded:touches withEvent:event];
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
if (self.dragging) {
[super touchesCancelled:touches withEvent:event];
} else {
[self.superview touchesMoved:touches withEvent:event];
}
}
In the touchesMoved there's some extra code based on a bug I was encountering. To start, if your self.delegate is not the UITableViewCell, than replace that property with a property to your cell.
The cell needs to retrieve the cancel touch event during movement to prevent the undesired results. It can be easily reproducible as follows.
Highlight the cell (assuming the scroll view is over the whole cell, if not highlight the scroll view)
While the cell is highlighted, drag the table view
Select any other cell and now the previously highlighted cell will retrieve the didSelectCell state
Another point to mention is that order matters! If the self.delegate is not called before the self.superview then the highlighted state wont happen.
Related
I'm posting this message because I've been reading the forum and I haven't been able to find a similar problem. I need to be able to discriminate taps and double taps (this is a standard thing) BUT my problem is that for whatever reasons I have a Scroll View inside another ScrollView. So, I had to sub-class my ScrollView in order to get touchedBegin method called.
I have a class called PhotoViewController (a sub-class of BaseViewController) this class contains another class called CustomScrollView (a subclass of ScrollView). I needed to sub-class this CustomScrollView from ScrollView in order to override the touchesBegin method, and to be able to capture the touches made by the user.
I tried calling the touchesBegin method from CustomScrollView using something like return [super touchesBegan:touches withEvent:event] inside the touchesBegin method, but when the touchesBegin method inside PhotoViewController gets called it's parameters are empty (and I can't discriminate if the user made a single or double tap, which is exactly what I need)
I have a class, called PhotoViewController:
#class PhotoViewController
#interface PhotoViewController : BaseViewController <UIScrollViewDelegate> {
CustomScrollView* myScrollView;
}
#implementation PhotoViewController
...
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
NSUInteger tapCount = [touch tapCount];
switch (tapCount) {
case 1:
[self performSelector:#selector(singleTapMethod) withObject:nil afterDelay:.4];
break;
case 2:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(singleTapMethod) object:nil];
[self performSelector:#selector(doubleTapMethod) withObject:nil afterDelay:.4];
break;
default:
break;
}
}
the class CustomScrollView is (CustomScrollView.h):
#interface CustomScrollViewPhoto : UIScrollView {
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
#end
and it's implementation is this(CustomScrollView.m):
#implementation CustomScrollViewPhoto
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.superview touchesBegan:[NSSet set] withEvent:event];
return [super touchesBegan:touches withEvent:event];
}
Am I going in the wrong direction with what I want to do? Maybe, I should capture the taps/double taps inside the CustomScrollView class(this works fine!), and from there using a #selector or something call the appropiate methods in PhotoViewController?
Thanks for reading!
I think you're going the wrong route (only slightly!). I do a very similar thing in a photo viewer and I capture the touches in the CustomScrollView. You shouldn't need to do anything in PhotoViewController.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
UITouch *touch = [touches anyObject];
if(touch.tapCount == 2)
{
if (self.zoomScale == self.minimumZoomScale)
{
//Zoom where the user has clicked from
CGPoint pos = [touch locationInView:self];
[self zoomToRect:CGRectMake((pos.x - 5.0)/self.zoomScale, (pos.y-5.0)/self.zoomScale, 10.0, 10.0) animated:YES];
}
else
{
//Zoom back out to full size
[self setZoomScale:self.minimumZoomScale animated:YES];
}
}
}
Cell doesn't receive touch events, when there is UIScrollView inside UITableViewCell. Is there any way to cancel tap events for UIScrollView (needs only to handle scrolling)?
If you need touches to go through, implement a subclass of UIScrollView, and add these:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// Pass to parent
[super touchesBegan:touches withEvent:event];
[self.nextResponder touchesBegan:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// Pass to parent
[super touchesEnded:touches withEvent:event];
[self.nextResponder touchesEnded:touches withEvent:event];
}
The cell only interecepts taps, so it'll work.
This is brilliant! I was pulling my hair on this one.
Basically...
I have a UIScrollView subclass which you can add a target and selector too, which get fired if a touch event is detected within the view. I am using the scroll view as an image gallery, and the touch inside the scroll view is used to fade out the HUD components (UItoolBar, etc):
#implementation TouchableScrollView
- (void)addTarget:(id)_target withAction:(SEL)_action
{
target = _target;
action = _action;
shouldRun = NO;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
shouldRun = YES;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
shouldRun = NO;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if(shouldRun)
{
shouldRun = NO;
[target performSelector:action];
}
}
#end
I then have another custom UIView added as a subview to this which has the following set (both of which I have played around with):
self.userInteractionEnabled = YES;
self.exclusiveTouch = YES;
and uses the following to trigger an animation:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
The problem is even when the custom UIView detects the touch and responds (by flipping itself over), the UIScrollView selector also gets fired causing everything to fade out.
Please help, I'm totally stuck?!
better to use this in custom ScrollView class
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
if (!self.dragging) {
[self.nextResponder touchesEnded: touches withEvent:event];
}
[super touchesEnded: touches withEvent: event];
self.nextResponder is scrollView subViews. so event other then dragging (scrolling) will be handled by your main viewController . in main View controller inherit touch event method. there u can check for touch event for particular view or image view.
I have a UIScrollView that contains a UITextView (not editable).
I can't make the UIScrollView gets the touch events, UITextView seems to get them and keep them . Any idea how to let UIScrollView gets the touch events?
I want UITextView to still be scrollable vertically (my UIScrollView is scrollable only horizontally).
In your UITextView subclass, do this:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.nextResponder touchesBegan:touches withEvent:event];
}
If you want UITextView to handle the touches too, then I believe you can do this:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.nextResponder touchesBegan:touches withEvent:event];
[super touchesBegan:touches withEvent:event];
}
but it might result in really weird behavior.
I have several UIButtons which I use to set the current action when tapping in the main area. I would also like to allow the user to drag from the button directly into the main area and take the same action; essentially, the touchesBegan and touchesMoved should be passed on to the main view when touching the UIButtons, but also should send the button press action.
Right now, I have the touch up inside changing the control. The drag exit calls the touch up inside section to set the control, then calls the touches began section to start the main area touching operation.
However, at this point, the touchesMoved and touchesEnded are obviously not being called, because the touches originated on the UIButton.
Is there a way to half-ignore the touches so they're passed to the main area, but also allow me to set the control first?
I know this question is two years old (and already answered), but nevertheless...
When I tried this myself, the touches were forwarded, but the buttons no longer behaved like buttons. I also passed the touches along to "super" and now all is well.
So, for beginners that might stumble upon this, this is what the code should look like:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
[self.nextResponder touchesBegan:touches withEvent:event];
}
In the documentation, look for Responder Objects and the Responder Chain
You can "share" touches between objects by forwarding the touch up the responder chain.
Your UIButton has a responder/controller that receives the UITouch events, my guess is that once it has preformed its interpretation of the message it returns - the touch has been handled and disposed of.
Apple suggests something like this (based on the type of touch of course):
[self.nextResponder touchesBegan:touches withEvent:event];
Rather than disposing of the touch event it is passed on.
Sub classed UIButton:
MyButton.h
#import <Foundation/Foundation.h>
#interface MyButton : UIButton {
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event ;
#end
MyButton.m
#import "MyButton.h"
#implementation MyButton
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
printf("MyButton touch Began\n");
[self.nextResponder touchesBegan:touches withEvent:event];
}
#end
No need to subclass either! Simply stick this on the top of your implementation before anything else:
#pragma mark PassTouch
#interface UIButton (PassTouch)
#end
#implementation UIButton (PassTouch)
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
[self.nextResponder touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
[self.nextResponder touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
[self.nextResponder touchesEnded:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
[self.nextResponder touchesCancelled:touches withEvent:event];
}
#end