0

Would you please help me? :) I've been struggling with this problem for like a week and couldn't solve it. my problem is - I can't pass data between my two view controllers, let me explain myself better: I've got one VC which is the main VC that has the Clock animation, and I've got another VC which takes care of the SettingsVC. now no matter what I do (delegation, anything..) it doesn't let me to pass my UILabel's text to the main VC. I beg for a help since I can't keep going with my app and I'm about to finish it.

MyCode: 1st ViewController:

    import UIKit
import CircleProgressView
import AVFoundation
import Foundation
import PickerViewCell

class PomodoroViewController: UIViewController{
    
    
    //MARK: - Outlets Section:
    @IBOutlet weak var playSymbol: UIButton!
    @IBOutlet weak var textField: UITextField!
    @IBOutlet weak var settingsSymbol: UIBarButtonItem!
    
    @IBOutlet weak var timeLabel: UILabel!
    
    @IBOutlet weak var deleteTask: UIButton!
    @IBOutlet weak var tasksLabel: UILabel!
    
    @IBOutlet weak var circleProgressView: CircleProgressView!
    
    
    
    // MARK: - Class Variables
    private var timerIsOn = false
    var timeRemaining = 1500
    
    private var totalTime = 1500
    private var completedTasks = 0
    
    
    private var secondsPassed = 0 // AINT SURE
    
    private var counter = 0
    
    
    
    
    //Instance objects:
    private var timer = Timer()
    var calculateManager = CalculateManager()
    
    
    
    //MARK:- ViewDidLoad Section:
    override func viewDidLoad() {
        textField.delegate = self
        deleteTask.isHidden = true
        
        
        
    }
    
    
    //MARK: - Play Function:
    @IBAction func playPreesedButton(_ sender: UIButton) {
        //What will happen once we hit the play button:
        counter += 1 //1 = play, 2 = pause, 3 = play, 4 = pause
        
        if !timerIsOn
        { // means its true cause the user pressed the play button:
            
            timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(onTimerFires), userInfo: nil, repeats: true)
            
            timerIsOn = true
            
            deleteTask.isHidden = false // showing up the delete button.
        }
        
        
        
        playSymbol.setBackgroundImage(UIImage(named: T.P.pausePic), for: .normal) // setting the image to pause button
        
        
        
        if counter.isMultiple(of: 2) {
            timer.invalidate()
            timerIsOn = false // cause we stopped the timer due to the Pause Button.
            
            playSymbol.setBackgroundImage(UIImage(named: T.P.playPic), for: .normal) // setting the image to play button
            
        }
        
        
        
    }
    
    
    

    
    
    //MARK: - Update Function:
    @objc func onTimerFires()
    {
        print("the total time is - \(timeRemaining)")
        
        timeRemaining -= 1 //1499 - in seconds
        
        
        secondsPassed = totalTime - timeRemaining
        
        //Updating the timeLabel:
        let minutesLeft = Int(timeRemaining) / 60 % 60
        let secondsLeft = Int(timeRemaining) % 60
        let timeToDisply = String(format:"%02i:%02i", minutesLeft, secondsLeft)
        timeLabel.text = timeToDisply
        
        //Updating the progressBar View:
        let cal = calculateManager.updateProgressView(seconds: secondsPassed, time: totalTime)
        circleProgressView.setProgress( cal , animated: true)
        
        
        if timeRemaining <= 0 {
            //what happen once we finish the 25mins:
            timer.invalidate()
            
            //Updating the tasks:
            if completedTasks < 10 {
                completedTasks += 1
            }
            tasksLabel.text = "Tasks: \(completedTasks)/10"
            
            
            //TODO: Play the sound that the user chose:
            
            
            
            
            //Setting up the timer to 5mins break after an interval is done:
            timeLabel.text = T.breakTime // Setting up the TimeLabel to be 5:00 of the break
            timeRemaining = T.breakInMins // Setting up the totalTime to 300seconds which is 5 mins
            playSymbol.setBackgroundImage(UIImage(named: T.P.playPic), for: .normal) // Setting up the Icon to the play button.
            
            
        }
        
        
    }
    
    
    //MARK: - Reset Function:
    @IBAction func resetTapped(_ sender: UIButton) {
        
        let alert = UIAlertController(title: "Are you sure you want to skip this task?", message: .none, preferredStyle: .alert)
        
        alert.addAction(UIAlertAction(title: "Skip", style: .destructive, handler: { action in
            //Once the user wants to reset the task - known as the Reset Button.
            
            self.timer.invalidate()
            
            self.timeRemaining = 1500
            
            self.timeLabel.text = "25:00"
            self.textField.text = "" //setting the textfield to be empty
            
            self.timerIsOn = false
            
            self.playSymbol.setBackgroundImage(UIImage(named: T.P.playPic), for: .normal)
            
            self.circleProgressView.setProgress( T.resetProgress , animated: false)
            
            
        }))
        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
        
        
        
        
        self.present(alert, animated: true)
        
    }
    
    
    
    
    
    //MARK: - Settings Section:
    @IBAction func settingsPreesedButton(_ sender: UIButton) {
        
        
        
    }
    
}





