Is it possible to detect the collision of two UIImageViews while one is travelling along a path during a CAKeyFrameAnimation?
If so how is this done, I have tried multiple methods including checking both the CGRects for collision during the animation - but can't find a suitable method for performing a method during a CAKeyFrameAnimation and trying to detect collision of the path and the UIImageView.
You need to get the properties from the presentation layer. It will have the best approximation of information that exists during animation. Access it by
view.layer.presentationLayer
Look at the documentation for CALayer/presentationLayer for more details.
When you want to check for collisions, you would grab the presentationLayer of each object, then access whatever properties you want to test for collision. The exact way to check would depend on which type of layer, and whether you wanted simple hitTest-ing or depth checking. Only you know when and what type of collisions you want to look for.
However, to access the properties of an object while it is animating, you need the presentationLayer.
EDIT
You can make these check whenever you want. You can do it in the context of another action, or with an NSTimer to do it at some interval. You can even use CADisplayLink, which while hook you into the animation timer itself.
If you use CADisplayLink, I suggest setting frameInterval at the highest value possible, and still do what you want, so as to not impact performance.
timer = [CADisplayLink displayLinkWithTarget:self selector:#selector(checkForCollisions)];
// Callback is for every frame, which is 60 times per second.
// Only callback every 6 frames (which is ten times per second)
timer.frameInterval = 6;
[timer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
Don't forget to invalidate the timer when you are done.
Related
Basically I want to give a physicsBody "health" and when it gets hit I want to decrease that health dynamically. If it gets hit hard, I want to take away a lot of health.
I know how to give the node the health and take away from that value using Node.userData, but I don't know how to detect the force of an impact.
I also know you can detect an "impact" with CGRectIntersectsRect but that doesn't return a magnitude obviously, as it is a method of the node not the physicsBody..
How can I accomplish this?
In didBeginContact: you get a SKPhysicsContact object which contains a collisionImpulse property:
The impulse that specifies how hard these two bodies struck each other
in newton-seconds.
The didBeginContact: message is received by the SKPhysicsWorld's delegate (SKPhysicsContactDelegate).
I have a situation where my UIPickerView is getting "starved" by a computation task; in other words, the UIPickerView is never updated -- and hence, never sends messages -- because a very heavy compute task is happening. The picker controls aspects of the computation, so the two have to play nice.
I thought of running the computation in a separate thread. Seems like that would leave the picker free to update. However, it'd be a massive undertaking to make my computation multithread-able, so I'd like to find another solution.
Is it possible for a picker (or other UI controls) to "preempt" the execution of a block of code? The computation is in a loop; the number of iterations is what makes it heavy. If the picker could even set a flag somewhere, the loop could break itself, which would work with the flow of the program.
If the loop could poll the picker, that would also work. But, I haven't found a way to do that.
Ideas?
(ps. I posted a similar question yesterday, but didn't really ask it correctly -- didn't quite know what the problem was at that time!)
I assume you mean by flag that if the picker is moved, set a flag. If so you can do this - look at the picker delegates, and when any or all of them get called, set a flag. If your computation is done by another class or classes, then create a new property on those classes "cancelled", the picker can set it, and when set the computation ends.
Before you start another computation you would clear that cancel flag, then kick off the computation.
You can also put a simple toolbar above the picker (common practice) where you have controls that could start the computation, show progress, and cancel it.
EDIT: if the issue is the picker is stuttering when the user is trying to manipulate it, then subclass UIPicker, intercept touch events, and while the picker is being touched, cancel all computations. The only complication is that if the user "spins" the picker, you'd want to wait til it settles, but you would not know how long to wait. Depending on the last touch message, you would have to use a heuristic to wait for didSelectRow: or a timeout before restarting the computation.
I've been working recently on some animations and it struck me that there is no delegate method of CAAnimation that updates animation progress. I've tried to use a timer and request a transform from a view being transformed (rotated by z axis) but it always returns the same transform.
Is there any way to get the values being updated by CAAnimation?
If you want the details of the current state of the animation of a layer, query [layer presentationLayer]. You can access the transform property within that object.
Unfortunately, no. However, core animation is time based, so you can always mimic it independently. Also, if you are looking for some discrete precise moments, you can split your animation into a chain and update your state within the animationDidStop callback.
I am trying to solve a problem in Objective-C, but I don't think the question is language specific.
I have to do some processing down in a model class that has no notion of UI. However, this processing takes some time and I want to let the user know the status via a progress bar.
My first attempt at this was defining a notion of a progress handler protocol/interface with some methods like
-startOperation;
-updateProgress:(double)currentValue ofMax:(double)maxValue
-endOperation;
This way my UI can implement that the the model need not know details about what goes on other than someone wants progress updates. Currently my UI unhides a progress bar, and updates it, then hides it when done. So far so good.
However, it turns out that sometimes this operation processing is very fast. Such that the UI updates result in a pretty disconcerting flicker as they execute. I don't know if the operation will be fast or slow beforehand.
One idea I had was to force the operation to take at least a certain duration to avoid the UI changes being so jarring to the eye, but this seemed to put knowledge of the UI in the model class, which must be wrong.
This would seem to be a common issue with (hopefully) some known pattern.
How would you address this?
Jonathan's and Darren's answers cover your actual problem, but I would add something regarding the question in the title: "How should the model update the UI of its progress?"
The answer, of course, is that it shouldn't. The model shouldn't have to know anything about any protocols for displaying data. There should be one uniform bindings layer taking care about propagating information from the model to the interface. Fortunately, Cocoa already includes such a bindings mechanism: Key-Value Observing.
What you should do is define a property on any model class where the concept of progress makes sense, something like #property (assign) float progress. Then you make sure the class is KVO compliant. Controller code that want to keep track of the progress simply registers to observe this value with something like:
[theObject addObserver:self forKeyPath:#"progress" options:0 context:NULL];
Make sure to read the documentation for the NSKeyValueObserving (KVO) informal protocol.
Also, you might want to have a look at Mike Ash's KVO-related notes and code: Key-Value Observing Done Right.
You can use NSTimer to delay the display of your progress bar until your operation had run for a given amount of time, say half a second:
-(void)startOperation {
// Show the progress bar in 0.5 seconds
if (!_timer) {
_timer = [[NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(showProgressBar:)
userInfo:nil
repeats:NO] retain];
}
}
In -endOperation, you cancel the timer and hide progress bar:
-(void)endOperation {
[_timer invalidate]; // cancel the timer
[_timer release];
_timer = nil;
[self hideProgressBar];
}
If the operation completes in less than 0.5 seconds, the timer is canceled before the progress bar is displayed.
One thing commonly done is to have your progress bar implementation not show itself right away, and apply some heuristic based on the first couple of updates (or a timeout) to determine whether it needs to show itself at all. That's how the Java ProgressMonitor behaves, for example. (The ProgressMonitor is a nice abstraction that separates the knowledge of progress from its graphical representation).
Once the progress widget is showing, you could repaint it on a leisurely timer, say 10 times per second, rather than reacting to every progress change event with a repaint.
I have a -(void)save method that is called when a user clicks a navigation bar button. In that method is the following NSTimer:
[NSTimer scheduledTimerWithTimeInterval:.25f target:self selector:#selector(flashBackgroundRed) userInfo: nil repeats: flashRepeat];
The timer repeats 4 times since the flashBackgroundRed keeps a count and sets flashRepeat to NO after 4 hits. All works well the first time a user clicks to save. But if the user keeps hitting save with incorrect data, I keep them on the current view. For some reason, flashes get increasing faster. As if each one is 25% of the previous one's interval. I'm not retaining this timer. It only lives in the save method.
If the user clicks the cancel button, viewB (one with NSTimer) is popped and viewA displays. Going from viewA back to viewB seems to reset the timer invterval. But the increasingly fast flashes cycle starts again. Any ideas?
NSTimer is retained by the run loop as long as it is scheduled.
It sounds like the problem is that you keep creating equivalent repeating timers at slightly different times, so they visually mesh together into one effect with a different frequency than you want. Try storing a reference to the timer and invalidating it when you're going to create a new one.
It sounds like you are scheduling more timers than you intend to.
Once scheduled, timers are retained until invalidated. Repeating timers must be manually invalidated.
The repeats argument specifies if the timer will repeat. You can't specify how many times a timer should repeat, only whether or not it will.
Also, the method signature for your selector is wrong. It should look like
-(void)timerFireMethod:(NSTimer*)theTimer
In your timer callback you can determine if you want the timer to continue repeating or not; if not, call [timer invalidate].