I have a quiz app that asks questions in triviaviewcontroller, after 10 questions it segues to the videoviewcontroller, plays the video then segues back to the triviaviewcontroller. The problem is that it restarts the questions from the start, not question 11. Do I need to launch the video from the triviaviewcontroller or what approach do I need to change?
#IBAction func submitButton(_ sender: AnyObject) {
if noWhitespaceUserAnswer == answers[currentQuestionIndex]
{
self.currentQuestionTimerLabel.text = ""
answerField.text = ""
currentQuestionIndex = currentQuestionIndex + 1 < questions.count ? currentQuestionIndex + 1 : 0
if (currentQuestionIndex == 4){
performSegue(withIdentifier: "videoview", sender: self)
}
nextQuestionLabel.text = questions[currentQuestionIndex]
animateLabelTransitions()
}
else {
incorrectAnswerHoldLabel.isHidden = false
submitbuttonwronganswer.isHidden = true
let time = DispatchTime(uptimeNanoseconds:UInt64(0.1) ) + Double(4 * Int64(NSEC_PER_SEC)) / Double(NSEC_PER_SEC)
DispatchQueue.main.asyncAfter(deadline: time) {
self.submitbuttonwronganswer.isHidden = false
self.incorrectAnswerHoldLabel.isHidden = true
}
answerField.text = ""
}
}
Here is my videoviewcontroller.
class VideoViewController: AVPlayerViewController, AVPlayerViewControllerDelegate {
fileprivate var firstAppear = true
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if firstAppear {
do {
try playVideo()
firstAppear = false
} catch AppError.invalidResource(let name, let type) {
debugPrint("Could not find resource \(name).\(type)")
} catch {
debugPrint("Generic error")
}
}
}
fileprivate func playVideo() throws {
guard let path = Bundle.main.path(forResource: "RossCliffJumping", ofType:"m4v") else {
throw AppError.invalidResource("RossCliffJumping", "m4v")
}
let player = AVPlayer(url: URL(fileURLWithPath: path))
let playerController = AVPlayerViewController()
playerController.player = player
playerController.showsPlaybackControls = false
NotificationCenter.default.addObserver(self, selector: #selector(VideoViewController.itemDidFinishPlaying(_:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem)
self.present(playerController, animated: true) {
player.play()
}
}
func itemDidFinishPlaying(_ notification:Notification) {
dismiss(animated: true, completion: nil)
performSegue(withIdentifier: "videofinished", sender: self)
print("finished")
}
deinit{
NotificationCenter.default.removeObserver(self)
}
}
enum AppError : Error { case invalidResource(String, String)
}
You are already launching another view controller. From the sound of your question, it seems you want to call self.dismiss(animated: true, completion: nil) instead of performing another segue, to get back to the first controller. By calling performSegue you are creating a new controller, which of course is going to go back to question 1.
Related
I am making an app that will display a random quote from a stoic philosopher. Right now, I am stuck on trying to make the correct picture pop up. (User clicks on a Button with the philosopher's name on it, and then a new view pops up with an image of the philosopher and a random quote by him).
class ViewController: UIViewController {
var allQuotes = [String]()
var pictures = [String]()
#IBOutlet var Epictetus: UIButton!
#IBOutlet var Seneca: UIButton!
#IBOutlet var MarcusAurelius: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Create a constant fm and assign it the value returned by FileManager.default (built in system type)
let fm = FileManager.default
// Declares a new constant called path that sets the resource path of ours apps buddle.
// A bundle is a directory containing our compiled program and all our assets
let path = Bundle.main.resourcePath!
// items array will be a constant collection of the names of all the files found in the directory of our app
let items = try! fm.contentsOfDirectory(atPath: path)
// create a loop to go through all of our items...
for item in items {
if item.hasSuffix("jpg"){
pictures.append(item)
}
}
print(pictures)
title = "Stoicism"
if let stoicQuotesURL = Bundle.main.url(forResource: "quotes", withExtension: "txt"){
if let stoicQuotes = try? String(contentsOf: stoicQuotesURL) {
allQuotes = stoicQuotes.components(separatedBy: "\n\n")
}
}
}
#IBAction func buttonTapped(_ sender: UIButton) {
if sender.tag == 0 {
if let vc = storyboard?.instantiateViewController(identifier: "Picture") as? PictureViewController {
vc.selectedImage = pictures[0]
navigationController?.pushViewController(vc, animated: true)
}
}
else if sender.tag == 1 {
if let vc = storyboard?.instantiateViewController(identifier: "Picture") as? PictureViewController {
vc.selectedImage = pictures[1]
navigationController?.pushViewController(vc, animated: true)
}
}
else if sender.tag == 2 {
if let vc = storyboard?.instantiateViewController(identifier: "Picture") as? PictureViewController {
vc.selectedImage = pictures[2]
navigationController?.pushViewController(vc, animated: true)
}
}
}
}
That's the code for my main viewController.
import UIKit
class PictureViewController: UIViewController {
#IBOutlet var picture: UIImageView!
#IBOutlet var imageView: UIImageView!
var selectedImage: String?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if let imageToLoad = selectedImage {
imageView.image = UIImage(named: imageToLoad)
}
}
override func viewWillAppear(_ animated: Bool) {
// doing it for the parent class
super.viewWillAppear(animated)
// if its a nav Cont then it will hide bars on tap...
}
// now make sure it turns off when you go back to the main screen
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
That's the code for the viewController that has the imageView. Right now, the image that's popping up is always the preset (Marcus Aurelius), even though my code looks correct to me. Obviously it isn't (also, I've already debugged and ensured through print statements that the jpg files add to the pictures array correctly).
Any help would be appreciated.
First of all, this code is really silly:
#IBAction func buttonTapped(_ sender: UIButton) {
if sender.tag == 0 {
if let vc = storyboard?.instantiateViewController(identifier: "Picture") as? PictureViewController {
vc.selectedImage = pictures[0]
navigationController?.pushViewController(vc, animated: true)
}
}
else if sender.tag == 1 {
if let vc = storyboard?.instantiateViewController(identifier: "Picture") as? PictureViewController {
vc.selectedImage = pictures[1]
navigationController?.pushViewController(vc, animated: true)
}
}
else if sender.tag == 2 {
if let vc = storyboard?.instantiateViewController(identifier: "Picture") as? PictureViewController {
vc.selectedImage = pictures[2]
navigationController?.pushViewController(vc, animated: true)
}
}
}
Do you see that everything in those lines is identical except for the numbers? So make the number a variable:
#IBAction func buttonTapped(_ sender: UIButton) {
if let vc = storyboard?.instantiateViewController(identifier: "Picture") as? PictureViewController {
print(sender.tag)
vc.selectedImage = pictures[sender.tag]
navigationController?.pushViewController(vc, animated: true)
}
}
See how much shorter and clearer that is? Okay, I've also added a print statement. This will print the tag to the console. You need to make sure that your buttons do have the right tags. If they do, your code should work.
I'm creating an extremely simple game, and I've hidden the video controls.
#IBAction func playVideo1(_ sender: Any) {
// play video connected to button 1
guard let firstVideo = Bundle.main.path(forResource: "Video1", ofType:"mp4") else {
debugPrint("Video not found")
return
}
// create an AVPlayer, passing it mp4
let player = AVPlayer(url: URL(fileURLWithPath: firstVideo))
// Create a new AVPlayerViewController and pass it a reference to the player.
let controller = AVPlayerViewController()
controller.player = player
controller.showsPlaybackControls = false
// Modally present the player and call the player's play() method when complete.
present(controller, animated: true) {
player.play()
}
} // end playVideo1
One of the two options would be OK.
Option 1: Tap to close the video.
Option 2: Have the AVPlayer close automatically at the end of the video.
I appreciate any help.
Thanks!
You can add a tap gesture recognizer to AVPlayerViewController's view (for closing on tap), or you could subscribe to AVPlayerItemDidPlayToEndTime notification (for closing when video ends playing). Something like this:
#IBAction func playVideo1(_ sender: Any) {
// play video connected to button 1
guard let firstVideo = Bundle.main.path(forResource: "Video1", ofType:"mp4") else {
debugPrint("Video not found")
return
}
// create an AVPlayer, passing it mp4
let player = AVPlayer(url: URL(fileURLWithPath: firstVideo))
// Create a new AVPlayerViewController and pass it a reference to the player.
let controller = AVPlayerViewController()
controller.player = player
controller.showsPlaybackControls = false
//for closing when video ends
NotificationCenter.default.addObserver(self, selector: #selector(closePlayer), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: controller.player?.currentItem)
//for closing on tap
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(closePlayer))
controller.view.addGestureRecognizer(tapGestureRecognizer)
// Modally present the player and call the player's play() method when complete.
present(controller, animated: true) {
player.play()
}
} // end playVideo1
#objc func closePlayer() {
dismiss(animated: true)
//if you go notification route, don't forget to remove observer
NotificationCenter.default.removeObserver(self)
}
For closing the videoPlayer when on user tap you can use the following-
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(closePlayerOnTouch))
controller.view.addGestureRecognizer(tapGestureRecognizer)
and then add this after the button -
#objc func closePlayerOnTouch() {
dismiss(animated: true)
NotificationCenter.default.removeObserver(self)
}
#Predrag's answer is really awesome though.
import UIKit
import AVFoundation
class ViewController: UIViewController {
#IBOutlet weak var btnPlay: UIButton!
var player:AVPlayer?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btnPress(sender: AnyObject) {
if (btnPlay.titleLabel?.text == "Play") {
initPlayer()
btnPlay.setTitle("Stop", forState: UIControlState.Normal)
} else {
stopPlayer()
btnPlay.setTitle("Play", forState: UIControlState.Normal)
}
}
func initPlayer() {
if let play = player {
print("playing")
play.play()
} else {
print("player allocated")
player = AVPlayer(URL: NSURL(string: "http://streaming.radio.rtl.fr/rtl-1-48-192")!)
print("playing")
player!.play()
}
}
func stopPlayer() {
if let play = player {
print("stopped")
play.pause()
player = nil
print("player deallocated")
} else {
print("player was already deallocated")
}
}
}
Being the Swift noob that I am, I find myself copying and pasting code. I know I'm supposed to use the DRY method and not do this, but this particular bit of code has me stumped. I tried creating a struct to hold it, but the struct threw all sorts of errors. I don't quite understand classes and how I would subclass it, and so maybe that's the solution. I just don't know how to do it? Or maybe an extension?
Anyway, here is the code I keep copying and pasting in each new view controller:
import UIKit
import AVKit
class Step3JobSummaryVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
...
var sourceVCIdentity = "setup"
var initialLaunch = true
let playerVC = AVPlayerViewController()
let video = Video.step3JobsSummary
...
// ------------------------
// Autoplay Video Functions
// ------------------------
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if initialLaunch == true {
showUnplayedVideo()
initialLaunch = false
}
Video.updatePlaybackTime(playerVC: playerVC, videoURL: video.url, firebaseVideoID: video.firebaseID)
}
func showUnplayedVideo() {
// 1. get current video data
Video.getFirebaseData(firebaseVideoID: video.firebaseID) { (playbackTime, watched) in
if !watched {
// 2. show setup video popup on first load
guard let videoURL = URL(string: self.video.url) else { print("url error"); return }
let player = AVPlayer(url: videoURL)
self.playerVC.player = player
// 3. fast forward to where user left off (if applicable)
player.seek(to: CMTimeMakeWithSeconds(playbackTime, 1))
// 4. dismiss the player once the video is over and update Firebase
NotificationCenter.default.addObserver(self,
selector: #selector(self.playerDidFinishPlaying),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
object: self.playerVC.player?.currentItem)
self.present(self.playerVC, animated: true) {
self.playerVC.player?.play()
}
}
}
}
#objc func playerDidFinishPlaying(note: NSNotification) {
self.playerVC.dismiss(animated: true)
Video.updateFirebase(firebaseVideoID: video.firebaseID)
}
Any help would be great. I'm just trying to learn :-)
EDIT #1
This is my attempt at an extension. I simplified and refactored my code, but as before, it's giving me an error. This time the error is 'extensions must not contain stored properties'. So how do I access the AVPlayerController?!?
extension UIViewController {
let playerVC = AVPlayerViewController()
func showUnplayedVideo(video: Video) {
// 1. get current video data
Video.getFirebaseData(firebaseVideoID: video.firebaseID) { (playbackTime, watched) in
if !watched {
// 2. show setup video popup on first load
guard let videoURL = URL(string: video.url) else { print("url error"); return }
let player = AVPlayer(url: videoURL)
self.playerVC.player = player
// 3. fast forward to where user left off (if applicable)
player.seek(to: CMTimeMakeWithSeconds(playbackTime, 1))
// 4. dismiss the player once the video is over and update Firebase
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime,
object: playerVC.player?.currentItem,
queue: .main) { (notification) in
self.playerDidFinishPlaying(note: notification as NSNotification)
self.present(self.playerVC, animated: true) {
self.playerVC.player?.play()
}
}
}
}
func playerDidFinishPlaying(note: NSNotification, video: Video) {
self.playerVC.dismiss(animated: true)
Video.updateFirebase(firebaseVideoID: video.firebaseID)
}
}
EDIT #2
So I got the code to compile without any errors, but now it's not firing. Aargh.
extension UIViewController {
func showUnplayedVideo(playerVC: AVPlayerViewController, video: Video) {
print("does this code even fire?")
// 1. get current video data
Video.getFirebaseData(firebaseVideoID: video.firebaseID) { (playbackTime, watched) in
if !watched {
// 2. show setup video popup on first load
guard let videoURL = URL(string: video.url) else { print("url error"); return }
let player = AVPlayer(url: videoURL)
playerVC.player = player
// 3. fast forward to where user left off (if applicable)
player.seek(to: CMTimeMakeWithSeconds(playbackTime, 1))
// 4. dismiss the player once the video is over and update Firebase
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime,
object: playerVC.player?.currentItem,
queue: .main) { (notification) in
self.playerDidFinishPlaying(playerVC: playerVC, note: notification as NSNotification, video: video)
self.present(playerVC, animated: true) {
playerVC.player?.play()
}
}
}
}
}
func playerDidFinishPlaying(playerVC: AVPlayerViewController, note: NSNotification, video: Video) {
playerVC.dismiss(animated: true)
Video.updateFirebase(firebaseVideoID: video.firebaseID)
}
}
Why won't this work?
I would start with defining a protocol for your functionality something like this:
protocol VideoPlayable {
func showUnplayedVideo(playerVC: AVPlayerViewController, video: Video)
}
And then add a default implementation to it
extension VideoPlayable where Self: UIViewController {
func showUnplayedVideo(playerVC: AVPlayerViewController, video: Video) {
print("does this code even fire?")
// 1. get current video data
Video.getFirebaseData(firebaseVideoID: video.firebaseID) { (playbackTime, watched) in
if !watched {
// 2. show setup video popup on first load
guard let videoURL = URL(string: video.url) else { print("url error"); return }
let player = AVPlayer(url: videoURL)
playerVC.player = player
// 3. fast forward to where user left off (if applicable)
player.seek(to: CMTimeMakeWithSeconds(playbackTime, 1))
// 4. dismiss the player once the video is over and update Firebase
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime,
object: playerVC.player?.currentItem,
queue: .main) { (notification) in
self.playerDidFinishPlaying(playerVC: playerVC, note: notification as NSNotification, video: video)
}
self.present(playerVC, animated: true) {
playerVC.player?.play()
}
}
}
}
private func playerDidFinishPlaying(playerVC: AVPlayerViewController, note: NSNotification, video: Video) {
playerVC.dismiss(animated: true)
Video.updateFirebase(firebaseVideoID: video.firebaseID)
}
}
Thanks to this when you add the VideoPlayable protocol to a controller you will have your custom functionality available, and other controllers that shouldn't have the functionality won't have the access to this method.
Also if you really want to have access to the method
func playerDidFinishPlaying(playerVC: AVPlayerViewController, note: NSNotification, video: Video)
Add it to the protocol and remove the private statement from the implementation.
And you video player wasn't being shown because you added the presenting of the player into the notification block.
Also, consider adding proper self handling to your blocks. Right now I think its possible that the self can be caught in the blocks.
Just to let you know the statement
where Self: UIViewController
Limits the access of the implementation to UIViewControllers, so if you add the protocol to a UIView subclass you wont get access to the default implementation. You will then need to add a new one :) this prevents the missing of the protocol in places you dont want it to be used.
You can just move all the reused code into a separate class:
class Step3JobSummaryVC: UIViewController {
let videoPlayer = VideoPlayer(video: Video.step3JobsSummary)
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
videoPlayer.start(on: self)
}
}
final
class VideoPlayer {
private var initialLaunch: Bool = true
private let playerVC = AVPlayerViewController()
private let video: Video
init(video: Video) {
self.video = video
}
func start(on viewController: UIViewController) {
if initialLaunch == true {
showUnplayedVideo(on: viewController)
initialLaunch = false
}
Video.updatePlaybackTime(playerVC: playerVC, videoURL: video.url, firebaseVideoID: video.firebaseID)
}
func showUnplayedVideo(on viewController: UIViewController) {
// 1. get current video data
Video.getFirebaseData(firebaseVideoID: video.firebaseID) { (playbackTime, watched) in
if !watched {
// 2. show setup video popup on first load
guard let videoURL = URL(string: self.video.url) else { print("url error"); return }
let player = AVPlayer(url: videoURL)
self.playerVC.player = player
// 3. fast forward to where user left off (if applicable)
player.seek(to: CMTimeMakeWithSeconds(playbackTime, preferredTimescale: 1))
// 4. dismiss the player once the video is over and update Firebase
NotificationCenter.default.addObserver(self,
selector: #selector(self.playerDidFinishPlaying),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
object: self.playerVC.player?.currentItem)
viewController.present(self.playerVC, animated: true) {
self.playerVC.player?.play()
}
}
}
}
#objc func playerDidFinishPlaying(note: NSNotification) {
self.playerVC.dismiss(animated: true)
Video.updateFirebase(firebaseVideoID: video.firebaseID)
}
}
Have you considered using a more traditional inheritance model?
class VideoPlayingBaseController: : UIViewController, UITableViewDataSource, UITableViewDelegate {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if initialLaunch == true {
showUnplayedVideo()
initialLaunch = false
}
Video.updatePlaybackTime(playerVC: playerVC, videoURL: video.url, firebaseVideoID: video.firebaseID)
}
func showUnplayedVideo() {
// 1. get current video data
Video.getFirebaseData(firebaseVideoID: video.firebaseID) { (playbackTime, watched) in
if !watched {
// 2. show setup video popup on first load
guard let videoURL = URL(string: self.video.url) else { print("url error"); return }
let player = AVPlayer(url: videoURL)
self.playerVC.player = player
// 3. fast forward to where user left off (if applicable)
player.seek(to: CMTimeMakeWithSeconds(playbackTime, 1))
// 4. dismiss the player once the video is over and update Firebase
NotificationCenter.default.addObserver(self,
selector: #selector(self.playerDidFinishPlaying),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
object: self.playerVC.player?.currentItem)
self.present(self.playerVC, animated: true) {
self.playerVC.player?.play()
}
}
}
}
#objc func playerDidFinishPlaying(note: NSNotification) {
self.playerVC.dismiss(animated: true)
Video.updateFirebase(firebaseVideoID: video.firebaseID)
}
}
Then have your classes that use it:
class Step3JobSummaryVC: VideoPlayingBaseController {
//more code here
}
AudioKit
I have two view controllers that are doing different audio related function. The first view viewController, needs to record audio and also do real-time processing of the amplitude and frequency of the sound being recorded. The second view controller needs to play the recorded audio, and do some stuff with the playhead.
My problem is that when I go back and forth a few times between the two screens on my iphone 6 device, the amplitude and frequency of the tracker only equal 0. This behavior does not happen on my simulator. Similarly, I've tried different combinations of starting + stopping the tracker and/or the audio engine at different points in the workflow but then I just get crashes along the lines of https://github.com/audiokit/AudioKit/issues/643. One thing I have been trying to figure out is how to make a singleton instance of the aukiokit so I can only start the engine once, but I can't get it from examples.
Here are the key sections of code that use the audiokit, microphone and tracker:
First view controller in the workflow
class ViewController: UIViewController {
#IBAction func analyzeSpeechAction(_ sender: Any) {
//Audiokit.stop()
stopTimers()
let vc = AnalysisViewController(nibName: "AnalysisViewController", bundle: nil)
print("ANALYZING")
do {
try player.reloadFile()
} catch { print("Errored reloading.") }
let recordedDuration = player != nil ? player.audioFile.duration : 0
if recordedDuration > 0.0 {
recorder.stop()
}
tracker.stop()
navigationController?.pushViewController(vc, animated: true)
}
func stopTimers(){
if timerTest != nil {
timerTest!.invalidate()
timerTest = nil
}
if timer != nil {
timer!.invalidate()
timer = nil
}
}
var mic: AKMicrophone!
var tracker: AKFrequencyTracker!
var silence: AKBooster!
var mainMixer: AKMixer!
var timerTest : Timer?
var micMixer: AKMixer!
var recorder: AKNodeRecorder!
var player: AKAudioPlayer!
var tape: AKAudioFile!
var micBooster: AKBooster!
override func viewDidLoad() {
super.viewDidLoad()
AKAudioFile.cleanTempDirectory()
AKSettings.bufferLength = .medium
do {
try AKSettings.setSession(category: .playAndRecord, with: .allowBluetoothA2DP)
} catch {
AKLog("Could not set session category.")
}
AKSettings.defaultToSpeaker = true
AKSettings.audioInputEnabled = true
mic = AKMicrophone()
tracker = AKFrequencyTracker(mic)
tracker.start()
silence = AKBooster(tracker, gain: 0)
micMixer = AKMixer(mic)
micBooster = AKBooster(micMixer)
// Will set the level of microphone monitoring
micBooster.gain = 0
recorder = try? AKNodeRecorder(node: micMixer)
if let file = recorder.audioFile {
player = try? AKAudioPlayer(file: file)
}
mainMixer = AKMixer(micBooster)
do {
try recorder.record()
} catch { print("Errored recording.") }
AudioKit.output = mainMixer
}
viewDidAppear(){
AudioKit.stop()
AudioKit.output = silence
AudioKit.start()
if timer == nil{
timer = Timer.scheduledTimer(timeInterval: 1, target:self,
selector: #selector(ViewController.updateCounter), userInfo: nil, repeats: true)
}
}
#objc func updateUI() {
frame = frame + 1
print("AMP")
print(tracker.amplitude)
print(tracker.frequency)
}
}
Second View Controller
class AnalysisViewController: UIViewController {
#IBOutlet weak var barChartView: LineChartView!
#IBAction func recordSpeechNavigation(_ sender: Any) {
let vc = ViewController(nibName: "ViewController", bundle: nil)
//AudioKit.stop()
player.stop()
navigationController?.pushViewController(vc, animated: true)
}
#IBAction func playButtonAction(_ sender: UIButton) {
playAudio()
}
override func viewDidLoad() {
super.viewDidLoad()
//move to completion handler
player.completionHandler = {() -> Void in
self.playerStatusButton.setTitle("Play", for: .normal)
self.timerTest!.invalidate()
self.relativePlayheadPosition = 0.0
self.movePlayhead()
print("complete")
}
}
}
I kind of mashed up a bunch of examples to get this far so I apologize if I am improperly using some of the AudioKit methods. The key behavior occurs between viewDidLoad(), viewDidAppear() and updateUI() of the first view and recordSpeechNavigation() of the second view.
Does anyone have any insight into what might be happening or how I can properly implement this stuff?
I have used the code below to create my GameCenter page in my app. However, I haven't been able to connect the leaderboard that I created on iTunes Connect to my code, so the app just produces a blank leaderboard page. How do I connect my Itunes Connect leaderboard to my code, and how do I make the app such that it places your score on the leaderboard, as right now the leaderboard is empty.
Here is the code I used:
override func viewDidAppear(animated: Bool) {
//check user is logged into GameCenter
var localPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler = {(viewController : UIViewController!, error : NSError!) -> Void in
if ((viewController) != nil) {
self.presentViewController(viewController, animated: true, completion: nil)
} else {
println((GKLocalPlayer.localPlayer().authenticated))
}
}
//display leaderboard
func showLeaderboard() {
var gcViewController: GKGameCenterViewController = GKGameCenterViewController()
gcViewController.gameCenterDelegate = self
gcViewController.viewState = GKGameCenterViewControllerState.Leaderboards
gcViewController.leaderboardIdentifier = "MyLeaderboard"
self.showViewController(gcViewController, sender: self)
self.navigationController?.pushViewController(gcViewController, animated: true)
}
//take leaderboard away
func gameCenterViewControllerDidFinish(gcViewController: GKGameCenterViewController!) {
self.dismissViewControllerAnimated(true, completion: nil)
}
How do I connect my Itunes Connect leaderboard to the app, and what code do I use to upload one's score to the leaderboard?
Firstly add the GKGameCenterControllerDelegate to your class:
class viewController: UIViewController, GKGameCenterControllerDelegate {
...
}
This is the code you need to use to authenticate the player:
func login() {
println("Game Center Login Called")
let localPlayer = GKLocalPlayer.localPlayer()
// Handle the authentication
localPlayer.authenticateHandler = {(Home: UIViewController!, error: NSError!) -> Void in
if Home != nil {
println("Authentication is being processed.")
self.presentViewController(Home, animated: true, completion: nil)
} else {
println("Player has been successfully authenticated.")
}
}
}
This is the code you should use to show up the leaderboard:
func showLeaderboard() {
var gcViewController: GKGameCenterViewController = GKGameCenterViewController()
gcViewController.gameCenterDelegate = self
gcViewController.viewState = GKGameCenterViewControllerState.Leaderboards
gcViewController.leaderboardIdentifier = "YOUR_LEADERBOARD_ID"
self.showViewController(gcViewController, sender: self)
self.presentViewController(gcViewController, animated: true, completion: nil)
}
This code is needed when the user taps on "Done".
func gameCenterViewControllerDidFinish(gcViewController: GKGameCenterViewController!)
{
self.dismissViewControllerAnimated(true, completion: nil)
}
You can call the authentication method login() in the viewDidLoad method:
override func viewDidLoad() {
super.viewDidLoad()
login()
...
}
Show up the leaderboard when the user taps on a button
#IBAction func button(sender: AnyObject) {
showLeaderboard()
}
If you want to submit the best score:
if GKLocalPlayer.localPlayer().authenticated {
println("I have submitted the score to Game Center")
let gkScore = GKScore(leaderboardIdentifier: "Best_Score")
gkScore.value = Int64(bestScore)
GKScore.reportScores([gkScore], withCompletionHandler: ( { (error: NSError!) -> Void in
if (error != nil) {
// handle error
println("Error: " + error.localizedDescription);
} else {
println("Score reported: \(gkScore.value)")
}
}))
}