//MARK: - UITextField Delegate Methods:
extension PomodoroViewController: UITextFieldDelegate
{
    
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }
}



extension PomodoroViewController: CalculateManagerDelegate {
    func updateTotalTime(time: Int) {
        timeRemaining = time
    }
    
    
}
    
    

my second ViewController:

    import Foundation
import UIKit
import AVFoundation
import PickerViewCell

//MARK: - Settings View Controller:
class SettingsViewController: UITableViewController, PickerTableCellDelegate, PickerTableCellDataSource {
    
    @IBOutlet weak var workInterval: UILabel!
    @IBOutlet weak var shortBreak: UILabel!
    @IBOutlet weak var longBreakAfter: UILabel!
    
    @IBOutlet weak var dailyIntervals: UILabel!
    
    let defaults = UserDefaults.standard
    
    var calculateManager = CalculateManager()
    
    
    override func viewDidLoad() {
        
        checkForSavedWorkTime()
        
    }
    
    
    var timesDictionary = [0: "30 Minutes",
                           1: "25 Minutes",
                           2: "20 Minutes",
                           3: "15 Minutes",
                           4: "10 Minutes",
                           5: "5 Minutes"
    ]
    
    
    
    var intervalsDictionary = [ 0: "1 Intervals",
                                1: "2 Intervals",
                                2: "3 Intervals",
                                3: "4 Intervals",
                                4: "5 Intervals",
                                5: "6 Intervals",
                                6: "7 Intervals",
                                7: "8 Intervals",
                                8: "9 Intervals",
                                9: "10 Intervals"
    ]
    
    
    
    
    var totalWorkTime = 0
    
    
    //MARK: Picker TableCell Delegate Methods:
    //This func chooses the title of every row in the UIPickerView selection:
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int, forCell cell: PickerTableViewCell) -> String? {
        let identifier = cell.reuseIdentifier
        
        switch identifier {
        case "timeCell":
            return timesDictionary[row]
        case "shortBreakCell":
            return timesDictionary[row]
        case "longBreakAfterCell":
            return timesDictionary[row]
        case "intervalsCell":
            return intervalsDictionary[row]
            
        default:
            print("There was a problem with titleforrow")
        }
        return nil
    }
    
    
    
    //This func takes control of what will happen once the user choose an optaion in the UIPickerView Selection:
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int, forCell cell: PickerTableViewCell) {
        let identifier = cell.reuseIdentifier
        
        switch identifier {
        case "timeCell":
            workInterval.text = timesDictionary[row]
            let myString = workInterval.text!
            
            if let totalTime = calculateManager.calculateTotalTime(convertString: myString) {
                totalWorkTime = totalTime
            }
            
            saveTime(labelText: myString)
            
        case "shortBreakCell":
            shortBreak.text = timesDictionary[row]
        case "longBreakAfterCell":
            longBreakAfter.text = timesDictionary[row]
            
        default:
            print("There was a problem with Switch")
        }
        
        
        self.view.endEditing(true)
        
    }
    
    
    
    
    func saveTime(labelText: String) {
        
        defaults.set(labelText, forKey: "workInterval")
        
    }
    
    
    
    func checkForSavedWorkTime() {
        
        if let time = defaults.string(forKey: "workInterval") {
            workInterval.text = time
        }
        
    }
    
    
    func onPickerOpen(_ cell: PickerTableViewCell) {
        
        
    }
    
    func onPickerClose(_ cell: PickerTableViewCell) {
        
        
    }
    
    
    //MARK: Picker TableCell DataSource:
    func numberOfComponents(in pickerView: UIPickerView, forCell cell: PickerTableViewCell) -> Int {
        return 1
    }
    
    
    //the amount of rows in component(UIPicker):
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int, forCell cell: PickerTableViewCell) -> Int {
        
        if cell.reuseIdentifier == "timeCell"{
            return timesDictionary.count
        }
        
        return intervalsDictionary.count
    }
    
    
    
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        
        if let cell = tableView.cellForRow(at: indexPath) as? DatePickerTableViewCell {
            //       cell.delegate = self
            if !cell.isFirstResponder {
                _ = cell.becomeFirstResponder()
            }
        } else if let cell = tableView.cellForRow(at: indexPath) as? PickerTableViewCell {
            cell.delegate = self
            cell.dataSource = self
            if !cell.isFirstResponder {
                _ = cell.becomeFirstResponder()
            }
        }
    }
    
    
}







