Gone uses the technology for playing a long sound, i.e., background music. This app uses the technology for playing a short sound i.e., a sound effect.
In iOS 6, a button had an outline. In iOS 7 there’s no outline, so I gave it a yellow color.
main.m
ButtonAppDelegate
creates, plays, and destroys the sound effect.View
contains the button.Add the AudioToolbox framework to the project. Select the project Button at the top of the Xcode Project Navigator. In the center pane of Code, scroll down to Linked Frameworks and Libraries. Press the plus sign, select AudioToolbox.framework, and press Add.
MP3 is on Apple’s list of
supported
audio formats.
Go
here
and save the file
China-sound--Lo-Fi-Preview-.mp3
onto your Mac desktop,
renaming it
chinese.mp3
.
Use the Macintosh command-I to make sure it’s really an mp3 file.
Warning: when saving this file in Safari, you must specify Page Source.
File →
Save As…
Export as: chinese.mp3
Format: Page Source
Save
If you don’t specify Page Source,
you’ll end up with a web archive file that
will cause the
AudioServicesCreateSystemSoudID
function to
return error code
–1500
(a.k.a. the notoriously unhelpful
kAudioServicesSystemSoundUnspecifiedError
).
Then
drag
chinese.mp3
into the Supporting Files folder in the Xcode Project Navigator.
Choose options for adding these files
Destination ☑ copy items into destiation group’s folder
(if needed)
Finish
The
initWithFrame:
method of the
View
creates the
button
and centers it in the
View
.
Our button has the most modest style,
rounded
rect.
The button’s title,
however,
is in flaming red,
set
in
initWithFrame:
and
retrieved
in the
touchUpInside:
method of the application delegate.
Each
“control
state”
of the button can have a different title.
Our title was the one for the
normal
state.
When the user presses and
releases
the button,
it can send any message to any object.
The object is called the
target
of the release.
A control such as a button can have many target objects,
and the target objects do not have to adopt any
protocol.
(Later we will see that a control can have only one
“delegate”
object,
and that the delegate must adopt a protocol.)
Let’s have the button send the
touchUpInside:
message to the
View
object.
To arrange this,
the
View
object passes three arguments to the button’s
addTarget:action:forControlEvents:
method.
The second argument is the expression
@selector(touchUpInside:)
,
called a
selector,
which specifies which method of the target should be called.
The first argument speciefies which object the method belongs to.
And the
UIControlEventTouchUpEventInside
is one of several possible
finger
events.
(Touch down or touch up?
Touch up inside or touch up outside?)
The selected method,
touchUpInside:
void
.
The method
touchUpInside:
could be the target of several different buttons.
To tell it which button was pressed,
the button is passed to it as an argument.
Since our app has only one button, we already know which one was pressed.
To prove that the button sent the
touchUpInside:
message to the application delegate,
the
touchUpInside:
method calls
NSLog
and can make the cellphone
vibrate.
But
NSLog
is too tame
and an iPod Touch cannot vibrate.
Let’s play a sound file instead.
The call to
AudioServicesPlaySystemSound
starts the sound file playing,
and returns without waiting for the sound to finish.
We must not call
AudioServicesDisposeSystemSoundID
immediately afterwards,
so I put the call to
AudioServicesDisposeSystemSoundID
in the
applicationWillTerminate:
method of the application delegate.
Now that
sid
is now mentioned in two methods,
it had to be an instance variable
rather than a local variable inside one of the methods.
I wanted to name it
id
,
but
id
is a keyword in the language Objective-C.
2014-06-08 21:33:25.217 Button[2998:60b] bundle.bundlePath == "/Users/myname/Library/Application Support/iPhone Simulator/7.1-64/Applications/D680F8B6-902D-4126-B19F-83390882387A/Button.app" 2014-06-08 21:33:25.220 Button[2998:60b] filename == "/Users/myname/Library/Application Support/iPhone Simulator/7.1-64/Applications/D680F8B6-902D-4126-B19F-83390882387A/Button.app/chinese.mp3" 2014-06-08 21:33:25.220 Button[2998:60b] url == "file:///Users/myname/Library/Application%20Support/iPhone%20Simulator/7.1-64/Applications/D680F8B6-902D-4126-B19F-83390882387A/Button.app/chinese.mp3" 2014-06-08 21:33:30.626 Button[2998:60b] The "Chinese sound effect" button was pressed.
buttonFontSize
method of class
UIFont
.
Make your buttons look like everyone else’s.
sizeWithFont:
NSString
that we saw in
Hello, World!.
addTarget:action:forControlEvents:
could be several
control
events
combined with “bitwise or”.
UIControlEventTouchDown | UIControlEventTouchDragEnter
First, let the button be a property of the
View
.
This will let the application delegate mention the button.
@property (strong, nonatomic) UIButton *button; //after } in View.h
@synthesize button; //after @implementation in View.m
Add the following statements to the
touchUpInside:
method of the application delegate
immediately before the call to
AudioServicesPlaySystemSound
.
We convert the argument
sender
from a very generic
id
to a very specific
“pointer to a
UIButton
”.
This conversion makes it possible for us to mention the
enabled
property of the button.
UIButton *button = sender; button.enabled = NO;Define the following function (which is not a method) in the
ButtonAppDelegate.m
file immediately above the
@implementation
ButtonAppDelegate
.
A
cast
is a conversion from one data type to another.
The
__bridge
cast
(with two underscores)
is necessary when converting from or to the data type “pointer to
void
”.
//Called when the sound has finished playing. static void complete(SystemSoundID sid, void *p) { NSLog(@"complete"); UIButton *button = (__bridge UIButton *)p; button.enabled = YES; }To have the above function called when the sound has finished playing, insert the following code into the
application:didFinishLaunchingWithOptions:
method of the application delegate
after creating the
view
.
The fourth argument of
AudioServicesAddSystemSoundCompletion
is the function to call,
which cannot be a method.
The fifth argument is the argument to be passed to that function.
error = AudioServicesAddSystemSoundCompletion( sid, NULL, NULL, complete, (__bridge void *)view.button); if (error != kAudioServicesNoError) { NSLog(@"AudioServicesAddSystemSoundCompletion error == %d", error); return YES; }To show that the button is disabled, make its text red and background yellow when enabled, faint pink and faint yellow when disabled. Hint:
setTitleColor:forState:
UIControlStateDisabled
.
View
.
When pressed,
the
View
will execute the
CGPathRelease
and
CGPathCreate
functions.