ViewController doesn't pass data on completion - swift

I have 2 ViewControllers.
TimerViewController passes a variable to the EditTimerViewConroller. EditTimerViewConroller edits it and should pass it back, but it looks like code in .completion is not executed.
Any advice how to fix it?
My code is:
TimerViewController
import UIKit
import AVFoundation //play sounds
class TimerViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
var player: AVAudioPlayer!
var timer = Timer()
var totalTime = 10.0
var secondsRemaining = 10.0
var secondsPassed = 0.0
let timerStep = 0.1
#IBOutlet weak var timerLabel: UILabel!
#IBOutlet weak var progressBar: UIProgressView!
#IBAction func startPressed(_ sender: UIButton) {
//works fine
}
#IBAction func editTimerButtinPresed(_ sender: UIButton) {
self.performSegue(withIdentifier: "goToEditTimer", sender: self)
let editTimer = EditTimerViewController()
editTimer.completion = { [weak self] duration in
DispatchQueue.main.async {
self?.totalTime = Double(duration!)
print("editTimer completed, totalTime now is \(self?.totalTime)")
}
}
}
func playSound(fileName: String) {
//works fine
}
#objc func updateTimer() {
//works fine
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToEditTimer" {
let destinationVC = segue.destination as! EditTimerViewController
destinationVC.duration = Int(totalTime)
print("Pasing duration = \(totalTime) to Edit screen")
}
}
EditTimerViewController
import UIKit
class EditTimerViewController: UIViewController {
let maxDuration = 60
var duration: Int? //timer duraton is passed from Timer
public var completion: ((Int?) -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
durationSlider.minimumValue = 0
durationSlider.maximumValue = Float(maxDuration)
durationSlider.value = Float(duration!)
durationLabel.text = String(duration!) + "s"
}
#IBOutlet weak var durationLabel: UILabel!
#IBOutlet weak var durationSlider: UISlider!
#IBAction func durationSliderChanged(_ sender: UISlider) {
duration = Int(sender.value)
print(duration!)
durationLabel.text = String(duration!) + "s"
}
#IBAction func cancelPressed(_ sender: UIButton) {
print("Cancel pressed, dismissing Edit screen")
self.dismiss(animated: true, completion: nil)
}
#IBAction func savePressed(_ sender: UIButton) {
print("Save pressed, duration is \(duration!)")
completion?(duration!)
self.dismiss(animated: true, completion: nil)
}
}
In the output after pressing Save button I see
Save pressed, duration is 11
but after it there is no sign of
editTimer completed, totalTime now is 11
and timer duration never changes

Change
#IBAction func editTimerButtinPresed(_ sender: UIButton) {
self.performSegue(withIdentifier: "goToEditTimer", sender: self)
let editTimer = EditTimerViewController()
editTimer.completion = { [weak self] duration in
DispatchQueue.main.async {
self?.totalTime = Double(duration!)
print("editTimer completed, totalTime now is \(self?.totalTime)")
}
}
}
To
#IBAction func editTimerButtinPresed(_ sender: UIButton) {
self.performSegue(withIdentifier: "goToEditTimer", sender: self)
}
And move completion inside prepare
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToEditTimer" {
let destinationVC = segue.destination as! EditTimerViewController
destinationVC.duration = Int(totalTime)
print("Pasing duration = \(totalTime) to Edit screen")
destinationVC.completion = { [weak self] duration in
DispatchQueue.main.async {
self?.totalTime = Double(duration!)
print("editTimer completed, totalTime now is \ (self?.totalTime)")
}
}
}
}

Related

How can I make variables inside an IBAction public to all view controllers?

