Detecting UITableView scrolling - objective-c

I've subclassed UITableView (as KRTableView) and implemented the four touch-based methods (touchesBegan, touchesEnded, touchesMoved, and touchesCancelled) so that I can detect when a touch-based event is being handled on a UITableView. Essentially what I need to detect is when the UITableView is scrolling up or down.
However, subclassing UITableView and creating the above methods only detects when scrolling or finger movement is occuring within a UITableViewCell, not on the entire UITableView.
As soon as my finger is moved onto the next cell, the touch events don't do anything.
This is how I'm subclassing UITableView:
#import "KRTableView.h"
#implementation KRTableView
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
NSLog(#"touches began...");
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
NSLog(#"touchesMoved occured");
}
- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent *)event {
[super touchesCancelled:touches withEvent:event];
NSLog(#"touchesCancelled occured");
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
NSLog(#"A tap was detected on KRTableView");
}
#end
How can I detect when the UITableView is scrolling up or down?

You don't need to intercept the event methods. Check the docs for the UIScrollViewDelegate protocol, and implement the -scrollViewDidScroll: or -scrollViewWillBeginDragging: methods as appropriate to your situation.

I would like to add the following:
If you are working with a UITableView it is likely that you are already implementing the UITableViewDelegate to fill the table with data.
The protocol UITableViewDelegate conforms to UIScrollViewDelegate, so all you need to do is to implement the methods -scrollViewWillBeginDragging and -scrollViewDidScroll directly in your UITableViewDelegate implementation and they will be called automatically if the implementation class is set as delegate to your UITableView.
If you also want to intercept clicks in your table and not only dragging and scrolling, you can extend and implement your own UITableViewCell and use the touchesBegan: methods in there. By combining these two methods you should be able to do most of the things you need when the user interacts with the UITableView.

It was my mistake, I tried to call the method before reloading the tableview. The following code helped me solve this issue.
[mytableview reloadData];
[mytableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:btn.tag-1] atScrollPosition:UITableViewScrollPositionTop animated:YES];

Related

iOS - touchesMoved called only once in subclassed UIScrollView

I am trying to drop movable UIImageViews within a subclassed UIScrollView so that I can drag them around my UIScrollView. I subclassed UISCrollView and the dropping behavior is working, but when I try to drag the images, touchesMoved is only evaluated once. The touchesMoved method in my subclass of UIScrollView looks like this:
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
if (!self.dragging) {
[self.nextResponder touchesMoved: touches withEvent:event];
}else{
[super touchesEnded:touches withEvent:event];
}
}
It is being called continuously during a moving touch, as it should. Can anyone think of a reason that the touchesMoved method in my view controller would only be called once?
IOS 5: UIScrollView not pass touches to nextResponder
Instead of:
[self.nextResponder touchesMoved:touches withEvent:event];
Use:
[[self.nextResponder nextResponder] touchesMoved:touches withEvent:event];

How to stop the UIScrollView get touch events from its parent view?

I have a UIView containing a UIScrollView.
This UIView is supposed to respond to some touch events but it is not responding because of the scrollView it contains.
This scrollView's contentView is set to a MoviePlayer inside the OuterView.
Now whenever I click in the area where the MoviePlayer is, touch events of my OuterView are not responding.
I have even set the movie player's userInteraction to NO.
but now scrollView is interfering it seems.
I have referred to this post on SO itself.
How can a superview interecept a touch sequence before any of its subviews?
But the solution there, asks me to set the userInteractionEnabled to NO for the scrollView
But if I do so, my pinch zoom doesn't take place either!
What to do?
An UIScrollView intercepts all touch events because it has to decide if/when the user has movd their finger in order to scroll the scroll view.
You may want to subclass UIView, then in the
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)evt;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)evt;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)evt;
methods, check for the neccessary touches, then forward all these methods to your UIScrollViewInstance. For example:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface MyView: UIView {
UIScrollView *scrollView;
}
#end
#implementation MyView
/* don't forget to alloc init your ScrollView init -init
and release it in -dealloc! Then add it as a subview of self. */
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)evt
{
/* check for a gesture */
[scrollView touchesBegan:touches withEvent:evt];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)evt;
{
/* check for a gesture */
[scrollView touchesMoved:touches withEvent:evt];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)evt;
{
/* check for a gesture */
[scrollView touchesEnded:touches withEvent:evt];
}
#end

