Shake event detection in all view controllers - iphone

Is this possible in iOS, or do I really have to register a shake event detection in every single view controller?
I want that a user of my app can shake his iPhone to return to the root. Regardless of the current view controller.

I've done this by writing a category on UIViewController. This way you don't need to subclass anything, you can implement it even if the user interface has been finished already.
In my implementation, the VCs will respond to the shake unless the specific VC has opted out of the mechanism. Opting out is done by setting a BOOL ivar to NO in the specific implementation. The shake mechanism will look for this ivar using key-value-coding and ignore the shake if the ivar has been set.
I'd further refine this by allowing only the VC to respond which is currently visible.

edited post (old information was wrong)
in your case I would write my own ViewController which subclasses UIViewController, implements the motion-delegates of UIResponder and will then call the popToRootViewController on the navigationController-property of the ViewController. And everytimes you create a new ViewController you should subclass your ViewCOntroller and not UIViewController. So every ViewController is able to receive the shake-event but it is only written once in your code :)

This is just a guess, but maybe you could do it by subclassing UIApplication (not your app delegate, the actual application). UIApplication is a UIResponder, so you can make it the first responder, and provide a motionBegan or motionEnded method on it.

Related

Separating delegates from ViewController to own classes

I'm pretty new to iOs development and I've become a bit confused how should I separate my code to avoid a huge ViewController.m-file. Currently my main viewcontroller has quite a many delegates:
#interface ViewController : UIViewController <MKMapViewDelegate, HttpClientDelegate, CLLocationManagerDelegate, NSFetchedResultsControllerDelegate>
I would like to reduce the viewController code and I guess I should create separate classes to handle delegate tasks. The problem is that if I for example create singleton class for CLLocationManager and let it implement delegate methods, how do I then communicate with the view controller?
Let's say that my CLLocationManager receives a location update, how do I tell the viewController to make changes to the UI? Should I use NSNotificationCenter to post a notification and then add observer to the view controller? Or is the best way just to let viewController implement all delegate methods as it is now?
Move some of that functionality into your data model.
It's hard to say how you should manage this given the limited information you've provided, but one has to wonder whether a single view controller should really be managing a map view and keeping track of location and managing a HTTP connection and managing a Core Data fetch. Why not move some of that into your model, where it'll be somewhat easier to divide into modules?
Let's say that my CLLocationManager receives a location update, how do
I tell the viewController to make changes to the UI? Should I use
NSNotificationCenter to post a notification and then add observer to
the view controller?
A notification would be a good solution -- it provides a way for the part of your program that manages location (again, this probably belongs in the model) to communicate the change without having to know anything in particular about the parts of the program that care about changes to location. It also allows one-to-many communication -- if you have another view controller that also cares about location, it can observe the same notification.
Or is the best way just to let viewController implement all delegate methods as it is now?
I try to think about dividing responsibilities appropriately more than limiting the size of the code. If you have a class that does one job but needs a lot of code to do it, that's fine. If you have one class that manages many unrelated things, that's not so good. The trouble is that a lot of jobs seem to fall into the traditional "manages a screenful of content" role of a view controller. Try to separate the task of managing the presentation of the data (which is the view controller's rightful job) from managing the data itself (which is the model's job).
Implement a class responsible for delegate methods:
#interface DelegateManager : NSObject <MKMapViewDelegate, HttpClientDelegate, CLLocationManagerDelegate, NSFetchedResultsControllerDelegate>
-(id)initWithViewController:(ViewController*)vc;
#property (weak) ViewController *delegate;
#end
In your ViewController:
#interface ViewController : UIViewController
-(void)doSomething;
#end
In your ViewController, create an instance of DelegateManager with self as parameter. Set all your delegates' target to your DelegateManager. In the delegate methods of your DelegateManager, call [self.delegate doSomething]; to communicate back to your ViewController.

How to set the delegate to another ViewController?

I've recently started developing for the iPhone and so far I'm doing pretty good but there's this basic pattern I really don't seem to get.
Say, I have a TabBar with two views and a custom delegate protocol, thus my structure is the following:
AppDelegate.h/.m
myDelegateProtocol.h
FirstViewController.h/.m
SecondViewController.h/.m
MainView.xib
FirstView.xib
SecondView.xib
Now I want to achieve the following: I placed a button in the FirstView.xib and I'd like the IBAction which it invokes (inside FirstViewController ofc.) to send a message to the SecondViewController ([self.delegate tellSecondViewContrToSayHi]) and invoke another method which simply prints a log into the console saying "hi I'm here."
So far I know what I need to do in theory:
Specify the protocol.
Implement the protocol in the SecondViewController.
Create an id< myDelegateProtocol > delegate inside my FirstViewController,...AND last but not least:
Set the self.delegate = secondViewControllerObject.
Now, nr.4 is where the problem's at. How on earth do I link the delegate to the other viewController? I mean I'm not the one instantiating the views as the tabBar kinda does that for me,... any advise? Or am I just way too tired to notice a really stupid thing I did somewhere?
Theoretically the same question also applies to the target:action: thing,... I mean, how do I define the target?
Thanks a lot,
wasabi
You have the right idea, assuming that you want relatively tight coupling between these controllers via that delegate protocol.
Since neither controller knows about the other until that delegate property is set you need to have some object which has a reference to both of them wire up that relationship. In your case that's probably the application delegate which can create both controllers, set one as the delegate of the other, and pass both along to your tab bar controller.
What you might actually want is to have the app delegate give both controllers a reference to some shared model object. Your FirstViewController can update that model when you tap a button and your SecondViewController can observe changes to the model to update it's display (or just update its view when it appears based on the current model state). That way your controllers don't need to know anything about each other.