So I'm trying to make a Chess Time app, where both players have access to a clock and they change the time between bullet(3minutes), Blitz(5minutes), and Rapid(10Minutes). Well in my second view controller SettingsController I made 3 IBActions UIButtons for this.
#IBAction func bulletPressed(_ sender: UIButton) {
var storedTime = bullet
self.delegate?.storedTimeTimer()
self.navigationController?.popViewController(animated: true)
}
#IBAction func blitzPressed(_ sender: UIButton) {
var storedTime = blitz
}
#IBAction func rapidPressed(_ sender: UIButton) {
var storedTime = rapid
}
This is my SettingsController, my whole point is trying to get the storedTime into the first controller. I tried to use a delegate, but I couldn't get it to work.
Here is the full First Controller:
import UIKit
class ChessTimer: UIViewController {
#IBOutlet weak var playerTimer1: UILabel!
#IBOutlet weak var playerTimer2: UILabel!
var timer = Timer()
var time = 10
var isTimerRunning = false
override func viewDidLoad() {
super.viewDidLoad()
if isTimerRunning == false {
runTimer()
}
}
#IBAction func restartButton(_ sender: UIButton) {
}
#IBAction func pausePressed(_ sender: UIButton) {
timer.invalidate()
}
#IBAction func settingsPressed(_ sender: UIButton) {
performSegue(withIdentifier: "goToSettings", sender: self)
}
func runTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self,selector:
(#selector(ChessTimer.updateTimer)),userInfo: nil, repeats: true)
isTimerRunning = true
}
#objc func updateTimer() {
if storedTime! < 1 {
timer.invalidate()
playerTimer1.text = "00:00"
playerTimer2.text = "00:00"
}
else {
storedTime! -= 1
playerTimer1.text = prodTimeString(time: TimeInterval(storedTime)!)
}
}
func prodTimeString(time: TimeInterval) -> String {
let prodMinutes = Int(time) / 60 % 60
let prodSeconds = Int(time) % 60
return String(format: "%02d:%02d", prodMinutes, prodSeconds)
}
#IBAction func playerButton1(_ sender: UIButton) {
}
#IBAction func playerButton2(_ sender: UIButton) {
}
}
extension ChessTimer: SettingsControllerDelegate {
func storedTimeTimer() {
}
}
This is the second full controller
import UIKit
class SettingsController: UIViewController {
var bullet = "03:00"
var blitz = "05:00"
var rapid = "10:00"
var storedTime = 0
var delegate: SettingsControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func bulletPressed(_ sender: UIButton) {
var storedTime = bullet
self.delegate?.storedTimeTimer()
self.navigationController?.popViewController(animated: true)
}
#IBAction func blitzPressed(_ sender: UIButton) {
var storedTime = blitz
}
#IBAction func rapidPressed(_ sender: UIButton) {
var storedTime = rapid
}
}
protocol SettingsControllerDelegate {
func storedTimeTimer()
}
This can be achieved using a Constants.swift file.
Click File -> New -> File -> Swift File and then name it Constants.swift.
In Constants.swift, declare var storedTime = 0.
Delete var storedTime = 0 from your view controllers, you'll only need it in Constants.swift. (So delete it from SettingsController, etc.)
The storedTime variable will now be public to all view controllers. 👍
Hope this helps someone!

How to pass different URL to webView from some buttons in swift?

