Switch

The old iOS 6 switch was wider and shorter:

Source code in Switch.zip

  1. main.m
  2. Class SwitchAppDelegate
  3. Class View

Create the project

SwitchAppDelegate.h has to import AVAudioPlayer.h before it can mention the word AVAudioPlayer. SwitchAppDelegate.h has to declare the method valueChanged: so that class View can mention this method.

MIDI was not on the list of supported formats, so I converted the file musette.mid to MP3 with this converter. Then I added the resulting file musette.mp3 to the project. (See America for adding a file to a project.)

Add the AVFoundation Framework to the project. (See Button for adding a framework to a project.)

The UISwitch control

The word switch is a keyword in Objective-C, so I had to name the switch mySwitch in View.h.

The UIButton in Button could be set to any desired size. But a UISwitch has a preferred size, 51 × 31 pairs of pixels (or 94 × 27 pairs of pixels in iOS 6), which I discovered as follows.

	NSLog(@"mySwitch.frame == (%g, %g), %g × %g",
		mySwitch.frame.origin.x,
		mySwitch.frame.origin.y,
		mySwitch.frame.size.width,
		mySwitch.frame.size.height
	);
We will stay with this preferred size, even though we could change it with the CGAffineTransformMakeScale we saw in Animate.
	mySwitch.transform = CGAffineTransformMakeScale(.5, .5);

I don’t want to hardcode the numbers 51 × 31 into the app, because Apple might change them in the future. The initWithFrame: method of class View creates a switch with the default size in the upper left corner of the View. Knowing the default size, we can then reposition the switch somewhere else, e.g., at the center of the View.

As usual, the target for the switch is designated with the method addTarget:action:forControlEvents. In this case, the target is the application delegate. We call the valueChanged: method of the application delegate when the switch changes from off to on or vice versa.

2013-11-06 15:37:51.011 Switch[3555:a0b] bundle.bundlePath ==
"/Users/myname/Library/Application Support/iPhone Simulator/7.0.3-64/Applications/41F08B1D-1942-4E11-BC76-1694AF2319F0/Switch.app"
2013-11-06 15:37:51.013 Switch[3555:a0b] filename ==
"/Users/myname/Library/Application Support/iPhone Simulator/7.0.3-64/Applications/41F08B1D-1942-4E11-BC76-1694AF2319F0/Switch.app/musette.mp3"
2013-11-06 15:37:51.013 Switch[3555:a0b] url ==
"file:///Users/myname/Library/Application%20Support/iPhone%20Simulator/7.0.3-64/Applications/41F08B1D-1942-4E11-BC76-1694AF2319F0/Switch.app/musette.mp3"
2013-11-06 15:37:51.079 Switch[3555:a0b] player.numberOfChannels == 2

2013-11-06 15:37:52.315 Switch[3555:a0b] Application windows are expected
to have a root view controller at the end of application launch

2013-11-06 15:37:54.575 Switch[3555:a0b] Playing at 0 of 25.0005 seconds.
2013-11-06 15:37:57.067 Switch[3555:a0b] Paused at 2.46821 of 25.0005 seconds.
2013-11-06 15:37:58.939 Switch[3555:a0b] Playing at 2.49465 of 25.0005 seconds.
2013-11-06 15:38:02.416 Switch[3555:a0b] Paused at 5.97166 of 25.0005 seconds.
2013-11-06 15:38:03.235 Switch[3555:a0b] Playing at 6.00093 of 25.0005 seconds.
2013-11-06 15:38:05.964 Switch[3555:a0b] Paused at 8.73048 of 25.0005 seconds.
2013-11-06 15:38:10.862 Switch[3555:a0b] Playing at 8.76408 of 25.0005 seconds.
2013-11-06 15:38:12.608 Switch[3555:a0b] Paused at 10.5101 of 25.0005 seconds.

The AVAudioPlayer and its documentation

Use the Audio Services in Button to play a short sound (a warning, Chinese sound effect, etc). Use an AVAudioPlayer object to play a long sound (Bach, Beethoven, Brahms). The application delegate is the target of the switch and the delegate of the audio player.

Johann Sebastian Bach (1685–1750)

The Musette in D Major (BWV Anhang 126) from the Notebook for Anna Magdalena Bach was the soundtrack for the holiday light show a few years back in Grand Central Terminal. PDF. MIDI.

Music preview

Things to try

  1. Remove the infinite loop. Play the file once and then stop:
    	player.numberOfLoops = 0;
    

  2. Now that we’re playing the file only once, put the switch back in the off position when the file has finished playing. Let the application delegate adopt the AVAudioPlayerDelegate protocol in the file SwitchAppDelegate.h.
    @interface SwitchAppDelegate: UIResponder <UIApplicationDelegate, AVAudioPlayerDelegate> {
    
    Now the application delegate is qualified to act as the delegate of the audio player. Insert the following statement into application:didFinishLaunchingWithOptions:.
    	//Let the application delegate be the delegate of the audio player.
    	player.delegate = self;
    
    Add the following method to class SwitchAppDelegate in the file SwitchAppDelegate.m.
    - (void) audioPlayerDidFinishPlaying: (AVAudioPlayer *) p successfully: (BOOL) flag {
    	if (p == player) {
    		[view.mySwitch setOn: NO animated: YES];	//Go back to the off position.
    	}
    }
    
    You will also have to let mySwitch be a property of class View.
    @property (strong, nonatomic) UISwitch *mySwitch;	//in View.h
    
    @synthesize mySwitch;	//in View.m
    

  3. Print the player’s dictionary of settings that describe the sound file. Immediately after
    		if (![player prepareToPlay]) {
    			NSLog(@"prepareToPlay failed");
    		}
    
    insert the following code. We saw the enumerator here. The format ID 778,924,083 in decimal is 2E6D7033 in hexadecimal. The four numbers 2E, 6D, 70, 33 are the ASCII codes of the four characters ".mp3". CHAR_BIT is the number of bits in a char. Why is the sample rate a perfect square (44,100 = 2102)?
    		NSEnumerator *e = [player.settings keyEnumerator];
    		NSString *key;
    
    		while ((key = [e nextObject]) != nil) {
    			if ([key isEqualToString: AVFormatIDKey]) {
    				const int i = ((NSNumber *)[player.settings objectForKey: key]).intValue;
    				NSLog(@"%@ %c%c%c%c", key,
    					i >> 3 * CHAR_BIT & 0xFF,
    					i >> 2 * CHAR_BIT & 0xFF,
    					i >> 1 * CHAR_BIT & 0xFF,
    					i >> 0 * CHAR_BIT & 0xFF
    				);
    			} else {
    				NSLog(@"%@ %@", key, [player.settings objectForKey: key]);
    			}
    		}
    
    2013-11-06 16:12:53.259 Switch[3682:a0b] AVFormatIDKey .mp3
    2013-11-06 16:12:53.260 Switch[3682:a0b] AVNumberOfChannelsKey 2
    2013-11-06 16:12:53.263 Switch[3682:a0b] AVEncoderBitRateKey 0
    2013-11-06 16:12:53.263 Switch[3682:a0b] AVSampleRateKey 44100
    

  4. Why do the AVAudioPlayer in this app and the and the MPMoviePlayerController in Video use such totally different mechanisms to inform the rest of the app that the media file has finished playing?