Detect if touch stopped on the screen - objective-c

The question is simple but extremely complicated: in UIResponder there are 4 methods for handling touches.
- touchesEnded:withEvent:event
– touchesBegan:withEvent:
– touchesMoved:withEvent:
– touchesCancelled:withEvent:
How do I detect if a touch stopped on the screen?
The problem is that I have to detect if something moved under a stationary touch (not cancelled or ended. It just doesn't move) And because it doesn't move, none of these methods gets called.
My idea was this:
I could add the touches to a NSMutableArray but then I'd have to update it for any touch move (and that's a lot). Also this creates more problems, I need to detect which of the touches stopped and if any ended. And because I get an NSSet from UIResponder, I don't have an organized array so... all kinds of problems.
I'm waiting for ideas.

i fixed it..and also found out something really cool about UITouch
what i did: in
– touchesBegan:withEvent:
for(UITouch*touch in touches){
[touchesSet addObject:touch]; } //touchesSet is a set that i store all the touches on the screen
in
- touchesEnded:withEvent:
for(UITouch*touch in touches){
[touchesSet removeObject:touch]; }
in
– touchesCancelled:withEvent:
[touchesSet removeAllObjects];
doing this i have a NSSet of all the touches on the screen at any given time, with position and UITouchPhase

The solution depends a little upon what you're trying to do (and you don't really describe what business problem or user experience you're going for). But assuming you're just trying to detect when a continuous gesture paused but hadn't been completed:
You could have touchesMoved keep track of where and when it was last invoked. E.g. if you have a subclassed gesture recognizer, give it a property of CGPoint lastLocation or something like that which you could inquire upon.
You could then setup a NSTimer that would be triggered a certain amount of time later, which would test for your "stopped" condition. E.g. if your NSTimer is called every 0.1 seconds and you're waiting for no change in location for, say 1 second, then that would qualify as a stopped condition.
And if you're looking to see if "something moved under a stationary touch", you could add this to your NSTimer routine.

Related

Loop problems in Objective-C

I have two problems both with loops.
First question, is it possible to make loop(while or something else) that would wait my command.
For example, I need to choose a possible move and until I have chance for next move I must have chance to choose.
While(eat==1){
if(CGRectContainsPoint([BOX9 frame], [touch locationInView:self.view])){
eat=0;
}
}
My problem is that while spinning in circle and BOX9 can't be pressed because spinning and spinning. I need some loop that spin circle but will allow selection of next command. I hope I was clear, English is not my strong point.
Second problem
Second problem is with while loop. On touch I show image from UIimageView controller to another. Sometimes I have more movements, and I need use while because i never know how movements I wold have. And I would like to make this movements slower.
If i write in code that i want uiimage1.image=someimage; that work fine. But problem is when I say for example:
uiimage1.image=someimage;
while(eat==1){
uiimage2.image=uiimage1.image;
uiimage1.image=NULL;
uiimage3.image=uiimage1.image;
}
(This is only example code,that I wrote now,to better explain problem.)
If i making some waiting inside "while loop", always I getting that slower show is of only first image,second two changes are fast.
I try with transition effect to make slower, but that works only when I load some picture in imagecontroller with touch. If i wrote in code, transition wont work.
All is in touchesBegin function.
Thank's for help.
So, from what I can tell, you are making a game, and want to animate things. Your problem is that your while loops are on the main thread, so until they finish no user input is accepted and the screen doesn't refresh.
There are several ways to solve this, all of which involve moving the execution of things that happen in "real-time," by which I mean things that are presented to the user, off of the main thread.
The first: You make an NSTimer with scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:. Your selector handles the animation of things in "real time". When touchesBegan is called, you set the flag eating. This looks roughly like this in code
-(void) setUp
{
NSTimer scheduleTimerWithTimeInterval: 1.0
target: self
selector: #selector(runLoop)
userInfo: NULL
repeats: YES];
}
-(void) runLoop
{
if(eatingDuration && eating)
{
eatingDuration--;
//do something because we're eating
}
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//if we need to set eating to yes do it
eating = YES;
eatingDuration = whatEverWeNeedItToBe;
}
This might be thought of as a "run loop," because it is called repeatedly until you get rid of the timer. It is probably the best method to do this.
The second: You use the animateWithDuration:animations: class method of UIView to animate each individual element.

What is the difference between beginTrackingWithTouch and touchesBegan?

Can someone please explain the high-level difference between these two methods? In particular, when would you use one over the other, and is there any overlap in terms of the purposes of these methods?
They seem like they serve the same purpose but don't appear to be related at all in documentation, and this has me confused.
beginTrackingWithTouch:withEvent:
1) subclass UIControl
2) Sent to the control when a touch related to the given event enters the control’s bounds.
3) To provide custom tracking behavior (for example, to change the highlight appearance).
To do this, use one or all of the following methods: beginTrackingWithTouch:withEvent:, continueTrackingWithTouch:withEvent:, endTrackingWithTouch:withEvent:
touchesBegan:withEvent:
1) subclass UIResponder
2) Tells the receiver when one or more fingers touch down in a view or window.
3) There are two general kinds of events: touch events and motion events.
The primary event-handling methods for touches are touchesBegan:withEvent:, touchesMoved:withEvent:, touchesEnded:withEvent:, and touchesCancelled:withEvent:.
The parameters of these methods associate touches with their events—especially touches that are new or have changed—and thus allow responder objects to track and handle the touches as the delivered events progress through the phases of a multi-touch sequence.
Any time a finger touches the screen, is dragged on the screen, or lifts from the screen, a UIEvent object is generated. The event object contains UITouch objects for all fingers on the screen or just lifted from it.
Having just run into this today, I think the key difference is that beginTrackingWithTouch and friends are only for tracking - not anything else - in particular not for target/action handling. So if you override touchesBegan, then you'd also be responsible for calling sendActionsForControlEvents when touches ended. But if you use beginTrackingWithTouch, that's handled for free.
I discovered this by implementing beginTrackingWithTouch (for a custom button control) thinking it was just a sideways replacement for handling touchesBegan. So in endTrackingWithTouch, I called sendActionsForControlEvents if touchInside was true. The end result was that the action was called twice, because first the builtin mechanism sent the action, then I called it. In my case, I'm just interesting in customizing highlighting, so took out the call to sendActionsForControlEvents, and all is good.
Summary: use beginTrackingWithTouch when all you need to do is customize tracking, and use touchesBegan when you need to customize the target/action handling (or other low-level details) as well.
If I properly understand Apple documentation:
beginTracking:
Use the provided event information to detect which part of your control was hit and to set up any initial state information
So, it's more for control state configuration.
touchesBegan:
Many UIKit classes override this method and use it to handle the corresponding touch events
This method is more for touch event handling.