I have some buttons in first view controller and a webView in second view controller. How to pass different url from different buttons to the webView? For example, the first button will leads to a google website and the second one is Facebook but using the same webView. Do I need to create different segues for each button or just one? If using just one, where should I start pulling that blue line (that line when you hold the control key)?
In first viewController:
class CafesView: UIViewController {
#IBOutlet weak var topBar: UIView!
#IBOutlet weak var button1: MDCFloatingButton!
#IBOutlet weak var button2: MDCRaisedButton!
#IBOutlet weak var button3: MDCRaisedButton!
#IBOutlet weak var button4: MDCRaisedButton!
#IBOutlet weak var button5: MDCRaisedButton!
#IBOutlet weak var button6: MDCRaisedButton!
#IBOutlet weak var button7: MDCRaisedButton!
#IBOutlet weak var button8: MDCRaisedButton!
#IBOutlet weak var button9: MDCRaisedButton!
let cafes = [
"Banana Joe's",
"College Eight Cafe",
"Global Village",
"Iveta",
"Oakes Cafe",
"Perk Coffee Bar",
"Stevenson Coffee House",
"Terra Fresca",
"Vivas"
]
var urlToPass: String!
override func viewDidLoad() {
super.viewDidLoad()
topBar.layer.shadowColor = UIColor.black.cgColor
topBar.layer.shadowOpacity = 0.5
topBar.layer.shadowOffset = CGSize(width: 0, height: 2)
topBar.layer.shadowRadius = 5
button1.layer.cornerRadius = 20
button2.layer.cornerRadius = 20
button3.layer.cornerRadius = 20
button4.layer.cornerRadius = 20
button5.layer.cornerRadius = 20
button6.layer.cornerRadius = 20
button7.layer.cornerRadius = 20
button8.layer.cornerRadius = 20
button9.layer.cornerRadius = 20
}
#IBAction func bananaJoes(_ sender: UIButton) {
urlToPass = "https://dining.ucsc.edu/pdf/banana-joes-menu.pdf"
}
#IBAction func collegeEightCafe(_ sender: UIButton) {
urlToPass = "https://dining.ucsc.edu/pdf/c8-menu.pdf"
}
#IBAction func globalVillage(_ sender: Any) {
urlToPass = "https://www.foodbooking.com/ordering/restaurant/menu?restaurant_uid=d368abee-3ccc-40d7-be7f-3ca5d4cbd513&glfa_cid=1263531392.1571083521&glfa_t=1571083566919"
}
#IBAction func iveta(_ sender: UIButton) {
urlToPass = "https://iveta.com/pages/iveta-ucsc-menu"
}
#IBAction func oakesCafe(_ sender: UIButton) {
urlToPass = "https://dining.ucsc.edu/pdf/oakes-menu-2019-20.pdf"
}
#IBAction func perkCoffeeBar(_ sender: UIButton) {
urlToPass = "https://google.com" //This url is just a placeholder
}
#IBAction func stevensonCoffeeHouse(_ sender: UIButton) {
urlToPass = "https://dining.ucsc.edu/pdf/stevenson-coffee-house-menu.pdf"
}
#IBAction func terraFresca(_ sender: UIButton) {
urlToPass = "https://dining.ucsc.edu/terra-fresca/pdf/terra-fresca-menu.pdf"
}
#IBAction func vivas(_ sender: UIButton) {
urlToPass = "https://dining.ucsc.edu/pdf/vivas-menu.pdf"
}
#IBAction func dismiss(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
guard let destination = segue.destination as? CafesMenu else { return }
destination.detailURL = urlToPass
urlToPass = nil
}
}
In the second one:
import UIKit
import WebKit
class CafesMenu: UIViewController {
#IBOutlet weak var webView: WKWebView!
var detailURL: String?
override func viewDidLoad() {
super.viewDidLoad()
print("URL Requested: \(detailURL)")
}
override func viewWillAppear(_ animated: Bool) {
let url = URL(string: detailURL!)
let request = URLRequest(url: url!)
webView.load(request)
}
#IBAction func dismiss(_ sender: UIBarButtonItem) {
self.dismiss(animated: true, completion: nil)
}
}
What you need to do is use prepareForSegue:sender: to set a property in your destination view controller. prepareForSegue:sender: will be called before your initial view controller segues to any destination view controller. Within this function, we can check which button was pressed and set the appropriate URL in the destination view controller accordingly.
This approach will allow you to use any segue between your buttons and your destination view controller. This means, you simply have to drag the blue line from the buttons to the view controller you want to segue to.
1. Within your storyboard, create a segue between your first view controller and your destination view controller. This is done by holding control, clicking on the first view controller in the interface builder, and dragging over the destination view controller. Then choose a segue type:
Now, select this segue and give it the Identifier "InitialVCToDestinationVC" in the attributes inspector:
2. Make a property called urlToPass of type URL in your initial view controller:
class InitialViewController: UIViewController {
var urlToPass: URL!
#IBAction func googleButtonPressed(_ sender: Any) {
}
#IBAction func facebookButtonPressed(_ sender: Any) {
}
}
3. Make a property called receivedUrl in the destination view controller:
class DestinationViewController: UIViewController {
var receivedUrl: URL!
#IBOutlet var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let request = URLRequest(url: receivedUrl)
webView.load(request)
}
}
4. Set the urlToPass depending on which button is pressed and use the prepareForSegue:sender: function to set the destination view controller's url accordingly. Then, make use of performSegue(withIdentifier:sender:) to perform the segue with identifier InitialVCToDestinationVC.
class InitialViewController: UIViewController {
var urlToPass: URL!
#IBAction func googleButtonPressed(_ sender: Any) {
urlToPass = URL(string: "www.google.com")
performSegue(withIdentifier: "InitialVCToDestinationVC", sender: nil)
}
#IBAction func facebookButtonPressed(_ sender: Any) {
urlToPass = URL(string: "www.facebook.com")
performSegue(withIdentifier: "InitialVCToDestinationVC", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
guard let destination = segue.destination as? DestinationViewController else { return }
destination.receivedUrl = urlToPass
urlToPass = nil
}
}
5. (optional) Make use of the shouldPerformSegueWithIdentifier:sender: method within InitialViewController and check whether or not urlToPass is valid. If urlToPass is valid, perform the segue, else present an alert.
class InitialViewController: UIViewController {
...
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if let urlToPass = urlToPass {
// check if your application can open the NSURL instance
if !UIApplication.shared.canOpenURL(urlToPass) {
let alertController = UIAlertController(title: "Cannot open URL.", message: "This is an invalid URL.", preferredStyle: .alert)
let ok = UIAlertAction(title: "Okay", style: .cancel, handler: nil)
alertController.addAction(ok)
present(alertController, animated: true, completion: nil)
}
return UIApplication.shared.canOpenURL(urlToPass)
}
return false
}
}
End result:
Here's a link to the Xcode project I made the above gif from: https://github.com/ChopinDavid/PrepareForSegue
Try using the following code snippet to pass the urlParameter to second viewcontroller
class FirstViewController: UIViewController{
func googleActionButton() {
let vc = SecondViewController()
vc.urlToOpen = "www.google.com"
self.present(vc, animated: true, completion: nil)
}
func facebookActionButton() {
let vc = SecondViewController()
vc.urlToOpen = "www.facebook.com"
self.present(vc, animated: true, completion: nil)
}
}
class SecondViewController: UIViewController{
var urlToOpen = String()
override func viewDidLoad() {
super.viewDidLoad()
// set webview url to the 'urlToOpen' which you received from FirstViewController
}
}
First of all, create an enum WebURL with all the url cases that you want to open, i.e.
enum WebURL {
case google
case facebook
var url: String {
switch self {
case .google:
return "https://www.google.com"
case .facebook:
return "https://www.facebook.com"
}
}
}
Next, in FirstVC, in the UIButton's #IBAction open SecondVC using the WebURL instance corresponding to that particular button, i.e.
class FirstVC: UIViewController{
#IBAction func openGoogle(_ sender: UIButton) {
self.openSecondVC(with: WebURL.google.url)
}
#IBAction func openFacebook(_ sender: UIButton) {
self.openSecondVC(with: WebURL.facebook.url)
}
func openSecondVC(with urlString: String) {
if let vc = self.storyboard?.instantiateViewController(withIdentifier: "SecondVC") as? SecondVC {
vc.urlString = urlString
self.present(vc, animated: true, completion: nil)
}
}
}
Then, use urlString in SecondVC to configure your webView, i.e.
class SecondVC: UIViewController {
var urlString: String?
override func viewDidLoad() {
super.viewDidLoad()
//Setup your webView using urlString here...
}
}

