app.addEventListener('dom-change', function() {
console.log('Our app is ready to rock!');
var pages = document.querySelector("#pages");
var next = document.querySelector("#next");
next.addEventListener("click", function(){
// choose an option here
pages.selectNext();
pages.selected = 2;
pages.selected = pages.selected +1;
});
});
这就是它在控制台上的说法:
可选(文件:///用户/大卫/库/开发商/ CoreSimulator /设备/ 12D7F78E-5FFA-4964-9AEE-F395D962264F /数据/容器/数据/应用/ A5B7545C-2800-495C-B016-07C3DF010D97 /文件/ my_audio.wav) 致命错误:在解包可选值时意外发现nil
答案 0 :(得分:0)
TOMCORP,你的代码充满了新手错误。有些人直接负责崩溃,有些人迟早会出现问题,有些人只是无法跟上良好的编程习惯。
所有内容都在评论中解释。
如果没有别的东西可以理解" MAJOR ERROR 2"因为这与ViewController生命周期有关。在segue标识符上获取大写错误也是一个严重的错误。
顺便说一下,似乎没有从播放VC返回的规定。您需要某种执行dismissViewControllerAnimated()
的按钮。
以下代码在我的Xcode 6.4 / iPhone模拟器8.4上运行良好。我遗漏了样板AppDelegate
代码。另外一定要放回' import'我在下面留下的陈述。
import UIKit
import AVFoundation
// FIX of Error 11
let stopRecordingSegue = "stopRecording"
// Error 1: (Style) Class names should always be capitalized
class PlaySoundsViewController: UIViewController {
var audioPlayer: AVAudioPlayer?
// MAJOR ERROR 2: viewDidLoad() runs **BEFORE** prepareForSegue(). So it was looking for the
// 'recordedAudio' instance variable but prepareForSegue had NOT YET had a chance to
// initialize it.
// viewDidLoad is the WRONG place for DYNAMIC, RUNTIME initialization.
// Instead we make a custom method, called by prepareForSegue(), so we're in full control
// of the flow of data from VC to VC.
func prepareAudio(receivedAudio: RecordedAudio) {
var err: NSError?
// Error 3: check carefully for all error conditions
if let ap = AVAudioPlayer(contentsOfURL: receivedAudio.filePathUrl, error: &err) {
audioPlayer = ap
ap.enableRate = true
}
else {
assertionFailure("Exiting program, couldn't create recorder: \(err?.localizedDescription)")
}
}
// Error 4: (Style) Don't have overridden methods that do nothign but call super().
// The clutter just creates confusion. didRecieveMemoryWarning function removed.
@IBAction func slowVoice(sender: UIButton) {
// Error 5: Handle case where player did not get initialized
if let ap = audioPlayer {
ap.stop()
ap.rate = 0.5
ap.currentTime = 0.0
ap.play()
}
else {
println("No audioplayer available in slowVoice!")
}
}
@IBAction func fastVoice(sender: UIButton) {
// Error 5: Handle case where player did not get initialized
if let ap = audioPlayer {
ap.stop()
ap.rate = 2.0
ap.currentTime = 0.0
ap.play()
}
else {
println("No audioplayer available in fastVoice!")
}
}
@IBAction func stopButtonSounds(sender: UIButton) {
// Error 5: Handle case where player did not get initialized
if let ap = audioPlayer {
ap.stop()
}
else {
println("No audioplayer available in stop!")
}
}
// Error 6: (Style) Leave out dead code that's commented out. Creates more confusing clutter.
}
class RecordSoundsViewController: UIViewController, AVAudioRecorderDelegate {
@IBOutlet weak var recordButton: UIButton!
@IBOutlet weak var recordingLabel: UILabel!
@IBOutlet weak var stopButton: UIButton!
var audioRecorder: AVAudioRecorder!
var recordedAudio: RecordedAudio!
override func viewDidLoad() {
super.viewDidLoad()
// Error 7: Initialization: All one-time initialization must go in viewDidLoad(), not when the user
// pressed a button. Sometimes redundant initialization is not actually harmful, but it's a terrible
// programming habit nonetheless.
let dirPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! String
let recordingName = "my_audio.wav"
let pathArray = [dirPath, recordingName]
let filePath = NSURL.fileURLWithPathComponents(pathArray)
println(filePath)
// Error 8: Don't create unnecessary variables just to do one-time initialization. Confusing clutter.
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord, error: nil)
// Error 9: Don't use 'nil' for error object; receive the error and check it
var err: NSError?
audioRecorder = AVAudioRecorder(URL: filePath, settings: nil, error: &err)
if let e = err {
assertionFailure("Exiting program, couldn't create recorder: \(e.localizedDescription)")
}
else {
println("successfully created audio recorder")
audioRecorder.delegate = self
audioRecorder.meteringEnabled = true
}
}
override func viewWillAppear(animated: Bool) {
// Error 10: overriding conventions: Failure to call super of a built-in method like this one
super.viewWillAppear(animated)
stopButton.hidden = true
recordButton.enabled = true
}
@IBAction func recordButton(sender: UIButton) {
recordButton.enabled = false
stopButton.hidden = false
recordingLabel.hidden = false
audioRecorder.record()
}
func audioRecorderDidFinishRecording(recorder: AVAudioRecorder!, successfully flag: Bool) {
if flag {
recordedAudio = RecordedAudio()
recordedAudio.filePathUrl = recorder.url
recordedAudio.title = recorder.url.lastPathComponent
// Error 11: sender should be the actual initiating VC
self.performSegueWithIdentifier(stopRecordingSegue, sender: self)
}
else {
println("Recording was not successful") // Error 12: Grammar
recordButton.enabled = true
stopButton.hidden = true
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Error 13: Not using a defined constant string, you've messed up capitalization
if segue.identifier == stopRecordingSegue {
let playSoundsVC = segue.destinationViewController as! PlaySoundsViewController
// REST OF FIX OF MAJOR ERROR 2: send in data to a method so runtime initialization code can run
playSoundsVC.prepareAudio(recordedAudio)
}
}
@IBAction func stopButton(sender: UIButton) {
recordButton.enabled = true
recordingLabel.hidden = true
audioRecorder.stop()
AVAudioSession.sharedInstance().setActive(false, error: nil)
}
}
class RecordedAudio: NSObject{
var filePathUrl: NSURL!
var title: String!
}