Detect touch globally - iphone

I trying to figure out how to solve this (fairly) simple problem but I failing miserably, so I really need your advice.
My application consists of a uitabbar with several tabs. In one of them I have a bunch of UIImageViews each of which represents the thumbnail of a picture. Similarly as you remove apps from the iPhone by pressing for a second on the app icon, I implemented a UILongPressGestureRecognizer recognizer which starts wobbling the thumb. If the user taps on the 'X' that appears on the corner of the thumb the picture gets removed.
The logic that starts and stops the wobbling animation is inside a subclass of UIImageView that is used to show the thumb.
What I'm trying to do is cancel the wobble effect if the user presses anywhere else outside the thumb. Ideally, if possible, I would prefer to place the code that detects this cancel touch inside the UIImageView subclass.

To catch all touch events globally I ended up subclassing UIWindow as follows:
// CustomUIWindow.h
#import <UIKit/UIKit.h>
#define kTouchPhaseBeganCustomNotification #"TouchPhaseBeganCustomNotification"
#interface CustomUIWindow : UIWindow
#property (nonatomic, assign) BOOL enableTouchNotifications;
#end
// CustomUIWindow.m
#import "CustomUIWindow.h"
#implementation CustomUIWindow
#synthesize enableTouchNotifications = enableTouchNotifications_;
- (void)sendEvent:(UIEvent *)event
{
[super sendEvent:event]; // Apple says you must always call this!
if (self.enableTouchNotification) {
[[NSNotificationCenter defaultCenter] postNotificationName:kTouchPhaseBeganCustomNotification object:event];
}
}#end
Then whenever I need to start listening to all touches globally I do the following:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(stopThumbnailWobble:)
name:kTouchPhaseBeganCustomNotification
object:nil];
((CustomUIWindow *)self.window).enableTouchNotification = YES;
In stopThumbnailWobble I remove the observer and process the UITouch event to decide whether to remove the thumb or not:
- (void)stopThumbnailWobble:(NSNotification *)event
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:kTouchPhaseBeganCustomNotification
object:nil];
((CustomUIWindow *)self.window).enableTouchNotification = NO;
UIEvent *touchEvent = event.object;
// process touchEvent and decide what to do
...
Hope this helps others.

If you must include the code detection in your uiimageview subclass then I would tell the appdelegate that a touch was received and where. The app delegate could then either tell all your uiimageviews or tell the viewcontroller which would tell it's uiimageviews.
untested code:
appDelegate = (myAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate touchedAt:(int)xPos yPos:(int)yPos];

Related

Get download progress from different ViewController

