Using NSCore and NSKeyedArchiver with SWIFT - swift

I been trying to get persistent data on my app to have a history of user entries. After I store my data in to array I want to archive it, and after I unarchive it i get weird value instead of what i want to see.
Here is my class for where i store my data
import Foundation
class MyHistory: NSObject, NSCoding {
var kicksNumber: Int
var durationNumber: Int
init(kicksNumber: Int,durationNumber: Int) {
self.kicksNumber = kicksNumber
self.durationNumber = durationNumber
}
required init(coder decoder: NSCoder) {
kicksNumber = decoder.decodeObjectForKey("kicksNumber") as! Int
durationNumber = decoder.decodeObjectForKey("durationNumber") as! Int
}
func encodeWithCoder(coder: NSCoder) {
coder.encodeObject(self.kicksNumber, forKey: "kicksNumber")
coder.encodeObject(self.durationNumber, forKey: "durationNumber")
}
}
Then here is my class where things happen, And where I am testing out the save and load process.
class Kicks: UIViewController {
var myHistoryArray: [MyHistory] = []
var currentMyHistory: MyHistory!
var newHistory = [MyHistory]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = UIColor(patternImage: UIImage(named: "background13.png")!)
let defaults = NSUserDefaults.standardUserDefaults()
if let savedPeople = defaults.objectForKey("MyHistory") as? NSData {
newHistory = NSKeyedUnarchiver.unarchiveObjectWithData(savedPeople) as! [MyHistory]
//print("this is archived ", newHistory[0])
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
var count = 0 as Int
var countKicks = 0 as Int
var kickReached = false as Bool
var pressedOnce = true as Bool
var timer = NSTimer()
var test: MyHistory!
#IBOutlet var timerLabel: UITextField!
#IBOutlet var kicksLabel: UITextField!
#IBAction func kickButton() {
//currentMyHistory.kicksNumber = 5
if pressedOnce {
pressedOnce = false
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("counter"), userInfo: nil, repeats: true)
} else if kickReached {
// let date = NSDate()
// let calendar = NSCalendar.currentCalendar()
// let timer_total = calendar.components([ .Hour, .Minute, .Second], fromDate: date)
} else if !pressedOnce {
countKicks++
kicksLabel.text = "\(countKicks)"
if countKicks == 10 {
kickReached = true
timer.invalidate()
congratsAlert()
currentMyHistory = MyHistory(kicksNumber: 5, durationNumber: 10)
print("this is currentMyHistory", currentMyHistory.kicksNumber )
myHistoryArray.append(currentMyHistory)
test = myHistoryArray[0]
print("this is myHistoryArray0", test.kicksNumber)
//save data
let savedData = NSKeyedArchiver.archivedDataWithRootObject(myHistoryArray)
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(savedData, forKey: "MyHistory")
//load data
//let defaults = NSUserDefaults.standardUserDefaults()
// let person = people[indexPath.item]
//let historyUnarchived = NSKeyedUnarchiver.unarchiveObjectWithFile("/path/to/archive") as? [MyHistory]
// let data1 = NSUserDefaults.standardUserDefaults().objectForKey("myHistoryArray")
print("this is unrachived",newHistory[0])
clear()
}
}
}
// save countKicks, count, and stamp i
func congratsAlert() {
let alert = UIAlertController(title: "Congratulation", message: "Yay!!! Angelina kicked 10 times in less than 2 hours.",preferredStyle: .Alert)
let okAction = UIAlertAction(title: "Ok",style: .Default,handler:{(action:UIAlertAction) -> Void in})
alert.addAction(okAction)
presentViewController(alert,animated: true,completion: nil)
}
func clear() {
count = 0
countKicks = 0
kickReached = false
pressedOnce = true
timerLabel.text = "00:00:0\(count)"
kicksLabel.text = "\(countKicks)"
}
func counter() {
++count
let (hour,minutes,seconds) = secondsToHoursMinutesSeconds(count)
if seconds < 10 && minutes < 10 {
timerLabel.text = "0\(hour):0\(minutes):0\(seconds)"
} else if seconds > 9 && minutes < 10 {
timerLabel.text = "0\(hour):0\(minutes):\(seconds)"
} else if seconds > 9 && minutes > 9 {
timerLabel.text = "0\(hour):\(minutes):\(seconds)"
} else if seconds < 10 && minutes > 9 {
timerLabel.text = "0\(hour):\(minutes):0\(seconds)"
}
}
func secondsToHoursMinutesSeconds (seconds : Int) -> (Int, Int, Int) {
return (seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60)
}
/*
func savePlaces() {
let placesArray = [myHistory(kicksNumber: 420, durationNumber: 89)]
let placesData = NSKeyedArchiver.archivedDataWithRootObject(placesArray)
NSUserDefaults.standardUserDefaults().setObject(placesData, forKey: "kicks")
}
func loadPlaces() {
let placesData = NSUserDefaults.standardUserDefaults().objectForKey("kicks") as? NSData
if let placesData = placesData {
let placesArray = NSKeyedUnarchiver.unarchiveObjectWithData(placesData) as? [myHistory]
if let placesArray = placesArray {
// do something…
}
}
}*/
}
My output is like this:
this is currentMyHistory 5
this is myHistoryArray0 5
this is unrachived
Message from debugger: Terminated due to signal 15
why is unarchived is weird value?

