Sending a string from one viewcontroller to another using delegate in swift - swift

I'm trying to make a button click in PopupViewController fill a textfield in DagbogsindlaegViewController with a string "svimmelhed, ".
I'm not getting any errors when running the code, but the text is not there when i run the app simulation and press "ButtonPressSvimmelhed" in the class PopupViewController.
Is there a kind stranger who can help me and tell me what i am doing wrong?
Code below:
import UIKit
import EventKit
class DagbogsindlaegViewController: UIViewController{
#IBAction func BackToSVC(_ sender: Any){
self.performSegue(withIdentifier: "BackToSVCSegue", sender: self)
}
#IBAction func ToCalenderButtonPress(_ sender: Any) {
self.performSegue(withIdentifier: "ToCalenderSegue", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
TitleTextField.delegate = self
DescriptionTextField.delegate = self
// Do any additional setup after loading the view.
}
#IBOutlet weak var TitleTextField: UITextField!
#IBOutlet weak var DescriptionTextField: UITextField!
func addEventToCalendar(title: String, description: String?, startDate: Date, endDate: Date, completion: ((_ success: Bool, _ error: NSError?) -> Void)? = nil) {
let eventStore = EKEventStore()
eventStore.requestAccess(to: .event, completion: { (granted, error) in
if (granted) && (error == nil) {
let event = EKEvent(eventStore: eventStore)
event.title = title
event.startDate = startDate
event.endDate = endDate
event.notes = description
event.calendar = eventStore.defaultCalendarForNewEvents
do {
try eventStore.save(event, span: .thisEvent)
} catch let e as NSError {
completion?(false, e)
return
}
completion?(true, nil)
} else {
completion?(false, error as NSError?)
}
})
}
#IBAction func ButtonPressGemDagbog(_ sender: Any) {
addEventToCalendar(title: "\(String(describing: TitleTextField.text!))", description: "\(String(describing: DescriptionTextField.text!))", startDate: NSDate() as Date, endDate: NSDate() as Date)
}
#IBAction func ButtonPressPopupMenu(_ sender: Any) {
let popOverVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PopupViewControllerID") as! PopupViewController
self.addChild(popOverVC)
popOverVC.view.frame = self.view.frame
self.view.addSubview(popOverVC.view)
popOverVC.didMove(toParent: self)
}
}
extension DagbogsindlaegViewController : UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}
extension DagbogsindlaegViewController : TextFraPopupDelegate {
func Symptomer(Svimmelhed: String) {
TitleTextField.text = Svimmelhed
}
}
And the other view controller:
import UIKit
protocol TextFraPopupDelegate{
func Symptomer(Svimmelhed: String)
}
class PopupViewController: UIViewController {
var symptomDelegate: TextFraPopupDelegate?
override func viewDidLoad() {
super.viewDidLoad()
self.showAnimate()
self.view.backgroundColor = UIColor.black.withAlphaComponent(0.8)
// Do any additional setup after loading the view.
}
#IBAction func ButtonPressBackToDagbogsindlaeg(_ sender: Any) {
self.removeAnimate()
//self.view.removeFromSuperview()
}
func showAnimate()
{
self.view.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
self.view.alpha = 0.0;
UIView.animate(withDuration: 0.25, animations: {
self.view.alpha = 1.0
self.view.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
});
}
func removeAnimate()
{
UIView.animate(withDuration: 0.25, animations: {
self.view.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
self.view.alpha = 0.0;
}, completion:{(finished : Bool) in
if (finished)
{
self.view.removeFromSuperview()
}
});
}
#IBAction func ButtonPressSvimmelhed(_ sender: UIButton) {
if sender.isSelected {
sender.isSelected = false
} else {
sender.isSelected = true
}
symptomDelegate?.Symptomer(Svimmelhed: "Svimmelhed, ")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
}
}