Trying to pass Doubles from a view to an another but it doesn't work

Hey I'm a newbie to Swift and Xcode and I am trying to make a little app but I have an error and can't fix it.
I'm trying to passe a double from a view to another but it says
Binary operator '+=' cannot be applied to operands of type 'String' and "int"
That's my first view :
#IBOutlet weak var Rned: UILabel!
var ArgentC: Double = 0
override func viewDidLoad() {
super.viewDidLoad()
Rned.text = String(ArgentC)
Rned.backgroundColor = UIColor(patternImage: UIImage(named: "Rectangle2")!)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let SecondViewController = segue.destination as! SacocheVCp2
SecondViewController.ArgentCV = Rned.text!
}
#IBAction func Reset(_ sender: Any) {
Rned.text = String(0)
ArgentC = 0
}
#IBAction func CashButton(_ sender: Any) {
performSegue(withIdentifier: "segueSac", sender: self)
}
and that is my second view with the error, and the error appears when I want to add numbers to my doubles.
#IBOutlet weak var Rend2Label: UILabel!
var ArgentCV = String()
override func viewDidLoad() {
super.viewDidLoad()
Rend2Label.backgroundColor = UIColor(patternImage: UIImage(named: "Label")!)
Rend2Label.text = ArgentCV
// Do any additional setup after loading the view.
}
#IBAction func CinqEur(_ sender: Any) {
ArgentCV += 5 // <== Here
Rend2Label.text = ArgentCV
}
#IBAction func DixEur(_ sender: Any) {
ArgentCV += 10 // <== Here
}
#IBAction func VingtEur(_ sender: Any) {
ArgentCV += 20 // <== Here
}
#IBAction func CinquanteEur(_ sender: Any) {
ArgentCV += 50 // <== Here
}
Thanks for your time.
You don't have to send it as a String , you can send it as Int
secondViewController.argentCV = Int(rned.text) ?? 0 // ?? to avoid crashes but make sure it doesn't destroy logic
Then inside SacocheVCp2
var argentCV = 0 // 0 is default
Finally
argentCV += 5 // <== Here
rend2Label.text = "\(argentCV)"

