I am trying to locate a message which I can override and then save changes to my application.
On MainWindow.xib I have placed a UIView and set its class (in interface builder) to be my Custom view TouchDrawView.
In TouchDrawView I have a bunch of code for handling touch events and 2 arrays which track these touch events.
My application is launched by the AppDelegate but it has no reference to this TouchDrawView. It simply launches the application.
What I want to do is save my 2 arrays when the application terminates - I can do this in the TouchDrawView but I don't know what message this UIView gets sent when the whole application is about to terminate and I can't do it in the AppDelegate because it doesn't have a reference to the 2 array or the custom UIView
UIView instances will not get send any messages when the app will terminate.
There's another easy way to get notified of app state changes: notifications. You can register for notifications sent through [NSNotificationCenter defaultCenter].
For older versions of iPhone OS there's a UIApplicationWillTerminateNotification. Beginning from iOS 4 you should also listen for UIApplicationDidEnterBackgroundNotification, to prepare for termination.
The short, unhelpful answer is that a UIView is not sent a message when the application terminates.
You need to think in terms of Model-View-Controller because that's how Cocoa and Cocoa Touch are designed. Of those three, if a message was directly sent to any it would be the controller. In your case that would be the UIViewController that talks to your view.
The bad news is there is no such message there either.
An application shuts down, not an individual screen/view.
There are (at least) two ways of doing what you want to do. Firstly, you could save the state of the view controller when it is released. (That kind of feels wrong to me.)
They way that I do it in my app is to listen for the applicationWillTerminate message in your UIApplicationDelegate and traverse the view hierarchy and save the state of each view controller. When the system starts up you can do the opposite. I blogged about it here.
Related
I'm trying to read the GCController.controllers() array after my app has launched to know which controllers were already connected to the AppleTV at app launch. But GCController.controllers().count is 0 until some point after viewDidAppear gets called on my initial UIViewController. Does anyone know the definitive point by which you can check GCController.controllers() to know that it has been populated with the currently connected controllers?
I am aware of the need to register for controller connection notifications with;
NSNotificationCenter.defaultCenter().addObserver(self, selector: "handleControllerDidConnectNotification:" , name: GCControllerDidConnectNotification , object: nil)
But that is for later after launch. First I need to know which ones are already connected. Anyone know?
You can call startWirelessControllerDiscoveryWithCompletionHandler on viewDidLoad and then check GCController.controllers() on viewWillAppear that seem to work for the game app I just finished.
Docs:
After your app has finished launching, the operating system
automatically creates a list of connected controllers. Call the
controllers class method to retrieve an array of GCController objects
for all connected controllers. Next, use these objects to configure
the controllers or read the controller’s inputs. If there are no
connected controllers or you call this method while your app is
launching, the array will be empty.
GCController will generate GCControllerDidConnectNotification notifications for each controller, including those connected to the device prior to launch. If you're not getting notifications for already-connected controllers, confirm the following:
Double-check that it is paired and turned on
Make sure it is a MFi controller.
I come from a .NET web application background and have just started iOS development. The initial design of my app focuses around the NSNotificationCenter. I was reasonably happy with this until I saw various posts explaining how reaching for the NSNotificationCentre was a common newbie mistake.
Here is a simplified version of the problem I am trying to address:
My application is trying to show a list of messages that are populated using web service calls, think Facebook messaging.
When the app is first loaded it pulls a collection of messages from the server and displays them in a table to the user. The user can add new messages (which get sent back over the API) and the app can receive Push Notifications about new messages which are added to the feed.
The messages are never persisted to disk so I'm just using POCOs for the model to keep things simple.
I have a MessageFeedController which is responsible for populating the message feed view (in a storyboard). I also have a message feed model, which stores the currently retrieved values and has various methods:
(void) loadFromServer;
(void) createMessage: (DCMMessage*) message;
(void) addMessage: (DCMMessage*) message;
(NSArray*) messages;
(int) unreadMessages;
The current implementation I have is this:
Use case 1 : Initial Load
When the view first appears the "loadFromServer" method is called. This populates the messages collection and raises an NSNotificationCenter event.
The controller observes this event, and when received it populates the tableview
Use Case 2: New Message
When a user clicks the "add" button a new view appears, they enter their message, hit send and then the view is dismissed.
This calls the createMessage method on the model, which calls the API
Once we have a response the model raises the NSNotificationCenter event
Once again the MessageFeedController listens for this event and re-populates the table
Use Case 3: Push Message
A push notification is received while the app is open, with the new message details
The AppDelegate (or some other class) will call the addMessage method on the model, to add it to the collection
Once again, assuming the MessageFeed view is open, it re-populates
In all three cases the MessageFeed view is updated. In addition to this a BadgeManager also listens to these events which has the responsibility of setting the app icon badge and the tabbar badge, in both cases the badge number relates to the number of unread messages.
It's also possible that another view is open and is listening to these events, this view holds a summary of messages so needs to know when the collection changes.
Right, thanks for sticking with me, my question is: Does this seem like a valid use of NSNotificationCentre, or have I misused it?
One concern I have is that I'm not 100% sure what will happen if the messages collection changes half-way through re-populating the message table. The only time I could see this happening is if a push notification was received about a new message. In this case would the population of the table have to finish before acting upon the NSNotification anyway?
Thanks for your help
Dan.
In other words, you're posting a notification whenever the message list is updated. That's a perfectly valid use of NSNotificationCenter.
Another option is to use Key-Value Observing.
Your controller (and anyone else) can register as an observer to the "messages" property, and will be notified whenever that property changes. On the model side, you get KVO for free; simply calling a method named setMessages: will trigger the KVO change notification. You can also trigger the notification manually, and, if so desired, the KVO notification can include which indexes of the array have been added, removed, or changed.
KVO is a standardized way to do these kinds of change notifications. It's particularly important when implementing an OS X app using Cocoa Data Binding.
NSNotificationCenter is more flexible in that you can bundle any additional info with each notification.
It's important to ensure that your messages list is only updated on the main thread, and that the messages list is never modified without also posting a corresponding change notification. Your controller should also take care to ignore these notifications whenever it is not the top-most view controller or not on screen. It's not uncommon to register for change notifications in viewWillAppear: and unregister in viewWillDisappear:.
In my opinion using a delegate protocol pattern would be a much better fit for this scenario. Consider the scenario where your "api layer" needs used across many view controllers in an application. If another developer were to be introduced to your code, they would have to hunt around for notificationcenter subscriptions instead of just following a clean 'interface' like protocol.
That being said, your code will work just fine and this is a valid use of notification center. It is just my personal preference for 'cleaner' code to use a protocol based approach. Take a look around in the iOS SDK itself and you will see scenarios where Apple themselves use protocols and use notifications. I feel it is much more easy to comprehend and use the protocols than having to dig around and determine what I must listen to for a notification.
NSNotifications run the receivers code synchronously as soon as they are posted, so a new message during repopulation would join the back of that execution queue. On the whole it seems valid to me, and it keeps a reasonable degree of separation between The view controllers and the model.
Depending on the number of classes that are likely to want to listen for the same information arriving, you may want to use a delegate pattern, maybe keeping an dictionary of delegate objects, but I personally don't feel as though this scales so well, and you also have to take care of nil-ing out delegates if a page is dealloced to avoid crashes. To sum up, your approach seems good to me.
I have subclassed RMMessageComposeViewController : MFMessageComposeViewController. The extra functionality that I'm aiming for is for the MFMessageComposeViewController to be able by itself to present a new message compose controller (over itself).
So I should from one RMMessageComposeViewController instance present a new one. The message result from the new instance should be sent to the parent (or "old" one). So I suppose I need to set the parent message compose controller as the delegate when I'm creating the child ("new" one).
Could someone please help me think this out, what instance variables I need to add (parents, children?) How to setup the child message compose controller?
From the docs:
The message composition interface itself is not customizable and must not be modified by your application. In addition, after presenting the interface, your application is unable to make further changes to the SMS content.
What you're trying to do there is explicitly not supported because of security concerns: It would make it easy for an application to forge messages. While you can probably push a view on top of it, I suspect your app would get rejected from the App Store for doing it.
I wouldn't be surprised if MFMessageComposeViewController prevents an application from creating more than one instance at a time, though I haven't confirmed this.
I've built an iOS 5 iPad app which makes use of a second screen. We have an admin view (on the iPad) and an external view through an HDMI enabled TV connected via the Apple DVI adapter. Both the iPad view and the TV view get the same data updates from a service call which is made every few seconds. We then present the data received as a series of charts; the charted data is presented very differently for the TV and iPad views - but the core dictionary of data is the same. I'm wondering about an elegant way to architect this solution. At the moment I have one of the view controllers (the admin iPad VC) doing the service calls using GCD and then dispatching NSNotifications which update the data (charts) properties on the other (TV) view controller. I'm considering moving the service calls away from the VC and creating a singleton which is initialized in the app controller. I then (somehow) set the two VCs as delegates and they get updated using a simple protocol. I'm not entirely sure if this is a good approach or if I should consider something else? Can I even set both VCs as the delegates of another class or is it typically only one delegate per class instance?
Thanks for any input.
Ben
Why not abstract the chart data into its own model class, which you can share in both view controllers? The model class can be responsible for fetching the new data. To make the controllers aware of updates, they can either use KVO on the model object, or they can observe notifications sent from the model object when an update occurs, or you can have an array of delegates for the model object and each view controller can be a delegate.
There doesn't seem to be any compelling reason to make it a singleton, although you can if you really want.
A question about didReceiveMemoryWarning / viewDidUnload.
If my app has many view controllers, one of them is shown, and the others back (because of I use a navigation controller or tab bar controller, it does not matter), which view controllers will receive didReceiveMemoryWarning / viewDidUnload, all of them, only hidden, or only shown?
Is it possible that shown VC receives didReceiveMemoryWarning but not viewDidUnload (because as is shown, it doesn't make any sense).
By the way, I have these questions after seeing this diagram:
Thanks a lot for help.
First, there are two methods didReceiveMemwarnings:
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
is called when the application receives a memory warning from the system.
and UIViewController's
- (void)didReceiveMemoryWarning
Sent to the view controller when the application receives a memory warning.
Second, firstly is called the code in these methods(well, of course), then in those controllers which don't have superviews(i.e those which are not displayed at the moment) the view is deleted and viewDidUnload is sent
When a memory warning is received, it is received at application level, all your viewControllers and appdelegate receives it.
It is not necessary that viewDidUnload is called for any or all controllers. It is strictly dependent on how critical OS thinks that memory warning is. Like first time - your app receives a Level 1 warning then Level 2 and most likely at third time (in short interval) OS will terminate the application believing it has gone to unstable state.