Notification Center


In the simulator, select
Hardware → Rotate Left (to get to the Rolling Stones)
Hardware → Rotate Right (to go back to the Beatles)
Hardware → Home (to silence the music)

We will need the notification center to play a video. See Notification Programming. This app is a simple example of how to use the center.

We saw the device object in exercises 6 and 8 of Hello. The device object can send out notification objects when its orientation changes (portrait to landscape). The particular type of notification that it sends out is called a UIDeviceOrientationDidChangeNotification. This is an expensive process, so we turn it on only when the app is in the foreground. And the only object in our app that knows when the app is in the foreground is the application delegate.

By themselves, the notifications sent out by the device do not get very far. But the notification center object would be willing to carry them to objects that are interested in receiving them. Our view controller is one such interested object.

The view controller calls the method addObserver(_:selector:name:object) to ask the notification center to carry notifications of type UIDeviceOrientationDidChangeNotification from the device object to the deviceOrientationDidChange method of its own self (i.e., of the view controller). Configuing the notification center is therefore similar to the way we configured a gesture recognizer in Swipe: we specify what method of what object should be called when something happens.

When each notification arrives, it is passed as parameter to the deviceOrientationDidChange method of the view controller. We can name this method whatever we want, but it must take one parameter of type NSNotification. This parameter might carry some useful information in its userInfo property, but our simple deviceOrientationDidChange method does not take advantage of this. It merely pauses one AVAudioPlayer and starts playing another. The view controller contains an array of two of these players.

deviceOrientationDidChange also displays a one-line text on the screen. But this app is not much interested in its visual user interface. It is intended to demonstrate notification passing.

Source code in Notify.zip

  1. Class AppDelegate. Generating device orientation notifications is expensive. Continuous music gets to be annoying. We turn these features on only when the app enters the foreground. We turn them off as soon as the app leaves the foreground.
  2. Class ViewController
  3. This app has no View.swift file. The view controller’s view is an off-the-shelf UILabel.
  4. Beatles.mp3: “She Loves You” (1963)
  5. Stones.mp3: “(I Can’t Get No) Satisfaction” (1965)

Create the app

Gone was our first example of background music. Find music on You Tube and convert it to mp3 format with http://www.youtube-mp3.org/. Drag the mp3 file(s) into the Supporting Files folder in the Xcode Project Navigator. Check the “copy items” checkbox.

The view created by this app’s view controller is a white UILabel that fills the entire screen. To tell the view controller to create a view of class UILabel, select the Main.storyboard file in the Xcode Project Navigator. In the left pane of the center panel, open the View Controller Scene as far as the View in the following list, and select the View.

▼View Controller Scene
   ▼View Controller
      Top Layout Guide
      Bottom Layout Guide
      View
In the right panel of Xcode, click on the Identity Inspector icon. It’s a rectangle with a smaller rectangle in its upper left corner.
Custom Class
Class: UILabel
Module: (leave it blank)

When you run the app, make sure the Mac’s sound is turned on.

The big picture

  1. The view is the object of the class View in View.swift that is a subclass of class UIView. The view should concentrate on things you can see: foreground color and background color, width and height, left justify vs. right justify, Roman vs. Italic.

  2. The application delegate is the object of class AppDelegate in AppDelegate.swift that adopts the protocol UIApplicationDelegate. The application delegate should concentrate on the events of the app’s lifecycle: birth and death, uncover and cover, spring to life and stop moving. Until now, we have written no code in the application delegate. But if your app does something that requires a lot of electricity and processing power, you can stop doing it in the stop moving method and start doing it again in the spring to life method. See the endGeneratingDeviceOrientationNotifications and beginGeneratingDeviceOrientationNotifications in the application delegate of this app.

  3. The view controller is the object of class ViewController in ViewController.swift that is a subclass of class UIViewController. For the time being, all other code (e.g., opening and playing an audio or video file) should go in the view controller by process of elimination. In the future we might have more than one view controller, or we might have additional objects such as a model. But these things haven’t happened yet, so for the time being please put all miscellaneous code into the view controller.

In exercise 3 of Hello, we saw that we could get our hands on (i.e., create a variable that refers to) the screen object by saying

		let screen: UIScreen = UIScreen.mainScreen();

In exercise 6 of Hello, we got our hands on the device object by saying

		let device: UIDevice = UIDevice.currentDevice();

In Gone, we got our hands on the bundle object by saying

		let bundle: NSBundle = NSBundle.mainBundle();

Similarly, in any method of any class, we can get our hands on the application object by saying

		let application: UIApplication = UIApplication.sharedApplication();

The application object can then be used to get our hands on the application delegate object, which can be used to get our hands on the window object, which can be used to get our hands on the view controller object, which can be used to get our hands on the view object. Of course, we can do this only after all of these objects have been created.

		let application: UIApplication = UIApplication.sharedApplication();
		let appDelegate: AppDelegate = application.delegate! as AppDelegate;
		let window: UIWindow = appDelegate.window!;
		let viewController: ViewController = window.rootViewController! as ViewController;
		let view: View = viewController.view as View;

		//We can go even farther down.

		if view.subviews.count > 0 {
			//The view has at least one subview.
			let lastSubview: UIView = view.subviews[view.subviews.count - 1] as UIView;
		}

		if view.subviews.count > 1 {
			//The view has at least two subviews.
			let nextToLastSubview: UIView = view.subviews[view.subviews.count - 2] as UIView;
		}

The init method of the class View in Button could have used the first four statements of the above code to get its hands on the view controller. But it didn’t need to, because it received the view controller as a parameter.

Things to try

  1. Look at the other types of notifications that the device object can send out to interested parties:
    1. UIDeviceBatteryLevelDidChangeNotification
    2. UIDeviceBatteryStateDidChangeNotification: unplugged, charging.
    3. UIDeviceProximityStateDidChangeNotification: is it next to a human being’s ear?

  2. The next app will show that a MPMoviePlayerController object can send out a MPMoviePlayerPlaybackDidFinishNotification when it reaches the end of a video file.

  3. In exercise 12 of TextField, some object sends out a UIKeyboardWillShowNotification when the keyboard slides up from the bottom of the window. There’s also a UIKeyboardDidHideNotification.