When you press the button, the text will flash and a sound will play. See Buttons in the iOS Human Interface Guidelines. A button is the simplest example of a control.
AppDelegate.swift
:
unchanged.ViewController.swift
:
added the property
sid
and the
init(coder:)
that takes a
NSCoder
;
added
touchUpInside(_:)
.
Import
AudioToolbox
.
View.swift
has the objects we see on the screen.
Added the
init(coder:)
that takes an
NSCoder
.
To specify the button’s border color,
we have to go down into the
layer
of software beneath the button.
(We did this in
Pinch.)
This layer uses
CGColor
s
(“Core
Graphics”)
instead of
UIColor
s
(“User
Interface”).
Main.storyboard
:
changed the class of the view controller’s
UIView
to my class
View
.
Assets.xcassets
Contents.json
chinese.dataset
Info.plist
:
unchanged.
The
documentation
for the function
AudioServicesPlaySystemSound
demands a
.caf
,
.aif
,
or
.wav
file that is no longer than 30 seconds,
but it successfully played
chinese.mp3
.
Other functions may have a wider list of
supported
audio formats.
To get a sound file,
point your Safari at
this
page,
control-click on the green word Preview on the right,
and download the file
onto your Macintosh Desktop,
renaming it
chinese.mp3
.
To make sure that the file you downloaded is an intact
.mp3
file,
select it and type control-i (the “information” command
in the Macintosh Finder).
In the information window,
open
▼General to make sure that
chinese.mp3
is an MP3 audio file
occupying a healthy number of bytes (not zero bytes!);
and
▼More Info to make sure its duration is no longer than 30 seconds.
To be totally sure that the file you downloaded is an intact
.mp3
file,
try playing it.
In previous versions of iOS
we had to convert
chinese.mp3
to
.wav
,
but this is no longer necessary.
Drag
chinese.mp3
into the set list of
Assets.xcassets
,
as in
America.
A view should contain only low-level, visual code: foreground colors, background colors, x and y coördinates. On the other hand, the application delegate should concern itself only with high-level lifecycle events: initialization and termination, entering the foreground and background. In between these levels, the working intelligence of the app—in this case, the audio code—should go on the view controller. Later, we’ll have another component: the “model”.
On iPhone 6s Plus simulator:
path = /Users/myname/Library/Developer/CoreSimulator/Devices/4248FDA4-DE7B-4D16-8A71-4BBF066C98F4 /data/Containers/Bundle/Application/EDEB4AEB-A42F-4766-B045-F5B88936F775/Button.app/chinese.mp3 url = file:///Users/myname/Library/Developer/CoreSimulator/Devices/4248FDA4-DE7B-4D16-8A71-4BBF066C98F4 /data/Containers/Bundle/Application/EDEB4AEB-A42F-4766-B045-F5B88936F775/Button.app/chinese.mp3 sid = 4097 touchUpInside(_:) title = Chinese sound effect
On iPod Touch with iOS 9.3.3:
path = /var/containers/Bundle/Application/51788FC8-4112-47FC-B833-5D884F4CDA39/Button.app/chinese.mp3 url = file:///var/containers/Bundle/Application/51788FC8-4112-47FC-B833-5D884F4CDA39/Button.app/chinese.mp3 sid = 4097 touchUpInside(_:) title = Chinese sound effect
titleLabel
is the
UILabel
inside the button.
let fontSize: CGFloat = UIFont.buttonFontSize(); button.titleLabel!.font = UIFont.systemFontOfSize(fontSize);
sizeWithAttributes(_:)
in
Hello.
let s: String = "Chinese sound effect"; let attributes: [String: AnyObject] = [NSFontAttributeName: button.titleLabel!.font]; var textSize: CGSize = s.sizeWithAttributes(attributes); textSize.width += UIFont.buttonFontSize(); //Add some padding. textSize.height += UIFont.buttonFontSize(); button.bounds.size = textSize;
UIButtonType.System
,
try
UIButtonType.DetailDisclosure
or
UIButtonType.ContactAdd
.
These types of buttons have no
titleLabel
.
addTarget(_:action:forControlEvents:)
could be an
array
of two or more
control
events:
[UIControlEvents.TouchDown, UIControlEvents.TouchDragEnter]or
[.TouchDown, .TouchDragEnter](
.TouchDragEnter
didn’t work for me.)
AudioServicesCreateSystemSoundID(_:_:)
fails,
print the name of the error instead of just the status number.
In the
viewDidLoad()
method of the
view
controller,
change
print("could not create system sound ID, status = \(status)");to the following.
let d: [OSStatus: String] = [ //a dictionary kAudioServicesNoError: "kAudioServicesNoError", kAudioServicesUnsupportedPropertyError: "kAudioServicesUnsupportedPropertyError", kAudioServicesBadPropertySizeError: "kAudioServicesBadPropertySizeError", kAudioServicesBadSpecifierSizeError: "kAudioServicesBadSpecifierSizeError", kAudioServicesSystemSoundUnspecifiedError: "kAudioServicesSystemSoundUnspecifiedError", kAudioServicesSystemSoundClientTimedOutError: "kAudioServicesSystemSoundClientTimedOutError" ]; var s: String? = d[status]; if s == nil { s = "unknown error \(status)"; } print("could not create system sound ID, status = \(s!)");How could you test this code?
init(coder:)
method of the
View
after setting the color and title for control state
normal:
//A low alpha level will make the title dim when disabled. let disabledColor: UIColor = UIColor(red: 1, green: 0, blue: 0, alpha: 0.2); button.setTitleColor(disabledColor, forState: UIControlState.Disabled);Add the following statement to the start of the
touchUpInside(:)
method of the
view
controller.
button.enabled = false; //Make the button unresponsive to touch.
Append the following to the
viewDidLoad()
method of the
view
controller.
When the playback of the audio file has completed,
we will execute the
closure
which is the fourth parameter of
AudioServicesAddSystemSoundCompletion
.
This closure must be of type
AudioServicesSystemSoundCompletionProc
.
The fifth parameter will be passed as the second parameter of the closure.
//The button is the subview most recently added to the view. let button: UIButton = view.subviews[view.subviews.count - 1] as! UIButton; //Store the address of the button into an UnsafeMutablePointer. let unmanagedButton: Unmanaged<UIButton> = Unmanaged<UIButton>.passRetained(button); let opaquePointer: COpaquePointer = unmanagedButton.toOpaque(); let unsafeMutablePointer: UnsafeMutablePointer<Void> = UnsafeMutablePointer<Void>(opaquePointer); let status: OSStatus = AudioServicesAddSystemSoundCompletion( sid, nil, nil, {(soundID: SystemSoundID, clientData: UnsafeMutablePointer<Void>) -> Void in //Arrive here when the sound file has finished playing. //Extract the button from the UnsafeMutablePointer. let opaquePointer: COpaquePointer = COpaquePointer(clientData); let unmanagedButton: Unmanaged<UIButton> = Unmanaged<UIButton>.fromOpaque(opaquePointer); let button: UIButton = unmanagedButton.takeRetainedValue(); button.enabled = true; }, unsafeMutablePointer ); if status != kAudioServicesNoError { print("could not add system sound completion, status = \(status)"); }
deinit { if sid != 0 { let status: OSStatus = AudioServicesDisposeSystemSoundID(sid); if status != kAudioServicesNoError { print("could not dispose of system sound, status = \(status)"); } } }
View
.
When pressed, the button will call the
clearPath
method of the
View
.