How to receive application wide events with multiview application

I am developing an iPhone application with multiviews (Nav controller), but i like to receive an event if user touches in any view of the view. I understand it can be done by subclassing application delegate? If that's true how can i do it? My requirement is, i like to receive an event as soon as user touches any where in any view within my application.
Thanks for your help and time.
Your reference to subclassing UIApplication will work. Read down through the comments and it covers a somewhat quirky IMO way to implement it (by having the AppDelegate be a subclass of UIApplication). Myself, I would create a separate class to be the UIApplication subclass, rather than having the app delegate do both jobs, but I see the merit of either way.
That said, this is a very large and unusual stick and may suggest a design failure. What problem are you solving with this?
A way to do it is to use a Singleton class (which acts as an observer/mediator), which the application is an example of, in which you have viewControllers subscribe to when they are intersted in the touch events of a certain view. When the touch occurs the Singleton class is informed of the event as a result it informs all subscribers to the event of the event.
Here is an example
#interface MyEventClass
{
-(void)TouchEventDidOccur;
-(void)subscribeToTouchEvent:(id)delegate selector(selector):sel
}
Above is the singleton class
now this is an example of what the view touchesBegan method might look like
-(void)touchesBegan...
{
[[MyEventClass sharedInstance] TouchEventDidOccur];
}
and how one would subscribe to the event
[[MyEventClass sharedInstance] subscribeToTouchEvent:self selector:#selector(receiveTouchEvent:)]
hope this helps
What's wrong with using notifications? If you have disconnected classes across your application, it's trivial to have them listen for a particular notification, then have your views or view controllers post that notification when a touch event happens. All of the observers will take action on the notification.

UIViewController not receiving touchesBegan message

I have a pretty simple UIViewController. It's initialized with a view I've created in Interaface Builder, which contains only a UIImageView. When the user touches the screen, I want the touchesBegan message of UIViewController to get called. So, I override it and added some logging, but nothing has happened.
I haven't done anything "special" at all, as since UIViewController inherits from UIResponder, I expect this to work right out of the box. From what I understand UIImageViews have user interaction disabled by default, so I have enabled it, both via InterfaceBuilder and in my UIViewcontroller's viewDidLoad method (I have tied the UIImageView to an IBOutlet). I also am ensuring that userInteraction is enabled in the parent view in Interface Builder.
Anything else that I am forgetting here?
OK, I'm a dummy. It works fine. The problem was, I didn't realize I was sending a release message to the UIViewController without having retained it elsewhere first. So that was causing the problem.
It is hard to say what your problem is, I don't know what you mean by overriding it?
Make sure you are connecting the touchesBegan event with an IBAction in Interface Builder?
You must create a IBAction function to handle the event, and explicitly connect the even to the IBAction. It is not as simple as simply overriding a method.
Although you have the View tied to an IBOutlet, you need to connect the event using an IBAction, or else you won't get any of those events.
Please ignore the first answer. It is, in fact, as easy as overriding the method in the custom view. The most common reason for not receiving touchesBegan is the canBecomeFirstResponder method not being implemented. This is not an IB hookup, these are the standard methods for touch handling.

More than 1 appDelegate object?

While fixing third-party code I've discovered a really brilliant idea) Guy was using 2 appDelegate objects in project xibs. I assume he thought that this would be some kind of singletone or such. But after some rethinking of that piece of code, I found that there is no technical restrictions on it.
Here is my example: simple project with navController and 2 views. Each with it's viewController. When app launched, first view is on screen. When user taps button, second view is pushed to navController. For now there is appDelegate object in MainWindow.xib. Now, if you'll add just the same appDelegate object to second view's xib. Now right when second view is pushed, you can see that one more instance of appDelegate is created and destroyed (if you'll override init and dealloc methods and insert log there).
Here I'm very surprised. Does it mean that only one appDelegte instance can be created? If yes, then why? appDelegate is just a NSObject subclass implementing UIApplicationDelegate protocol.
The appDelegate object created by xCode on every iphone project is the entry and exit point of an application. It does not make sence to have more than one instance of this class, if you do (besides perhaps some application settings being lost) which class does the applicaiton delegate to? Why can you only make one? Most probably this is because the class is implementing a Singleton patterns under covers so ensure only one instance of the app delegate is made, i bet that even when you try to alloc another one of these, the original app delegate is the only one kept. You can probably dig around the docs and find more info on apples site at http://developer.apple.com/iphone
UIApplicationDelegate is a protocol and doesn't have state in itself, thus there's nothing that prevents you to have several of them. Contrast this with UIApplication that has state and
provides sharedApplication singleton accessor
It should be totally possible to replace UIApplication's delegate property on the fly. I don't see much of the benefit, though.
I think what's happening here is that there is an instance of the AppDelegate class in the second Nib, but no other objects are retaining it. Therefore it gets created and immediately released. If you added a retained property to the view controller that connected to the AppDelegate, then it wouldn't get released immediately.
You can have multiple objects that implement the UIApplicationDelegate protocol, but it's not usually done because 90% of the behavior would be identical in all cases.
I think you could do something along these lines:
Note the old UIApplication.delegate.
Create your instance of UIApplicationDelegate with the old delegate as parameter.
Make sure to call the old delegate in each method you implement.
Make it return the old delegate in the - (id)forwardingTargetForSelector:(SEL)aSelector method.
Replace the [UIApplication sharedApplication].delegate with yours.
It replaces the original app delegate with your, making sure that the old delegate will still be called, especially if you did not override each and every method the UIApplicationDelegate protocol defines.