As mentioned in the comments the delegate is not set.
However in Swift there is a more convenient way to pass the string back to the presenting view controller, a callback closure. You get rid of the protocol and of the duty to set the delegate
Delete
protocol TextFraPopupDelegate{
func Symptomer(Svimmelhed: String)
}
In PopupViewController replace
var symptomDelegate: TextFraPopupDelegate?
with
var callback : ((String) -> Void)?
and
#IBAction func ButtonPressSvimmelhed(_ sender: UIButton) {
if sender.isSelected {
sender.isSelected = false
} else {
sender.isSelected = true
}
symptomDelegate?.Symptomer(Svimmelhed: "Svimmelhed, ")
}
with
#IBAction func ButtonPressSvimmelhed(_ sender: UIButton) {
sender.isSelected.toggle()
callback?("Svimmelhed, ")
}
In DagbogsindlaegViewController in ButtonPressPopupMenu add the closure
#IBAction func ButtonPressPopupMenu(_ sender: Any) {
let popOverVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PopupViewControllerID") as! PopupViewController
self.addChild(popOverVC)
popOverVC.view.frame = self.view.frame
popOverVC.callback = { string in
self.TitleTextField.text = string
}
self.view.addSubview(popOverVC.view)
popOverVC.didMove(toParent: self)
}
Finally delete
extension DagbogsindlaegViewController : TextFraPopupDelegate {
func Symptomer(Svimmelhed: String) {
TitleTextField.text = Svimmelhed
}
}
Notes:
The syntax (_ success: Bool, _ error: NSError?) -> Void)? is outdated. Since Swift 3 the parameter labels are gone, this is sufficient: (Bool, NSError?) -> Void)?
Don't use NS... classes in Swift if there are native counterparts for example Date
Please conform to the naming convention to name variables and functions / methods with a starting lowercase letter.
The syntax "\(String(describing: TitleTextField.text!))" is double redundant. You create a string from a string from a string. Replace it with TitleTextField.text!

Related

How to show alerts in Swift UIKit using MVVM, Firebase and Delegates

