Zwischen ViewControllern wechseln ohne diese zu schließen?

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Zwischen ViewControllern wechseln ohne diese zu schließen?

    Servus zusammen, jetzt bin ich´s noch mal :)


    Ich habe folgendes Problem:
    Und zwar besteht meine App aus zwei "Seiten" (zwei UIViewControllern), zwischen denen man durch klicken auf den entsprechenden Menü Button wechseln kann.
    Klickt man auf entsprechenden Button, wird die nächste Seite durch "present modaly" aufgerufen.

    Nun läuft auf der zweiten Seite aber ein Timer, welcher abgebrochen wird sobald zurück auf die erste Seite getauscht wird, was dem Sin des ganzen ein wenig entgegenwirkt.
    Ich habe bereits rausgefunden dass die, auf die man wechselt alles komplett neu lädt, kann man diese auch nur in den Hintergrund schieben und da "weiterlaufen lassen"?

    Vielen dank für eure Hilfe :)


    Grüße

    Ferdi
  • Stimmt, das wäre auch eine gute Lösung.
    Den Ansatz habe ich soweit auch schon versucht, musste jedoch feststellen dass der Timer dann beim öffnen der zweiten Seite genauso abgebrochen wird.

    Vielleicht ist mein Timer auch einfach viel zu einfach gelöst...
    Denn hierbei habe ich auch das Problem dass der Timer nicht weiterläuft sobald jemand die App verlässt um beispielsweise seine Nachrichten zu checken :/

    Code:

    Quellcode

    1. OurTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(Action), userInfo: nil, repeats: true)
    2. @objc func Action (){
    3. if TimerDisplayed > 0
    4. {
    5. TimerDisplayed -= 1
    6. Label.text = String((TimerDisplayed/60)+1)
    7. }
    8. print("time")
    9. }
    10. else
    11. {
    12. OurTimer.invalidate()
    13. TimerDisplayed = 0
    14. Label.text = String(0)
    15. }
    16. }
    17. }
    Alles anzeigen



    Grüße

    Ferdi
  • Ferdinand schrieb:

    Denn hierbei habe ich auch das Problem dass der Timer nicht weiterläuft sobald jemand die App verlässt um beispielsweise seine Nachrichten zu checken :/
    Das ist kein Problem, sondern ein Feature - und Dein Problem ist eher, dass Du die Philosophie auf Mobilgeräten noch nicht verinnerlicht hast:

    Warum sollte Deine App im Hintergrund laufen und Ressourcen verschwenden, nur damit beim nächsten Anschauen der App ein Timer weitergelaufen ist? Das verbraucht Speicher und Batteriekapazität für ... nix.

    Merke Dir, zu welchem Zeitpunkt der Benutzer die Zeiterfassung startet und nutze diese Information um bei Öffnen der View die verstrichene Zeit darzustellen, deren Anzeige über einen Timer aktualisiert wird. Sobald die View "verschwindet" (wie auch immer, durch in den Hintergrund wechseln oder durch eine andere View), beendest Du den Timer. Es gibt keinen Grund, im Hintergrund, wenn der Benutzer die Zeitanzeige eh nicht sehen kann, Timer-Events zu feuern.

    Sollte Dein Hintergedanke ein Wecker sein: Der funktioniert so oder so nicht per Timer, da Deine App aus verschiedenen Gründen im Hintergrund beendet werden kann. Dafür bietet sich ein UNNotificationRequest an.

    Mattes

    P.S.: Bei einem Count-Down natürlich entsprechend: Ablaufzeitpunkt merken und übrig bleibende Zeitspanne anzeigen...
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • Da muss ich dir schweren Herzens recht geben, ich bin noch recht neu auf dem Gebiet und versuche mich daran als Hobby...
    aber wie sagt man so schön, man lernt nie aus :whistling: :thumbsup:


    An genau so etwas habe ich auch schon gedacht, also dass eine lokale Nachricht abgefeuert wird. Quasi **Bing "Hey du, die Eier sind fertig gekocht!"

    Toll fände ich es aber auch, wenn der User den Fortschritt einsehen kann.
    Also nachschaut ob er noch genug Zeit hat sich was zu trinken ausm Keller zu holen bevor der Wecker klingelt.
    ---> Verbleibende Zeit wird durch ein Label angezeigt.

    Da ich auf dem gebiet noch sehr wenig Erfahrung habe, hab ich das eben durch den obigen Code gelöst.
    Nach ablaufen der Zeit, wird eben diese Benachrichtigung ausgegeben.

    Wie hättest du den Timer / Countdown denn gelöst?
    Ich habe im Internet schon ein paar Möglichkeiten gefunden, jedoch konnten diese mir leider nicht weiterhelfen....


    Grüße

    Ferdi
  • Die Benachrichtigung habe ich folgendermaßen gelöst:

    Quellcode

    1. //Benachrichtigung erlaubnis
    2. let center = UNUserNotificationCenter.current()
    3. center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in
    4. }
    5. //Benachrichtigung
    6. let content = UNMutableNotificationContent()
    7. content.title = "Eier Alarm!"
    8. content.body = "Die Eier sollten aus dem Wasser geholt werden!"
    9. //Trigger
    10. let date = Date().addingTimeInterval(20)
    11. let dateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: date)
    12. let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)
    13. //request
    14. let uuidString = UUID().uuidString
    15. let request = UNNotificationRequest(identifier: uuidString, content: content, trigger: trigger)
    16. //notificationCenter
    17. center.add(request) { (error) in
    18. }
    Alles anzeigen
  • Ferdinand schrieb:


    Wie hättest du den Timer / Countdown denn gelöst?
    Eigentlich habe ich das oben schon beschrieben:
    1. Der Benutzer startet in einer View einen Countdown über die Zeitspanne x
    2. Die App ermittelt die "Zielzeit" des Countdowns und speichert diese z. B. in den User Defaults
    3. Parallel stell die App einen UNNotificationRequest ein, der zu der gegebenen Zeit erfüllt wird. Hierbei an die notwendigen Delegate-Methoden denken, damit die Notification auch angezeigt wird, wenn Deine App gerade aktiv ist.
    4. In der App gibt es eine View (die aus 1. oder eine andere), welche die Zeitspanne zwischen "jetzt" und der gespeicherten Zielzeit anzeigt. Zum Aktualisieren dieser Anzeige verwendest Du einen Timer, der aber nur während der Anzeige der View aktiv ist.
    5. Sollte es eine Funktion zum Stoppen des Countdowns geben, muss Du an dieser Stelle auch den Notification-Request löschen
    Nur einer von vielen Wegen, aber so würde ich es lösen. Für ein konzeptionelles Vorgehen finde ich Google wenig hilfreich, da ist m. E. eher gefragt, ein Problem strukturiert zu beschreiben und in Teilaspekte zu zerlegen ... daraus kann so etwas wie oben werden. Internet-Recherchen sind dann für ganz konkrete Umsetzungen hilfreich, z. B. anhand der obigen Stichworte.

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • Die Notifications habe ich nun "Timergetreu" eingepflegt.
    Hierfür habe ich einfach auf das aktuelle Datum die entsprechende Anzahl an Minuten in Sekunden aufgerechnet.
    Funktioniert hervorragend danke :)

    Das gleiche werde ich mit dem Angezeigten Timer nun auch versuchen.
    Jedoch habe ich immer noch das Problem dass der Timer nicht automatisch weiterläuft nach dem man zur Eier-Uhr zurückkehrt :/
  • Ja, tut mir leid, ich wollte dir damit nicht auf die nerven gehen.

    Ich hab das ganze jetzt mal mithilfe der Userdefaults versucht, leider ist es aber beim versuch geblieben.
    Ich habe einen Wert in Sekunden auf das aktuelle Datum aufgerechnet und dieses dann abgespeichert.
    Beim nächsten öffnen des entsprechenden ViewControllers wird die aktuelle Zeit mit der abgespeicherten Zeit verglichen.

    DateNow < DateGeplant?

    --> DateGeplant-DateNow (in sekunden) = Timer Zeit
    --> Timer wird wieder gestartet mit Wert Timer Zeit

    else

    --> Label = "0"


    Leider klappt das überhaupt nicht wie ich mir das ganze vorgestellt habe...


    Userdefaults:

    Quellcode

    1. var sTimer = UserDefaults.standard.set(Date().timeIntervalSince1970, forKey: "sTimer123")


    Button:

    Quellcode

    1. @IBAction func b3(_ sender: Any) {
    2. //Benachrichtigung erlaubnis
    3. let center = UNUserNotificationCenter.current()
    4. center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in
    5. }
    6. //Benachrichtigung
    7. let content = UNMutableNotificationContent()
    8. //content.title = "Eier Alarm!"
    9. content.body = "Die Eier sind fertig!"
    10. content.sound = UNNotificationSound.default
    11. //Trigger
    12. let date = Date().addingTimeInterval(180)
    13. let dateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: date)
    14. let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)
    15. //request
    16. let uuidString = UUID().uuidString
    17. let request = UNNotificationRequest(identifier: uuidString, content: content, trigger: trigger)
    18. //notificationCenter
    19. center.add(request) { (error) in
    20. }
    21. TimerDisplayed = 180
    22. value3 = 100.0/180
    23. let soon = Date().addingTimeInterval(180)
    24. UserDefaults.standard.set(soon, forKey: "sTimer123")
    25. UserDefaults.standard.synchronize()
    26. OurTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(Action), userInfo: nil, repeats: true)
    27. UIView.animate(withDuration: value3){
    28. self.progressView.value = CGFloat(self.value3)
    29. }
    30. print("time")
    31. }
    Alles anzeigen





    ViewDidLoad:

    Quellcode

    1. override func viewDidLoad() {
    2. super.viewDidLoad()
    3. let dateNow = Date()
    4. let dateGeplant = sTimer
    5. let timeInterval1 = dateNow.timeIntervalSince1970
    6. let timeInterval2 = dateGeplant.timeIntervalSince1970
    7. if dateNow() < dateGeplant() //wie können die Daten verglichen werden?
    8. {
    9. TimerDisplayed = dateGeplant - dateNow //abstand in sekunden -> Integer
    10. if TimerDisplayed > 0
    11. {
    12. TimerDisplayed -= 1
    13. Label.text = String((TimerDisplayed/60)+1)
    14. UIView.animate(withDuration: value3){ [self] in
    15. self.progressView.value = self.progressView.value + CGFloat(value3)
    16. }
    17. print("time")
    18. }
    19. else
    20. {
    21. OurTimer.invalidate()
    22. TimerDisplayed = 0
    23. Label.text = String(0)
    24. UIView.animate(withDuration: 10.0){
    25. self.progressView.value = 0
    26. }
    27. }
    28. }
    Alles anzeigen
    Ich würde mich freuen wenn du da mal drüber schauen könntest, sofern du zeit hast :)

    Grüße und schöne Feiertage,

    Ferdi
  • Ferdinand schrieb:

    Quellcode

    1. var sTimer = UserDefaults.standard.set(Date().timeIntervalSince1970, forKey: "sTimer123")
    [...]
    Ich würde mich freuen wenn du da mal drüber schauen könntest, sofern du zeit hast :)
    Ich habe ehrlich gesagt keine Lust, in fremden Code "Eisenbahn" zu spielen, um Fehler zu finden. Was mir aber auf den ersten Blick auffiel, war die obige Zeile, die mich verwundert - und nix mit meinem Lösungsvorschlag zu tun hat. Muss sie ja auch nicht, er ist ja nur ein Weg von vielen, aber beschreibe doch bitte einmal in Prosa in kleinen Schritten, was Du machen möchtest. Das hilft, ein Problem zu strukturieren und idealerweise ist dann das Codieren nur noch Fleissarbeit (naja, fast). Wenn Dich Softwareentwicklung interessiert, solltest Du Dich mal mit Themen wie Struktogrammen, Pseudo-Code o. ä. befassen, konzeptionelle Arbeit ist die Hauptaufgabe eines Entwicklers, nicht Tippen.

    Aber zurück zum Thema: Du speicherst oben den Zeitpunkt, wann der Countdown gestartet wurde. Der ist aber eigentlich uninteressant und kann nur mit Hilfe der Countdown-Zeitspanne zur Bestimmung des "Ablauf-Zeitpunktes" benutzt werden. Warum beim Start nicht direkt den Ablaufzeitpunkt merken? Dann brauchst Du in der View nur die Differenz zwischen dem gespeicherten Wert und der aktuellen Zeit ausgeben. Und genau diese Berechnung mittels Timer z. B. einmal pro Sekunde aktualisieren.

    MyMattes schrieb:

    1. Der Benutzer startet in einer View einen Countdown über die Zeitspanne x
    2. Die App ermittelt die "Zielzeit" des Countdowns und speichert diese z. B. in den User Defaults
    3. Parallel stell die App einen UNNotificationRequest ein, der zu der gegebenen Zeit erfüllt wird. Hierbei an die notwendigen Delegate-Methoden denken, damit die Notification auch angezeigt wird, wenn Deine App gerade aktiv ist.
    4. In der App gibt es eine View (die aus 1. oder eine andere), welche die Zeitspanne zwischen "jetzt" und der gespeicherten Zielzeit anzeigt. Zum Aktualisieren dieser Anzeige verwendest Du einen Timer, der aber nur während der Anzeige der View aktiv ist.
    5. Sollte es eine Funktion zum Stoppen des Countdowns geben, muss Du an dieser Stelle auch den Notification-Request löschen

    Und genau deshalb habe ich den Eindruck, dass Du meine Antworten zwar irgendwie überfliegst und auf Basis von Stichworten "wild" agierst, nicht aber einmal in Ruhe liest und nachvollziehst. Ich bin jetzt erst mal ruhig und lass' Dich weiterforschen...

    Mattes

    P.S.: Wenn Du möchtest, dass in Zukunft ausser Dir jemand mit Deinem Code arbeitet, solltest Du Dich an ein paar Konventionen (z. B. Gross-/Kleinschreibung von Variablen, Klassennamen etc.) halten: Durch allgemein übliche Formalien wird Code für Fremde leichter lesbar - nur so als Tipp.
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • Okay, es hat mich gereizt, Dein Problem für mich als eine Swift-Aufgabe zu sehen. Hier einmal ein erster Wurf: Einfach nur eine View mit Label und Start-/Stop-Buttons. Der Countdown überlebt auch - per Userdefaults - ein Beenden der App. Das Thema Notification habe ich dabei ausgeklammert, aber es würde nur bei Starten ein Requesten und Stoppen ein Abbrechen bedeuten.

    Quellcode

    1. import UIKit
    2. class ViewController: UIViewController {
    3. @IBOutlet weak var countDownLabel: UILabel!
    4. var countDownTimer: Timer = Timer()
    5. override func viewDidLoad() {
    6. super.viewDidLoad()
    7. countDownLabel.text = "--"
    8. if UserDefaults.standard.object(forKey: "stopTime") is Date {
    9. countDownTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(countDown), userInfo: nil, repeats: true)
    10. }
    11. }
    12. @IBAction func startCountDown(_sender: Any) {
    13. let countDownDuration: TimeInterval = 50.0
    14. let countDownStopTime = Date().addingTimeInterval(countDownDuration)
    15. UserDefaults.standard.set(countDownStopTime, forKey: "stopTime")
    16. countDownTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(countDown), userInfo: nil, repeats: true)
    17. }
    18. @IBAction func stopCountDown(_sender: Any) {
    19. countDownTimer.invalidate()
    20. countDownLabel.text = "--"
    21. UserDefaults.standard.removeObject(forKey: "stopTime")
    22. }
    23. @objc func countDown() {
    24. if let countDownStopTime = UserDefaults.standard.object(forKey: "stopTime") as? Date {
    25. let countDownRemaining = countDownStopTime.timeIntervalSinceNow as TimeInterval
    26. if (countDownRemaining > 0) {
    27. countDownLabel.text = String(format: "%0.f", countDownRemaining)
    28. }
    29. else {
    30. self.stopCountDown(_sender: self)
    31. }
    32. }
    33. }
    34. }
    Alles anzeigen
    @All: Bitte gerne korrigieren ... insbesondere bei der richtigen Behandlung von Optionals bin ich noch sehr unsicher und würde mich bei Fehlern sehr über eine Erklärung der Korrektur freuen.

    Fröhliche Eiersuche, Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • H

    MyMattes schrieb:

    Okay, es hat mich gereizt, Dein Problem für mich als eine Swift-Aufgabe zu sehen. Hier einmal ein erster Wurf: Einfach nur eine View mit Label und Start-/Stop-Buttons. Der Countdown überlebt auch - per Userdefaults - ein Beenden der App. Das Thema Notification habe ich dabei ausgeklammert, aber es würde nur bei Starten ein Requesten und Stoppen ein Abbrechen bedeuten.

    Quellcode

    1. import UIKit
    2. class ViewController: UIViewController {
    3. @IBOutlet weak var countDownLabel: UILabel!
    4. var countDownTimer: Timer = Timer()
    5. override func viewDidLoad() {
    6. super.viewDidLoad()
    7. countDownLabel.text = "--"
    8. if UserDefaults.standard.object(forKey: "stopTime") is Date {
    9. countDownTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(countDown), userInfo: nil, repeats: true)
    10. }
    11. }
    12. @IBAction func startCountDown(_sender: Any) {
    13. let countDownDuration: TimeInterval = 50.0
    14. let countDownStopTime = Date().addingTimeInterval(countDownDuration)
    15. UserDefaults.standard.set(countDownStopTime, forKey: "stopTime")
    16. countDownTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(countDown), userInfo: nil, repeats: true)
    17. }
    18. @IBAction func stopCountDown(_sender: Any) {
    19. countDownTimer.invalidate()
    20. countDownLabel.text = "--"
    21. UserDefaults.standard.removeObject(forKey: "stopTime")
    22. }
    23. @objc func countDown() {
    24. if let countDownStopTime = UserDefaults.standard.object(forKey: "stopTime") as? Date {
    25. let countDownRemaining = countDownStopTime.timeIntervalSinceNow as TimeInterval
    26. if (countDownRemaining > 0) {
    27. countDownLabel.text = String(format: "%0.f", countDownRemaining)
    28. }
    29. else {
    30. self.stopCountDown(_sender: self)
    31. }
    32. }
    33. }
    34. }
    Alles anzeigen
    @All: Bitte gerne korrigieren ... insbesondere bei der richtigen Behandlung von Optionals bin ich noch sehr unsicher und würde mich bei Fehlern sehr über eine Erklärung der Korrektur freuen.

    Fröhliche Eiersuche, Mattes
    Hi,
    Also für erstes Mal Swift top! Statt if let countDown…. (Sorry für das blöde zitieren bin nur am Handy) hätte ich guard let verwendet. Das macht dein Code schöner und du hast nicht If in Ifs.

    guard let kannst du immer verwenden, wenn dein Code ohne die Bedingung kein Sinn ergibt und so und so nicht weiter laufen könnte. (Mal in eigenen Worten ausgedrückt)

    Sonst fällt mir auf den ersten Blick nichts auf. Also die Prüfung nach „is Type“ findet man in Swift eher selten. Aber für den Anwendungsfall find ich es angemessen.
  • Hallo Zusammen, ich bins noch mal unter diesem Thema :)

    Und zwar habe ich den Timer bisher immer in "3 Minuten verbleibend" angegeben.
    Da das ganze unter einer Minute aber konstant auf "0 Minuten verbleibend" steht, möchte ich jetzt die Sekunden mit anzeigen.

    Hierfür habe ich die Zeile so umgeschrieben:
    (Orientiert an Dokumentation)


    Quellcode

    1. let countDownRemaining = countDownStopTime.timeIntervalSinceNow as TimeInterval
    2. if (countDownRemaining > 0) {
    3. Label.text = String(format: "%02.f:%02.f", countDownRemaining/60, countDownRemaining/60 )
    Jedoch fehlt mir glaube ich einfach der Mathematische Ansatz, wie ich die Sekunden jetzt dementsprechend von 60 auf 0 laufen lassen kann...
    countDownRemaining wird immer in Sekunden angegeben, also beispielsweise 180 Sekunden verbleibend.


    Liebe Grüße,
    Ferdi
  • MyMattes schrieb:

    So etwas wie x - floor(x / 60) * 60 vielleicht? Oder in Prosa "Zeit minus Zeit in abgerundeten Minuten"...

    Mattes

    Hm, wenn ich das so einsetze:

    Quellcode

    1. Label.text = String(format: "%02.f:%02.f", countDownRemaining/60, countDownRemaining-floor((countDownRemaining/60)*60) )

    Kommt erscheinen bei mir in der Sekunden Anzeige keine Sekunden, sondern immer nur eine 01 :/
    Also beispielsweise 03:01

    Das hier habe ich im Internet gefunden, weiß aber nicht sprecht was damit alles gemeint ist...

    Quellcode

    1. return String(format:"%02d:%02d", Int(self/60), Int(ceil(truncatingRemainder(dividingBy: 60))) )
  • Ich habe jetzt herausgefunden dass die Minuten Zahl nicht aus dem Takt ist sondern so beschrieben wird:
    Ich klicke auf drei Minuten dann steht dran:
    3:59... 3:58... 3:57... ab 3:30 steht dann dran: 2:29... 2:28...
    gegen ende beendet sich der Timer zwar nach genau drei Minuten mit 00:03... 00:02... 00:01...,
    jedoch fängt ein drei Minuten Timer ja nicht an bei 3:59 zu zählen ?(

    Weißt du hierfür vielleicht auch eine Lösung?
    Ich habe es schon mit -1 am ende probiert, das verfehlt aber das ziel ganz deutlich, da ich am Ende ja gerade raus kommen würde :thumbsup:


    !! GELÖST !!
    Durch das simple einsetzten von floor :thumbup:

    !! Zusatzfrage!!
    Durch das ständige ändern der Sekunden springt mein Label immer hin und her, da beispielsweise eine 11 weniger platz braucht als eine 48.
    Kann man das irgendwie ausschalten?

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von Ferdinand ()