update UIViewController in Real Time from Popover Viewcontroller in Swift 4

right now i'm experimenting with SceneKit DebugOptions.
i'm trying to update/ show Scenekits Debug Options in real time, using switch controllers from a Popover ViewController.
i've tried many things, like UserDefaults, Delegation and Protocols, but stil i wasn't able to see the result in real time, every time i have to kill the app en relaunch it to see the results.
so, i would be greatfull if someone would have an answer to my question :D
extension i added to my MainVC
extension ViewController: UIPopoverPresentationControllerDelegate, DebugOptions {
func wireFrameEnabled(enabled: Bool) {
if enabled == true {
print(enabled)
}
}
func showCameraEnabled(enabled: Bool) {
}
func showAllDebugOptions(enabled: Bool) {
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let popoverController = segue.destination.popoverPresentationController, let button = sender as? UIButton else { return }
popoverController.delegate = self
popoverController.sourceRect = button.bounds
let debugMenuVC = popoverController.presentedViewController as! DebugMenuVC
debugMenuVC.delegate? = self
}
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}
}
Protocol
protocol DebugOptions {
func wireFrameEnabled(enabled: Bool)
func showCameraEnabled(enabled: Bool)
func showAllDebugOptions(enabled: Bool)
}
DebugMenuVC
class DebugMenuVC: UIViewController {
#IBOutlet weak var bgView: UIView!
#IBOutlet weak var showWireFrameSwitch: UISwitch!
#IBOutlet weak var showCameraSwitch: UISwitch!
#IBOutlet weak var showAllSwitch: UISwitch!
var delegate: DebugOptions?
override func viewWillLayoutSubviews() {
preferredContentSize = CGSize(width: 150, height: 300)
}
override func viewDidLoad() {
super.viewDidLoad()
buttonCheck()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
#IBAction func aSwitchBtnWasPressed( _ sender: UISwitch ) {
if (sender.tag == 0) && (sender.isOn == true) {
userDefaults.set(true, forKey: SHOW_WIRE_FRAME)
delegate?.wireFrameEnabled(enabled: true)
} else if (sender.tag == 0) && (sender.isOn == false) {
userDefaults.set(false, forKey: SHOW_WIRE_FRAME)
delegate?.wireFrameEnabled(enabled: false)
}
}
func buttonCheck() {
if userDefaults.bool(forKey: SHOW_WIRE_FRAME) == true{
showWireFrameSwitch.isOn = true
} else {
showWireFrameSwitch.isOn = false
}
}
}
in debubMenuVC.delegate shouldn't be an optional. thats the reason the delegation method always failed :D
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let popoverController = segue.destination.popoverPresentationController, let button = sender as? UIButton else { return }
popoverController.delegate = self
popoverController.sourceRect = button.bounds
let debugMenuVC = popoverController.presentedViewController as! DebugMenuVC
debugMenuVC.delegate? = self
}

