NSTimer is ruining all of my animations - ios

I have added an NSTimer which updates my interface, mainly labels with information 10 times every second. It calls a function which dispatches the work back to the main thread.
The view controller also has a scroll view. I have a paging system where I animate my scroll view's moving from page to page as a user taps on the tab (a button) which corresponds to each tab. That scrolling animation has just stopped happening - it is as if I am calling scrollToRect with animation as NO, even though I am calling it with YES.
I think that because I am updating my labels, auto layout is doing something dodgy in the background and ruining my scroll view animations. The same is happening for other animations which are using layout constraints to move views.
I know there are issues with Autolayout and NSTimer.
What can I do to fix this?
Thanks

I would create a boolean flag that inhibits the updates from the timer method. Set this to true just before you begin the animations and false once the animation completes. You probably also want to update the data 'manually' once the animation completes to capture any blocked updates.

Related

Animate view and stop midway

I want to make a view/header collaspe/expand animation, but I also want to be able to pause or stop from expanding/collapsing while the user takes his finger off the screen.
I have a current implementation for this, but the animation is not very smooth.
I will also add some images to explain better what I want to do.
So the first image is how it should look when expanded.
The second image is how it should look if the user decides to take take his finger off the screen while he would scroll slowly so the animation/the view should stop in a state similar to that.
The third image is how it should look when it is collapsed.
If someone has any recommendation how I could achieve this I would be very grateful.
If you are targeting iOS 10+, use UIViewPropertyAnimator which encapsulates an animation and allows scrubbing and controlling the animation:
Start, pause, resume, and stop animations; see the methods of the UIViewAnimating protocol.
Add animation blocks after the original animations start using the addAnimations(_:) and addAnimations(_:delayFactor:) methods.
Scrub through a paused animation by modifying the fractionComplete property.
Change the animation’s direction using the isReversed property.
Modify the timing and duration of a partially complete animation by pausing the animation and using the continueAnimation(withTimingParameters:durationFactor:) method to finish it.
You just need to wire up touches to UIViewPropertyAnimator object encapsulating the animation.
There are many good tutorials on how to start with it, e.g. this one.

iOS - limit on UIView animate?

So I'm making a simple trivia game and I have a timerView that shrinks as time passes. When the user selects an answer, it needs to stop shrinking immediately - it must be very responsive. I give the user 10 seconds per question. Originally I would animate 10 times (with a duration of 1.0f), calling the next "segment" of animation in the completion block of the previous animation. In the completion block I would check to see if the user has tapped an answer, and if so I don't continue the chain. That solution works fine except that it's not very responsive because it's on a per second basis-- user taps an answer at the start of the second segment and the bar has a noticeable continuation.
My solution to THAT problem was to instead have 1000 animation calls with a duration of 0.01f. After doing that, the responsiveness was on point - the view stops animating as soon as I tap an answer -- the issue though, is that it's not actually 10 seconds, it takes more like 20.
So question number 1: what's the smallest time interval animateWithDuration can actually process properly?
Question number 2: is there a better way to accomplish what I'm trying to do accomplish?
ill answer question two: yes there definitely is a better way, have a look at CADisplayLink
use it to shrink your view a little bit each frame, and end the display link when you need to
the most responsive way is: the user taps an answer, you response in the touch callback, remove animations. you can remove animations by CALayer's removeAllAnimations method
Another way to do it is to set the view to shrinking using a single animation with linear timing, and then set the speed of the view's layer to 0 to pause the animation. When you set the speed on the layer to 0 the animation pauses instantly.
This works because under the covers, UIView animation actually creates and installs CAAnimation objects on the view's layers. It's possible to pause and continue an in-flight UIView animation just like you can a CAAnimation.
I have a project called KeyframeViewAnimations (link) on github that allows you to pause, continue, or "scrub" UIView and CAAnimations back and forth with a slider. You could use that technique. The tricky bit will be figuring out how far along the animation is.

UIView subviews updating visually but interact as though in previous state

I'm creating a UIViewController that houses some of RosyWriter's functionality, which I'm reworking to create a video recorder. The screen contains a title, a clipped-to-bounds subview that contains the AVCaptureVideoPreviewLayer (so the sublayer CALayer of the video content is added into that subview, so I think it's quite deep-nested), and two buttons acting like a toggle button - in the form of Start and Stop buttons placed in the Storyboard for the UIViewController.
The Start button works fine, even though the video's preview layer is on screen and showing the camera. When I start recording, though, I switch the buttons round, making the Start button hidden and the Stop button hidden=false.
The start button works - this is pressed when the video preview is on-screen and updating, but the actual recording (grabbing samples in buffers and writing them to a file - nothing UIKit related as far as I can see) has not started.
When the video recording is active, with the Stop button showing and the Start button hidden, the visible stop button isn't pressable, but the hidden start button can still be pressed.
I've tried moving the Stop button above the UIView containing the video, in case the CALayer or something else stretches outwith the clipped UIView bounds. This doesn't help. The stop button always acts as though it's not enabled - but it is enabled, and nothing appears to overlap the button. The button works fine if the UIView containing the video (which, I'll reiterate, is lower than the broken button) is never shown.
Can anyone think why this'd happen? I immediately thought about setNeedsLayout and setNeedsDisplay and tried just throwing some of those in, because it's almost as though the view had updated with my request to hide or show buttons, but an interaction layer hadn't updated.
Thanks
I now think this is due to my ignorant use (or misuse) of dispatch queues. I'm still not sure how I was able to interact with buttons that aren't showing - perhaps it's because their own state change responds in part, immediately, and the rest can't take place except on the main queue (the visual refresh, for example).
So I solved the problem, in a sense, by forcing a particular asynchronous delegate method to be called on the main queue. This doesn't affect the operation of this particular step in my recording process.

Scrolling UIScrollView - block main thread

I have weird problem while UITableView is scrolling I'm not able to invoke any events (touch down button, change layout colour). Funny thing is that when I touches on UISeachBar, the keyboard appears immediately.
I realize that main thread has been blocked, however I wonder is there any solution to fix this problem.
With the short description of your problem, I suppose that it is because when you scroll your tableView run loop change from NSDefaultRunLoopMode to NSEventTrackingRunLoopMode (see Run loops)
To change your UI while scrolling, the solution seems to perform your UI updates on the NSRunLoopCommonModes which includes the two loop mode you target.
Be prevent that it could make your tableview scrolling clunky.

App stops receiving NSTimer selector callbacks during any UI scrolling operation

I have an OpenGL animation drawing on the main window fired by an NSTimer. If I bring up a popover window with a scrollable UITableView menu, the animation freezes while scrolling is in process. Once the scrolling animation stops, the timer callbacks start again. Its only when the user actively tries to scroll that the main window stops updating.
It seems like Apple's scroll animation is somehow blocking dispatch on the main loop. Is this true and is there a way to fix it?
I dont really want to introduce multithreading if I can help it because that will exponentially increase the complexity of my code.
Also I tried using CADisplayLink instead of NSTimer and the display link calls are also blocked by the scrolling animation.
As answered by bdesham, "Apple’s #1 priority is UI responsiveness, and so in situations like this, UI actions will block other parts of your code. You need to move your timer to another thread."
NSTimer are tied to a run loop and mode. During event tracking, the mode is UITrackingRunLoopMode. By default, timers created by scheduledTimerWithTimeInterval are added with NSDefaultRunLoopMode and so do not fire during tracking.
[[NSRunLoop currentRunLoop] addTimer:yourTimer forMode:UITrackingRunLoopMode];

Resources