In your MyHistory class you are using ints, so in your encodeWithCoder function you should be using
coder.encodeInteger(self.kicksNumber, forKey: "kicksNumber")
coder.encodeInteger(self.durationNumber, forKey: "durationNumber")
Likewise for your decoder you should be using decodeIntForKey, not decodeObjectForKey.
kicksNumber = decoder.decodeIntegerForKey("kicksNumber")
durationNumber = decoder.decodeIntegerForKey("durationNumber")

Related

How to prevent periodicTimeObserver updating the progressBar while user drags the slider manually?

I have a periodicTimeObserver and it updates the elapsed and remaining timeLabels in the way I want, but the slider is jumping. How to prevent periodicTimeObserver updating the UISlider while user drags the slider manually?
This is my UISlider
private lazy var progressBar: UISlider = {
let v = UISlider()
v.translatesAutoresizingMaskIntoConstraints = false
//v.minimumTrackTintColor = UIColor(named: "PlayerColors")
v.isContinuous = false
return v
}()
Periodic time observer which updates the UISlider and the elapsed and remaining time labels.
player = AVPlayer(playerItem: playerItem)
player!.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, preferredTimescale: 1), queue: DispatchQueue.main) { (CMTime) -> Void in
if self.player!.currentItem?.status == .readyToPlay {
let currentTime : Float64 = CMTimeGetSeconds(self.player!.currentTime());
let totalTime : Float64 = CMTimeGetSeconds(self.player!.currentItem!.duration);
self.progressBar.value = Float(currentTime)
self.progressBar.minimumValue = 0
self.progressBar.maximumValue = Float(totalTime)
self.elapsedTimeLabel.text = self.stringFromTimeInterval(interval: currentTime)
self.remainingTimeLabel.text = self.stringFromTimeIntervalRemaining(interval: totalTime - currentTime)
The function that should seek to a point of the audio and update the time labels.
#objc func progressScrubbed(_ :UISlider) {
let seconds : Int64 = Int64(self.progressBar.value)
let targetTime:CMTime = CMTimeMake(value: seconds, timescale: 1)
player!.seek(to: targetTime)
if player!.rate == 0
{
play()
}
}
You need to know if user is interacting with a slider in order to ignore PeriodicTimeObserver. Moreover you need to reset PeriodicTimeObserver on each seek. So let's create a custom UISlider and override a one method:
class MySlider: UISlider {
var onTouchesBegan: (() -> ())?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
onTouchesBegan?()
}
}
Now you can create a parameter which will track if slider is touched or not and set it in closures of MySlider:
private var isTouchingSlider: Bool = false
private lazy var progressBar: MySlider = {
let v = MySlider()
v.translatesAutoresizingMaskIntoConstraints = false
v.isContinuous = false
v.onTouchesBegan = { [weak self] in
self?.isTouchingSlider = true
}
return v
}()
And your periodic observer methods would look like this:
var periodicObserverToken: Any?
func addPeriodicTimeObserver() {
let interval = CMTime(
seconds: 1,
preferredTimescale: CMTimeScale(NSEC_PER_SEC)
)
periodicObserverToken = player?.addPeriodicTimeObserver(forInterval: interval, queue: .main) { [weak self] _ in
guard let `self` = self, let player = self.player, player.currentItem?.status == .readyToPlay, let currentItem = self.player.currentItem, !self.isTouchingSlider else { return }
let currentTime : Float64 = CMTimeGetSeconds(player.currentTime());
let totalTime : Float64 = CMTimeGetSeconds(currentItem.duration);
self.progressBar.value = Float(currentTime)
self.progressBar.minimumValue = 0
self.progressBar.maximumValue = Float(totalTime)
}
}
private func removePeriodicTimeObserver() {
guard let periodicObserverToken = periodicObserverToken else { return }
player?.removeTimeObserver(periodicObserverToken)
self.playerPeriodicTimeObserver = nil
}
You need to make all the necessary updates when slider is updated:
#objc func progressScrubbed(_ :UISlider) {
let seconds : Int64 = Int64(self.progressBar.value)
let targetTime:CMTime = CMTimeMake(value: seconds, timescale: 1)
removePeriodicTimeObserver()
isTouchingSlider = false
player!.seek(to: targetTime)
addPeriodicTimeObserver()
if player!.rate == 0 {
play()
}
}