Passing Data through 3 view Controllers [Swift 3.0 - Xcode]

Im passing data through 3 VCs, so in the end I want to achieve sending data from the third VC to the first. I send Data from V2 to V3 with a segue and then send it back from V3 to V2 by delegate. Im then trying to send it from V2 to V1 through a segue but I cant seem to collect the data (sent back from V3) in V2 to then send to V1.
The data from V3 doesn't show up in V1, but the code still runs.
Can anyone help?
heres my code from V2 and V3:
V2:
import UIKit
class SecondViewController: UIViewController, thirdDelegate {
var GetBack: String?
var SendForward = [String]()
var Datacollect = [String]()
var Collect = [String]()
let ct = "Conner#2"
let new = "All#2"
#IBOutlet var Hinput: UITextField!
#IBOutlet var Ninput: UITextField!
#IBAction func MAP(_ sender: Any) {
if Hinput.text != ""{
performSegue(withIdentifier: "SegueSearch", sender: self)}
}
#IBAction func Info(_ sender: Any) {
performSegue(withIdentifier: "SegueInfo", sender: self)
}
func DataToPass(ArrayName: [String]) { //function from delegate
Datacollect = ArrayName
print(ArrayName)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if segue.identifier == "SegueSearch"{
let thirdController = segue.destination as! ThirdViewController
SendForward.append(Hinput.text!)
SendForward.append(ct)
thirdController.height = SendForward
thirdController.delegate = self
} else if segue.identifier == "SegueInfo" {
let firstController = segue.destination as! ViewController
if Datacollect.count != 0{
Collect.append(Datacollect[1])
Collect.append(Datacollect[0])}
Collect.append(Ninput.text!)
Collect.append(new)
firstController.AllData = Collect
}
}
override func viewDidLoad() {
super.viewDidLoad()
print("check",Datacollect)
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Is this the part that isn't working?
func DataToPass(ArrayName: [String]) {
Datacollect = ArrayName
print(ArrayName)
}
V3:
import UIKit
protocol thirdDelegate{
func DataToPass (ArrayName: [String])
}
class ThirdViewController: UIViewController {
var height = [String]()
var SendBack = [String]()
let ko = "Keith#3"
var delegate: thirdDelegate! = nil
#IBOutlet var Houtput: UILabel!
#IBAction func Home(_ sender: Any) {
let StrH = String(height[0])
SendBack.append(ko)
SendBack.append(StrH!)
delegate.DataToPass(ArrayName: SendBack)
}
override func viewDidLoad() {
Houtput.text = height[0]
super.viewDidLoad()
print(height)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}