UIViewController visible callback - objective-c

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.

Related

Is There A Lost Focus Window Event in Objective C?

Coming from a .NET background I'm used to events getting fired so trapping a lost focus event is easy but I'm not sure how to do this in Obj-C. Basically I want my app to know when another application has gotten focus and it no longer has it so it can perform some actions.
Can you please tell me how I can implement this kind of functionality in Obj-C for an OSX app?
Have a look at the NSWindow notifications. Specifically, you're interested in NSWindowDidBecomeKeyNotification and NSWindowDidResignKeyNotification. You can also create a delegate for the window and implement its windowDidBecomeKey: and windowDidResignKey: methods, as noted in the NSWindowDelegate protocol documentation.
Or, if you just wanted to know when the application (not a window) has gained focus, you can subscribe to the NSApplicationDidBecomeActiveNotification. Likewise, NSApplicationDidResignActiveNotification will notify you when your app loses focus. These notifications are discussed more here. You can also implement applicationWillBecomeActive: and applicationWillResignActive: in the application delegate.
It's unclear if you want notification of a single window losing focus or notification of your entire app losing focus. My answer here provides notification for the entire application losing focus. (See mipadi's answer if you just want to know when one of your app's window loses focus.)
Observe the appropriate notification:
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(appDeactivated:)
name:NSWorkspaceDidDeactivateApplicationNotification
object:nil];
Then add the handler method:
-(void) appDeactivated:(NSNotification *)notification
{
NSRunningApplication* app = [notification.userInfo objectForKey:#"NSWorkspaceApplicationKey"];
if (app == [NSRunningApplication currentApplication]) {
// your cleanup code here
}
}

how to handle “sendDidFinish” in sharekit

I use sharekit with mail/twitter/facebook and I am really new to objective-c. sharekit works well and sends my images like it should.
in my app I have a screenshot function. I want the app to 'freeze' when a screenshot is taken, stopping to send any shake- or touch-event to the scene behind the sharekit-action.
in my screenshot-layer I have three buttons which call the shareItem-methods of their specified service, like
[SHKTwitter shareItem:item];
vereything works fine 'till here. but now when the sending is finished (or canceled or errored) I need the app to 'unfreeze', sharekit should tell my app that it is allowed to listen to any touch- or shake-action again.
I am sorry but I think I don't understand the concept of using the delegate here. I mean, is 'sendDidFinish' meant to be inside a delegate? and if so, how could I tell sharekit who is its delegate? or do I have to edit the send-service classes (like SHKItem or SHKFacebook) itself?
please don't downrate me for this question. I really want to get behind this mystery...
SHKTwitter inherit from SHKOAuthSharer, who inherit from SHKSharer. SHKSharer has a delegate protocol called "SharerDelegate".
So you can use an instance of SHKTwitter, then set it's delegate as :
shkTwitterInstance.shareDelegate = yourDelegateObject.
And implement the delegate method
- (void)sharerFinishedSending:(SHKSharer *)sharer;.
Try that.
EDIT (OTHER, AND MORE POPULAR, SOLUTION)
Also, you can suscribe your object to "SHKSendDidFinish" notification from SHKTwitter object.
[[NSNotificationCenter defaultCenter] addObserver:yourObject selector:#selector(theMethodthatYouWantToExecuteWhenTheNotificationIsRaised:) name:#"SHKSendDidFinish" object:shkTwitterObject];

How to activate a custom screensaver preview in Cocoa/Obj-C?

I have created a fairly simple screensaver that runs on Mac OS 10.6.5 without issue.
The configuration screen has accumulated quite a few different options and I'm trying to implement my own preview on the configureSheet window so the user (just me, currently) can immediately see the effect of a change without having to OK and Test each change.
I've added an NSView to the configureSheet and set the custom class in Interface Builder to my ScreenSaverView subclass. I know that drawRect: is firing, because I can remove the condition for clearing the view to black, and my custom preview no longer appears with the black background.
Here is that function (based on several fine tutorials on the Internet):
- (void)drawRect:(NSRect)rect
{
if ( shouldDrawBackground )
{
[super drawRect:rect];
shouldDrawBackground = NO;
}
if (pausing == NO)
[spiroForm drawForm];
}
The spiroForm class simply draws itself into the ScreenSaverView frame using NSBezierPath and, as mentioned, is not problematical for the actual screensaver or the built-in System Preferences preview. The custom preview (configureView) frame is passed into the init method for, um, itself (since its custom class is my ScreenSaverView subclass.) The -initWithFrame method is called in configureSheet before returning the configureSheet object to the OS:
[configureView initWithFrame:[configureView bounds] isPreview:YES];
Maybe I don't have to do that? It was just something I tried to see if it was required for drawing.
I eventually added a delegate to the configureSheet to try triggering the startAnimation and stopAnimation functions of my preview via windowWillBeginSheet and windowWillEndSheet notifications, but those don't appear to be getting called for some reason. The delegate is declared as NSObject <NSWindowDelegate> and I set the delegate in the configureSheet method before returning the configureSheet object.
I've been working on this for days, but haven't been able to find anything about how the OS manages the ScreenSaverView objects (which I think is what I'm trying to emulate by running my own copy.)
Does anybody have any suggestions on how to manage this or if Apple documents it somewhere that I haven't found? This isn't really required for the screensaver to work, I just think it would be fun (I also looked for a way to use the OS preview, but it's blocked while the configureSheet is activated.)
OK, there are a couple of 'duh' moments involved with the solution:
First of all, I was setting the delegate for the sheet notifications to the sheet itself. The window that the sheet belongs to gets the notifications.
Secondly, that very window that the sheet belongs to is owned by System Preferences, I don't see any way to set my delegate class as a delegate to that window, so the whole delegate thing doesn't appear to be a viable solution.
I ended up subclassing NSWindow for the configureSheet so that I could start and stop animation on my preview by over-riding the makeKeyWindow and close methods.
- (void) makeKeyWindow
{
if (myPreview != nil)
if ( ! [myPreview isAnimating])
{
[myPreview startAnimation];
}
[super makeKeyWindow];
}
I also had to add an IBOutlet for my preview object itself and connect it in Interface Builder.
Still working out a couple of issues, but now when I click on my screensaver Options button, my configureSheet drops down and displays its own preview while you set options. Sheesh. The hoops I jump through for these little niceties. Anyway, I like it. Onward and upward.

applicationWillTerminate does not get invoked

I want to save my data before terminating, so my AppControll class conforms the NSApplicationDelegate protocol, and declared the method; and in the interface builder I bound the window's delegate outlet to AppController, but I cannot get the method invoked.
Where I am wrong, what should I do?
Are you terminating the app from Xcode? Alternatively, is sudden termination enabled in your Info.plist?
Either of these will cause a SIGTERM signal to be sent to the application, terminating it immediately, with no chance for the NSApplication instance to send its delegate an applicationWillTerminate: message. (This is the point of sudden termination: Your app dies instantly. You can turn it off and on programmatically for times when this would be bad.)
Try quitting your application within itself (the Quit menu item in your Application menu), or using the Dock to quit it (right-click on your application's tile and choose “Quit”). As long as sudden termination is disabled (or never was enabled), either of these will cause your application object to send the applicationWillTerminate: message.
Also check that your delegate is getting sent other application-delegate messages, such as applicationWillFinishLaunching:, and make sure you hooked up the outlet in the correct nib (your MainMenu nib).
Did you remember to add the handler to the application?
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(applicationWillTerminate:)
name:UIApplicationWillTerminateNotification object:app];
Is multitasking still enabled? That could be the problem - tapping the home button doesn't cause applicationWillTerminate: to be called if the app goes into the background.
I'm assuming this question applies to macOS apps (as it mentions NSApplicationDelegate).
By default, Xcode 11 (and maybe earlier versions?) includes the NSSupportsSuddenTermination property in new applications' Info.plist file:
...
<key>NSSupportsSuddenTermination</key>
<true/>
...
This property is associated with the enableSuddenTermination and disableSuddenTermination pair of methods in the ProcessInfo (NSProcessInfo) class.
The relevant part of ProcessInfo documentation states:
Sudden Termination
macOS 10.6 and later includes a mechanism that allows the system to log out or shut down more quickly by, whenever possible, killing applications instead of requesting that they quit themselves.
Your application can enable this capability on a global basis and then manually override its availability during actions that could cause data corruption or a poor user experience by allowing sudden termination. Alternately, your application can just manually enable and disable this functionality.
In other words, when NSSupportsSuddenTermination is true, when the user tries to quit the application (directly or indirectly), macOS terminates it, instead of requesting it to quit. This bypasses any events that would otherwise be triggered during a regular quit request.
The good new is that you can either disable that in the Info.plist file, or manually override it, according to your application's needs, by calling ProcessInfo.processInfo.disableSuddenTermination().
In applicationWillFinishLaunching: add:
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(applicationWillTerminate:)
name:UIApplicationWillTerminateNotification object:nil];

What message is sent to a UIView when the application terminates?

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.