I have a VideoListController. It has list of videos with download button. When I press the download button, the control is transferred to DetailViewController. In DetailViewController, I am using AFNetworking to download file.
If I go back to VideoListController from DetailViewController. How can I know progress of download or when download gets completed from VideoListController. I want to know this because based upon that I will reload the list to show play button instead of download.
I think that the best solution for your problem would be to create a custom delegate protocol that the DetailViewController and the VideoListController can use to communicate with each other. Check this post for additional information How to use custom delegates in Objective-C
In a nutshell the strategy is the following:
1. The DetailViewController defines a delegate protocol that it uses to pass events to its delegate
2. The VideoListController becomes the delegate to that it knows whenever an upload has progressed or been completed
3. The VideoListController keeps track of which DetailViewControllers have completed the download
Here is come code:
DetailViewController.h:
#class DetailViewController;
#protocol Delegate <NSObject>
- (void) detailViewController: (DetailViewController *) theDetailViewController didFinishDownloadingVideoWithResults:(BOOL)successful;
#end
#property (nonatomic, weak) id<DetailViewController> delegate;
DetailViewController.m:
Whenever a download is complete do the following:
if ([[self delegate] respondsToSelector:#selector(detailViewController:didFinishDownloadingVideoWithResults:)]){
[[self delegate] detailViewController:self didFinishDownloadingVideoWithResults:YES];
}
Now, in the VideoListController.m make sure you establish yourself as the delegate of the DetailViewController.
[theDetailViewController setDelegate:self];
And implement the delegate method. You can for instance have a dictionary that defines which DetailViewControllers have completed the download:
- (void) detailViewController: (DetailViewController *) theDetailViewController didFinishDownloadingVideoWithResults:(BOOL)successful{
detailViewControllersDownloadInformation[theDetailViewController] = #(successful);
}
Now, whenever you need to check if a DetailViewController did indeed complete a download, all you have to do is check that dictionary
if (detailViewControllersDownloadInformation[theDetailViewController] && detailViewControllersDownloadInformation[theDetailViewController] == #(YES)){
// Did download a video
}
Keep in mind that the solution I provide will only let you know if the download has been completed. If you also want to keep track of the progress you need to pass that as an additional parameter in the delegate. We are also assuming that you keep all of the DetailViewControllers in memory. If you release and reuse them you will need to keep track of which element was downloaded in a different data structure.
I got it working using NSNotificationCenter.
In viewDidLoad of DetailViewController, I added this
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieDownloadDidFinish)
name:#"MovieDownloadDidFinishNotification"
object:nil];
When download gets complete. I call this:
[[NSNotificationCenter defaultCenter] postNotificationName:#"MovieDownloadDidFinishNotification" object:self];
I remove the observer from DetailViewController when when backbutton in navigation controller is clicked
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"MovieDownloadDidFinishNotification" object:nil];
And added method in DetailViewController that is called when download gets completed.
-(void) movieDownloadDidFinish {
NSLog(#"MovieDownloadDidFinish on DetailViewController");
}
Now in viewDidAppear of VideoListController, I added the observer
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieDownloadDidFinish)
name:#"MovieDownloadDidFinishNotification"
object:nil];
And in viewDidDisappear Of VideoListController, I remove the observer
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"MovieDownloadDidFinishNotification" object:nil];
And added method in VideoListController that is called when download gets completed.
-(void) movieDownloadDidFinish {
NSLog(#"MovieDownloadDidFinish On VideoListController");
}
In this way, when DetailViewController is visible, the method movieDownloadDidFinish of DetailViewController is called and similarly movieDownloadDidFinish of VideoListController is called when VideoListController is visible.

Move a value between UITabBarController /UIViewControllers

I have a project i'm working on which involves 3 tabs in a UITabBarController (all done in a storyboard).
Each tab is running off a different view controller.
I have a button on tab 1 that performs a calculation and returns a result in a text box. I want it so that when I hit calculate, the result is also returned in a text box in tab 2.
I'm not really sure how to pass data between UIViewControllers so any help is appreciated.
as per vshall says you can do this stuff like bellow:-
yourAppdelegate.h
#interface yourAppdelegate : UIResponder <UIApplicationDelegate,UITabBarControllerDelegate>
{
NSString *myCalResult;
}
#property (nonatomic,retain) NSString *myCalResult;
yourAppdelegate.m
#implementation yourAppdelegate
#synthesize myCalResult,
yourCalclass.h
#import "yourAppdelegate.h"
#interface yourCalclass : UIViewController
{
yourAppdelegate *objAppdelegate;
}
yourCalclass.m
- (void)viewDidLoad
{
objAppdelegate = (yourAppdelegate *) [[UIApplication sharedApplication]delegate];
[super viewDidLoad];
}
-(IBAction)ActionTotal
{
objAppdelegate.myCalResult=result;
}
Now you result stored in objAppdelegate.myCalResult you can use this variable in your another tab with creating object of yourAppdelegat. Hope its helps you
You can define a variable in app delegate and store the result in that variable for class one. And once you switch the class you can fetch that value in your class two by creating an instance of your appDelegate and assign it to your textfield.
As Sanjit has suggested, NSUserDefaults is also a very convenient and clean way to achieve this.
Thanks.
If you don't really need to store the computed value but just notify the other controller in tab2 that the value changed, you can use NSNotificationCenter to post an NSNotification.
When you initialize the controller in tab2 you'll need to add it as an observer of the notification.
Something like that:
in tab1:
NSNumber *value = nil; // the computed value
[[NSNotificationCenter defaultCenter]
postNotificationName:#"com.company.app:ValueChangedNotification"
object:self
userInfo:#{#"value" : value}];
in tab2: register as an observer (in init or viewDidLoad methods)
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(valueChanged:)
name:#"com.company.app:ValueChangedNotification"
object:nil];
the method that will be called when the notification is posted:
- (void)valueChanged:(NSNotification *)note
{
NSDictionary *userInfo = note.userInfo;
NSNumber *value = userInfo[#"value"];
// do something with value
}
Don't forget to remove the controller from the observers in viewDidUnload or sooner:
[[NSNotificationCenter defaultCenter] removeObserver:self];

Get firstresponder in Objective C

I am having trouble trying to figure out which UITextField is the current First Responder.
What I am trying to do is a set a boolean value if the user clicks in a particular UITextField. So to do that I need to be able to tell if this particular text field has become the first responder.
I know how to set the first responder but just not sure how to tell if a field has actually become the first responder.
[...] but just not sure how to tell if a field has actually become the
first responder.
UIView inherits the isFirstResponder method from UIResponder which tells you this.
The easiest way to find whatever the first responder is without checking individual controls/views and without multiple methods for all of the different types of possible first responders is to just make a category on UIView which adds a findFirstResponder method:
UIView+FirstResponder.h
#import <UIKit/UIKit.h>
#interface UIView (FirstResponder)
- (UIView *)findFirstResponder;
#end
UIView+FirstResponder.m
#import "UIView+FirstResponder.h"
#implementation UIView (FirstResponder)
- (UIView *)findFirstResponder
{
if ([self isFirstResponder])
return self;
for (UIView * subView in self.subviews)
{
UIView * fr = [subView findFirstResponder];
if (fr != nil)
return fr;
}
return nil;
}
#end
Why not using the UITextFieldDelegate and implement the textFieldDidBeginEditing: method? This will be called as soon as the textfield gains the focus.
UITextFieldDelegate Protocol Reference
if(![myTextField isFirstResponder])
{
// do stuff
}
I might do like this
Add property to ViewController
#property (nonatomic, strong) UITextField *firstRespondField;
Register TextField Notificaiton
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(textFieldTextDidBeginEditing:)
name:UITextFieldTextDidBeginEditingNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(textFieldTextDidEndEdition:)
name:UITextFieldTextDidEndEditingNotification
object:nil];
Dont forget remove it on Dealloc
[[NSNotificationCenter defaultCenter] removeObserver:self];
Then Implement 2 method
- (void)textFieldTextDidBeginEditing:(NSNotification *)aNotification {
self.firstRespondField = aNotification.object;
}
- (void)textFieldTextDidEndEdition:(NSNotification *)aNotification {
self.firstRespondField = nil;
}
Finally, I might get FirstResponder object by
self.firstRespondField
You can just call UIView.IsFirstResponder on the view you want to know if it's the first responder.

shaking on 3.0 in a UITableViewController

after reading some posts about implementing shaking on 3.0, I think I get the idea but I'm not getting any call to the:
motionBegan
motionEnded
motionCancelled
this is an example of what I've read:
how to detect and program around shakes for the iphone
I'm sure I've added the
[self becomeFirstResponder];
and the
-(BOOL)canBecomeFirstResponder {
NSLog(#"First responder");
return YES;
}
Should I enable a special delegate for those events ?
I understand that those events are controlled by the system, and they are passed to the first responder, and go on ...
any idea ?
thanks,
r.
I had loads of problems getting this to work and I finally gave up and followed jandrea's advice. He suggested subclassing UIWindow and implement the motionEnded there. This is a quote from his post here, look for it quite far down.
First, I subclassed UIWindow. This is
easy peasy. Create a new class file
with an interface such as MotionWindow
: UIWindow (feel free to pick your
own, natch). Add a method like so:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"DeviceShaken" object:self];
}
}
Change #"DeviceShaken" to the
notification name of your choice. Save
the file.
Now, if you use a MainWindow.xib
(stock Xcode template stuff), go in
there and change the class of your
Window object from UIWindow to
MotionWindow or whatever you called
it. Save the xib. If you set up
UIWindow programmatically, use your
new Window class there instead.
Now your app is using the specialized
UIWindow class. Wherever you want to
be told about a shake, sign up for
them notifications! Like this:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(deviceShaken) name:#"DeviceShaken" object:nil];
To remove yourself as an observer:
[[NSNotificationCenter defaultCenter] removeObserver:self];
Where do you call becomeFirstResponder? You should do it in viewDidAppear. Does this get fired?