app reverts all input variables to defaults after some time

I wrote my fist app, very simple, that creates a user defined number of badges at random times during a user defined window of time. It works fine but after some time (not sure how long, 2-4 hours), all of the user input information reverts to the defaults of the program. The issue is it is supposed to run each day but it is annoying to have to set it each morning. I am not sure if this is a coding issue or if the app 'reboots' when it is not doing anything in the background. Note that this occurs on my iPhone 8 but not on the simulator (or I am not patient enough for it to occur on the simulator).
I have put several print and label to try to identify when it occurs; I am sure I am putting them in the correct places. I apologize for including so much code - I tried to weed some of the mistakes out but I do not know where the problem is.
import UserNotifications
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var EarlyTimePicker: UITextField!
#IBOutlet weak var LateTimePicker: UITextField!
#IBOutlet weak var NumQuestions: UITextField!
#IBOutlet weak var myLabel_Questions: UILabel!// Attached to the label box
#IBOutlet weak var myLabel_StartEndTime: UILabel!
#IBOutlet weak var myLabel_TestResetTime: UILabel!
#IBOutlet weak var myLabel_CurrentEarlyTime: UILabel!
private var earlyTimePicker: UIDatePicker?
private var lateTimePicker: UIDatePicker?
override func viewDidLoad() {
super.viewDidLoad()
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in //ask for permission in order to show messages on the lock screen
if granted {
print("Yay!")
} else {
print("D'oh")
}
}
earlyTimePicker = UIDatePicker()
earlyTimePicker?.datePickerMode = .time //change to .time
earlyTimePicker?.addTarget(self, action: #selector(ViewController.earlyTimeChanged(earlyTimePicker:)),for: .valueChanged)
lateTimePicker = UIDatePicker()
lateTimePicker?.datePickerMode = .time //change to .time
lateTimePicker?.addTarget(self, action: #selector(ViewController.lateTimeChanged(lateTimePicker:)),for: .valueChanged)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ViewController.viewTapped(gestureRecognizer:)))
view.addGestureRecognizer(tapGesture)
EarlyTimePicker.inputView = earlyTimePicker
LateTimePicker.inputView = lateTimePicker
}
#objc func viewTapped(gestureRecognizer: UITapGestureRecognizer){
view.endEditing(true)
}
var earlyTime=480
var earlyTimehour=0
var earlyTimeminute=0
#objc func earlyTimeChanged(earlyTimePicker: UIDatePicker){
let earlyTimeFormatter = DateFormatter()
earlyTimeFormatter.dateFormat = "h:mm a"
earlyTimeFormatter.amSymbol = "AM"
earlyTimeFormatter.pmSymbol = "PM"
EarlyTimePicker.text = earlyTimeFormatter.string(from: earlyTimePicker.date)
view.endEditing(true)
let earlyTimedate = earlyTimePicker.date
let earlyTimecomponents = Calendar.current.dateComponents([.hour, .minute], from: earlyTimedate)
earlyTimehour = earlyTimecomponents.hour!
earlyTimeminute = earlyTimecomponents.minute!
earlyTime = earlyTimecomponents.hour! * 60 + earlyTimecomponents.minute!
print("earlyTimehour: \(earlyTimecomponents.hour!)")
print("earlyTimeminute: \(earlyTimecomponents)")
print("earlyTime: \(earlyTime)")
print("Current Time: \(Date())")
}
var lateTime=1200
var lateTimehour=0
var lateTimeminute=0
#objc func lateTimeChanged(lateTimePicker: UIDatePicker){
let lateTimeFormatter = DateFormatter()
lateTimeFormatter.dateFormat = "h:mm a"
lateTimeFormatter.amSymbol = "AM"
lateTimeFormatter.pmSymbol = "PM"
LateTimePicker.text = lateTimeFormatter.string(from: lateTimePicker.date)
view.endEditing(true)
let lateTimedate = lateTimePicker.date
let lateTimecomponents = Calendar.current.dateComponents([.hour, .minute], from: lateTimedate)
lateTimehour = lateTimecomponents.hour!
lateTimeminute = lateTimecomponents.minute!
lateTime = lateTimecomponents.hour! * 60 + lateTimecomponents.minute!
let testMinute = lateTime % 60
let testHour = lateTime / 60
print("lateTimehour: \(lateTimecomponents.hour!)")
print("lateTimeminute: \(lateTimecomponents)")
print("lateTime: \(lateTime)")
print("testHour: \(testHour)")
print("testMinute: \(testMinute)")
myLabel_TestResetTime.text = "Time Set \(Date())"
myLabel_CurrentEarlyTime.text = "Current Early Time: \(earlyTime) / OnOff: \(OnOff)"
}
let PickedString = ["One","Two","Three","Four", "Five","Six","Seven","Eight"]
// #IBAction func TestCallFunction(_ sender: UIButton) {
// scheduleLocal()
// }
//NEED TO REPEAT THIS FUNCTION AT EARLY TIME - 10
//need to stop repeating with a cancel button (while bool true, do it, while false, stop. Default is false)
var RunDaily: Timer?
var OnOff = false
var QuestionNum = 1
#IBAction func Launch(_ sender: UIButton) {
OnOff = true
let center = UNUserNotificationCenter.current()
center.removeAllPendingNotificationRequests()
guard let QuestionNumA = Int(NumQuestions.text!) else { //This is how to get the UserInterface VALUE as a number
print("not a number!: \(String(describing: NumQuestions.text))")
return
}
print("Number of Questions: \(QuestionNumA)")
// var QuestionNum = 1
if QuestionNumA > 10 {QuestionNum=10} else {QuestionNum=QuestionNumA}
print("QuestionNumA:\(QuestionNumA) vs QuestionNum: \(QuestionNum)")
printStuff()
showMessage()
}
#IBAction func Stop(_ sender: UIButton) {
OnOff = false
printStuff()
}
func printStuff() {
if OnOff == true {
print("Bool is On : \(OnOff)")
RunDaily = Timer.scheduledTimer(timeInterval: 86400, target: self, selector: #selector(showMessage), userInfo: nil, repeats: true)//86400
}
if OnOff == false {
print("Bool is Off : \(OnOff)")
RunDaily?.invalidate()
let center = UNUserNotificationCenter.current()
center.removeAllPendingNotificationRequests()
}
}
func SaveDefaultData(){ // THis is the structure to SAVE input data for when the app relaunches (causes error when run.
let defaults = UserDefaults.standard
defaults.set("Date()", forKey:"key1")
//defaults.set(earlyTimePicker, forKey:"earlyTimePickerSet") cannot set earlyTimePicker. causes crash
// defaults.set(lateTimePicker, forKey:"lateTimePickerSet")
defaults.set(earlyTime, forKey:"earlyTimeSet")
defaults.set(lateTime, forKey:"lateTimeSet")
defaults.set(QuestionNum, forKey:"QuestionNumSet")
}
func SetDefaultData(){// THis is the structure to Set input for when the app relaunches
let defaults = UserDefaults.standard
if let savedValue = defaults.string(forKey: "key1"){
print("Here you will get saved value \(savedValue)")
} else {
print("No value in Userdefault,Either you can save value here or perform other operation")
defaults.set("Here you can save value", forKey: "key1")
}
if let earlyTimeValue = defaults.string(forKey: "earlyTimeSet"){
print("Here you will get saved value \(earlyTimeValue)")
earlyTime = UserDefaults.standard.value(forKey: "earlyTimeSet") as? Int ?? 485
} else {
print("No value in Userdefault,Either you can save value here or perform other operation")
defaults.set("Here you can save value", forKey: "earlyTimeSet")
earlyTime = 500
}
if let lateTimeValue = defaults.string(forKey: "lateTimeSet"){
print("Here you will get saved value \(lateTimeValue)")
lateTime = UserDefaults.standard.value(forKey: "lateTimeSet") as? Int ?? 1265
} else {
print("No value in Userdefault,Either you can save value here or perform other operation")
defaults.set("Here you can save value", forKey: "lateTimeSet")
lateTime = 1230
}
if let QuestionNumValue = defaults.string(forKey: "QuestionNumSet"){
print("Here you will get saved value \(QuestionNumValue)")
QuestionNum = UserDefaults.standard.value(forKey: "QuestionNumSet") as? Int ?? 4
} else {
print("No value in Userdefault,Either you can save value here or perform other operation")
defaults.set("Here you can save value", forKey: "QuestionNumSet")
QuestionNum = 2
}
}
#objc func showMessage() {
let center = UNUserNotificationCenter.current()
if lateTime <= earlyTime {
lateTime = earlyTime+1
if earlyTimehour <= 12 {
LateTimePicker.text = "\(earlyTimehour):\(earlyTimeminute) AM"
}
if earlyTimehour > 12 {
let EarlyTimeAfternoon = earlyTimehour - 12
LateTimePicker.text = "\(EarlyTimeAfternoon):\(earlyTimeminute) PM"
}
}
// center.removeAllPendingNotificationRequests()
// THIS IS WHERE ALL THE USER INPUT GETS INTO THE PROGRAM //
// guard let QuestionNumA = Int(NumQuestions.text!) else { //This is how to get the UserInterface VALUE as a number
// print("not a number!: \(String(describing: NumQuestions.text))")
// return
// }
//print("Number of Questions: \(QuestionNumA)")
// THIS IS WHERE ALL THE USER INPUT GETS INTO THE PROGRAM //
var RandHourArray:[Int] = [0]
var RandMinArray:[Int] = [0]
var RandQuestionArray:[Int] = [0]
var Counter = 1
// var QuestionNum = 1
//if QuestionNumA > 2 {QuestionNum=10} else {QuestionNum=QuestionNumA}
// print("QuestionNumA:\(QuestionNumA) vs QuestionNum: \(QuestionNum)")
for _ in 0 ... QuestionNum-1{
// Pick random times for badges
//let RandHour = Int.random(in: earlyTimehour ... lateTimehour)
let RandTimeMinFromMidnight = Int.random(in: self.earlyTime ... self.lateTime)
let ConvertRandTimeHours = RandTimeMinFromMidnight / 60
let ConvertRandTimeMinutes = RandTimeMinFromMidnight % 60
RandHourArray.append(ConvertRandTimeHours)
//let RandMin = Int.random(in: earlyTimeminute ... lateTimeminute)
RandMinArray.append(ConvertRandTimeMinutes)
let RandQuestion = Int.random(in: 0 ... self.PickedString.count-1)
RandQuestionArray.append(RandQuestion)
//print("RandTimeMinFromMidnight: \(RandTimeMinFromMidnight)")
// print("RandHourArray: \(RandHourArray)")
// print("ConvertRandTimeHours: \(ConvertRandTimeHours)")
// print("RandMinArray: \(RandMinArray)")
// print("ConvertRandTimeMinutes: \(ConvertRandTimeMinutes)")
}
myLabel_Questions.text = "# of questions: \(QuestionNum)"//\(QuestionNumA)"
myLabel_StartEndTime.text = "Start Time \(earlyTime) / End Time \(lateTime)"
let content_A = UNMutableNotificationContent()
content_A.title = "Prompt"
content_A.body = self.PickedString[RandQuestionArray[Counter]] //
content_A.categoryIdentifier = "alarm"
content_A.userInfo = ["customData": "fizzbuzz"]
content_A.sound = UNNotificationSound.default
var dateComponents_A = DateComponents()
dateComponents_A.hour = RandHourArray[Counter]
dateComponents_A.minute = RandMinArray[Counter]
let trigger_A = UNCalendarNotificationTrigger(dateMatching: dateComponents_A, repeats: false)
let request_A = UNNotificationRequest(identifier: UUID().uuidString, content: content_A, trigger: trigger_A)
center.add(request_A)
print("Request A time: \(RandHourArray[Counter]) : \(RandMinArray[Counter])")
print("Question String picked A: \(self.PickedString[RandQuestionArray[Counter]])")
Counter=2
if Counter<=QuestionNum {
let content_B = UNMutableNotificationContent()
content_B.title = "Prompt"
content_B.body = self.PickedString[RandQuestionArray[Counter]]
content_B.categoryIdentifier = "alarm"
content_B.userInfo = ["customData": "fizzbuzz"]
content_B.sound = UNNotificationSound.default
var dateComponents_B = DateComponents()
dateComponents_B.hour = RandHourArray[Counter]
dateComponents_B.minute = RandMinArray[Counter]
let trigger_B = UNCalendarNotificationTrigger(dateMatching: dateComponents_B, repeats: false)
let request_B = UNNotificationRequest(identifier: UUID().uuidString, content: content_B, trigger: trigger_B)
center.add(request_B)
print("Request B time: \(RandHourArray[Counter]) : \(RandMinArray[Counter])")
print("Question String picked B: \(self.PickedString[RandQuestionArray[Counter]])")
}
}
}
You should store your data inside UserDefaults, Keychain and Core Data or other stuff. if you dont store your data every time you close the application all the data will deallocate from the memory because they were stored in the heap.
Unsaved data:
let myLabel: UILabel = UILabel()
myLabel.text = "Some text"
Should save like:
UserDefaults.standard.setValue(myLabel.text, forKey: "it.is.custom")
And load like:
myLabel.text = UserDefaults.standard.value(forKey: "it.is.custom") as? String
refrence to study: https://fluffy.es/persist-data/