//MARK: - Completed View Controller:
class CompletetedSoundViewController: UITableViewController {
    /// Class Variables
    
    
    
    private var soundsArray = [Track]()
    
    
    
    override func viewDidLoad() {
        //        tableView.delegate = self
        //        tableView.dataSource = self
        
    }
    
    
    
    
    
    ///TableView Data Soruce Methods:
    // Return the number of rows for the table.
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return soundsArray.count
    }
    
    
    // Provide a cell object for each row.
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // Fetch a cell of the appropriate type.
        tableView.deselectRow(at: indexPath, animated: true)
        
        let cell = tableView.dequeueReusableCell(withIdentifier: "trackCell", for: indexPath)
        
        // Configure the cell’s contents.
        cell.textLabel!.text = soundsArray[indexPath.row].trackName
        
        return cell
    }
    
    
    ///TableView Delegate Methods:
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
        
        
        
    }
}




//MARK: - Break Sound Controller:
class BreakSound: UITableViewController {
    
    
    
    override func viewDidLoad() {
        //
    }
    
}

my Struct:

    import Foundation
import UIKit


protocol CalculateManagerDelegate {
    func updateTotalTime(time: Int)
}

struct CalculateManager {
    
    var totalAmountOfTime = 0
    
    var delegate: CalculateManagerDelegate?
    
    //TODO: Calculate the Progress-View:
    func updateProgressView(seconds: Int, time: Int ) -> Double {
        
        return Double(seconds) / Double(time)
    }
    
    mutating func calculateTotalTime(convertString: String) -> Int? {
        
        
        let newString = convertString.dropLast(8)
        let subToString = String(newString)
        if let turnToInt = Int(subToString)
        {
            totalAmountOfTime = turnToInt * 60
            delegate?.updateTotalTime(time: totalAmountOfTime)
            return totalAmountOfTime
        }
        
        return nil
    }
    
}
    