UIWebView's inside UIScrollView consuming touch events

I have multiple custom UIWebView's as subviews within a UIScrollView. I have implemented the touch methods within the UIViewController for the UIWebView's, but these methods are not being called.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { }
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { }
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { }
I have read elsewhere and it says that the UIScrollView will consume the events. This makes sense, but should the UIWebView's not receive the touch event before the UIScrollView itself? What is probably happening, is that the UIWebBrowserView (a subview of the UIWebView) is consuming the events. This class can't be subclassed however, and so I can't send the events up the superview chain to the UIScrollView. I have read on the Apple iOS Documentation that one shouldn't ever place a UIWebView inside a UIScrollView, but I need to do so for my purposes.
So my question is: how can my custom UIWebView's (that are subviews of a UIScrollView) receive touch events?
My controllers and views are set up as follows:
HomeViewController.h /.m /.xib
HomeView.h /.m (is a UIScrollView)
DashboardViewController.h /.m /.xib (custom UIWebView's)
HomeViewController is loaded from a Nib file that contains a custom view: HomeView.
DashboardViewController is loaded from a Nib file. It contains a few labels, sliders, etc. (note that there is no custom view here - I am not subclassing UIView here).
HomeViewController dynamically loads several DashboardViewControllers and adds its view to the HomeView.
DashboardViewController *dashboardViewController = [[DashboardViewController alloc] initWithNibName:#"DashboardViewController" bundle:[NSBundle mainBundle];
[homeView addSubview:dashboardViewController.view];
You are probably looking for canCancelContentTouches.
scrollView.canCancelContentTouches = NO;
The User Interaction is standard enabled.
Did you connect your views delegate to the File's Owner?
And did you bind your View to the Controller? And make sure you connect the corresponding actions to the correct methods. (CTRL Click on the View and drag the plus sign onto the corresponding method in your Class.h file.
#interface MyScrollView : UIViewController {
UIView *myFirstView;
UIView *mySecondView;
UIView *myThirdView;
}
#property (nonatomic, retain) IBOutlet UIView *myView;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
#end
Have you set userInteractionEnabled = YES for all the subviews?
UIViewController gets the touches* messages before the UIView. The UIViewController doesn't pass the message along to the UIView (by calling [super touchesBegan]) by default. So you need to move or forward the touches* functions if your UIView has a controller.
Personally I put the various touches* methods on the view subclass, not the view controller subclass.
You can try this, when you get the touches methods being called for UIScrollView in turn you can call the same touches method in subview from scrollview.

How to subclass UITableView?

I honestly don't know how to subclass a UITableView. I'm super confused and looking for whatever help I can get. How do I go about "subclassing" a UITableView? Reason I need to do it is because I need for the table to respond to touches on the background so that keyboards can be hidden. I've tried googling but couldn't find anything. Any help is extremely appreciated!
Most of the time you shouldn't need to subclass UITableView, so see if you can avoid doing so first. If you absolutely have to, create a subclass that inherits from UITableView and override the touch-related methods in the implementation:
// MyTableView.h
#interface MyTableView : UITableView {
}
#end
// MyTableView.m
#implementation MyTableView
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event {
[super touchesCancelled:touches withEvent:event];
}
#end
In your .h file declare it as a subclass:
#interface myViewController: UITableViewController
Then implement the UITableViewController delegate methods:
http://developer.apple.com/iphone/library/documentation/uikit/reference/UITableViewDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intf/UITableViewDelegate
To detect touches on a cell write your logic in the tableView:didSelectRowAtIndexPath: method: http://developer.apple.com/iphone/library/documentation/uikit/reference/UITableViewDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intfm/UITableViewDelegate/tableView:didSelectRowAtIndexPath:

Responding to touchesBegan in UIPickerView instead of UIView

I have a UIPickerView that gets faded out to 20% alpha when not in use. I want the user to be able to touch the picker and have it fade back in.
I can get it to work if I put a touchesBegan method on the main View, but this only works when the user touches the View. I tried sub-classing UIPickerView and having a touchesBegan in there, but it didn't work.
I'm guessing it's something to do with the Responder chain, but can't seem to work it out.
I've been searching for a solution to this problem for over a week. I'm answering you even if you're question is over a year old hoping this helps others.
Sorry if my language is not very technical, but I'm pretty new to Objective-C and iPhone development.
Subclassing UIpickerView is the right way to do it. But you've to override the - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event method. This is the method called whenever you touch the screen and it returns the view that will react to the touch. In other words the view whose touchesBegan:withEvent: method will be called.
The UIPickerView has 9 subviews! In the UIPickerView class implementation - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event won't return self (this means the touchesBegan:withEvent: you write in the subclass won't be called) but will return a subview, exactly the view at index 4 (an undocumented subclass called UIPickerTable).
The trick is to make the - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event method to return self so you have control over the touchesBegan:withEvent:, touchesMoved:withEvent: and touchesEnded:withEvent: methods.
In these methods, in order to keep the standard functionalities of the UIPickerView, you MUST remember to call them again but on the UIPickerTable subview.
I hope this makes sense. I can't write code now, as soon as I'm at home I will edit this answer and add some code.
Here is some code that does what you want:
#interface TouchDetectionView : UIPickerView {
}
- (UIView *)getNextResponderView:(NSSet *)touches withEvent:(UIEvent *)event;
#end
#implementation TouchDetectionView
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UIView * hitTestView = [self getNextResponderView:touches withEvent:event];
[hitTestView touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UIView * hitTestView = [self getNextResponderView:touches withEvent:event];
[hitTestView touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UIView * hitTestView = [self getNextResponderView:touches withEvent:event];
[hitTestView touchesEnded:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
UIView * hitTestView = [self getNextResponderView:touches withEvent:event];
[hitTestView touchesCancelled:touches withEvent:event];
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
return self;
}
- (UIView *)getNextResponderView:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch * touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
UIView * hitTestView = [super hitTest:point withEvent:event];
return ( hitTestView == self ) ? nil : hitTestView;
}
Both of the above answers were very helpful, but I have a UIPickerView nested within a UIScrollView. I'm also doing continual rendering elsewhere on-screen while the GUI is present. The problem is that the UIPickerView doesn't update fully when: a non-selected row is tapped, the picker is moved so that two rows straddle the selection area, or a row is dragged but the finger slides outside of the UIPickerView. Then it's not until the UIScrollView is moved that the picker instantly updates. This result is ugly.
The problem's cause: my continual rendering was keeping the UIPickerView's animation from getting the CPU cycles it needed to finish, hence to show the correct, current selection. My solution --which works-- was this: in the UIPickerView's touchesEnded:withEvent:, execute something to pause my rendering for a short while. Here's the code:
#import "SubUIPickerView.h"
#implementation SubUIPickerView
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
[pickerTable touchesBegan:touches withEvent:event];
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
[pickerTable touchesMoved:touches withEvent:event];
}
- (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
[singleton set_secondsPauseRendering:0.5f]; // <-- my code to pause rendering
[pickerTable touchesEnded:touches withEvent:event];
}
- (void) touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event
{
[pickerTable touchesCancelled:touches withEvent:event];
}
- (UIView*) hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
if (CGRectContainsPoint(self.bounds, point))
{
if (pickerTable == nil)
{
int nSubviews = self.subviews.count;
for (int i = 0; i < nSubviews; ++i)
{
UIView* view = (UIView*) [self.subviews objectAtIndex:i];
if ([view isKindOfClass:NSClassFromString(#"UIPickerTable")])
{
pickerTable = (UIPickerTable*) view;
break;
}
}
}
return self; // i.e., *WE* will respond to the hit and pass it to UIPickerTable, above.
}
return [super hitTest:point withEvent:event];
}
#end
and then the header, SubUIPickerView.h:
#class UIPickerTable;
#interface SubUIPickerView : UIPickerView
{
UIPickerTable* pickerTable;
}
#end
Like I said, this works. Rendering pauses for an additional 1/2 second (it already pauses when you slide the UIScrollView) allowing the UIPickerView animation to finish. Using NSClassFromString() means you're not using any undocumented APIs. Messing with the Responder chain was not necessary. Thanks to checcco and Tylerc230 for helping me come up with my own solution!
Set canCancelContentTouches and delaysContentTouches of parent view to NO, that worked for me
Couldn't find a recent, easier solution to this so I rigged something to solve my problem. Created an invisible button that stretches over the picker view. Connected that button with a "Touch Down" recognizer to my parent UIView. Now any functionality can be added, I happened to need a random selection timer to be invalidated when someone touches the picker view. Not elegant but it works.