Saving HighScore in Swift 4

This code has all my labels Im trying to use. I can't save the High score and implement it into the game. Now its saying I need to type more so i'm just going to keep typing until it tells me i'm good. It still hasn't told me i'm goo i'm actually very surprised wow.
import UIKit
import CoreData
import SpriteKit
var timer:Timer?
var seconds:Int = 5
var maxSeconds: Int = 5
var totalPoints:Int = 0
var high:Int = 0
let userDefaults = UserDefaults.standard
let defaults = UserDefaults.standard
class ViewController: UIViewController {
#IBOutlet weak var menu: UIButton!
var i = 0
var point = 0
#IBOutlet weak var highScore: UILabel!
#IBOutlet weak var timeLabel:UILabel?
#IBOutlet weak var points:UILabel?
#IBOutlet weak var totalPoint: UILabel!
#objc func tapped(){
i += 1
switch i {
case 1:
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.error)
case 2:
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.success)
case 3:
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.warning)
case 4:
let generator = UIImpactFeedbackGenerator(style: .light)
generator.impactOccurred()
case 5:
let generator = UIImpactFeedbackGenerator(style: .medium)
generator.impactOccurred()
case 6:
let generator = UIImpactFeedbackGenerator(style: .heavy)
generator.impactOccurred()
default:
let generator = UISelectionFeedbackGenerator()
generator.selectionChanged()
i = 0
}
}
func updateTimeLabel()
{
if(timeLabel != nil)
{
let sec:Int = seconds % 30
let sec_p:String = String(format: "%02d", sec)
timeLabel!.text = "\(sec_p)"
}
}
#objc func onUpdateTimer() -> Void
{
if(seconds > 0 && seconds <= maxSeconds)
{
seconds -= 1
updateTimeLabel()
}
else if(seconds == 0)
{
if(timer != nil)
{
timer!.invalidate()
timer = nil
userDefaults.set(totalPoints, forKey: "totalPoints")
let alertController = UIAlertController(title: "Time Up!", message: "Your time is up! You got a score of \(point) points and your total coins now is \(totalPoints). You Can Do Better", preferredStyle: .alert)
let restartAction = UIAlertAction(title: "Play Again!", style: .default, handler: nil)
alertController.addAction(restartAction)
let FirstSubview = alertController.view.subviews.first
let AlertContentView = FirstSubview?.subviews.first
for subview in (AlertContentView?.subviews)! {
subview.backgroundColor = UIColor(red: 226/255.0, green: 158/255.0, blue: 152/255.0, alpha: 5.0)
subview.layer.cornerRadius = 1
subview.alpha = 1
}
self.present(alertController, animated: true, completion: nil)
point = 0
seconds = maxSeconds
updateTimeLabel()
menu.isHidden = false
defaults.set(high, forKey: "high")
}
}
}
#IBAction func Restart(_ sender: Any) {
}
#IBAction func adder(_ sender: Any)
{
point += 1
points?.text = "\(point)"
if point % 10 == 0 {
totalPoints = 10 + totalPoints
totalPoint?.text = String(totalPoints)
}
if(timer == nil)
{
timer = Timer.scheduledTimer(timeInterval: 1.0, target:self, selector:#selector(onUpdateTimer), userInfo:nil, repeats:true)
}
tapped()
menu.isHidden = true
}
override func viewDidLoad() {
points?.text = "\(point)"
let total = userDefaults.integer(forKey: "totalPoints")
if total != 0 {
totalPoints = total
} else {
totalPoints = 0
}
let score = defaults.integer(forKey: "high")
if high < point {
high = score
} else {
high = 0
}
totalPoint?.text = String(totalPoints)
updateTimeLabel()
highScore.text = String(high)
}
}
Do I need to put something at the end? Well it looks like that didn't work either!
#for example
func saveHighScore() {
UserDefaults.standard.set(score, forKey: "HIGHSCORE")
}

