I tried for a few days to make a button which includes the next song but I can not
Here is my question
How to put the next екфсл at the click of a button with avPlayer
it is my code
class ViewControllerAudioDetail: UIViewController {
var avPlayer:AVQueuePlayer?
var status = false
fileprivate let seekDuration: Float64 = 10
fileprivate let seekDurationThirty: Float64 = 30
var mod = [Modal]()
#IBOutlet weak var ImageView: UIImageView!
#IBOutlet weak var startTime: UILabel!
#IBOutlet weak var endTime: UILabel!
#IBOutlet weak var sliderSong: UISlider!
#IBOutlet weak var name: UILabel!
#IBOutlet weak var Volume: UISlider!
#IBOutlet weak var iconChange: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
Volume.setThumbImage(UIImage(named:"Play.png"), for: .normal)
sliderSong.minimumValue = 0
sliderSong.maximumValue = 1
name.text = mod[thisSong].AudioName
ImageView.image = mod[0].ImageViewAudio
let url = URL(string: mod[thisSong].UrlName!)
let playerItem = AVPlayerItem(url: url!)
avPlayer = AVQueuePlayer(playerItem:playerItem)
let _ = avPlayer!.addPeriodicTimeObserver(forInterval: CMTime(seconds: 1, preferredTimescale: CMTimeScale(NSEC_PER_SEC)), queue: DispatchQueue.main) { [weak self] (time) in
let duration = CMTimeGetSeconds((self?.avPlayer!.currentItem!.asset.duration)!)
self?.sliderSong.value = Float(CMTimeGetSeconds(time)) / Float(duration)
}
let duration = CMTimeGetSeconds(avPlayer!.currentItem!.asset.duration)
let minutesTextOut = Int(duration) / 60 % 60
let secondsTextOut = Int(duration) % 60
let strDuration = String(format:"%02d:%02d", minutesTextOut, secondsTextOut)
endTime.text = strDuration
}
#IBAction func sliderSong(_ sender: UISlider) {
//перемотка аудиозвука
let duration = CMTimeGetSeconds(avPlayer!.currentItem!.asset.duration)
let value = sliderSong.value
let durationToSeek = Float(duration) * value
avPlayer?.seek(to: CMTimeMakeWithSeconds(Float64(durationToSeek),avPlayer!.currentItem!.duration.timescale)) { [](state) in
if (self.iconChange.currentImage?.isEqual(UIImage(named: "Play.png")))! {
self.avPlayer?.pause()
} else if (self.iconChange.currentImage?.isEqual(UIImage(named: "Pause.png")))!{
self.avPlayer?.play()
}
}
}
#IBAction func volume(_ sender: UISlider) {
avPlayer?.volume = sender.value
}
#IBAction func minusThirtySec(_ sender: Any) {
let playerCurrentTime = CMTimeGetSeconds((avPlayer?.currentTime())!)
var newTime = playerCurrentTime - seekDurationThirty
if newTime < 0 {
newTime = 0
}
let time2: CMTime = CMTimeMake(Int64(newTime * 1000 as Float64), 1000)
avPlayer?.seek(to: time2, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)
}
#IBAction func minusTenSec(_ sender: Any) {
let playerCurrentTime = CMTimeGetSeconds((avPlayer?.currentTime())!)
var newTime = playerCurrentTime - seekDuration
if newTime < 0 {
newTime = 0
}
let time2: CMTime = CMTimeMake(Int64(newTime * 1000 as Float64), 1000)
avPlayer?.seek(to: time2, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)
}
#IBAction func plusTenSec(_ sender: Any) {
guard let duration = avPlayer?.currentItem?.duration else{
return
}
let playerCurrentTime = CMTimeGetSeconds((avPlayer?.currentTime())!)
let newTime = playerCurrentTime + seekDuration
if newTime < (CMTimeGetSeconds(duration) - seekDuration) {
let time2: CMTime = CMTimeMake(Int64(newTime * 1000 as Float64), 1000)
avPlayer?.seek(to: time2, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)
}
}
#IBAction func plusThirtySec(_ sender: Any) {
guard let duration = avPlayer?.currentItem?.duration else{
return
}
let playerCurrentTime = CMTimeGetSeconds((avPlayer?.currentTime())!)
let newTime = playerCurrentTime + seekDurationThirty
if newTime < (CMTimeGetSeconds(duration) - seekDuration) {
let time2: CMTime = CMTimeMake(Int64(newTime * 1000 as Float64), 1000)
avPlayer?.seek(to: time2, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)
}
}
#IBAction func Next(_ sender: Any) {
let url = URL(string: mod[thisSong].UrlName!)
let playerItem = AVPlayerItem(url: url!)
avPlayer = AVQueuePlayer(playerItem:playerItem)
avPlayer?.insert(playerItem, after: playerItem)
avPlayer?.advanceToNextItem()
// if thisSong + 1 > mod.count {
// thisSong = 0
// } else {
// thisSong += 1
// }
//
//
// if thisSong != mod.count{
//
// name.text = mod[thisSong].AudioName
// player(urlSong:mod[thisSong].UrlName!)
// avPlayer?.play()
//
//
//
// }
}
#IBAction func Back(_ sender: Any) {
// if thisSong != 0{
// thisSong -= 1
// name.text = mod[thisSong].AudioName
// player(urlSong:mod[thisSong].UrlName!)
// avPlayer?.play()
//
//
// }
}
#IBAction func Play(_ sender: Any) {
if avPlayer?.rate == 0 {
avPlayer?.play()
avPlayer?.rate = 1.0
iconChange.setImage(UIImage(named:"Pause.png"), for: .normal)
avPlayer?.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, 1), queue: nil, using: {
(CMTime) -> Void in
self.updateProgressBar()
})
} else {
avPlayer?.rate = 0.0
avPlayer?.pause()
iconChange.setImage(UIImage(named:"Play.png"), for: .normal)
}
}
func player(urlSong:String) {
}
func updateProgressBar(){
let timeNow = Int(avPlayer!.currentTime().value) / Int(avPlayer!.currentTime().timescale)
let minutesText = timeNow / 60
let secondsText = timeNow % 60
let duration = String(format:"%02d:%02d", minutesText, secondsText)
startTime.text = duration
}
And tableview from here I get the data
var thisSong = 0
class TableViewControllerAudioList: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = 50
tableView.rowHeight = UITableViewAutomaticDimension
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return modalsF.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCellAudioList
cell.name.text = modalsF[indexPath.row].AudioName
cell.number.text = "\(indexPath.row + 1)"
thisSong = indexPath.row
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as? ViewControllerAudioDetail
vc?.mod = [modalsF[(tableView.indexPathForSelectedRow?.row)!]]
}
Play the selected / current song
func play(at index: Int) {
audioPlayer.removeAllItems()
playerItem = audioItems[index]
playerItem?.seek(to: kCMTimeZero)
audioPlayer.insert(playerItem!, after: nil)
audioPlayer.play()
isPlaying = true
self.playAllSongsTableView.reloadData()
}
To play previuos song....
#IBAction func backWordAction(_ sender: UIButton) {
var index = audioItems.index(of: audioPlayer.currentItem!) ?? 0
if index > 0 {
index = index - 1
}
play(at: index)
}
To play next song....
#IBAction func forwordAction(_ sender: UIButton) {
var index = audioItems.index(of: audioPlayer.currentItem!) ?? 0
if index < (audioItems.count - 1) {
index = index + 1
}
play(at: index)
}
Using AVQueuePlayer instead of AVPlayer will let you directly add items;
AVQueuePlayer.insert(item: AVPlayerItem, after: AVPlayerItem?)
AVQueuePlayer.advanceToNextItem()
You can also initialize the player with an array of AVPlayerItems
AVQueuePlayer = AVQueuePlayer(items: [AVPlayerItem])
In its tableview I added func didSelectRowAt in which I wrote a global counter that received the cell number
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
thisSong = indexPath.row
}
And pass the data to the controller through prepareForSegue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "listToDetail"{
let vc = segue.destination as? ViewControllerAudioDetail
vc?.mod = mod
}
}
In my detailed controller I created two buttons for the previous song and the next one
var avPlayer:AVPlayer?
var mod = [Modal]()
#IBAction func Next(_ sender: Any) {
if thisSong == mod.count - 1 {
thisSong = 0
} else {
thisSong += 1
}
if thisSong != mod.count{
avPlayer?.removeTimeObserver(sliderDuration)
name.text = mod[thisSong].AudioName
player(urlSong:mod[thisSong].UrlName!)
Status()
}
}
#IBAction func Back(_ sender: Any) {
if thisSong != 0 {
thisSong -= 1
} else {
thisSong = mod.endIndex
}
avPlayer?.removeTimeObserver(sliderDuration)
name.text = mod[thisSong].AudioName
player(urlSong:mod[thisSong].UrlName!)
Status()
}
And the function determines which was the status of the player and sets the same data
func Status(){
if status == true {
iconChange.setImage(UIImage(named:"Pause.png"), for: .normal)
avPlayer?.play()
} else {
iconChange.setImage(UIImage(named:"Play.png"), for: .normal)
avPlayer?.pause()
}
}
Here I create a player
func player(urlSong:String) {
let url = URL(string: urlSong)
let playerItem = AVPlayerItem(url: url!)
avPlayer = AVPlayer(playerItem:playerItem)
sliderDuration = avPlayer!.addPeriodicTimeObserver(forInterval: CMTime(seconds: 1, preferredTimescale: CMTimeScale(NSEC_PER_SEC)), queue: DispatchQueue.main) { [weak self] (time) in
let duration = CMTimeGetSeconds((self?.avPlayer!.currentItem!.asset.duration)!)
self?.sliderSong.value = Float(CMTimeGetSeconds(time)) / Float(duration)
}
let duration = CMTimeGetSeconds(avPlayer!.currentItem!.asset.duration)
let minutesTextOut = Int(duration) / 60 % 60
let secondsTextOut = Int(duration) % 60
let strDuration = String(format:"%02d:%02d", minutesTextOut, secondsTextOut)
endTime.text = strDuration
}
Related
enter image description hereimport UIKit
class MyCircleViewPresenter: BaseViewPresenter {
typealias V = MyCircleViewDelegate
var mView: V?
func getMyCircle(url: String){
mView?.onNetworkCallStarted("please_wait".localized())
AlamofireService.getSession()
.request(url, method: .get).validate()
.responseJSON { (response) in
self.mView?.onNetworkCallEnded()
switch(response.result){
case .success(_):
guard let decodedObj = response.decode(objectType: MyCircleResponse.self) else {
self.mView?.myCircleDidReceived(response: nil)
return
}
self.mView?.myCircleDidReceived(response: decodedObj)
break
case .failure(let error):
self.mView?.handleError(httpSatusCode: response.response?.statusCode, errorData: response.data, error: error)
break
}
}
}
}
//
// MyCircleViewController.swift
import UIKit
struct MyCircleSection {
let number: String
let recharges: [SingeRecharge]
var isOpened: Bool = false
struct SingeRecharge {
let amount: String
let rechargeType: Int
let create_at: String
}
}
class MyCircleViewController: BaseViewController {
// Out let >>>>>
#IBOutlet weak var card_current_month: CardView!
#IBOutlet weak var card_last_3o_days: CardView!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var lb_my_circle_total: RegularLabel!
var sections = [MyCircleSection]()
var mPresenter = MyCircleViewPresenter()
var historyType = Constants.HistoryType.CURRENT_MONTH
override func onViewDidLoaded() {
mPresenter.setViewDelegate(mView: self)
// Set Up table View
self.tableView.delegate = self
self.tableView.dataSource = self
self.tableView.rowHeight = 62
// Swipe
let swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(handleGesture))
swipeLeft.direction = .left
self.view!.addGestureRecognizer(swipeLeft)
setupClickListener()
card_current_month.borderColor = UIColor(hexaRGB: Color.PRIMARY)!
card_last_3o_days.borderColor = UIColor(hexaRGB: Color.GREY_LIGHT)!
card_current_month.shadowColor = UIColor(hexaRGB: Color.GREY_LIGHT)!
card_last_3o_days.shadowColor = UIColor(hexaRGB: Color.GREY_LIGHT)!
}
override func viewWillAppear(_ animated: Bool) {
NotificationCenter.default.addObserver(self, selector: #selector(menuDidTapped), name: .MENU_DID_TAPPED, object: nil)
if(historyType == Constants.HistoryType.CURRENT_MONTH){ // Curent Month
card_current_month.borderColor = UIColor(hexaRGB: Color.PRIMARY)!
card_last_3o_days.borderColor = UIColor(hexaRGB: Color.GREY_LIGHT)!
mPresenter.getMyCircle(url: APIs.MY_CIRCLE + "/\(historyType)")
}else{ // Last 30 days
card_current_month.borderColor = UIColor(hexaRGB: Color.GREY_LIGHT)!
card_last_3o_days.borderColor = UIColor(hexaRGB: Color.PRIMARY)!
mPresenter.getMyCircle(url: APIs.MY_CIRCLE + "/\(historyType)")
}
lb_my_circle_total.text = "..."
tableView.isHidden = true
}
override func viewWillDisappear(_ animated: Bool) {
NotificationCenter.default.removeObserver(self, name: .MENU_DID_TAPPED, object: nil)
}
#objc func menuDidTapped(_ notification: NSNotification){
guard let menuType = notification.object as? MenuType else {
return
}
print("Menu Type: Reward >>> \(menuType)")
switch menuType {
case .PROFILE:
performSegue(withIdentifier: ProfileViewController.className, sender: self)
break
case .REFER_SC_APP:
AppUtils.shared.referThisApp(fromVc: self)
break
case .TRANSACTION_AND_STATEMENT:
performSegue(withIdentifier: TransactionViewController.className, sender: self)
break
case .FAQ:
performSegue(withIdentifier: FaqViewController.className, sender: self)
break
case .SUPPORT:
performSegue(withIdentifier: SupportViewController.className, sender: self)
break
case .SETTINGS:
performSegue(withIdentifier: SettingsViewController.className, sender: self)
break
case .LOGOUT:
showLogoutDialog()
break
}
}
func setupClickListener(){
card_current_month.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tabClickListener(_:))))
card_last_3o_days.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tabClickListener(_:))))
}
#objc func tabClickListener(_ gesture: UIGestureRecognizer){
guard let tag = gesture.view?.tag else { return }
if(tag == 1){ // Curent Month
if(historyType != Constants.HistoryType.CURRENT_MONTH){
historyType = Constants.HistoryType.CURRENT_MONTH
lb_my_circle_total.text = "..."
tableView.isHidden = true
card_current_month.borderColor = UIColor(hexaRGB: Color.PRIMARY)!
card_last_3o_days.borderColor = UIColor(hexaRGB: Color.GREY_LIGHT)!
mPresenter.getMyCircle(url: APIs.MY_CIRCLE + "/\(Constants.HistoryType.CURRENT_MONTH)")
}
}else{ // Last 30 days
if(historyType != Constants.HistoryType.LAST_30_DAYS){
historyType = Constants.HistoryType.LAST_30_DAYS
lb_my_circle_total.text = "..."
tableView.isHidden = true
card_current_month.borderColor = UIColor(hexaRGB: Color.GREY_LIGHT)!
card_last_3o_days.borderColor = UIColor(hexaRGB: Color.PRIMARY)!
mPresenter.getMyCircle(url: APIs.MY_CIRCLE + "/\(Constants.HistoryType.LAST_30_DAYS)")
}
}
}
#objc func handleGesture(gesture: UISwipeGestureRecognizer) -> Void {
if gesture.direction == UISwipeGestureRecognizer.Direction.left {
NotificationCenter.default.post(name: .USER_DID_SWIPE_TO_LEFT, object: nil)
}
}
}
extension MyCircleViewController: MyCircleViewDelegate {
func myCircleDidReceived(response: MyCircleResponse?) {
if(response == nil){
lb_my_circle_total.text = "0"
showErrorDialog(title: "No data found!", msg: nil, titleFullRed: true)
}else{
lb_my_circle_total.text = "\(response!.data.count)"
// Format Data for Table View >>>
if (!sections.isEmpty) {sections.removeAll()}
for (number , recharges) in response!.data {
var rechargeHistories = [MyCircleSection.SingeRecharge]()
for singleRecharge in recharges {
rechargeHistories.append(MyCircleSection.SingeRecharge(amount: singleRecharge.amount, rechargeType : singleRecharge.rechargeType, create_at: singleRecharge.createdAt))
}
sections.append(MyCircleSection(number: number, recharges: rechargeHistories))
}
// Reload Table view data >>>
self.tableView.reloadData()
self.tableView.isHidden = false
}
}
}
//// Table View Delegate >>>
extension MyCircleViewController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let section = sections[section]
return section.isOpened ? section.recharges.count + 1 : 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if(indexPath.row == 0){
guard let cell = tableView.dequeueReusableCell(withIdentifier: MyCircleHeaderCell.className) as? MyCircleHeaderCell else {
return UITableViewCell()
}
let section = sections[indexPath.section]
cell.lb_circle_number.text = section.recharges.isEmpty ? section.number : section.number + " (\(section.recharges.count))"
cell.img_arrow.image = section.isOpened ? UIImage(named: "ic-arrow-up") : UIImage(named: "ic-down-arrow")
return cell
}else{
guard let cell = tableView.dequeueReusableCell(withIdentifier: MyCircleRowCell.className) as? MyCircleRowCell else {
return UITableViewCell()
}
let data = sections[indexPath.section].recharges[indexPath.row - 1]
cell.lb_amount.text = Constants.TAKA_UNICODE + "\(data.amount)"
let formattedDateTime = DateUtils.shared.convertUTCtimeToLocale(dateToFormat: data.create_at)
cell.lb_date.text = formattedDateTime.0
cell.lb_time.text = formattedDateTime.1
switch "\(data.rechargeType)" {
case Constants.RechargeType.EASY_LOAD:
cell.lb_type.text = "Easyload"
case Constants.RechargeType.BUNDLE:
cell.lb_type.text = "Bundle"
case Constants.RechargeType.INTERNET:
cell.lb_type.text = "Internet"
case Constants.RechargeType.VOICE:
cell.lb_type.text = "Voice"
case Constants.RechargeType.VAS:
cell.lb_type.text = "Vas"
default:
cell.lb_type.text = ""
break
}
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if(indexPath.row == 0){
sections[indexPath.section].isOpened = !sections[indexPath.section].isOpened
tableView.reloadSections([indexPath.section], with: .none)
}
}
}
// Qeustion Cell >>>>
class MyCircleHeaderCell: UITableViewCell{
#IBOutlet weak var lb_circle_number : RegularLabel!
#IBOutlet weak var img_arrow: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
self.selectionStyle = .none
}
}
// Answer Cell >>>
class MyCircleRowCell: UITableViewCell {
#IBOutlet weak var lb_date : PreRegularLight!
#IBOutlet weak var lb_time : PreRegularLight!
#IBOutlet weak var lb_amount: PreRegularBoldLabel!
#IBOutlet weak var lb_type: PreRegularLight!
override func awakeFromNib() {
super.awakeFromNib()
self.selectionStyle = .none
}
}
a JSON response is never sorted in some way. it always come in different orders, and are decoded as such. i suggest you yourself sort the array on date or something to get a consistent order of the objects.
I have TableViewController and AudioPlayerViewController. I have a problem with using MPRemoteCommandCenter. For example: In TableViewController I click on cell and go toAudioPlayerViewController next I lock device and control my music with MPRemoteCommandCenter - all works fine. But if I further unlock device return to TableViewController go again to AudioPlayerViewController lock device and press play/pause button my music will play two times at the same time. If I will repeat the action my music will play three times at the same time. And etc... How to fix it?
code:
import UIKit
import AVFoundation
import MediaPlayer
class ViewController: UIViewController, AVAudioPlayerDelegate, UIAlertViewDelegate {
var audioPlayer: AVAudioPlayer!
let musicOperation = OperationQueue()
var timer: Timer?
#IBOutlet weak var playButton: UIButton!
#IBOutlet var timeElapsed: UILabel!
#IBOutlet var timeDuration: UILabel!
#IBOutlet weak var logo: UIImageView!
#IBOutlet weak var slider: UISlider!
#IBOutlet weak var volumeView: UIView!
var index = 0
var buttonIndex = 0
var endOfChapterSleepTimer = false
override func viewDidLoad() {
super.viewDidLoad()
// Table View Index
buttonIndex = masterIndex
musicOperation.maxConcurrentOperationCount = 1
try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
UserDefaults.standard.set(index, forKey: "index")
if index > 0 {
let fileManager = FileManager.default
let urls = fileManager.urls(for: .documentDirectory, in: .userDomainMask)
if let documentDirectoryURL: NSURL = urls.first as NSURL? {
let soundURL = documentDirectoryURL.appendingPathComponent("/\(masterIndex)/\(index).mp3")
UserDefaults.standard.set(index, forKey: "\(masterIndex)")
do {
audioPlayer = try AVAudioPlayer(contentsOf: soundURL!)
audioPlayer.delegate = self
audioPlayer.prepareToPlay()
play(sender:AnyObject.self as AnyObject)
restorePlayerCurrentTime()
setupMediaPlayerNotificationView()
lockScreen()
} catch {
}
}
} else {
let url = Bundle.main.url(forResource: "\(masterIndex)0", withExtension: "m4a")!
do {
audioPlayer = try AVAudioPlayer(contentsOf: url)
audioPlayer.delegate = self
audioPlayer.prepareToPlay()
play(sender:AnyObject.self as AnyObject)
setupMediaPlayerNotificationView()
lockScreen()
} catch {
}
}
}
// MARK: - Audio player controller
#IBAction func play(sender: AnyObject) {
if !audioPlayer.isPlaying{
audioPlayer.play()
slider.maximumValue = Float(audioPlayer.duration)
timer = Timer(timeInterval: 0.1, target: self, selector: #selector(self.updateTime), userInfo: nil, repeats: true)
RunLoop.main.add(timer!, forMode: .commonModes)
restorePlayerCurrentTime()
playButton.setImage(UIImage(named: "pause.png"), for: UIControlState.normal)
} else {
audioPlayer.pause()
playButton.setImage(UIImage(named: "play.png"), for: UIControlState.normal)
timer?.invalidate()
}
}
#IBAction func fastForward(sender: AnyObject) {
var time: TimeInterval = audioPlayer.currentTime
time += 15.0 // Go Forward by 15 Seconds
if time > audioPlayer.duration {
audioPlayerDidFinishPlaying(audioPlayer, successfully: true)
} else {
audioPlayer.currentTime = time
updateTime()
}
}
#IBAction func fastBackward(sender: AnyObject) {
var time: TimeInterval = audioPlayer.currentTime
time -= 15.0 // Go Back by 15 Seconds
if time < 0 {
audioPlayer.currentTime = 0
updateTime()
} else {
audioPlayer.currentTime = time
updateTime()
}
}
// MARK: - Audio player time
private func restorePlayerCurrentTime() {
let currentTimeFromUserDefaults : Double? = UserDefaults.standard.value(forKey: "currentTime\(masterIndex)\(index)") as! Double?
if let currentTimeFromUserDefaultsValue = currentTimeFromUserDefaults {
audioPlayer.currentTime = currentTimeFromUserDefaultsValue
slider.value = Float.init(audioPlayer.currentTime)
}
}
#objc func updateTime() {
let currentTime = Int(audioPlayer.currentTime)
let minutes = currentTime/60
let seconds = currentTime - minutes * 60
let durationTime = Int(audioPlayer.duration) - Int(audioPlayer.currentTime)
let minutes1 = durationTime/60
let seconds1 = durationTime - minutes1 * 60
timeElapsed.text = NSString(format: "%02d:%02d", minutes,seconds) as String
timeDuration.text = NSString(format: "-%02d:%02d", minutes1,seconds1) as String
UserDefaults.standard.set(currentTime, forKey: "currentTime\(masterIndex)\(index)")
UserDefaults.standard.set(durationTime, forKey: "durationTime\(masterIndex)\(index)")
slider.value = Float.init(audioPlayer.currentTime)
}
func audioPlayerDidFinishPlaying(_ audioPlayer: AVAudioPlayer, successfully flag: Bool) {
playButton.setImage(UIImage(named: "play.png"), for: UIControlState.normal)
let currentTime = 0
let durationTime = 0.1
UserDefaults.standard.set(currentTime, forKey: "currentTime\(masterIndex)\(index)")
UserDefaults.standard.set(durationTime, forKey: "durationTime\(masterIndex)\(index)")
slider.value = Float.init(audioPlayer.currentTime)
timer?.invalidate()
let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
let documentDirectoryPath:String = path[0]
let fileManager = FileManager()
let destinationURLForFile = URL(fileURLWithPath: documentDirectoryPath.appendingFormat("/\(masterIndex)/\(index+1).mp3"))
if fileManager.fileExists(atPath: destinationURLForFile.path){
index = index + 1
viewDidLoad()
} else {
}
}
// MARK: - Audio player lock screen
func lockScreen() {
var albumArtwork : MPMediaItemArtwork!
let image:UIImage = UIImage(named: "infoImage")!
albumArtwork = MPMediaItemArtwork.init(boundsSize: image.size, requestHandler: { (size) -> UIImage in
return image
})
let infotitle = "title"
MPNowPlayingInfoCenter.default().nowPlayingInfo = [
MPMediaItemPropertyArtist : "",
MPMediaItemPropertyTitle : infotitle,
MPMediaItemPropertyArtwork : albumArtwork,
MPMediaItemPropertyAlbumTitle : "",
MPNowPlayingInfoPropertyElapsedPlaybackTime : Int(audioPlayer.currentTime),
MPMediaItemPropertyPlaybackDuration: Int(audioPlayer.duration)]
}
func setupMediaPlayerNotificationView() {
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.playCommand.addTarget { event in
self.audioPlayer.play()
self.lockScreen()
self.playButton.setImage(UIImage(named: "pause.png"), for: UIControlState.normal)
print("play")
return .success
}
commandCenter.pauseCommand.addTarget { event in
self.audioPlayer.pause()
self.lockScreen()
self.playButton.setImage(UIImage(named: "play.png"), for: UIControlState.normal)
print("pause")
return .success
}
commandCenter.skipBackwardCommand.preferredIntervals = [15]
commandCenter.skipBackwardCommand.addTarget { event in
self.fastBackward(sender: self)
self.lockScreen()
print("fastBackward")
return .success
}
commandCenter.skipForwardCommand.preferredIntervals = [15]
commandCenter.skipForwardCommand.addTarget { event in
self.fastForward(sender: self)
self.lockScreen()
print("fastForward")
return .success
}
commandCenter.changePlaybackPositionCommand.addTarget(self, action: #selector(self.changedThumbSlider(_:)))
#objc func changedThumbSlider(_ event: MPChangePlaybackPositionCommandEvent) -> MPRemoteCommandHandlerStatus {
let time = event.positionTime
audioPlayer.currentTime = TimeInterval(time)
self.lockScreen()
return .success
}
// MARK: - Audio player slider
#IBAction func slide(_ slider: UISlider) {
musicOperation.cancelAllOperations()
let operation = BlockOperation()
audioPlayer.currentTime = TimeInterval(slider.value)
self.lockScreen()
musicOperation.addOperation(operation)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
audioPlayer.pause()
timer?.invalidate()
musicOperation.cancelAllOperations()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
UIView.setAnimationsEnabled(true)
// Navigation Bar
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationItem.largeTitleDisplayMode = .always
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.barTintColor = UIColor(red: 55/255, green: 60/255, blue: 65/255, alpha: 1.0)
self.navigationController?.navigationBar.isTranslucent = true
self.navigationController?.navigationBar.tintColor = .white
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
whats happening is that everytime you goto AudioPlayerViewController you enable MPRemoteCommandCenter, however when you go back to TableViewController you are not calling removeTarget. this is your issue.
calling something like the below in viewWillDisappear to removeTarget
func setupMediaPlayerNotificationView(_ enable: Bool) {
let commandCenter = MPRemoteCommandCenter.shared()
if enable {
commandCenter.playCommand.addTarget(self, action: #selector(self.lockScreenPlay(_:)))
commandCenter.pauseCommand.addTarget(self, action: #selector(self.lockScreenPlay(_:)))
commandCenter.nextTrackCommand.addTarget(self, action: #selector(self.lockScreenNextTrack(_:)))
commandCenter.previousTrackCommand.addTarget(self, action: #selector(self.lockScreenPreviousTrack(_:)))
} else {
commandCenter.playCommand.removeTarget(self, action: #selector(self.lockScreenPlay(_:)))
commandCenter.pauseCommand.removeTarget(self, action: #selector(self.lockScreenPlay(_:)))
commandCenter.nextTrackCommand.removeTarget(self, action: #selector(self.lockScreenNextTrack(_:)))
commandCenter.previousTrackCommand.removeTarget(self, action: #selector(self.lockScreenPreviousTrack(_:)))
}
}
example to follow up question:
#objc func lockScreenPlay(_ event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
self.playButton(AnyObject.self)
return .success
}
#objc func lockScreenNextTrack(_ event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
self.playerDidFinishPlaying()
return .success
}
Beginner here - I have a TableViewController that prints a story in a DetailView.
I have successfully implemented Last and Next buttons in my DetailView Tab Bar that when tapped, print the story at the row - 1 or row + 1 currently indexRow value. I have set the Last button to disappear at the first Row and the Next button to disappear at the last Row.
However, when I'm in DetailView and navigate Next and Last and reach the last row or first row, the Last or Next disappear respectively, and they do not come back. I suspect its due to how I've frame the if statements and need some help with fixing this. Can someone tell me the error?
Another related issue I'm having is that if you select the first or last row in the TableView, the DetailView loads with the Last or Next and these only disappear if I navigate away from the first or last story and back to the first or last story. What is the best way to solve this?
Here is my code so far.
Class DetailViewController: UIViewController: {
var selectedStory : URL!
var selectedIndex: Int!
var stories: [URL] = []
#IBOutlet weak var lastLabel: UIBarButtonItem!
#IBOutlet weak var nextLabel: UIBarButtonItem!
//MARK: - Tab Bar Next/Last Toolbar button actions
#IBAction func lastButton(_ sender: Any) {
if ((self.selectedIndex - 1) < self.stories.count) {
self.selectedIndex = self.selectedIndex - 1
let nextStory = self.stories[self.selectedIndex]
DispatchQueue.global().async {
let nextStoryText = try? String(contentsOf: nextStory)
DispatchQueue.main.async {
self.textView.text = nextStoryText
}
}
let storyTitle = nextStory.deletingPathExtension().lastPathComponent
storyTitleLabel.title = storyTitle
}
if selectedIndex == 0 {
lastLabel.title = ""
}
else {
lastLabel.title = "Last"
}
}
#IBAction func nextButton(_ sender: Any) {
if ((self.selectedIndex + 1) < self.stories.count) {
self.selectedIndex = self.selectedIndex + 1
let nextStory = self.stories[self.selectedIndex]
DispatchQueue.global().async {
let nextStoryText = try? String(contentsOf: nextStory)
DispatchQueue.main.async {
self.textView.text = nextStoryText
}
}
let storyTitle = nextStory.deletingPathExtension().lastPathComponent
storyTitleLabel.title = storyTitle
}
if ((self.selectedIndex + 1) == self.stories.count) {
nextLabel.title = ""
}
else {
nextLabel.title = "Next"
}
}
}
class TableViewController: UITableViewController {
var stories: [URL] = []
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let vc = storyboard?.instantiateViewController(withIdentifier: "DetailVC") as? DetailViewController {
vc.selectedStory = stories[indexPath.row]
vc.stories = stories
vc.selectedIndex = indexPath.row
navigationController?.pushViewController(vc, animated: true)
}
}
}
It is better to change the user interaction rather than changing the title
#IBAction func lastButton(_ sender: Any) {
if ((self.selectedIndex - 1) < self.stories.count) {
self.selectedIndex = self.selectedIndex - 1
let nextStory = self.stories[self.selectedIndex]
DispatchQueue.global().async {
let nextStoryText = try? String(contentsOf: nextStory)
DispatchQueue.main.async {
self.textView.text = nextStoryText
}
}
let storyTitle = nextStory.deletingPathExtension().lastPathComponent
storyTitleLabel.title = storyTitle
}
changeTheBarButtonUserInteraction()
}
#IBAction func nextButton(_ sender: Any) {
if ((self.selectedIndex + 1) < self.stories.count) {
self.selectedIndex = self.selectedIndex + 1
let nextStory = self.stories[self.selectedIndex]
DispatchQueue.global().async {
let nextStoryText = try? String(contentsOf: nextStory)
DispatchQueue.main.async {
self.textView.text = nextStoryText
}
}
let storyTitle = nextStory.deletingPathExtension().lastPathComponent
storyTitleLabel.title = storyTitle
}
changeTheBarButtonUserInteraction()
}
func changeTheBarButtonUserInteraction(){
lastLabel.isEnabled = selectedIndex != 0 ? true : false
nextLabel.isEnabled = ((self.selectedIndex + 1) == self.stories.count) ? false : true
}
#IBAction func lastButton(_ sender: Any) { if ((self.selectedIndex - 1) < self.stories.count) { self.selectedIndex = self.selectedIndex - 1 let nextStory = self.stories[self.selectedIndex] DispatchQueue.global().async { let nextStoryText = try? String(contentsOf: nextStory) DispatchQueue.main.async { self.textView.text = nextStoryText } } let storyTitle = nextStory.deletingPathExtension().lastPathComponent storyTitleLabel.title = storyTitle } if selectedIndex == 0 { lastLabel.title = "" } else { lastLabel.title = "Last" } nextLabel.title = "Next"}
#IBAction func nextButton(_ sender: Any) { if ((self.selectedIndex + 1) < self.stories.count) { self.selectedIndex = self.selectedIndex + 1 let nextStory = self.stories[self.selectedIndex] DispatchQueue.global().async { let nextStoryText = try? String(contentsOf: nextStory) DispatchQueue.main.async { self.textView.text = nextStoryText } } let storyTitle = nextStory.deletingPathExtension().lastPathComponent storyTitleLabel.title = storyTitle } if ((self.selectedIndex + 1) == self.stories.count) { nextLabel.title = "" } else { nextLabel.title = "Next" }lastLabel.title = "Last"} }
But it would be better if you hide and show the button because it may be possible if button have width then user is still able to press.
If you still facing difficulties please let me know.
Hey I am working on a project(which is in swift) and it compares two audio signals and measure the correctness. AUDIOKIT pod is used to convert the audio from microphone(AKAmplitudeTracker) to float numbers. I am trying to implement the same method by applying the tracker on the AKAudioPlayer. What I am trying to do is performing sampling on the source signal and the reference signal and get it as amplitude data only, and then performing DTW(Dynamic time warping) algorithm.
Is there any means by which I can get the AKAudioPlayer music to be converted as amplitude data? Is it possible to add a tracker to the AKAudioPlayer currently playing music? Codes are given below. I need some expert advices, Thanks in advance and Happy coding.
//
// Conductor.swift
// AmplitudeTracker
//
// Created by Mark Jeschke on 10/3/17.
// Copyright © 2017 Mark Jeschke. All rights reserved.
//
import AudioKit
import AudioKitUI
// Treat the conductor like a manager for the audio engine.
class Conductor {
// Singleton of the Conductor class to avoid multiple instances of the audio engine
var url:URL?
var fileName:String?
var type:String?
static let sharedInstance = Conductor()
var isPlayingKit:Bool?
var micTracker: AKAmplitudeTracker!
var mp3Tracker: AKAmplitudeTracker!
var player:AKAudioPlayer!
var mic: AKMicrophone!
var delay: AKDelay!
var reverb: AKCostelloReverb!
// Balance between the delay and reverb mix.
var reverbAmountMixer = AKDryWetMixer()
func play(file: String, type: String) -> AKAudioPlayer? {
let url = Bundle.main.url(forResource: file, withExtension: type)
let file = try! AKAudioFile(forReading: url!)
player = try! AKAudioPlayer(file: file)
if self.isPlayingKit! {
player.play()
mp3Tracker = AKAmplitudeTracker(player)
delay = AKDelay(mp3Tracker)
delay.time = 0.0
delay.feedback = 0.0
delay.dryWetMix = 0.5
reverb = AKCostelloReverb(delay)
reverb.presetShortTailCostelloReverb()
reverbAmountMixer = AKDryWetMixer(delay, reverb, balance: 0.8)
AudioKit.output = reverbAmountMixer
}
else {
self.isPlayingKit = true
AudioKit.output = nil
player.stop()
}
return player
}
init() {
AKSettings.playbackWhileMuted = true
mic = AKMicrophone()
print("INIT CONDUCTOR")
micTracker = AKAmplitudeTracker(mic)
delay = AKDelay(micTracker)
delay.time = 0.5
delay.feedback = 0.1
delay.dryWetMix = 0.5
reverb = AKCostelloReverb(delay)
reverb.presetShortTailCostelloReverb()
reverbAmountMixer = AKDryWetMixer(delay, reverb, balance: 0.8)
AudioKit.output = reverbAmountMixer
isPlayingKit = true
startAudioEngine()
}
func startAudioEngine() {
AudioKit.start()
isPlayingKit = true
print("Audio engine started")
}
func stopAudioEngine() {
AudioKit.stop()
isPlayingKit = false
print("Audio engine stopped")
}
}
The above mentioned method captures the amplitude of the microphone.
The below given is the location where I tried to use AKAmplitudeTracker on AKAudioPlayer.
//
// ViewController.swift
// AudioBoom
//
// Created by Alex Babu on 20/06/18.
// Copyright © 2018 Naico. All rights reserved.
//
import AudioKit
class ViewController: UIViewController {
var instantanousAmplitudeData = [Double]()
var timer:Timer?
var timerCount:Int?
let conductor = Conductor.sharedInstance
var player:AKAudioPlayer?
#IBOutlet weak var holderView: UIView!
#IBOutlet weak var equalizer: UILabel!
#IBOutlet weak var percentageLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
timerCount = 0
playMusicOutlet.layer.cornerRadius = playMusicOutlet.frame.size.height/2
playMusicOutlet.layer.borderColor = UIColor.cyan.cgColor
playMusicOutlet.layer.borderWidth = 2.0
playMusicOutlet.clipsToBounds = true
musicDTW.layer.cornerRadius = musicDTW.frame.size.height/2
musicDTW.layer.borderColor = UIColor.cyan.cgColor
musicDTW.layer.borderWidth = 2.0
musicDTW.clipsToBounds = true
holderView.layer.cornerRadius = holderView.frame.size.width/2
holderView.clipsToBounds = true
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBOutlet weak var playMusicOutlet: UIButton!
#IBAction func playMusic(_ sender: Any) {
playMusicOutlet.setTitle("Talk now", for: .normal)
self.equalizer.isHidden = false
timerCount = 0
AVAudioSession.sharedInstance().requestRecordPermission({(_ granted: Bool) -> Void in
if granted {
print("Permission granted")
self.timer = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { [unowned self] (timer) in
if let count = self.timerCount {
DispatchQueue.main.async {
self.timerCount = count + 1
print("Amplitude of mic detected:\(self.conductor.micTracker.amplitude)")
print("Amplitude of mic counter:\(String(describing: count))")
print("Amplitude of mp3 detected:\(self.conductor.micTracker.amplitude)")
print("Amplitude of mp3 Counter:\(String(describing: count))")
self.instantanousAmplitudeData.append(self.conductor.micTracker.amplitude)
self.equalizer.frame.size.height = CGFloat(self.conductor.micTracker.amplitude * 500)
self.percentageLabel.text = String(Int(((self.conductor.micTracker.amplitude * 500)/500) * 100)) + "%"
if count == 10000 {
timer.invalidate()
self.equalizer.isHidden = true
}
}
}
}
}
else {
print("Permission denied")
}
})
}
#IBOutlet weak var musicDTW: UIButton!
#IBAction func musicDTWAction(_ sender: Any) {
let anotherConductor = Conductor.sharedInstance
if let ccc = anotherConductor.play(file: "Timebomb", type: "mp3") {
musicDTW.setTitle("Music DTW on", for: .normal)
if let mp3Tracker = conductor.mp3Tracker {
self.equalizer.frame.size.height = CGFloat(mp3Tracker.amplitude * 500)
}
}
else {
musicDTW.setTitle("Music DTW off", for: .normal)
}
}
}
There's a lot going on with all this code, so its hard to debug it but what you describe is definitely possible and you probably just have some small thing wrong. Perhaps you can share the repo with me privately and I can fix it for you.
Try these out!
Conductor Class
import AudioKit
import AudioKitUI
// Treat the conductor like a manager for the audio engine.
class Conductor {
// Singleton of the Conductor class to avoid multiple instances of the audio engine
var url:URL?
var fileName:String?
var type:String?
static let sharedInstance = Conductor()
var isPlayingKit:Bool?
var micTracker: AKAmplitudeTracker!
var mp3Tracker: AKAmplitudeTracker!
var player:AKAudioPlayer!
var mic: AKMicrophone!
var delay: AKDelay!
var reverb: AKCostelloReverb!
// Balance between the delay and reverb mix.
var reverbAmountMixer = AKDryWetMixer()
func play(file: String, type: String) -> AKAudioPlayer? {
let url = Bundle.main.url(forResource: file, withExtension: type)
let file = try! AKAudioFile(forReading: url!)
player = try! AKAudioPlayer(file: file)
if self.isPlayingKit! {
mp3Tracker = AKAmplitudeTracker(player)
delay = AKDelay(mp3Tracker)
delay.time = 0.0
delay.feedback = 0.0
delay.dryWetMix = 0.5
reverb = AKCostelloReverb(delay)
reverb.presetShortTailCostelloReverb()
reverbAmountMixer = AKDryWetMixer(delay, reverb, balance: 0.8)
AudioKit.output = reverbAmountMixer //#1
player.play() //#2
}
else {
self.isPlayingKit = true
AudioKit.output = nil
// player.stop()
stopAudioEngine()
}
return player
}
func isPlayingAudioKit() -> Bool {
return isPlayingKit!
}
init() {
self.isPlayingKit = false
}
func initMicrophone() {
AKSettings.playbackWhileMuted = true
mic = AKMicrophone()
print("INIT CONDUCTOR")
micTracker = AKAmplitudeTracker(mic)
delay = AKDelay(micTracker)
delay.time = 1.5
delay.feedback = 0.1
delay.dryWetMix = 1.0
reverb = AKCostelloReverb(delay)
reverb.presetShortTailCostelloReverb()
reverbAmountMixer = AKDryWetMixer(delay, reverb, balance: 0.5)
AudioKit.output = reverbAmountMixer
isPlayingKit = true
startAudioEngine()
}
func startAudioEngine() {
AudioKit.start()
isPlayingKit = true
print("Audio engine started")
}
func stopAudioEngine() {
AudioKit.stop()
isPlayingKit = false
print("Audio engine stopped")
}
}
ViewController
import AudioKit
class ViewController: UIViewController {
var instantanousUserAudioData = [Float]()
var referenceAudioData = [Float]()
var timer:Timer?
var timerCount:Int?
let conductor = Conductor.sharedInstance
#IBOutlet weak var holderView: UIView!
#IBOutlet weak var equalizer: UILabel!
#IBOutlet weak var percentageLabel: UILabel!
#IBOutlet weak var timerOutlet: UIButton!
#IBOutlet weak var micOutlet: UIButton!
#IBOutlet weak var DTWOutlet: UIButton!
#IBOutlet weak var musicOutlet: UIButton!
#IBOutlet weak var resultLabel: UILabel!
#IBAction func timerAction(_ sender: Any) {
self.timer?.invalidate()
}
override func viewDidLoad() {
super.viewDidLoad()
timerCount = 0
micOutlet.layer.cornerRadius = micOutlet.frame.size.height/2
micOutlet.layer.borderColor = UIColor.cyan.cgColor
micOutlet.layer.borderWidth = 2.0
micOutlet.clipsToBounds = true
musicOutlet.layer.cornerRadius = musicOutlet.frame.size.height/2
musicOutlet.layer.borderColor = UIColor.cyan.cgColor
musicOutlet.layer.borderWidth = 2.0
musicOutlet.clipsToBounds = true
DTWOutlet.layer.cornerRadius = DTWOutlet.frame.size.height/2
DTWOutlet.layer.borderColor = UIColor.cyan.cgColor
DTWOutlet.layer.borderWidth = 2.0
DTWOutlet.clipsToBounds = true
timerOutlet.layer.cornerRadius = timerOutlet.frame.size.height/2
timerOutlet.layer.borderColor = UIColor.cyan.cgColor
timerOutlet.layer.borderWidth = 2.0
timerOutlet.clipsToBounds = true
holderView.layer.cornerRadius = holderView.frame.size.width/2
holderView.clipsToBounds = true
self.micOutlet.isEnabled = false
self.musicOutlet.isEnabled = false
AVAudioSession.sharedInstance().requestRecordPermission({(_ granted: Bool) -> Void in
self.micOutlet.isEnabled = granted
self.musicOutlet.isEnabled = granted
})
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func micAction(_ sender: Any) {
conductor.initMicrophone()
self.timerCount = 0
self.equalizer.isHidden = false
self.timer = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { [unowned self] (timer) in
if let count = self.timerCount {
DispatchQueue.main.async {
self.timerCount = count + 1
print("Amplitude of mic detected:\(self.conductor.micTracker.amplitude)")
print("Amplitude of mp3 Counter:\(String(describing: count))")
self.instantanousUserAudioData.append(Float(self.conductor.micTracker.amplitude))
self.equalizer.frame.size.height = CGFloat(self.conductor.micTracker.amplitude * 500)
self.percentageLabel.text = String(Int(((self.conductor.micTracker.amplitude * 500)/500) * 100)) + "%"
if count > 10 && self.conductor.micTracker.amplitude == 0.0 && self.instantanousUserAudioData.last == 0.0 {
self.micOutlet.backgroundColor = .green
self.micOutlet.setTitleColor(.black, for: .normal)
self.micOutlet.layer.borderColor = UIColor.red.cgColor
timer.invalidate()
}
if count == 0 {
self.micOutlet.backgroundColor = .clear
self.micOutlet.setTitleColor(.cyan, for: .normal)
self.micOutlet.layer.borderColor = UIColor.cyan.cgColor
}
}
}
}
}
#IBAction func musicAction(_ sender: Any) {
self.timerCount = 0
if self.conductor.play(file: voiceReference, type: type_mp3) != nil {
self.timer?.invalidate()
self.timer = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { [unowned self] (timer) in
if let count = self.timerCount {
DispatchQueue.main.async {
self.timerCount = count + 1
print("Amplitude of mp3 detected:\(self.conductor.mp3Tracker.amplitude)")
print("Amplitude of mp3 Counter:\(String(describing: count))")
self.referenceAudioData.append(Float(self.conductor.mp3Tracker.amplitude))
self.equalizer.frame.size.height = CGFloat(self.conductor.mp3Tracker.amplitude * 500)
self.equalizer.isHidden = false
self.percentageLabel.text = String(Int(((self.conductor.mp3Tracker.amplitude * 500)/500) * 100)) + "%"
if count > 10 && self.conductor.mp3Tracker.amplitude == 0.0 && self.referenceAudioData.last == 0.0 {
self.musicOutlet.backgroundColor = .green
self.musicOutlet.setTitleColor(.black, for: .normal)
self.musicOutlet.layer.borderColor = UIColor.red.cgColor
timer.invalidate()
}
if count == 0 {
self.musicOutlet.backgroundColor = .clear
self.musicOutlet.setTitleColor(.cyan, for: .normal)
self.musicOutlet.layer.borderColor = UIColor.cyan.cgColor
}
}
}
}
}
else {
}
}
#IBAction func resultAction(_ sender: Any) {
print("mic array:\(instantanousUserAudioData)")
print("song array:\(referenceAudioData)")
self.timer?.invalidate()
if referenceAudioData.count > 0, instantanousUserAudioData.count > 0 {
let refData = knn_curve_label_pair(curve: referenceAudioData,label: "reference")
let userData = knn_curve_label_pair(curve: instantanousUserAudioData,label: "userData")
let attempt:KNNDTW = KNNDTW()
attempt.train(data_sets: [refData,userData])
let prediction: knn_certainty_label_pair = attempt.predict(curve_to_test: referenceAudioData)
print("predicted :" + prediction.label, "with ", prediction.probability * 100,"% certainty")
resultLabel.text = "DTW cost is " + String(attempt.dtw_cost(y: referenceAudioData, x: instantanousUserAudioData))
print("COST OF THE DTW ALGORITHM IS : \(String(attempt.dtw_cost(y: referenceAudioData, x: instantanousUserAudioData)))")
}
}
}
I am a beginner to Xcode and Swift and I am currently creating an application where the user adds a person on the application and after that it right the amount of money they owe that person or that person owes him/her.
Note: I have used core data to store all the value
I actually want to change the value of a variable when switch is on and off. For instance, in the following I want the "amount" to be negative when the switch is on and positive when it is off. Also, when I try to do this and send amount variable to previous view controller I can't send the value depending on the UISwitch because it always shows positive. I am trying to find a solution to this from past 3 days therefore can you please help me? Thanks a lot in advance
Owe ViewController
import UIKit
class NewOweTableViewController: UIViewController {
#IBOutlet weak var titleTextField: UITextField!
#IBOutlet weak var locationTextField: UITextField!
#IBOutlet weak var amountTextField: UITextField!
#IBOutlet weak var datePicker: UIDatePicker!
let context = (UIApplication.shared.delegate as!
AppDelegate).persistentContainer.viewContext
var owe: Owe?
var dataInfo: [Owe] = []
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.viewWithTag(1)?.isHidden = true
let saveBTN = UIBarButtonItem(barButtonSystemItem:UIBarButtonSystemItem.save, target:self,
action: #selector(saveButtonTapped(_:)))
let deleteBTN = UIBarButtonItem(barButtonSystemItem:UIBarButtonSystemItem.trash, target:self,
action: #selector(deleteButtonTapped(_:)))
self.navigationItem.rightBarButtonItems = [saveBTN, deleteBTN]
if !dataInfo.isEmpty {
titleTextField.text = dataInfo[0].name
amountTextField.text = (NSString(format: "%.2f", (dataInfo[0].amount) as CVarArg) as String)
locationTextField.text = dataInfo[0].location
datePicker.date = dataInfo[0].date!
}
}
#objc func saveButtonTapped(_ sender: UIButton){
if !dataInfo.isEmpty{
let data = dataInfo[0]
data.name = titleTextField.text
data.amount = Double(amountTextField.text!)!
data.location = locationTextField.text
data.date = datePicker.date
}
else if titleTextField.text == "" || amountTextField.text == "" || locationTextField.text == "" {
return
}
else{
let data = Owe(context: context)
data.name = titleTextField.text
data.amount = Double(amountTextField.text!)!
data.location = locationTextField.text
data.date = datePicker.date
}
do {
try context.save()
navigationController?.popViewController(animated: true)
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
#objc func deleteButtonTapped(_ sender: UIButton){
if !dataInfo.isEmpty{
let data = dataInfo[0]
context.delete(data)
do {
try context.save()
navigationController?.popViewController(animated: true)
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
#IBAction func oweSwitch(_ sender: UISwitch) {
if sender.isOn {
owe?.amount = (owe?.amount)! * (-1)
amountTextField.textColor = UIColor.green
} else {
owe?.amount = (owe?.amount)! * (1)
amountTextField.textColor = UIColor.red
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Previous View Controller
import UIKit
class PersonDetailTableViewController: UITableViewController {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var totalLabel: UILabel?
var person: People?
var owe: Owe?
#IBOutlet var personTable: UITableView!
var dataInfo: [Owe] = []
var selectedObject: [Owe] = []
var balanceAmount = "Balance: "
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (dataInfo.count)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = personTable
.dequeueReusableCell(withIdentifier: "detailsCell", for: indexPath)
cell.textLabel?.text = dataInfo[indexPath.row].name
cell.detailTextLabel?.text = "₹ \(dataInfo[indexPath.row].amount)"
if dataInfo[indexPath.row].amount < 0 {
cell.detailTextLabel?.textColor = UIColor.red
} else {
cell.detailTextLabel?.textColor = UIColor.green
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedObject = [dataInfo[indexPath.row]]
performSegue(withIdentifier: "addOweDetails", sender: nil)
tableView.deselectRow(at: indexPath, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
getData()
personTable.dataSource = self
addTotalToNav()
print(dataInfo as Any)
}
// MARK: - Table view data source
func addTotalToNav() -> Void {
if let navigationBar = self.navigationController?.navigationBar {
let totalFrame = CGRect(x: 10, y: 0, width: navigationBar.frame.width/2, height: navigationBar.frame.height)
totalLabel = UILabel(frame: totalFrame)
totalLabel?.text = balanceAmount
totalLabel?.tag = 1
totalLabel?.font = UIFont.boldSystemFont(ofSize: 14)
totalLabel?.textColor = UIColor.red
// navigationBar.large = totalLabel?.text
self.title = totalLabel?.text
}
}
func getData() -> Void {
do{
dataInfo = try context.fetch(Owe.fetchRequest())
var total:Double = 0.00
for i in 0 ..< dataInfo.count {
total += dataInfo[i].amount as! Double
}
balanceAmount = "Balance: ₹" + (NSString(format: "%.2f", total as CVarArg) as String)
}
catch{
print("Fetching Failed")
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! NewOweTableViewController
vc.dataInfo = selectedObject
selectedObject.removeAll()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
getData()
personTable.reloadData()
if (self.navigationController?.navigationBar.viewWithTag(1)?.isHidden == true){
self.navigationController?.navigationBar.viewWithTag(1)?.removeFromSuperview()
addTotalToNav()
}
}
}
Core Data for owe
import UIKit
import CoreData
#objc(Owe)
public class Owe: NSManagedObject {
var date: Date? {
get{
return rawDate as Date?
}
set {
rawDate = newValue as NSDate?
}
}
convenience init?(name: String?, location: String?, amount: Double, date: Date?) {
let appDelegate = UIApplication.shared.delegate as? AppDelegate
guard let context = appDelegate?.persistentContainer.viewContext
else {
return nil
}
self.init(entity: Owe.entity(), insertInto: context)
self.name = name
self.location = location
self.amount = amount
self.date = date
}
}
Hi there and welcome to the Swift Community!
If I understand correctly, you are trying to propagates updates backwards from NewOweTableViewController to PersonDetailTableViewController. If that is the case, an easy way to achieve this with your MVC architecture is by passing a closure to NewOweTableViewController when you initialize it in PersonDetailTableViewController.
In order to do so,
Update NewOweTableViewController and add a closure property.
class NewOweTableViewController: UIViewController {
// ...
var switchValueUpdate: ((Bool) -> ())?
// ...
}
Make sure you call this closure inside your #IBAction that you link to your switch in NewOweTableViewController
#IBAction func oweSwitch(_ sender: UISwitch) {
if sender.isOn {
owe?.amount = (owe?.amount)! * (-1)
amountTextField.textColor = UIColor.green
} else {
owe?.amount = (owe?.amount)! * (1)
amountTextField.textColor = UIColor.red
}
switchValueUpdate?(sender.isOn)
}
update PersonDetailTableViewController to set the closure
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! NewOweTableViewController
vc.dataInfo = selectedObject
selectedObject.removeAll()
vc.switchValueUpdate = { (isOn) in
// Here you go, update PersonDetailTableViewController to reflect changes related to the switch!
}
}
That's it! Let me know if you have any question on that code, hope it helps!