- (void)scrollViewDidScroll:(UIScrollView *)scrollView too slow in iOS 8 - ios

I have a problem where scrolling up/down and setting the contentoffset from within scrollviewdidscroll on a secondary scrollview causes a minor jittery behaviour... or more accurately a low frame rate.
I currently rely on scrollViewDidScroll to manage effects such as Parallax in my UIScrollView, these effects are applied by listening for scrollViewDidScroll, but the turn around time for each call of this method is (for some reason), too slow and causes enough of a delay for it to look kind of bad when scrolling.
Interestingly, iOS 9, runs fine.
I've tried alternative methods, such as turning off images or using AsyncDisplayKit but both have no effect on the number of times scrollViewDidScroll is fired.
It looks to me that I may need to rearchitect the way I create the parallax effect, but I'm hesitant to in case there is a quick fix.

First of all - use Xcode Instruments debug tool "Time Profiler" (Xcode menu Product->Profile, then select Time Profiler from instruments).
Don't forget to check there "Invert Call Tree" and "Hide System Libraries" checkmarks, and detect problem place in your code.
After that you can find some solution.
At least you can try to add additional check before setting contentOffset property for second scroll view:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat contentOffsetForSecondScrollView = 123.0; // Calculate second scroll view content offset
// Add additional check, if content offset doesn't change
if (self.secondScrollView.contentOffset.y != contentOffsetForSecondScrollView) {
self.secondScrollView.contentOffset = CGPointMake(0, contentOffsetForSecondScrollView);
}
}

Put all the effects in the below method.
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate

Please put below delegate method to improve and detect scroling
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
//Your code here
}

Related

how to check if scrollView grinds to a halt when tabbar is clicked?

A viewcontroller is the child of tabbarcontroller, it has a tableview, when the tableview grinds to a halt, one of three methods is executed.
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if(!decelerate) {
// stop
}
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
// stop
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
// stop
}
but when I quick swipe the tableview , and at the same click the tabbaritem to change other viewcontroller as the selectedviewcontroller . none of three methods is executed。
I try my best to find a method to detect the time ,but I cannot solve it
I am not aware if there is any documentation for what I am writing below.
However, I know from practical knowledge and observation that this is not possible because the scrollview will stop scrolling the moment you change the tab.
You can validate this behaviour in all of Apple's apps that have a tabbar (Photos, AppStore, Developer) and even other prominent apps from 3rd-party devs (Youtube, Youtube Studio).
As far as I know we do not have control over this behaviour.
However, you should be able to treat the tabbarcontroller's delegate method of selected tab index change as the substitute for the scroll view delegate methods you have mentioned, specially to "detect the time" when the scroll view stops scrolling (it is at the moment when you switch to a different tab).
You would need to ensure that the user has selected a different tab than the one they are already on.
Also I would like to mention that I think this is not really related to tab change. I think this happens when a scrollview disappears. So you may also be able to detect this change using viewDidDisappear(_:).

How to implement efficient UI updates while scrolling UIScrollView on iOS

I'm looking for a way to update UI elements while an UIScrollView is scrolled. There is the function - (void)scrollViewDidScroll:(UIScrollView *)scrollView which is called multiple times per second and the method - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollViewwhich is called at the end of the scrolling (when there was a flick).
I have the feeling that doing updates in the scrollViewDidScroll is inefficient and the scrolling is not smooth anymore. What is the recommended way to do something like this?

Parallax UIScrollView – handling two views in one scrollViewDidScroll method?

I’m creating a basic parallax effect much like the iOS7 app switcher, using two UIScrollView instances (cardScrollView and tileScrollView). I scroll one alongside the other, at a different rate, like so:
if ([scrollView isEqual:self.tileScrollView]) {
[self.cardScrollView setContentOffset:CGPointMake((self.tileScrollView.contentOffset.x + 110) * TILE_CARD_DELTA,
self.cardScrollView.contentOffset.y)];
}
This works fine when scrolling tileScrollView. However, I’d like the same to work in reverse, meaning I can scroll cardScrollView and have tileScrollView move accordingly. The issue I’m having is that calling setContentOffset actually causes cardScrollView to call scrollViewDidScroll itself, meaning they’re continually trying to set each other at the same time, and all kinds of hell break loose.
Basically, the issue here is that both scrollView instances are relying on the same scrollViewDidScroll, and so I can’t differentiate between the two of them in there.
How can I get around this one?
You are getting reference of both in this method and work as per requirement :
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView == self.tileScrollView) {
// do something
}
else {
// do something
}
}

scrollView.dragging == true after scrollViewDidEndDragging?

It seems that in the UIScrollViewDelegate function
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
sometimes, scrollView.dragging is true even after
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)willDecelerate
is called.
Is that somehow expected?
If willDecelerate of scrollViewDidEndDragging:willDecelerate: is YES, then yes, that's expected. You get scrollViewDidEndDragging when the user lets go of the drag, but dragging doesn't change until the visual scrolling/decelerating is done. Thus, when you let go of a drag, you may see scrollViewDidEndDragging followed by some scrollViewDidScroll (and dragging still YES).
Alternatively, you can check out tracking, which turns to NO as soon as the user lets go (even though the scroll view may still be scrolling).

UIScrollView - tell the difference between setContentOffset and manual scrolling

I have a scrollview, which automatically advances every 3 seconds to the next "page" (page control also used). However, once the user touches the scrollview, I want to stop the auto-advancing.
I have subclassed the scrollview. I can detect touchesBegan in the subclass, but touchesMoved is not called, so I can't tell if the user has manually swiped the scrollview. I can't use scrollviewDidScroll, because that gets called when I set the contentOffset when the auto-advance timer fires.
So what are my other options for detecting touches? Why isn't touchesMoved called on the scrollview subclass?
Thank you for the suggestions. They helped me stumble upon this easy solution:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
[self.scrollTimer invalidate];
}
You may want to look into the following delegate method:
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
Per Apple:
The scroll view calls this method at the end of its implementations of the UIScrollView and setContentOffset:animated: and scrollRectToVisible:animated: methods, but only if animations are requested.
So that delegate method is only called when programatic scrolling occurs. You could set up your autoscroll call in that method, only calling it if some BOOL is false. Setting that BOOL to true in your touch event.
Or something completely different =]
It is a useful delegate method though.
~ Good luck
The scrollview probably nominates a subview to receive touch input — UIKit objects are very fussy about this sort of thing and generally can't even handle forwarded events.
What you probably want to do is to key-value observe either tracking or dragging (and it sounds like you want the latter). If the relevant property changes to true then you know the user has initiated scrolling.

Resources