I am looking for a way to simply play audio files one after another.
AVAudioPlayer using an array seems to be the best solution. In fact, I was able to play the first element of the array using Sneak's recommendations found on this page : Here.
But I don't understand where and how to write the second call to AVAudioPlayer in order to play the second file?
The "audioPlayerDidFinishPlaying" function is not reacting. Why?
Thanks for watching.
import Cocoa
import AVFoundation
var action = AVAudioPlayer()
let path = Bundle.main.path(forResource: "test.aif", ofType:nil)!
let url = URL(fileURLWithPath: path)
let path2 = Bundle.main.path(forResource: "test2.aif", ofType:nil)!
let url2 = URL(fileURLWithPath: path2)
let array1 = NSMutableArray(array: [url, url2])
class ViewController: NSViewController
{
#IBOutlet weak var LanceStop: NSButton!
override func viewDidLoad()
{
super.viewDidLoad()
do
{
action = try AVAudioPlayer(contentsOf: array1[0] as! URL)
action.numberOfLoops = 0
action.prepareToPlay()
action.volume = 1
}catch{print("error")}
}
...
#IBAction func Lancer(_ sender: NSButton)
{
if action.isPlaying == true
{
action.stop()
action.currentTime = 0.0
LanceStop.title = "Lancer"
}
else
{
action.play()
LanceStop.title = "Stopper"
}
}
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool)
{
if flag == true
{
LanceStop.title = "Lancer"
}
}
}
But I don't understand where and how to write the second call to
AVAudioPlayer in order to play the second file?
So in order to play the second file, you need to write one method where you will need to initialize the audioplayer and invoke the same method inside audioplayer delegate method audioPlayerDidFinishPlaying like this:-
func playAudioFile(_ index: Int) {
do
{
action = try AVAudioPlayer(contentsOf: array1[index] as! URL)
action.numberOfLoops = 0
action.prepareToPlay()
action.volume = 1
} catch{print("error")
}
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
//This delegate method will called once it finished playing audio file.
//Here you can invoke your second file. You can declare counter variable
//and increment it based on your file and stop playing your file acordingly.
counter = counter + 1
playAudioFile(counter)
}
Note:- Set Audioplayer delegate to your ViewController in order to get invoke
audioPlayerDidFinishPlaying method like this.
action.delegate = self
Changed Code...
class ViewController: NSViewController, AVAudioPlayerDelegate
{
#IBOutlet weak var LanceStop: NSButton!
override func viewDidLoad()
{
super.viewDidLoad()
}
override var representedObject: Any?
{
didSet
{
// Update the view, if already loaded.
}
}
func playAudioFile(_ index: Int)
{
do
{
action = try AVAudioPlayer(contentsOf: array1[index] as! URL)
action.delegate = self
action.numberOfLoops = 0
action.prepareToPlay()
action.volume = 1
action.play()
}
catch{print("error")}
}
#IBAction func Lancer(_ sender: NSButton)
{
playAudioFile(0)
}
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool)
{
if flag == true
{
playAudioFile(1)
}
}
}
Related
I want my swift code to call the playSound and play the sound based for the duration of each item in array playamount. So I want the user to play the sound for the first time for 10 seconds then play the sound starting at the beginning for 20 and then the same thing for 30 seconds. So the sound always starts at the beginning each time it is called.
import UIKit; import AVFoundation
class ViewController: UIViewController {
var player: AVAudioPlayer?
func playSound() {
let url = Bundle.mainBundle().URLForResource("rock", withExtension: "mp3")!
do {
player = try AVAudioPlayer(contentsOfURL: url)
guard let player = player else { return }
player.prepareToPlay()
player.play()
} catch let error as NSError {
print(error.description)
}
}
var playAmount : [Int] = [10,20,30]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
class ViewController: UIViewController {
var player: AVAudioPlayer?
var currentIndex: Int = 0
func runMusicBox() {
guard !playAmount.isEmpty else { return }
currentIndex = 0
newTimerForIndex(index: 0)
}
func newTimerForIndex(index: Int) {
player?.prepareToPlay()
player?.play()
Timer.scheduledTimer(withTimeInterval: Double(playAmount[index]), repeats: false) { timer in
self.player?.stop()
if self.currentIndex + 1 < self.playAmount.count {
self.currentIndex += 1
self.newTimerForIndex(index: self.currentIndex)
} else {
self.player?.stop()
}
}
}
func playSound() {
let url = Bundle.mainBundle().URLForResource("rock", withExtension: "mp3")!
do {
player = try AVAudioPlayer(contentsOfURL: url)
guard let player = player else { return }
runMusicBox()
} catch let error as NSError {
print(error.description)
}
}
var playAmount : [Int] = [10,20,30]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
Hi, can you try this code. Here I have a timer, and when one stops, then we check if we have another element in the array and will run a new timer. The Timer is working, but i didn;t check the player. if it works as expected - it should work for you
I get all the data from my snapshot and create an object list with the data.
My problem: I can't return a list to use my objects in other code functions.
I tried to browse my list to create using my snapshot to implement a new list of objects declared above in my code.
class ViewController: UIViewController {
lazy var usersCollection = Firestore.firestore().collection("ship")
var ships: [MyShip] = []
override func viewDidLoad() {
super.viewDidLoad()
getUsers()
print(ships.count)
}
The getData function:
func getUsers() {
usersCollection.getDocuments { (snapshot, _) in
//let documents = snapshot!.documents
// try! documents.forEach { document in
//let myUser: MyUser = try document.decoded()
//print(myUser)
//}
let myShip: [MyShip] = try! snapshot!.decoded()
// myShip.forEach({print($0)})
for elt in myShip {
print(elt)
self.ships.append(elt)
}
print(self.ships[1].nlloyds)
}
}
result console
Result in the console:
- my list is not filled return 0
- I print the objects well and I print them well
- I print the ships object[1].nloyds = 555 well in the function
Your print(ships.count) call in viewDidLoad is printing an empty array because the .getDocuments() method is asynchronous. Try writing getUsers as a closure like this:
func getUsers(completion: #escaping ([MyShip]) -> Void) {
usersCollection.getDocuments { (snapshot, _) in
let myShip: [MyShip] = try! snapshot!.decoded()
completion(myShip)
}
}
and then use it in the viewDidLoad method like this:
override func viewDidLoad() {
super.viewDidLoad()
getUsers() { shipsFound in
self.ships = shipsFound
print(self.ships.count)
}
}
I have 2 arrays
var messages = [Message]()
var screenMessages = [screenMessage]()
I have the messages array items in a NSTableView.. when I press an IBOutlet I would like to pass the items in that row to the screenMessages array to present in another NSTableView.
My NSTableView starts like so..
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? {
let result = tableView.makeViewWithIdentifier("cell", owner: self) as? secondviewTableCell
let mess = messages[row]
I've tried a number of ways of appending the screenMessages with the messages[row] but I can't put my finger on it. If anyone could demonstrate or point me in the right direction that would be brilliant.
Thank you.
Added more detail:
Screen one looks like so and when pressing the add button it should then pass that data from that row into screen twos tableview..
Screen two:
My View for screen one is as:
import Firebase
import Cocoa
var messages = [Message]()
var screenMessages = [screenMessage]()
class secondVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
#IBOutlet weak var tableView: NSTableView!
#IBOutlet weak var screenRefreshBtn: NSButton!
#IBOutlet weak var refreshButton: NSButton!
var senderImageUrl: String!
var ref: Firebase!
var messagesRef: Firebase!
func setupFirebase() {
messagesRef = Firebase(url: "https://url.firebaseio.com/screenmessages")
messagesRef.queryLimitedToLast(25).observeEventType(FEventType.ChildAdded, withBlock: { (snapshot) in
let text = snapshot.value["text"] as? String
let sender = snapshot.value["senderName"] as? String
let imageUrl = snapshot.value["profileImageURL"] as? String
let MediaType = snapshot.value["MediaType"] as! String
let fileUrl = snapshot.value["fileUrl"] as? String
let message = Message(text: text, sender: sender, imageUrl: imageUrl, MediaType: MediaType, fileUrl: fileUrl)
messages.append(message)
let screenmessage = screenMessage(text: text, sender: sender, imageUrl: imageUrl, MediaType: MediaType, fileUrl: fileUrl)
screenMessages.append(screenmessage)
switch MediaType{
case "TEXT":
print("text message")
case "PHOTO":
print("photo message")
default:
print("default")
}
self.tableView.reloadData()
})
}
override func viewDidLoad() {
super.viewDidLoad()
setupFirebase()
}
// MARK: - Table View
func numberOfRowsInTableView(tableView: NSTableView) -> Int {
return messages.count
}
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? {
let result = tableView.makeViewWithIdentifier("cell", owner: self) as? secondviewTableCell
let mess = messages[row]
if mess.text() == nil {
result?.textField?.alphaValue = 0
result!.sendertextView.stringValue = mess.sender()
let url = NSURL(string: mess.fileUrl()!)!
// Download task:
// - sharedSession = global NSURLCache, NSHTTPCookieStorage and NSURLCredentialStorage objects.
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (responseData, responseUrl, error) -> Void in
// if responseData is not null...
if let data = responseData{
// execute in UI thread
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let photo = NSImage(data: data)!
result?.mediaPhoto.image = photo
})
}
}
task.resume()
} else {
result!.textField!.stringValue = mess.text()!
result!.sendertextView.stringValue = mess.sender()
}
return result
}
#IBAction func addtablerow(object: NSButton) {
let row = tableView.rowForView( object as NSView )
if ( row > -1 ) {
}
}
And my second screen is:
import Cocoa
class screenVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
var addedObserver = false
#IBOutlet weak var tableView: NSTableView!
override func viewDidLoad() {
super.viewDidLoad()
refreshObs()
clearObs()
self.tableView.backgroundColor = NSColor.clearColor()
if let window = self.view.window {
// custom window here
window.level = Int(CGWindowLevelForKey(.FloatingWindowLevelKey))
} else {
addedObserver = true
self.addObserver(self, forKeyPath: "view.window", options: [.New, .Initial], context: nil)
}
}
func refreshList(notification: NSNotification){
self.tableView.alphaValue = 0
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
animateViewRefresh()
tableView.scrollToEndOfDocument(self)
}
func numberOfRowsInTableView(tableView: NSTableView) -> Int {
return screenMessages.count
}
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? {
let result = tableView.makeViewWithIdentifier("cell2", owner: self) as? screenviewTableCell
let mess = screenMessages[row]
result?.senderLabel.stringValue = mess.sender()
if mess.text() != nil {
result?.messageTextView.stringValue = mess.text()!
let url = NSURL(string: mess.imageUrl()!)!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (responseData, responseUrl, error) -> Void in
if let data = responseData{
dispatch_async(dispatch_get_main_queue(), { () -> Void in
result?.avatarImage.image = NSImage(data: data)
})
}}
task.resume()
} else {
result?.messageTextView.alphaValue = 0
let mess = screenMessages[row]
let url = NSURL(string: mess.fileUrl()!)!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (responseData, responseUrl, error) -> Void in
if let data = responseData{
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let photo = NSImage(data: data)!
result?.mediaPhoto.image = photo
})
}
}
let url2 = NSURL(string: mess.imageUrl()!)!
let task2 = NSURLSession.sharedSession().dataTaskWithURL(url2) { (responseData, responseUrl, error) -> Void in
if let data = responseData{
dispatch_async(dispatch_get_main_queue(), { () -> Void in
result?.avatarImage.image = NSImage(data: data)
})
}}
task.resume()
task2.resume()
}
return result
}
// MARK : Animate
func animateView(notification: NSNotification){
NSAnimationContext.runAnimationGroup({ (context) in
context.duration = 2
self.tableView.animator().alphaValue = 0
screenMessages.removeAll()
}, completionHandler: { () -> Void in
})}
func animateViewRefresh(){
NSAnimationContext.runAnimationGroup({ (context) in
context.duration = 4
self.tableView.animator().alphaValue = 1
}, completionHandler: { () -> Void in
})}
func refreshObs(){
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(screenVC.refreshList(_:)), name:"refreshMyTableView", object: nil)
}
func clearObs(){
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(screenVC.animateView(_:)), name:"clearMyTableView", object: nil)
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if let window = self.view.window {
// custom window here
window.level = Int(CGWindowLevelForKey(.FloatingWindowLevelKey))
window.titlebarAppearsTransparent = true
window.movableByWindowBackground = true
window.opaque = true
window.backgroundColor = NSColor.clearColor()
}
}
deinit {
if addedObserver {
self.removeObserver(self, forKeyPath: "view.window")
}
}
}
I have tried a number of things such as 'screenMessages += messages(row)' and appending to add that row to the screenMessages array but I've had no luck.
Am I going about this in the right way or is there a better way of doing so?
Thank you.
To append an element from one array to another array just write
let index = index of element you need
let message = messages[index]
screenMessages.append(message)
If message is not the same type as the contents of the screenMessages array you will need to convert it, I would need more details of the types to help with that.
If you are having trouble passing the data to another ViewController I would need more information on the current architecture to give good advice, but for example you might define a protocol MessageDelegate that one of the controllers implements and the other has as a property.
update
If you update your data array for a table and want the new information to appear remember to call reloadData on the UITableView
I am learning to program in swift. I want to load some data in JSON format (using swiftyJSON and alamoFire) in an array and then use that array in outside the function. When I print the array it is empty and printed before the output of the loop. how can I fill naamArray2 with the content of naamArray
import UIKit
import Alamofire
import SwiftyJSON
class ViewController: UIViewController {
var naamArray = [String]()
var naamArray2 = [String]()
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Alamofire.request(.GET, "http://217.149.68.51:8080/xfind.php?userId=mike", parameters: ["foo": "bar"])
.responseJSON
{ response in
if let value = response.result.value
{
print("JSON: \(value)")
let json = JSON(value)
print(json["producten"][0]["productnaam"].stringValue)
let loopCounter = json["producten"].count
for i in 0...loopCounter
{
let tempstring = json["producten"][i]["productnaam"].stringValue
self.naamArray.append(tempstring)
}
print("\(self.naamArray)")
}
}
print("koekkoek")
print("tweede \(self.naamArray)")
naamArray2 = self.naamArray
}
Call request is run async so when you put it at bottom of viewDidload it can't assign when it have value.
you can create function call assignValue and call after request success:
func assignValue() {
naamArray2 = self.naamArray
}
change to:
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Alamofire.request(.GET, "http://217.149.68.51:8080/xfind.php?userId=mike", parameters: ["foo": "bar"])
.responseJSON
{ response in
if let value = response.result.value
{
print("JSON: \(value)")
let json = JSON(value)
print(json["producten"][0]["productnaam"].stringValue)
let loopCounter = json["producten"].count
for i in 0...loopCounter
{
let tempstring = json["producten"][i]["productnaam"].stringValue
self.naamArray.append(tempstring)
}
print("\(self.naamArray)")
assignValue()
}
}
}
I'm having a bit trouble saving an array of strings to userDefaults. I have an Array of strings declaired in a class, with a property observer ment to sync to userDefaults. Furthermore, I want the array to be limited to a maximum of 5 Strings.
let userDefaults = NSUserDefaults.standardUserDefaults()
var suggestions: [String]! {
didSet {
var arrayEnd = suggestions.count
if arrayEnd >= 5 {
arrayEnd = 4
}
let newArray = Array(suggestions[0...arrayEnd])
userDefaults.setObject(newArray, forKey: "suggestions")
userDefaults.synchronize()
}
}
func getSuggestionData() {
if userDefaults.valueForKey("suggestions") != nil {
self.suggestions = userDefaults.valueForKey("suggestions") as? [String]
}
}
override func viewDidLoad() {
super.viewDidLoad()
getSuggestionData()
suggestions.insert("someString", atIndex: 0)
}
}
When i run this i get:
fatal error: unexpectedly found nil while unwrapping an Optional value
on the line where I try to insert a new object to the array.
I have tried following the approach in this thread, but it didn't save anything to the list.
I'm new to swift, and optional-types aren't my strong side, maybe someone know what's wrong?
As reading from user defaults could return nil values, use optional binding to be safe:
func getSuggestionData() {
if let suggestionArray = userDefaults.objectForKey("suggestions") {
self.suggestions = suggestionArray
} else {
self.suggestions = [String]()
}
}
But I'd recommend to use an non-optional variable with a defined default value.
In AppDelegate of your app, override init() or insert the code to register the key/value pair.
override init()
{
let defaults = NSUserDefaults.standardUserDefaults()
let defaultValue = ["suggestions" : [String]()];
defaults.registerDefaults(defaultValue)
super.init()
}
Registering the default keys and values is the way Apple suggests in the documentation.
If no value is written yet, the default value (empty array) is read.
If there is a value, the actual value is read
Instead of the value observer of the variable suggestions implement a method to insert a new value, delete the last one and write the array to disk.
There is no need to use optional binding because the array in user defaults has always a defined state.
let userDefaults = NSUserDefaults.standardUserDefaults()
var suggestions = [String]()
func insertSuggestion(suggestion : String)
{
if suggestions.count == 5 { suggestions.removeLast() }
suggestions.insert(suggestion, atIndex: 0)
userDefaults.setObject(suggestions, forKey: "suggestions")
userDefaults.synchronize()
}
func getSuggestionData() {
suggestions = userDefaults.objectForKey("suggestions") as! [String]
}
override func viewDidLoad() {
super.viewDidLoad()
getSuggestionData()
insertSuggestion("someString")
}
A side note:
never use valueForKey: rather than objectForKey: if you don't need explicitly the key-value-coding method.
Hey Just try like this way.
var suggestions: [String] = Array() {
didSet {
var arrayEnd = suggestions.count
if arrayEnd >= 5 {
arrayEnd = 4
}
if arrayEnd > 0
{
let newArray = Array(suggestions[0...(arrayEnd-1)])
userDefaults.setObject(newArray, forKey: "suggestions")
userDefaults.synchronize()
}
}
}
func getSuggestionData() {
if userDefaults.valueForKey("suggestions") != nil {
self.suggestions = userDefaults.objectForKey("suggestions") as! Array
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
getSuggestionData()
if self.suggestions.count > 4
{
var str = "\(self.suggestions.count)"
self.suggestions.insert(str, atIndex: self.suggestions.count)
}
}