I am trying to intercept the locationInView from the UIScrollView.panGestureRecognizer as the scroll is happening.
I was able to get the initial touch coordinates (when the scroll begins) implementing the following code inside the delegate method
#pragma mark UIScrollViewDelegate
- (void) scrollViewWillBeginDragging:(UIScrollView *)scrollView {
CGPoint point = [scrollView.panGestureRecognizer locationInView:scrollView];
NSLog(#"%#", NSStringFromCGPoint(point));}
Is there a way to get the coordinates continuously as the scroll is happening? I understand that the scrollView.panGestureRecognizer is a UIPanGestureRecognizer #property of the UIScrollView object, so there should be a panGestureDetected #selector somewhere.
Thx in advance.... e
Add the UIScrollViewDelegate method:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
And in there you can access the scrollView while it is scrolling. If you are not using a view that subclasses UIScrollView, be sure to explicitly implement the UIScrollViewDelegate. I assume that you should not have to since the delegate method in your question is being called but I just thought I would point that out.
Be sure you really want to use the locationInView: method because it will return the point of the gesture and not the contentOffset of the UIScrollView. In other words, the gesture point may be outside of the desired range, where the contentOffset is in direct relation to the UIScrollView object.
EDIT:
Just as an FYI, the method will be called a lot, so make sure you are not doing any heavy processing every time the method is called.
Related
I have a non scrollable UITableView inside an UIScrollView. And I'm having the problem that when I touch a row, the callback didSelectRowAtIndexPath: is not being called on the first tap, but after the first tap everithing works.
A few considerations:
After the first tap, the table view works normally, every tap works in every cell.
This happens just after I scroll the UIScrollView. If I don't scroll the UIScrollView, this never happens.
I have overriden the UIScrollView's touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view and the event does pass throw here, the view is a UITableViewCellContentView effectively.
I just don't know why the event is not been sent to the UITableView on the first time, and on the following ones it does.
If you have UIScrollView that contains vertical content and UITableView as part of this content, you must at least disable scrolling on UITableView - otherwise it's confusing for the user when he will scroll your mainView and when tableView, and also confusing for the framework because it's not clear where to send panning gestures.
As a rule of thumb - you should avoid putting table views inside scrollViews, unless you really know what you're doing.
Please disable scrolling of your table view and check the datasource and delegate are connected to your table view. If not follow the bellow code
#interface myClass ()<UITableViewDataSource, UITableViewDelegate>
- (void)viewDidLoad {
[super viewDidLoad];
self.myTableView.scrollEnabled = NO;
self.myTableView.delegate = self;
self.myTableView.dataSource = self;
}
I have a UIView with a CATiledLayer visible within a UIScrollView.
At certain times I need to relocate the window contents relative to the scroll view origin.
I need to change the contentOffset property of the scroll view but, when I redraw, the contents are the same.
The sequence is
scrollView.contentOffset = newoffset ;
[contentView setNeedsDisplay]
This creates a jerky effect as the scrollView shifts.
Is there any way to freeze the visible area of the scroll view until the drawing is complete?
Essentially, I'd like to
[FREEZE DISPLAY]
scrollView.contentOffset = newoffset ; // But have no visible change
[contentView setNeedsDisplay]
[UNFREEZE DISPLAY AFTER a SHORT DELAY]
You should be doing that in your delegate methods of UIScrollView. It has a very specific set of methods that you can use to time your update appropriately.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView;
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
Between these 3 methods, I'm sure that one of these is going to make that call with the timing that you're looking for.
Last thing, watch out for how many times your calling the setNeedsDisplay method when utilizing these delegates. setNeedsDisplay isn't cheap on memory if you're calling it every millisecond!
Cheers
I'm new to iOS programming and i want to learn something about UITableView.
I have some images in UITableViewCell. I want to hide my images when scrolling start then i want to show them when scrolling stop.
How can i do that ?
Is there a method or something else about that ?
UITableView inherits from UIScrollView, thus you can in your tableView delegate receive
UIScrollViewDelegate callbacks.
Implement the delegate method
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
which tells the delegate when the scroll view is about to start scrolling the content.
and
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
which tells the delegate when the user finishes scrolling the content.
Read the ScrollViewDelegate Reference.
It is not a big deal you simply need to implement scrollview delegated methodes to know when scrolling stops ,Then set a bool value and reload the table and on basis of bool value you can show and hide the images.When scrolling starts set bool to NO.
I've got a a few UIScrollView on a page. You can scroll them independently or lock them together and scroll them as one. The problem occurs when they are locked.
I use UIScrollViewDelegate and scrollViewDidScroll: to track movement. I query the contentOffset of the UIScrollView which changed and then reflect change to other scroll views by setting their contentOffset property to match.
Great.... except I noticed a lot of extra calls. Programmatically changing the contentOffset of my scroll views triggers the delegate method scrollViewDidScroll: to be called. I've tried using setContentOffset:animated: instead, but I'm still getting the trigger on the delegate.
How can I modify my contentOffsets programmatically to not trigger scrollViewDidScroll:?
Implementation notes....
Each UIScrollView is part of a custom UIView which uses delegate pattern to call back to the presenting UIViewController subclass that handles coordinating the various contentOffset values.
It is possible to change the content offset of a UIScrollView without triggering the delegate callback scrollViewDidScroll:, by setting the bounds of the UIScrollView with the origin set to the desired content offset.
CGRect scrollBounds = scrollView.bounds;
scrollBounds.origin = desiredContentOffset;
scrollView.bounds = scrollBounds;
Try
id scrollDelegate = scrollView.delegate;
scrollView.delegate = nil;
scrollView.contentOffset = point;
scrollView.delegate = scrollDelegate;
Worked for me.
What about using existing properties of UIScrollView?
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if (scrollView.isTracking || scrollView.isDragging || scrollView.isDecelerating) {
/// The content offset was changed programmatically.
/// Your code goes here.
}
}
Another approach is to add some logic in your scrollViewDidScroll delegate to determine whether or not the change in content offset was triggered programatically or by the user's touch.
Add an 'isManualScroll' boolean variable to your class.
Set its initial value to false.
In scrollViewWillBeginDragging set it to true.
In your scrollViewDidScroll check to see that is it true and only respond if it is.
In scrollViewDidEndDecelerating set it to false.
In scrollViewWillEndDragging add logic to set it to false if the velocity is 0 (as scrollViewDidEndDecelerating won't be called in this case).
Simplifying #Tark's answer, you can position the scrollview without firing scrollViewDidScroll in one line like this:
scrollView.bounds.origin = CGPoint(x:0, y:100); // whatever values you'd like
This is not a direct answer to the question, but if you are getting what appear to be spurious such messages, it can ALSO be because you are changing the bounds. I am using some Apple sample code with a "tilePages" method that removes and adds subview to a scrollview. This infrequently results in additional scrollViewDidScroll: messages called immediately, so you get into a recursion which you for sure didn't expect. In my case I got a nasty impossible to find crash.
What I ended up doing was queuing the call on the main queue:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if(scrollView == yourScrollView) {
// dispatch fixes some recursive call to scrollViewDidScroll in tilePages (related to removeFromSuperView)
// The reason can be found here: http://stackoverflow.com/questions/9418311
dispatch_async(dispatch_get_main_queue(), ^{ [self tilePages]; });
}
}
Why does a UIScrollView instance reset its contentSize (in my case to the frame, and screen, width) by the time the delegate scrollViewDidEndZooming:(UIScrollView *) aScrollView withView:(UIView *) aView atScale:(float) aScale gets called?
What should I do about this? For example, do I really need to remind the instance of its contentSize, perhaps in the layoutSubviews method?
I believe the delegate method you are looking for is
(void)scrollViewDidZoom:(UIScrollView *)scrollView
It tells the delegate that the scroll view’s zoom factor changed.
http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIScrollViewDelegate_Protocol/Reference/UIScrollViewDelegate.html