Press the switch and hear background music. See Switches in the Human Interface Guidelines.
AppDelegate
:
unchanged.ViewController
has all the audio code.
Added the
init(coder:)
whose parameter is an
NSCoder
,
and the
valueChaged(:)
method.
View
has the objects we see on the screen.
Added the
init(coder:)
whose parameter is an
NSCoder
.
Assets.xcassets
Contents.json
musette.dataset
Info.plist
Class
View
is a subclass of
UIView
.
The file
ViewController.swift
has to import the
AVFoundation
framework before it can mention the word
AVAudioPlayer
.
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
Assets.xcassets
as we added the photo in
America.
See
Switches
in the
UIKit
User Interface Catalog.
The word
switch
is a
keyword
in Swift (and in Objective-C),
so I had to name the switch
mySwitch
in
View.swift
ViewController.swift
.
The
UIButton
in
Button
could be given any desired
size.
But a
UISwitch
has a preferred size,
51 × 31 points on iPhone 6.
To discover these dimensions,
uncomment the
drawRect(_:)
method of the
View
and insert the following statements.
The dimensions of the
View
on iPhone 6s Plus are 414 × 736 points,
which explains where the
x,
y
coördinates come from (approximately):
(414 − 51) / 2 = 181.5
and
(736 − 31) / 2 = 352.5
print("frame = \(frame)"); //The switch was the last subview added to the view. let mySwitch: UISwitch = subviews[subviews.count - 1] as! UISwitch; print("mySwitch.frame = \(mySwitch.frame)");
frame = (0.0, 0.0, 414.0, 736.0) mySwitch.frame = (181.333333333333, 352.333333333333, 51.0, 31.0)We will stay with this preferred size, even though we could change it with the
CGAffineTransformMakeScale
we saw in
Animate.
//We elect not to do this. mySwitch.transform = CGAffineTransformMakeScale(0.5, 0.5);
I don’t want to hardcode the numbers 51 × 31 into the app,
because Apple might change them in the future.
I therefore created the
mySWitch
property without specifyings its size or position.
Then the
init(coder:)
method of class
View
centers the switch in the
View
and keeps it there.
As usual,
the target for the switch is designated with the method
addTarget(_:action:forControlEvents)
ViewController
object.
The application delegate concerns itself with lifecycle events;
the view object concerns itself with what you see on the screen.
The view controller therefore receives the audio code by process of elimination.
We call the
valueChanged(_:)
method of the view controller
when the switch changes from off to
on
or vice versa.
path = /Users/myname/Library/Developer/CoreSimulator/Devices/4248FDA4-DE7B-4D16-8A71-4BBF066C98F4/data/Containers/Bundle/Application/1CD514C6-9E56-4D59-9743-ACC712059E6D/Switch.app/musette.mp3 url = file:///Users/myname/Library/Developer/CoreSimulator/Devices/4248FDA4-DE7B-4D16-8A71-4BBF066C98F4/data/Containers/Bundle/Application/1CD514C6-9E56-4D59-9743-ACC712059E6D/Switch.app/musette.mp3 player!.numberOfChannels = 2 Playing at 0.0 of 25.0004535147392 seconds. Paused at 3.88814058956916 of 25.0004535147392 seconds. Playing at 3.91031746031746 of 25.0004535147392 seconds. Paused at 7.84843537414966 of 25.0004535147392 seconds.
Use the
System
Sound Services
functions in
Button
to play a short sound
(a warning,
beep,
explosion,
Chinese sound effect, etc).
Use an
AVAudioPlayer
object to play a long sound
(Bach, Beethoven, Brahms).
The
ViewController
is the target of the
switch
and will be the
delegate
of the
audio player.
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.
player!.numberOfLoops = 0; //No loops. Just play it once.
ViewController.swift
,AVAudioPlayerDelegate
protocol.
class ViewController: UIViewController, AVAudioPlayerDelegate {The view controller is now qualified to act as the delegate of the audio player. Insert the following statement into the code that configures the audio player.
//Let the view controller be the delegate of the audio player. player!.delegate = self;Let
mySwitch
be a
stored
property
of class
View
instead of a variable inside of
init(coder:)
.
Add the following method to class
ViewController
in the file
ViewController.swift
.//This method of the audio player's delegate object is called //automatically at the end of the sound file. func audioPlayerDidFinishPlaying(player: AVAudioPlayer, successfully flag: Bool) { //Go back to the off position. let mySwitch: UISwitch = (view as! View).mySwitch; mySwitch.setOn(false, animated: true); }
The conscientious programmer will also give the method
audioPlayerDecodeErrorDidOccur:error:
to the audio player delegate.
init(coder:)
method of the view controller immediately after the existing
if !player!.prepareToPlay() { print("player!.prepareToPlay failed"); }The format ID value is 778,924,083 in decimal and
2E6D7033
in hexadecimal.
The four bytes
2E
,
6D
,
70
,
33
are the
ASCII
codes of the four characters
".mp3"
.
The 8 is the number of bits in a
Character
;
I could have said
Int(CHAR_BIT)
instead of
8
.
The 3 is one less than the number of bytes in an
Int
.
Why is the sample rate a perfect square
let settings: [String: AnyObject]! = player!.settings; for (key, value) in settings { if key == AVFormatIDKey { let s: Int = value as! Int; var format: String = ""; for i in 3.stride(through: 0, by: -1) { format += String(UnicodeScalar(s >> (i * 8) & 0xFF)); } print("\(key): \(format)"); } else { print("\(key): \(value)"); } }
AVEncoderBitRateKey: 0 AVNumberOfChannelsKey: 2 AVFormatIDKey: .mp3 AVSampleRateKey: 44100
AVAudioPlayer
in this app,
the
MPMoviePlayerController
in
Video,
and the
AudioServicesPlaySystemSound
function in
Button
use such totally different mechanisms to inform the rest of the app
that the media file has finished playing?