How to assign a different audio URL for each reusable cell in Swift

I have 2 (or more) reusable collectionView cell and each one have to play a different audio. My problem is that when the audio1 finish, audio2 file start in the same cell of the audio2. If I manually play on each cell there's no problem, but if I want to play all audio automatically one after the other, all audio are played in the same cell. How I can start the next audio in the next cell if the cell has not yet been created?
Here how I append to array:
func appendToArray() {
for (_, page) in self.resources.enumerate() {
for (index,resource) in page.enumerate() {
print("Passa di qui") // Qui passa
if resource.fileType() == .Audio {
S3Client.sharedInstance.downloadResource(resourceKey: resource.value, completion: { (success, file) in
// let files = String(file)
self.audioURLs.append(file)
/**
if self.audioURLs.count == self.resources.count {
// print("audioURLs \(self.audioURLs[index])")
MediaAudioPlayer.sharedInstance.queueTrack(self.audioURLs)
}
*/
})
}
}
}
}
This is the cellForItemAtIndexPath:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
case .Audio:
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(MediaAudioCell.kCellIdentifier, forIndexPath: indexPath) as! MediaAudioCell
cell.activityIndicator.startAnimating()
cell.activityIndicator.hidden = false
S3Client.sharedInstance.downloadResource(resourceKey: resource.value, completion: { (success, file) in
if success == true && file != nil {
cell.activityIndicator.stopAnimating()
cell.activityIndicator.hidden = true
cell.audioURL = file!
// Make slider indipendent from cell to another
cell.sliderAudio.tag = indexPath.row
cell.sliderAudio.addTarget(self, action: "sliderChange:", forControlEvents: .ValueChanged)
// print("ArrayURL: \(file)")
// print("CiaoCell : \(self.audioURLs.count)")
// print("Ciaoself.resources.countCell : \(self.resources.count)")
/**
if self.audioURLs.count == self.resources.count {
// print("audioURLs \(self.audioURLs[index])")
let item = self.audioURLs[indexPath.row] print("item: \(item)")
}
if self.audioURLs.count == self.resources.count {
// print("audioURLs \(self.audioURLs[index])")
// MediaAudioPlayer.sharedInstance.queueTrack(self.audioURLs)
}
*/
// Display total audio leinght
let asset = AVURLAsset(URL: file!, options: nil)
let audios = asset.tracksWithMediaType(AVMediaTypeAudio)
if let audios: AVAssetTrack = audios[0] {
let audioDuration:CMTime = audios.timeRange.duration
let seconds:Float64 = CMTimeGetSeconds(audioDuration)
cell.labelAudio.text = cell.stringFromTimeInterval(NSTimeInterval(seconds)) as String
}
}
})
return cell
}
This is part of cell's Class:
override func awakeFromNib() {
super.awakeFromNib()
// Partenza automatica, dopo 2secondi, se AccessibilitĂ  su ON
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(2 * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue()) {
if self.defaults.boolForKey("AutomaticStart") == true && self.defaults.boolForKey("goBackPressed") == false {
if let audioURL = self.audioURL {
// Set AVAudioSession for recording and playing at the same time
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(AVAudioSessionCategoryPlayback)
try session.setActive(true)
} catch _ {}
// If audio is playing, do not pass to next if cell is created, but continue to playing.
if MediaAudioPlayer.sharedInstance.player?.playing == true { // Se metto a 'false', ed elimino 'else', non parte in automatico.
} else {
MediaVideoPlayer.sharedInstance.stop()
MediaAudioPlayer.sharedInstance.playPauseAudio(audioURL: audioURL, delegate: self)
}
}
}
}
}
And this is the player class:
class MediaAudioPlayer: NSObject, AVAudioPlayerDelegate {
static let sharedInstance = MediaAudioPlayer()
private var delegate: MediaAudioPlayerDelegate?
var player: AVAudioPlayer?
private var lastURL: NSURL?
private var timer: NSTimer?
internal var sliderTouched: Bool = false
var tracks = Array<NSURL?>()
var currentTrackIndex = 0
override init() {
super.init()
}
// MARK: Setup
func playPauseAudio(audioURL url: NSURL, delegate: MediaAudioPlayerDelegate) {
self.delegate?.playing = true // Set default play button on last delegate
self.delegate = delegate // Save delegate
self.sliderTouched = false
// Setup as new only when this audio has not been already set up
if (self.lastURL == nil) || (url != self.lastURL) {
self.lastURL = url
self.setupAudioSession(category: AVAudioSessionCategoryPlayback)
do { // Setup Player
self.player = try AVAudioPlayer(contentsOfURL: url)
} catch _ {}
self.player?.delegate = self
self.player?.prepareToPlay()
timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: #selector(MediaAudioPlayer.update), userInfo: nil, repeats: true)
}
// Play - Pause
if self.player?.playing == true {
self.player?.pause()
self.delegate?.playing = true
} else {
self.player?.play()
self.delegate?.playing = false
}
}
// Transform second to minute
func stringFromTimeInterval(interval: NSTimeInterval) -> NSString {
let ti = NSInteger(interval)
let seconds = ti % 60
let minutes = (ti / 60) % 60
return NSString(format: "%0.2d:%0.2d", minutes, seconds)
}
// MARK: Audio Session
private func setupAudioSession(category category: String) {
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(category)
try session.setActive(true)
} catch _ {}
}
// MARK: Stop
func stop() {
self.player?.stop()
self.player = nil // Deinit player
self.delegate?.playing = true
self.delegate = nil // Deinit delegate
self.timer?.invalidate(); self.timer = nil
self.lastURL = nil
}
// MARK: Playing
internal func playing() -> Bool {
if player != nil {
return player?.rate == 1.0 ? true : false
}
return false
}
// MARK: Seek
func seekToPosition(position position: Float) {
if let duration = self.player?.duration {
player?.currentTime = Double(position) * duration
self.delegate?.currentTimeAudio = stringFromTimeInterval((player?.currentTime)!) as String
}
}
func update() {
if sliderTouched == false {
if let currentTime = self.player?.currentTime, duration = player?.duration {
let time = Float(currentTime) / Float(duration)
self.delegate?.sliderPosition = time
self.delegate?.currentTimeAudio = stringFromTimeInterval((player?.currentTime)!) as String
}
}
}
// MARK: Delegate
var counter = 0
func audioPlayerDidFinishPlaying(player: AVAudioPlayer, successfully flag: Bool) {
print("Called")
self.lastURL = nil
self.delegate?.playing = true
/**
if flag == true {
nextSong(true)
}*/
/**
if ((counter + 1) == tracks.count) {
counter = 0
self.delegate?.playing = false
nextSong(false)
} else {
self.delegate?.playing = true
nextSong(true)
}
*/
}
}
Thank you!!