I would like to know how can i implement a delegate or a function that helps me to show an alert in the ViewModel. Basically i need to validate two text fields and when the user writes a wrong password or a wrong email it should trigger an alert. The function to trigger this alert is inside of the ViewController and i need to create a new class (ViewModel) that helps me to validate the text fields and sign to firebase
Here is my code:
ViewController
import UIKit
import FirebaseAuth
class ViewController: UIViewController, UIWindowSceneDelegate {
//MARK: - #IBOutlets
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var startButton: UIButton!
//MARK: - LifeCycle
override func viewDidLoad() {
super.viewDidLoad()
setDelegates()
startButton.isEnabled = false
}
//MARK: - Delegates
func setDelegates()->Void {
emailTextField.delegate = self
passwordTextField.delegate = self
}
// Function that i need to trigger in the ViewModel
fileprivate func showErrorMessage(_ errorMessage : String) {
let alertController = UIAlertController(title: "UPS!", message: errorMessage, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Aceptar", style: .default))
self.present(alertController, animated: true, completion: nil)
}
// Function to go to the next ViewController
fileprivate func navigateToHomeViewController() {
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let homeViewController = storyBoard.instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController
homeViewController.modalPresentationStyle = .fullScreen
self.present(homeViewController, animated: true, completion: nil)
}
// Function of the ViewModel all validations
fileprivate func validateUserLogin(_ error: Error?, _ result: AuthDataResult?) {
switch error {
case .some(let error as NSError) where error.code == AuthErrorCode.wrongPassword.rawValue:
// validate the text field and then show the alert, i need to this, but inside the ViewModel
self.showErrorMessage("Contraseña incorrecta")
case .some(let error as NSError) where error.code == AuthErrorCode.userNotFound.rawValue:
// validate the text field and then show the alert, i need to this, but inside the ViewModel
self.showErrorMessage("Correo incorrecto")
case .some(let error):
// validate the text field and then show the alert, i need to this, but inside the ViewModel
self.showErrorMessage("Login error: \(error.localizedDescription)")
self.hideActivityIndicatorView()
case .none:
if (result?.user) != nil {
navigateToHomeViewController()
}
}
}
#IBAction func startButtonAction(_ sender: Any) {
if let email = emailTextField.text, let password = passwordTextField.text{
// Function to sing in firebase, it would be inside the ViewModel
Auth.auth().signIn(withEmail: email, password: password){(result, error) in
self.validateUserLogin(error, result)
}
}
}
fileprivate func validateFields() -> Bool {
return (passwordTextField.text!.count >= 8) && (isValidEmail(emailTextField.text ?? ""))
}
fileprivate func updateView() {
if(validateFields()){
startButton.isEnabled = true
startButton.tintColor = UIColor.blue
}else{
startButton.isEnabled = false
startButton.tintColor = UIColor.gray
}
}
}
func isValidEmail(_ email: String) -> Bool {
let emailRegEx = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let emailPred = NSPredicate(format:"SELF MATCHES %#", emailRegEx)
return emailPred.evaluate(with: email)
}
//MARK: - Text Field Delegate Methods
extension ViewController : UITextFieldDelegate{
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString
string: String) -> Bool {
if textField == self.emailTextField {
updateView()
} else if textField == self.passwordTextField {
updateView()
}
return true
}
}
StoryBoard
Here is the solution, thanks to Not Bad.
Model
import Foundation
struct User{
var email : String
var password : String
}
ViewModel
import Foundation
import FirebaseAuth
class AuthViewModel {
var delegate: AuthViewModelDelegate?
var user : User? = User(email: "", password: "")
func checkCredentials(user: User) {
Auth.auth().signIn(withEmail: user.email, password: user.password){(result, error) in
switch error {
case .some(let error as NSError) where error.code == AuthErrorCode.wrongPassword.rawValue:
self.delegate?.showError("Contraseña incorrecta")
self.delegate?.hideActivityIndicator()
case .some(let error as NSError) where error.code == AuthErrorCode.userNotFound.rawValue:
self.delegate?.showError("Correo incorrecto")
self.delegate?.hideActivityIndicator()
case .some(let error):
self.delegate?.showError("Login error: \(error.localizedDescription)")
self.delegate?.hideActivityIndicator()
case .none:
if (result?.user) != nil {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let homeViewController = storyboard.instantiateViewController(identifier: "HomeViewController")
self.delegate?.navigateToHomeViewController(homeViewController)
self.delegate?.hideActivityIndicator()
}
}
}
}
}
View
import UIKit
import FirebaseAuth
//MARK: - # protocols
protocol AuthViewModelDelegate {
func showError(_ message: String)
func hideActivityIndicator()
func navigateToHomeViewController(_ homeViewController: UIViewController)
}
class ViewController: UIViewController {
//MARK: - #IBOutlets
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var startButton: UIButton!
//MARK: - # Variables
var activityIndicator:UIActivityIndicatorView!
var authViewModel : AuthViewModel = AuthViewModel()
//MARK: - # Life cycle
override func viewDidLoad() {
super.viewDidLoad()
setDelegates()
setupUI()
}
fileprivate func setupUI() {
setActivityIndicator()
startButton.isEnabled = false
}
func setDelegates()->Void {
authViewModel.delegate = self
emailTextField.delegate = self
passwordTextField.delegate = self
}
func setActivityIndicator()->Void{
activityIndicator = UIActivityIndicatorView(style: UIActivityIndicatorView.Style.medium)
activityIndicator.center = view.center
activityIndicator.isHidden = true
self.view.addSubview(activityIndicator)
}
func displayActivityIndicatorView() -> () {
self.view.isUserInteractionEnabled = false
self.view.bringSubviewToFront(self.activityIndicator)
self.activityIndicator.isHidden = false
self.activityIndicator.startAnimating()
}
#IBAction func startButtonAction(_ sender: Any) {
self.displayActivityIndicatorView()
guard let email = emailTextField.text else {return}
guard let password = passwordTextField.text else {return}
self.authViewModel.user?.email = email
self.authViewModel.user?.password = password
self.authViewModel.checkCredentials(user: self.authViewModel.user!)
}
fileprivate func validateFields() -> Bool {
return (passwordTextField.text!.count >= 8) && (isValidEmail(emailTextField.text ?? ""))
}
fileprivate func updateView() {
if(validateFields()){
startButton.isEnabled = true
}else{
startButton.isEnabled = false
}
}
}
func isValidEmail(_ email: String) -> Bool {
let emailRegEx = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let emailPred = NSPredicate(format:"SELF MATCHES %#", emailRegEx)
return emailPred.evaluate(with: email)
}
//MARK: - Text Field Delegate Methods
extension ViewController : UITextFieldDelegate{
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString
string: String) -> Bool {
if textField == self.emailTextField {
updateView()
} else if textField == self.passwordTextField {
updateView()
}
return true
}
}
//MARK: - # AuthViewModel Delegate Methods
extension ViewController : AuthViewModelDelegate{
func hideActivityIndicator() {
DispatchQueue.main.async {
if !self.activityIndicator.isHidden{
DispatchQueue.main.async {
self.view.isUserInteractionEnabled = true
self.activityIndicator.stopAnimating()
self.activityIndicator.isHidden = true
}
}
}
}
func navigateToHomeViewController(_ homeViewController: UIViewController) {
(UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?.changeRootViewController(homeViewController)
}
func showError(_ message: String) {
DispatchQueue.main.async {
let alertController = UIAlertController(title: "UPS!", message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Aceptar", style: .default))
self.present(alertController, animated: true, completion: nil)
}
}
}

ViewController doesn't pass data on completion

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)")
}
}
}
}