Call method repeatedly after time delay until end condition reached

I have a crosshair on the iphone screen and when a pavilion (dot on map) moves under it i want to start zooming on the map.
I all ready got the detection to work when a pavilion comes under the crosshair and the moment it is not under it anymore.
For now i post it in here in psuedo code:
- (void) checkForPavilionUnderCrosshair {
if(new pavilion under crosshair) {
// start zooming
}
else if(just left pavilion under crosshair){
// stop zooming
}
}
So what i need to do now is keep triggering:
mapView setZoom:(mapView.zoom+0.1) animated:NO];
And being able to stop that progress when the crosshair moves off the pavilion.
I did some searches on stackoverflow but the posts i found did not include stopping it for example. I have no experience with timing things in programs so could someone help me a litle by telling what i'm looking for?
I tried
[self performSelector:#selector(zoomTest) withObject:self afterDelay:0.0];
If i keep touching the map and move my finger then it keeps checkForPavilionUnderCrosshair just like i want.
But the perform selector get's fired after i stop touching the screen, so if i touch the screen for 20 seconds it fires 20 seconds to late.
Any workaround for that?
You can call [self performSelector:#selector(checkForPavilionUnderCrosshair) withObject:nil afterDelay:1.0] at the end of your method to have it called again after the specified time period. Note that the delay is in seconds.
If you want something more advanced, you can look at NSTimer over at the Apple docs, or see this SO question for an example and explanation.
If you decide to use the simple performSelector method, you can simply not call the method when you don't want to repeat anymore. If you choose to use NSTimer, call invalidate on the timer to stop it.

Calling -setNeedsDisplay:YES from within -drawRect?

I am customizing my drawRect: method, which serves to draw a NSImage if it has been "loaded" (loading taking a few seconds worth of time because I'm grabbing it from a WebView), and putting off drawing the image till later if the image has not yet been loaded.
- (void)drawRect:(NSRect)dirtyRect
{
NSImage *imageToDraw = [self cachedImage];
if (imageToDraw != nil) {
[imageToDraw drawInRect:dirtyRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0 respectFlipped:YES hints:nil];
} else {
//I need help here
[self setNeedsDisplay:YES];
}
}
My question is how to do the latter. [self cachedImage] returns nil if the image is unavailable, but anytime within the next few seconds it may become available and at that time I want to draw it because the custom view is already on screen.
My initial instinct was to try calling [self setNeedsDisplay:YES]; if the image wasn't available, in hopes that it would tell Cocoa to call drawRect again the next time around (and again and again and again until the image is drawn), but that doesn't work.
Any pointers as to where I can go from here?
EDIT:
I am very much aware of the delegate methods for WebView that fire when the loadRequest has been completely processed. Using these, however, will be very difficult due to the structure of the rest of the application, but I think I will try to somehow use them now given the current answers. (also note that my drawRect: method is relatively light weight, there being nothing except the code I already have above.)
I currently have about 10+ custom views each with custom data asking the same WebView to generate images for each of them. At the same time, I am grabbing the image from an NSCache (using an identifier corresponding to each custom view) and creating it if it doesn't exist or needs to be updated, and returning nil if it is not yet available. Hence, it's not as easy as calling [view setNeedsDisplay:YES] from - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame or another method.
My initial instinct was to try calling [self setNeedsDisplay:YES]; if the image wasn't available, in hopes that it would tell Cocoa to call drawRect again the next time around (and again and again and again until the image is drawn)
This would be incredibly inefficient, even if it worked.
anytime within the next few seconds it may become available and at that time I want to draw it
So, when that happens, call [view setNeedsDisplay:YES].
If you have no means of directly determining when the image becomes available, you'll have to poll. Set up a repeating NSTimer with an interval of something reasonable -- say 0.25 second or so. (This is also pretty inefficient, but at least it's running only 4 times per second instead of 60 or worse. It's a tradeoff between two factors: how much CPU and battery power you want to use, and how long the delay is between the time the image becomes available and the time you show it.)
my drawRect: method is relatively light weight, there being nothing except the code I already have above.
Even if you do nothing at all in -drawRect:, Cocoa still needs to do a lot of work behind the scenes -- it needs to manage dirty rects, clear the appropriate area of the window's backing store, flush it to the screen, etc. None of that is free.
Well, usually there is some delegate method that is called, when a download of something finishes. You should implement that method and call setNeedsDisplay:YES there.
The documentation for webkit:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/DisplayWebContent/Tasks/ResourceLoading.html#//apple_ref/doc/uid/20002028-CJBEHAAG
You have to implement the following method in your webview delegate:
- webView:resource:didFinishLoadingFromDataSource:
There you can call [view setNeedsDisplay:Yes]

How should the model update the UI of its progress?

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.