detect when shakes an iPhone

i have questions regarding the shake detection that posted here before,
here is a reminder:
"Now ... I wanted to do something similar (in iPhone OS 3.0+), only in my case I wanted it app-wide so I could alert various parts of the app when a shake occurred. Here's what I ended up doing.
First, I subclassed UIWindow. This is easy peasy. Create a new class file with an interface such as MotionWindow : UIWindow (feel free to pick your own, natch). Add a method like so:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"DeviceShaken" object:self];
}
}
Now, if you use a MainWindow.xib (stock Xcode template stuff), go in there and change the class of your Window object from UIWindow to MotionWindow or whatever you called it. Save the xib. If you set up UIWindow programmatically, use your new Window class there instead.
Now your app is using the specialized UIWindow class. Wherever you want to be told about a shake, sign up for them notifications! Like this:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(deviceShaken) name:#"DeviceShaken" object:nil];
To remove yourself as an observer:
[[NSNotificationCenter defaultCenter] removeObserver:self];
questions:
where to put the notifications (i have a view based app) ?
do i have to remove myself as an observe, what does it mean ?
what is the if statement that i use to check if the shake accrued?
how can i know if the shake event know it is "already in progress" ?
thanks.
In iPhone OS 3.x it is simple to receive motion events form any view that is set as the first responder.
In you view class override the method motionEnded:, like this:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if(motion == UIEventSubtypeMotionShake && [self isViewLoaded])
{
//handle shake here...
}
}
In addition, you will need to become the First Responder when the view is loaded and appears:
- (void)viewDidAppear:(BOOL)animated
{
[self becomeFirstResponder];
[super viewDidAppear:animated];
//any extra set up code...
}
You may also have to respond to the canBecomeFirstResponder method.
- (BOOL)canBecomeFirstResponder
{
return YES;
}
These can be used in any object that inherits form UIView.