Image appear when timer is at 1

im making a reaction time app, and I have a label set to count with Seconds. I want to make my image appear when the label is at 1 second, but it doesn't appear. The timer works fine, just the image. Help me in swift please.
import UIKit
class timerViewController: UIViewController {
var go = NSTimeInterval()
var timer = NSTimer()
#IBOutlet weak var tapStop: UIImageView!
#IBOutlet weak var displayTimer: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
if displayTimer.text == "00:01:00"{
tapStop.image = UIImage(named: "carcrash")
}
}
#IBAction func stop(sender: UIButton) {
timer.invalidate()
}
#IBAction func start(sender: UIButton) {
if (!timer.valid) {
let test:Selector = "updateTime"
timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: test, userInfo: nil, repeats: true)
go = NSDate.timeIntervalSinceReferenceDate()
}
}
func updateTime(){
var timeNow = NSDate.timeIntervalSinceReferenceDate()
var pass: NSTimeInterval = timeNow - go
let minutes1 = UInt8(pass / 60.0)
pass -= (NSTimeInterval(minutes1) * 60)
let seconds1 = UInt8(pass)
pass -= NSTimeInterval(seconds1)
let milli1 = UInt8(pass * 100)
let minutes2 = String(format: "%02d", minutes1)
let seconds2 = String(format: "%02d", seconds1)
let milli2 = String(format: "%02d", milli1)
displayTimer!.text = "\(minutes2):\(seconds2):\(milli2)"
}
}
You should put this:
if displayTimer.text == "00:01:00"{
tapStop.image = UIImage(named: "carcrash")
}
in your func updateTime() instead of viewDidLoad(), and put them into main thread, so that your image will be updated on the UI. Your func updateTime() will look like this:
func updateTime(){
var timeNow = NSDate.timeIntervalSinceReferenceDate()
var pass: NSTimeInterval = timeNow - go
let minutes1 = UInt8(pass / 60.0)
pass -= (NSTimeInterval(minutes1) * 60)
let seconds1 = UInt8(pass)
pass -= NSTimeInterval(seconds1)
let milli1 = UInt8(pass * 100)
let minutes2 = String(format: "%02d", minutes1)
let seconds2 = String(format: "%02d", seconds1)
let milli2 = String(format: "%02d", milli1)
displayTimer!.text = "\(minutes2):\(seconds2):\(milli2)"
if displayTimer.text == "00:01:00"{
dispatch_async(dispatch_get_main_queue(),{
tapStop.image = UIImage(named: "carcrash")
})
}
}