Possible to Subclass UILocalNotification and Change Default "Close" Button Text and Method? - objective-c

Searching for a way to change the "Close" button text/functionality of a UILocalNotification...
I've found that it's impossible to access/call the text/function from another object, although subclassing UILocalNotification should allow implementation method overrides... not to mention the creation of an accessor to get/set the "Close" button text field.
What do you guys think about this? What would Apple?
Has anyone tried?...
EDIT: 12/21/2011 12:01 PM
The question that I'm asking involves an understanding of oop: late/early binding, dynamic method lookup, and declared type vs. runtime type field and method handling.
Subclassing of UILocalNotification does work...
UILocalNotificationExampleSubclass * example = [UILocalNotificationExampleSubclass init];
...and the device does create an object, however, with type UILocalNotification and not UILocalNotificationExampleSubclass.
I'm looking for insight into the UILocalNotification.m file's methods.
If it does not have its own methods, what object (name please) takes an instance of a UILocalNotification, uses its fields, and displays the object (name please) we see on screen?

A UILocalNotification is just a storage for the notification's information. It does not perform anything.
Moreover your application does not display the notification. Another process does. So subclassing UILocalNotification is just useless.
EDIT at December 22nd, 17:53 UTC+1:
Yes, you can subclass UILocalNotification. But UILocalNotification is an abstract class and none of its properties is implemented. The alloc method is overridden so it returns an instance of UILocalNotification, a private subclass. That's why you cannot instantiate UILocalNotificationExampleSubclass.
But still, there is not point to subclass UILocalNotification because when you schedule a notification using -[UIApplication scheduleLocalNotification:] or present the notification immediately using -[UIApplication presentLocalNotification:], the operating system copies the notification.
That copy is stored in another process managed by the system, which uses its own private storage mechanism. A UILocalNotification is just a storage for a bunch of properties that is destined to get serialized and sent from the application to the operating system.
Now, we have that other process storing all the scheduled local notifications and waiting for a notification to fire. When that happens, that process will check if your application is in the foreground.
If your application is not in the foreground, that other process, which is totally out of our control, will create an alert and display the notification. We cannot customize that alert in any way, except by using the properties of the UILocalNotification class.
If your application is in the foreground, the notification will be sent back to the application that will create a new instance of UILocalNotification. Then, the UIApplication shared instance will access its delegate property and check if that delegate implements application:didReceiveLocalNotification:. If it does, you get the notification back and can do anything you want with that notification. For example, you may choose to display the notification using an alert view.
Configuring and displaying the alert view can be done like this:
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
UIAlertView *alertView =
[[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Alert", nil)
message:NSLocalizedString(notification.alertBody, nil)
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:NSLocalizedString(#"OK", nil), nil];
[alertView show];
[alertView release]; // unless your project uses Automatic Reference Counting
}
I hope this longer response did answer your question, if what I'm saying is true.

Related

Define a controller for NSDocument for document-based application

I'm not very sure how Document-Based Applications works.
I've created some actions for NSObject in the Mainmenu.xib. One of this is called when the user click on "File>new":
-(IBAction) newDocument:(id)sender{
Document* newDoc =[[Document alloc] init];
[[NSDocumentController sharedDocumentController]addDocument:newDoc];
[newDoc addWindowController: [[NSWindowController alloc] initWithWindowNibName:[newDoc windowNibName] owner:newDoc]];
[newDoc showWindows];
}
I've also this code inside the openDocument:(id) sender action that does the same but of course loading data to define the application workspace.
If I run the application it show a blank document without to call newDocument action. I don't know how to stop default blank document and to set newDocument: to be called.
Then if i do openDocument: too (so I've two documents, one blank and one not) and I do some operation on the second document it also replicate in the first blank one.
I've double check delegates, file owners, and also if the - (void)windowDidBecomeMain:(NSNotification *)notification return different pointers and all seem to be ok.
Probably I've not understood document based application work flow but I've read the Apple guide and other istructions. What do I miss?
An IBAction method is called, when the user did something. So this is not called from the system at app launch.
You can customize the behavior at app launch with -applicationShouldOpenUntitledFile: (NSApplicationDelegate) and – this is probably your next question – -applicationShouldHandleReopen:hasVisibleWindows: (NSApplicationDelegate). Changing the behavior in both cases is not recommended.
Looking to your action method, I see no reason, why you want to customize it.
A instance of your document class is created automatically.
You can create a window controller for it in your document subclass. This is documented.
Just let NSDocumentController do the work for you. What is the problem of the default behavior?
No. I thought to be confused instead the only problem was about releasing observer notification. When you call the close message for a NSDocument notification observers still persist. Working in ARC I miss this point.
So This is the solution at my issue. Thank you anyway.

Utility of notification 'UIApplicationDidEnterBackgroundNotification'

I am using Cordova 2.1.0 for IOS app development. Since, I am new to app development, I have a very basic question.
I am using applicationDidEnterBackground method to handle app control when app enters background. But i want to understand the utility of UIApplicationDidEnterBackgroundNotification which is sent when the app is entering the background. In what way can i use this notification and other notifications(like UIApplicationWillEnterForegroundNotification, etc.) sent by the system. What is the USP of these notifications.
According to the documentation, the method applicationDidEnterBackground: tells the UIApplication's delegate that the application is now in the background. In Cocoa, many delegate messages have corresponding UINotifications that are also sent. This is no exception.
According to the documentation:
The application also posts a UIApplicationDidEnterBackgroundNotification notification around the same time it calls this method to give interested objects a chance to respond to the transition.
Therefore, if there are objects in your object graph that need to respond to the state transition, they can observe this notification. I'm not sure there's really an unstated purpose beyond allowing all objects in the graph to respond to application state transition. I suppose if you had a long-running task to perform somewhere down the object hierarchy when the application transitions to background task you could use beginBackgroundTaskWithExpirationHandler: in a manner similar to what you do in the applicationDidEnterBackground.
EDIT:
// example, save NSArray *_myArray to disk when app enters background
// this is contrived, and untested, just meant to show how you can
// observe the UIApplicationDidEnterBackgroundNotification and save state
// in an arbitrary point in the object graph. (as opposed, or in addition to, the
// application's delegate.
// long-running tasks, e.g. web service connections, etc. will need to
// get a background task identifier from the UIApplication and manage that.
__block id enteredBackground = nil;
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
enteredBackground = [center addObserverForName:UIApplicationDidEnterBackgroundNotification
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
[_myArray writeToFile:#"/path/to/you/file" atomically:YES];
}];

UIViewController visible callback

I am developing an iOS application where need to do some stuff when I have Internet connection and other, when I haven't. If I haven't at some point I will show a message to the user to give me internet and come back. The question it is how to detect the following situation:
the user press the Home button twice, goes to multitasking , Settings and will connect to internet
the user comes back with multitasking to my app, but doesn't press anything
I know I will get callbacks to the AppDelegate:
- (void)applicationDidEnterBackground:(UIApplication *)application
- (void) applicationDidBecomeActive:(UIApplication *)application
but the code ( it is not started by me) it is very big, and I don't want to handle there the UIViewController needs, if there is any alternative.
My UIViewController's - (void)viewDidAppear:(BOOL)animated it isn't called when the user came back.
The breakpoint it is not hited for sure!
Any usable ideas, except in AppDelegate?
You can use the notification center to listen to applicationDidEnterBackground within the view controller:
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(handleEnteredBackground:)
name: UIApplicationDidEnterBackgroundNotification
object: nil];
Do this in viewDidLoad. Similarily for applicationDidBecomeActive.
Don't forget to remove yourself as an observer in viewDidUnload.
The application delegate is the correct place to be handling application state changes, but just because that is the case, it doesn't mean you must put all the logic that is triggered by the application state change in there.
Put the logic where it belongs. If it's networking code, that's not in the application delegate and it's not in the view controller, it's in a separate class. Then look into ways of tying the different parts of your application together. In most cases, notifications, KVO and the shared instance pattern are good approaches to take.

Prevent ARC from deallocating something that's not currently being used but will be soon?

I've run into this problem a couple of times and want to know the correct approach to take.
For an example, let's say I'm writing an iPhone app and I want a custom alert view class that uses blocks.
So I write the class, then later on in my code I go:
MyAlertView *alert = [MyAlertView alertWithBlahBlahBlah...];
[alert addButton:#"button" withBlock:^{ ... }];
[alert show];
Somewhere in the alert view class, we have
- (void)addButton:(NSString *)button withBlock:(void (^))block {
[_blocks setObject:[block copy] forKey:button];
}
- (void)show {
... drawing stuff ...
UIButton *button = ...
[button addTarget:self selector:#selector(buttonPressed:) ...];
...
}
- (void)buttonPressed:(id)sender {
((void (^)())[_blocks objectForKey:[sender title]])();
}
So, the alert view now shows up just fine. The problem is, if I tap a button, it attempts to send the buttonPressed: selector to the MyAlertView object that was displayed. The MyAlertView has, however, been removed from the superview at this time. ARC decides that because the alert view is not owned by anyone anymore, it should be deallocated, not knowing that a button needs to message it in the future. This causes a crash when the button is tapped.
What's the right way to keep the alert view in memory? I could make the MyAlertView object a property of the class that's using it, but that's kind of silly (what if I want to show two alerts at once?).
If an object were to remain in memory, and you do not have a reference to it, this is known as a memory leak. As I said in my comments, you need to keep some kind of reference to it so that a) it is not deallocated, b) you can send a message to it, and c) you can deallocate it before your class is deallocated.
The most obvious way to do this would be with a property in your class. Since you said that you don't want to do that (maybe you have a lot of them) then another possible solution would be to keep an array of cached objects that you plan on reusing and eventually deallocating.
I think you can use performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay to retain the alert view in runloop.
Actually, I just came across an wrapper implementation for UIAlertView using this skill.
Check UIAlertView input wrapper for more detail.
Quite simply, you're breaking the memory management rules. ARC doesn't change the rules, it just automates them. If you need an object to stay alive, it needs to have an owner. Every object in your app's object graph, all the way back to the application delegate, has an owner. It may not be obvious what that owner is (and sometimes the owner may be an autorelease pool), but there is one.
If you want this view to stick around, it needs to be owned by something, even if it's not "currently being used". If it's onscreen, it should be part of the view hierarchy. If it's not, the ideal owner is likely to be the object that created it.

strange behaviour from Mail and Message controller - pre initialised

I have pre allocated the mail and messaging controllers on startup in my app delegate to save the initialisation time (over 10 secs) when the user is using my application...
__mailController = [[MFMailComposeViewController alloc] init];
__messageController = [[MFMessageComposeViewController alloc] init];
It works fine the first time the controller is displayed then the next time the message is not changed and the old message is still displayed ?? ... Is it likely that the controller is being deallocated ??? Strange as the views work correctly just that the message is not correct ?
- (IBAction)actionSMS:(id)sender {
if([MFMessageComposeViewController canSendText])
{
self.messageController.body = self.MessageDetail.text;
// controller.recipients = [NSArray arrayWithObjects:#"+919999999999", nil];
[self presentModalViewController:self.messageController animated:YES];
}
}
Once MFMailComposeViewController and MFMessageComposeViewController are presented to the user you can't make changes to the content they display.
MFMailComposeViewController Class Reference:
Important The mail composition interface itself is not customizable
and must not be modified by your application. In addition, after
presenting the interface, your application is not allowed to make
further changes to the email content. The user may still edit the
content using the interface, but programmatic changes are ignored.
Thus, you must set the values of content fields before presenting the
interface.
That means those values are somehow locked in the implementation of the MFM*ViewController at the moment you present the controller. So you can't reuse these viewControllers. iOS doesn't care if the controller is, like in your case, invisible or not. If it is presented the content is locked.
I would figure out why it takes 10 seconds to allocate them. And then dump that whole pre-allocation thingie. 10 seconds are definitely to much.
I had the same problem. Not only are the MF controllers only good for one time use, as you discovered, they also cannot be init-ed in the background because their UI elements need to be init-ed in the main thread.
In the end, I just present a UIActivityIndicatorView over a HUD, so the users will know the app is responding.