Data is not connected to tableView

I'm building an app, which has three tableViews: One for clubs, which leads to another for members and this leads to transactions. The first two tableViews plus their "add" controller are working, whilst the third one isn't. The add controller works as far as I know, because I know that the transaction gets saved to Core Data. But the transaction isn't displayed in the tableView and I have no idea how I can fix this because this is my first App.
The Transaction Vc Code
import UIKit
import MessageUI
import CoreData
class TransactionViewController: UIViewController, MFMailComposeViewControllerDelegate, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableViewTransaction: UITableView!
let dateFromatter = DateFormatter()
var member: Member?
override func viewDidLoad() {
super.viewDidLoad()
dateFromatter.timeStyle = .long
dateFromatter.dateStyle = .long
}
override func viewWillAppear(_ animated: Bool) {
self.tableViewTransaction.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func addNewTransaction(_ sender: Any) {
performSegue(withIdentifier: "newTransaction", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let destination = segue.destination as? AddTransactionViewController else {
return
}
destination.member = member
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return member?.transactions?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableViewTransaction.dequeueReusableCell(withIdentifier: "transactionCell", for: indexPath)
if let transaction = member?.transactions?[indexPath.row] {
cell.textLabel?.text = transaction.reason
if let date = transaction.date {
cell.detailTextLabel?.text = dateFromatter.string(from: date)
}
}
return cell
}
#IBAction func sendEmail(_ sender: Any) {
let mailComposeVC = configureMailController()
if MFMailComposeViewController.canSendMail() {
self.present(mailComposeVC, animated: true, completion: nil)
} else {
showMailError()
}
}
func configureMailController() -> MFMailComposeViewController {
let mailComposerVC = MFMailComposeViewController()
mailComposerVC.mailComposeDelegate = self
mailComposerVC.setSubject("MAHNUNG")
mailComposerVC.setMessageBody("Hey, ich habe gesehen, dass dein Kontostand in der Buchhaltung zu tief ist. Ich bitte dich daher schnellst möglich mir das Geld zu bringen. Gruss", isHTML: false)
return mailComposerVC
}
func showMailError() {
let senMailErrorAlert = UIAlertController(title: "Could not send email", message: "Dein Gerät konnte die Email nicht senden.", preferredStyle: .alert)
let dismiss = UIAlertAction(title: "Ok", style: .default, handler: nil)
senMailErrorAlert.addAction(dismiss)
self.present(senMailErrorAlert, animated: true, completion: nil)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true, completion: nil)
}
}
The addTransaction Code:
import UIKit
class AddTransactionViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var reasonTextField: UITextField!
#IBOutlet weak var amountTextField: UITextField!
#IBOutlet weak var datePicker: UIDatePicker!
var member: Member?
override func viewDidLoad() {
super.viewDidLoad()
reasonTextField.delegate = self
amountTextField.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
reasonTextField.resignFirstResponder()
amountTextField.resignFirstResponder()
}
#IBAction func saveTransaction(_ sender: Any) {
let reason = reasonTextField.text
let date = datePicker.date
let moneyText = amountTextField.text ?? ""
let money = Double(moneyText) ?? 0.0
if let transaction = Trancsaction(money: money, reason: reason, date: date) {
member?.addToRawTransactions(transaction)
do {
try transaction.managedObjectContext?.save()
self.navigationController?.popViewController(animated: true)
} catch {
let alert = UIAlertController(title: "Transaktion konnte nicht gespeichert werden", message: "Bitte wiederholen Sie ihre Eingabe", preferredStyle: .alert)
present(alert, animated: true, completion: nil)
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}
}
Core Data:
import UIKit
import CoreData
#objc(Trancsaction)
public class Trancsaction: NSManagedObject {
var date: Date? {
get {
return rawDate as Date?
}
set {
rawDate = newValue as NSDate?
}
}
convenience init?(money: Double, reason: String?, date: Date?) {
let appDelegate = UIApplication.shared.delegate as? AppDelegate
guard let context = appDelegate?.persistentContainer.viewContext else {
return nil
}
self.init(entity: Trancsaction.entity() , insertInto: context)
self.date = date
self.money = money
self.reason = reason
}
}

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
}

