Prevent delegate method from being called too often - ios

How would you add a delay between certain method being called?
This is my code that I want to only trigger 30 times per second:
- (void) scrollViewDidScroll: (UIScrollView*)scrollView {
[self performSelector:#selector(needsDisplay) withObject:nil afterDelay:0.033];
}
- (void) needsDisplay {
[captureView setNeedsDisplay];
}
If I leave it like this, it only gets called after the user stopped scrolling.
What I want to do is call the method when the user is scrolling, but with a delay of 33 milliseconds between each call.

There are different delegate methods which will call on different occausion. This method will only call when user finish scrolling. So you can perform some task if you want to. If you want to do some thing while scrolling or before scrolling you can use different delegate method. Select one of the below depending on your functionality.
– scrollViewDidScroll:
– scrollViewWillBeginDragging:
– scrollViewWillEndDragging:withVelocity:targetContentOffset:
– scrollViewDidEndDragging:willDecelerate:
– scrollViewShouldScrollToTop:
– scrollViewDidScrollToTop:
– scrollViewWillBeginDecelerating:
– scrollViewDidEndDecelerating:
For detail description upon these delegates please follow this link.
https://developer.apple.com/library/ios/documentation/uikit/reference/uiscrollviewdelegate_protocol/Reference/UIScrollViewDelegate.html
Delegate should call when it should be... other wise you gonna cause some glitch.

Since I couldn't find a solution, and other scroll view delegate methods weren't good, I did it by limiting based on scroll view's content offset, as suggested in a comment. (Ex: only calling it if the user scrolled more then 3 pixels).
This is a decent solution, since I doubt someone can scroll more then 90 pixels per second and STILL read the text in between those 90 pixels.
if (ABS(self.oldOffset.y - webView.scrollView.contentOffset.y) > 3) {
[captureView setNeedsDisplay];
}

The delegate method will get called, you can't setup a limit in that. I think a good option for you is to add the operations in NSOperationQueue, and since you are calling the same selector again and again, make sure you only keep a maximum of 30 operations in the queue at the same time. NSOperationQueue Class Reference.

One cannot have the control over the calling of the delegate methods, and also there is no parameter to set how often delegate method should call or to set the sensitivity of the scrollview,
U cannot control it.
Only thing remaining is to ignoring the call (return at the beginning of the function call if you do not need that) or else process the call..

Related

Call method once after viewDidLayoutSubviews

I need a little help to understand the viewDidLAyoutSubviewsmethod and how to safely use it.
I need to edit some of my subviews programmatically before the viewDidAppear method. And in order for it to work properly I of course need to wait until the targeted subviews are layed out before I edit them.
Now I thought this was what the viewDidLayoutSubviewsmethod was for, but when I tested it I found out that it was actually called two times before my viewDidAppear method. I tried to edit my subviews only the first time viewDidLayoutSubviews was called, because I just want to run [self editMySubviews] once, but then the targeted subview wasn't ready and it got messed up. This is how I tried:
- (void)viewDidLayoutSubviews {
if (!myBoolean) {
[self editMySubviews];
myBoolean = YES;
}
}
Of course if I remove the if-statement it fixes itself next time the method is called, but I only want [self editMySubviews]to be called once.
So my question is, when I can call the [self editMySubviews] method before the viewDidAppear method, and be 100% sure that all subviews are ready to be edited? Now in my case the viewDidLayoutSubviews gets called two times before viewDidAppear, but will that be the case every time? Is it safe to just call [self editMySubviews] after the second time viewDidLayputSubviews is called?
According to your comment you said
Editing some button constraints according to its superView.frame.size.width which is different on different devices
After viewdidload you will get the proper size from view.bounds
so you can easily set/update constraint and add at the end of this
setNeedsUpdateConstraints and layoutIfNeeded method call

Does willMoveToSuperview will also deallocate the UIView on which its got called?

I was wondering if I can call willMoveToSuperview on UIView and after that retain that view to reuse later for one ? something like following
if (!CGRectIntersectsRect(cell.frame, visibleRegion)) {
[cell willMoveToSuperview:nil];
[self.resuableCells addObject:cell];
}
I am not sure about your intent here...
But WillMoveToSuperview - According to doc:
The default implementation of this method does nothing. Subclasses can override it to perform additional actions whenever the superview changes.
So your code,
[cell willMoveToSuperview:nil];
Has no effect unless you override this method in a cell subclass and implement your own logic there.
Coming to your question -
Does willMoveToSuperview will also deallocate the UIView on which its got called?
Answer is obvious - NO.
willMoveToSuperview is an observer method that the system calls as a courtesy to you in order to give you a chance to handle special cases before it completes some other hidden tasks.
It's default behavior is to do nothing, but you might want to tidy up something in your code prior to a move by overriding this method.
A proper use case might be if you had a view playing a video clip or an animation, and something else in your code is about to rip the view out of it's current hierarchy and place it in some other un-related view hierarchy. You might want the chance to pause the clip or suspend the animation before the move took place.
I doubt it's the right method to handle what you are attempting, and I definitely know you should not be calling it directly.
Feel free to post some more code to show us what you're trying to accomplish and where it's going wrong.

setNeedsDisplay is submitted on main queue but is not called

The method below is called on a non-main thread, to be specific, in a recording audio queue callback
- (void)myMethod
{
//...
dispatch_async(dispatch_get_main_queue(), ^{
[myGraphView setNeedsDisplayInRect:CGRectMake(a, b, c, d)];
NSLog(#"Block called");
});
//...
}
where myGraphView is a custom UIView object. For what I know, setNeedsDisplayInRect: should be called on main thread which is why I have dispatch_async... in place. Now the problem is the method - (void)drawRect:(CGRect)rect I implemented for myGraph is never called even though the NSLog in the block has been called for many times.
There are a few possibilities here.
From the Class Reference:
Note: If your view is backed by a CAEAGLLayer object, this method has
no effect. It is intended for use only with views that use native
drawing technologies (such as UIKit and Core Graphics) to render their
content.
The other option, which is probably the cause in this case, has to do with the actual geometry. If the provided rectangle is invalid or off screen, the call does nothing. I would suggest you verify the that the rectangle is being calculated as it should be.
Thanks to #Neal's answer which led me to find out that myGraphView was, after it had been alloc-inited the first time, alloc-inited again. However, unlike the first alloc-init after which I added myGraphView to its superview, I forgot to do so after the second alloc-init.
The lesson I've learned here is that when a view is not doing what it's expected to do, such as not being displayed or updated, always check this third possibility where you forget to add it back to its superview after it's got alloc-inited again somewhere in your code. Also, if the view has a delegate you would tend to forget to set it as well.

UIScrollView subclass trigger event

I've tried for some days understand Xcode Subclasses and Categories - and after all I found one event that are fired.
- (void)setContentOffset:(CGPoint)contentOffset {
NSLog(#"foo");
}
And for more confusion, after read Apple iOS Documentation I get this stuff:
- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated {
NSLog(#"bar");
}
First event are fired, but from Apple documentation are not. Why?!
But in the first case, although he was fired the UIScrollView loses their scroll/drag'n' bounce behavior. I think it's because after overrride setContentOffset I would need to call the parent method to keep the default behavior of the UIScrollView. But I'm already exhausted from test obsolete Xcode approaches.
Than why second code are not fired and how call parent overridden method?
Thanks in advance.
To call the super (:parent) here
- (void)setContentOffset:(CGPoint)contentOffset {
NSLog(#"foo New Offset x: %.0f y: %.0f", contentOffset.x, contentOffset.y);
[super setContentOffset:contentOffset];
}
And, for the second one; That is not a delegate method (:event), this is a method provided to developer actually, to initiate scrolling to a specific offset with/without animation. You probably do not need to override this.
- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated;
Even more; even the first one is not an event, that's a message sent to scrollview to change the offset, but you can get in between and do your thing using that as an event trigger, and call super again to let it do it's work.
If you want to get real events on scrollView, you need to set up a delegate as documented here;
https://developer.apple.com/library/ios/documentation/uikit/reference/UIScrollViewDelegate_Protocol/Reference/UIScrollViewDelegate.html#//apple_ref/occ/intf/UIScrollViewDelegate
And I also agree with Wain on sharing this link,
https://developer.apple.com/library/ios/documentation/general/conceptual/DevPedia-CocoaCore/Delegation.html

Ideal place to put a method after orientation has changed

I have an issue and here how it goes,
I have a view with a subview, the subview is loaded conditionally, only if the parent view is setHidden property is set to YES;
something like [parentView setHidden:YES] and if([parentView isHidden]),
I want to call a method when the orientation changes and that is the cited snippet above, but I have observed that the method shouldAutorotateToInterfaceOrientation is called 4 times during loading and 2 times during runtime, since the method is called more than once, how can I possibly implement a method call ideally since apple's existing method doesn't seem to give me the intuitiveness to put my custom method call with the existing method.
If I would hack this thing, it is possible, but somebody might have a better idea before resorting to things that in the future would just cause me more trouble than benefit.
TIA
Have you tried with
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration {
// check here for your desired rotation
}

Resources