Avi Sabag
  • 61
  • 8
  • 6
    Please edit your code by removing the excessive blank lines and any code that is not relevant to the question – Joakim Danielson Jun 24 '20 at 07:37
  • 2
    Very difficult to follow with all that useless code. You said you used delegation but I can't see anywhere the protocol you are referring to. Please edit your question. – Mat Jun 24 '20 at 08:11
  • I edited my post, about the delegation I removed it since it didn't work. @Mat – Avi Sabag Jun 24 '20 at 08:14
  • A first quick view shows that you have two different `CalculateManager` properties, one in your `PomodoroViewController` (read-access), the other in the `SettingsViewController` (write-access), and I guess you either only want to have just one, or at least you need to transfer it back. – Andreas Oetjen Jun 24 '20 at 08:20
  • @AndreasOetjen well the problem is I can't transfer any data from SettingsViewController to PomodoroViewController. the CalculateManager is a struct where all the calculates stuff happen. – Avi Sabag Jun 24 '20 at 08:23
  • How do you present the `SettingsViewController`? Programatically? Segue? So If I understand correctly you want to get the user selection from `intervalsDictionary` or/and `timesDictionary` and pass it to the Initial Controller? I would use delegation. I guess ` @IBAction func settingsPreesedButton(_ sender: UIButton)` is where you want to push the setting VC into the stacl. Is that correct? – Mat Jun 24 '20 at 08:30
  • @Mat okay let me explain myself again, first of all I edited the main post and added my struct which takes care of all the calculation. now what I would like to do is when the user chose the time in the settingsVC by using the PickerView, I want to use that time in my main VC, u can see in my PomodoroViewController there's a variable which calls "timeRemaining", I'm using that variable to calculate the time of the main clock. now I tried to pass the data that the user chose to the main VC with delegation but it didn't work. – Avi Sabag Jun 24 '20 at 08:44
  • @Mat also the answer to your question is I present the SettingsViewController wit a Segue when pressing the Settings symbol at the left. – Avi Sabag Jun 24 '20 at 08:45
  • Does this answer your question? [Passing Data between View Controllers in Swift](https://stackoverflow.com/questions/24222640/passing-data-between-view-controllers-in-swift) – Bram Jun 24 '20 at 09:05
  • @Craz1k0ek nope I also looked at that before I submitted this question – Avi Sabag Jun 24 '20 at 09:16
  • `I tried to pass the data that the user chose to the main VC with delegation but it didn't work.` I usually use delegation to pass content from a secondary VC to a main VC, so this should work. You probably made a mistake in your implementation, please show us what you tried. – Eric Aya Jun 24 '20 at 09:30
  • @EricAya edited my main post :) – Avi Sabag Jun 24 '20 at 09:39

1 Answers1

0

first declare the delegate:

protocol SettingsDelegate {
   func userSettings(interval:String?,shortBreak:String?,longBreak:String?)
}

then inside I would declare 3 strings (or whatever you want to pass back) and the delegate:

class SettingsViewController: UIViewController {

    // this variables are optionals but it would be a good idea to set the initial value, maybe from UserSettings
    var workIntervalString: String?
    var shortBreakString: String?
    var longBreakAfterString: String?
    // your delegate
    var delegate: SettingsDelegate?

 // the inside the didSelectRow set the string variables 
 func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int, forCell cell: PickerTableViewCell) {
        
    let identifier = cell.reuseIdentifier
        
       switch identifier {
          case "timeCell":
             workInterval.text = timesDictionary[row]
             self.workInterval = timesDictionary[row] // set string

             let myString = workInterval.text!
                if let totalTime = calculateManager.calculateTotalTime(convertString: myString) {
                   totalWorkTime = totalTime
                 }
            
              saveTime(labelText: myString)     
          case "shortBreakCell":
             shortBreak.text = timesDictionary[row]
             self.shortBreakString = timesDictionary[row] // set string

          case "longBreakAfterCell":
             longBreakAfter.text = timesDictionary[row]  
             self.longBreakAfterString = timesDictionary[row] // set string

          default:
            print("There was a problem with Switch")
       }

       // use your delegate 
       self.delegate?.userSettings(interval: self.workIntervalString,shortBreak:self.shortBreakString ,longBreak:self.longBreakAfterString)
       
       self.view.endEditing(true)
        
    }

in your PomodoroViewController:

class PomodoroViewController: UIViewController, SettingsDelegate {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }
    
    func userSettings(interval: String?, shortBreak: String?, longBreak: String?) {
        
        guard let interval = interval else {return}
        guard let shortBreak = shortBreak else {return}
        guard let longBreak = longBreak else {return}
        
        //User your variables .......

    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier  == "your identifier" {
            let destination = segue.destination as! SettingsViewController
            destination.delegate = self // set the delefate as self
        }
    }
Mat
  • 6,236
  • 9
  • 42
  • 55
  • I will try that :) I also edited my main post with that way I used delegate. – Avi Sabag Jun 24 '20 at 09:39
  • add a breakpoint inside ` func userSettings(interval: String?, shortBreak: String?, longBreak: String?)` to see if that function gets called. – Mat Jun 24 '20 at 09:47
  • Thank you very much! btw only if u can of course, could u explain me what I did wrong and why your code worked and mine not? :( – Avi Sabag Jun 24 '20 at 09:59
  • if I am not mistaken, protocols only work with classes not with structs and you declared your delegate in a struct. I can be wrong though. – Mat Jun 24 '20 at 10:17