How to pass the variable from a ViewController Class to other Struct?

I have no idea how to pass the data from the class to another struct. This is my ViewController.swift file. And I have another file called Meme.swift which is used to save the struct. I tried to put the struct in ViewController.swift file as well as Meme.swift file but I cannot access the value like topTextField.text and lowTextField.text and use them in the struct. Can anyone help me with this?
ViewController:
import UIKit
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITextFieldDelegate {
#IBOutlet weak var cameraButton: UIBarButtonItem!
#IBOutlet weak var bottomTextField: UITextField!
#IBOutlet weak var topTextField: UITextField!
#IBOutlet weak var imagePickerView: UIImageView!
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var actionButton: UIBarButtonItem!
let bottomTextFieldDelegate = BottomTextFieldDelegate()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
topTextField.text = "TOP"
bottomTextField.text = "BOTTOM"
topTextField.delegate = self
bottomTextField.delegate = self.bottomTextFieldDelegate
}
func textFieldDidBeginEditing(_ textField: UITextField) {
topTextField.text = ""
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.view.endEditing(true)
textField.resignFirstResponder()
actionButton.isEnabled = true
return true
}
#IBAction func shareAction(_ sender: Any) {
let image = generateMemedImage()
let controller = UIActivityViewController(activityItems: [image as Any], applicationActivities: nil)
self.present(controller, animated: true, completion: nil)
controller.completionWithItemsHandler = {(activityType: UIActivityType?, completed:Bool, returnedItems:[Any]?, error: Error?) in
if !completed {
debugPrint("cancelled")
return
}else{
self.save()
self.dismiss(animated: true, completion: nil)
}
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
cameraButton.isEnabled = UIImagePickerController.isSourceTypeAvailable(.camera)
configureTextField(textField: topTextField)
configureTextField(textField: bottomTextField)
topTextField.textAlignment = .center
bottomTextField.textAlignment = .center
subscribeToKeyboardNotifications()
actionButton.isEnabled = false
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
unsubscribeFromKeyboardNotifications()
}
func configureTextField(textField: UITextField) {
textField.defaultTextAttributes = memeTextAttributes
}
func save() -> Meme {
// Create the meme
let memedImage = generateMemedImage()
let meme = Meme(topText: topTextField.text!, bottomText: bottomTextField.text!, originalImage: imageView.image!, memedImage: memedImage)
return meme
}
func generateMemedImage() -> UIImage {
// TODO: Hide toolbar and navbar
navigationController?.setToolbarHidden(true, animated: true)
self.navigationController?.isNavigationBarHidden = true
// Render view to an image
UIGraphicsBeginImageContext(self.view.frame.size)
view.drawHierarchy(in: self.view.frame, afterScreenUpdates: true)
let memedImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
// TODO: Show toolbar and navbar
navigationController?.setToolbarHidden(false, animated: false)
self.navigationController?.isNavigationBarHidden = false
return memedImage
}
func keyboardWillShow(_ notification:Notification) {
view.frame.origin.y = 0 - getKeyboardHeight(notification)
}
func keyboardWillHide(_ notification:Notification) {
view.frame.origin.y = 0
}
func getKeyboardHeight(_ notification:Notification) -> CGFloat { //getting the height of keyboard and use it for func keyboardWillShow to relocate
//the keyboard using keyboardWillShow function
let userInfo = notification.userInfo
let keyboardSize = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue // of CGRect
return keyboardSize.cgRectValue.height
}
func subscribeToKeyboardNotifications() { //setting up the obeserver to be notified when keyboard is shown or not, then execute keyboardWillShow function
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: .UIKeyboardWillHide, object: nil)
}
func unsubscribeFromKeyboardNotifications() { //unsubscribe the notification
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
}
//setting the arributes of the text
let memeTextAttributes:[String:Any] = [
NSStrokeColorAttributeName: UIColor.black,
NSForegroundColorAttributeName: UIColor.white,
NSFontAttributeName: UIFont(name: "HelveticaNeue-CondensedBlack", size: 40)!,
NSStrokeWidthAttributeName: 3,]
#IBAction func pickAnImageFromAlbum(_ sender: Any) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .photoLibrary
present(imagePicker, animated: true, completion: nil)
}
#IBAction func pickAnImageFromCamera(_ sender: Any) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .camera
present(imagePicker, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
imagePickerView.image = image
imagePickerView.contentMode = .scaleAspectFit
dismiss(animated: true, completion: nil)
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController){
dismiss(animated: true, completion: nil)
}
}
BottomFieldDelegate:
import Foundation
import UIKit
class BottomTextFieldDelegate: NSObject, UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
textField.text = ""
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
let viewController = ViewController()
textField.resignFirstResponder()
viewController.view.endEditing(true)
let actionButton = viewController.actionButton
actionButton?.isEnabled = true
return true
}
}
First u need to move this function like save() and generateMemedImage() into your ViewController class and then you can access topTextField.text and lowTextField.text
Modify your struct like below, and do not put nsobject in struct
struct Meme {
var topText: String
var bottomText: String
var memedImage: UIImage
var originalImage: UIImage
init(topText: String, bottomText: String, originalImage: UIImage, memedImage: UIImage) {
self.topText = topText
self.bottomText = bottomText
self.originalImage = originalImage
self.memedImage = memedImage
}
}
Remove save() from Meme struct and put that code in your Viewcontroller file.
Modify your function which return Meme struct object.
func save() -> Meme {
// Create the meme
let meme = Meme(topText: topTextField.text!, bottomText: bottomTextField.text!, originalImage: imageView.image!, memedImage: memedImage)
return meme
}
And you can store in Array like below.
let array:[Meme] = []
array.append(save())