I've 2 ViewController's: VC1 and VC2. In VC1 i've :
#IBAction func cliclOnBtn(_ sender: UIButton) {
CameraController.takePicture()
}
...
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
print("didFinishPickingMediaWithInfo", picker.sourceType)
let _image = (info[UIImagePickerControllerOriginalImage] as! UIImage)
ImageView.image = _image
Images.append(_image)
}
In VC2 i'm passing array of _image, but when i try to back on VC1, photos from camera adding by clicking again. How to reset data? How to make reset of an array of photo, when back button pressed ?
You can implement viewWillAppear in VC1, and then remove all elements of the image Array. This way, when you tap back on VC2, the VC1 will have it's image Array empty.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
Images.removeAll()
}
Btw, consider renaming your instance variables, lowercasing the initial character, it's a good practice.
Use protocol with delegate for sending data to back
You can update UI or code in delegate function
import UIKit
protocol DestinationViewControllerDelegate {
func updateData(text:String); // you can use Array, Dictonary, Modal as per your requirment
}
class DestinationViewController: UIViewController {
var delegate:FullCalendarViewDelegate! = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func doneButton(sender: AnyObject) {
delegate?.updateData("Got new data")
self.navigationController?.popViewControllerAnimated(true)
}
}
// Sender View Controller
class SenderViewController: UIViewController, DestinationViewControllerDelegate {
#IBOutlet weak var titleLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func NextButton(sender: AnyObject) {
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let dash:DestinationViewControllerDelegate = storyBoard.instantiateViewControllerWithIdentifier("DestinationViewControllerDelegate") as! DestinationViewControllerDelegate
dash.delegate = self
self.navigationController?.pushViewController(dash, animated: true)
}
func updateData(text:String) {
titleLabel.text = text
}
}
Related
I have my main screen with only one button on it "Show next screen". When the second screen(VC) pops up it has 2 buttons (go back and toSelect button).
My goal is to when I show my second screen and select a button on it then go back to first. The button on my second screen will stay selected. How can I do that?
So basically I need to save my actions on the second screen so if I go back to it it will show everything I did.
What is the best way to do it?
Storyboard
The easiest way to achieve this using Delegate and protocol.
you should listen and save the changes of SecondViewController at FirstViewController using delegate methods.
And when you are presenting the secondViewController you will share the saved changes to secondViewController so that button can be selected on behalf of that information
Code -
class FirstViewController: UIViewController {
//secondViewController States
private var isButtonSelected = false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func gotoSecondVCAction(_ sender: Any) {
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
guard let secondVC = storyBoard.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController else { return }
secondVC.isButtonSelected = isButtonSelected
secondVC.delegate = self
self.present(secondVC, animated: true, completion: nil)
}
}
extension FirstViewController: ButtonSelectionProtocol {
func button(isSelected: Bool) {
isButtonSelected = isSelected
}
}
and for secondViewController
protocol ButtonSelectionProtocol {
func button(isSelected:Bool)
}
class SecondViewController: UIViewController {
var isButtonSelected : Bool = false
var delegate:ButtonSelectionProtocol?
#IBOutlet weak var selectButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
if isButtonSelected {
selectButton.tintColor = .red
selectButton.setTitle("Selected", for: .normal)
}else{
selectButton.tintColor = .green
selectButton.setTitle("Select Me", for: .normal)
}
}
#IBAction func gobackAction(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
#IBAction func selectAction(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
isButtonSelected.toggle()
delegate?.button(isSelected: isButtonSelected)
}
}
i am new to iOS here is my question:
I have a saveCardViewController (Presented Modally) with some textFields and Save button.
#IBAction func Save(_ sender: UIButton) {
date = datePicker.date
try! realm.write() {
sessionCard.pokerType = pokerTypeSegment.titleForSegment(at: pokerTypeSegment.selectedSegmentIndex)!
date = dateFormatter.string(from: date)
sessionCard.handsPlayed = Int(handPlayedTextlabel.text!) ?? 0
sessionCard.moneyIn = Int(moneyInTextLabel.text!) ?? 0
sessionCard.moneyOut = Int(moneyOutTextLabel.text!) ?? 0
sessionCard.timePlayed = Int(timePlayedTextLabel.text!) ?? 0
sessionCard.sortDate = date
realm.add(sessionCard)
}
dismiss(animated: true, completion: nil)
}
How can I reloadData() on my main ViewController, after Save button is pressed and saveCardViewController is dismissed.
Thanks!
EDIT # 1:
Thank you #davidev for your answer,I made changes but still does not update
My ViewController With TableView:
class SessionViewController: BackgroundViewController, RefreshViewDelegate {
func refreshView() {
tableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
My ViewController with data and Save button:
protocol RefreshViewDelegate {
func refreshView()
}
class AddSessionViewController: UIViewController, UITextFieldDelegate {
var delegate: RefreshViewDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func TEST2(_ sender: UIButton) {
delegate?.refreshView()
dismiss(animated: true, completion: nil)
}
You can use delegate pattern to achieve this.
Declare your Refresh Protocol like this:
protocol RefreshViewDelegate {
func refreshView()
}
Make your parent view conform to this protocol and implement refreshView() with your custom refresh action. Also make sure to set the delegate of the child view to self.
Inside saveCardViewController declare your delegate variable
var delegate : RefreshViewDelegate?
And call the delegate action inside your IBaction
delegate?.refreshView()
Edit:
I just saw your updated code. As you are using Storyboard segues, you still have to set the delegate via code. In your main view controller add the function:
override func prepareForSegue(segue: UIStoryboardSegue?, sender: AnyObject?) {
if let viewController = segue.destinationViewController as? AddSessionViewController
{
viewController.delegate = self
}
}
any help is appreciated.
I am new to Ios development and I am trying to change a label text which is located in my first initial view controller. I want this text to change as I press a button in the second view controller which is segued to the initial one.
here is my first view controller
import UIKit
protocol gameModeDelegate {
func didTapChoice(test:String)
}
class ViewController2: UIViewController {
var selectionDelegate:gameModeDelegate!
#IBAction func chooseButton(_ sender: Any) {
selectionDelegate.didTapChoice(test: "TEST")
let selectVC = storyboard?.instantiateViewController(withIdentifier: "VC1") as! ViewController
present(selectVC,animated: true,completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
here is what i have done in the second where the label is
override func viewDidLoad() {
super.viewDidLoad()
let selectVC2 = storyboard?.instantiateViewController(withIdentifier: "VC1") as! ViewController2
selectVC2.selectionDelegate = self
winningLabel.isHidden = true
winningLabel.center = CGPoint(x: winningLabel.center.x, y: winningLabel.center.y - 400)
playAgainoutlet.isHidden = true
playAgainoutlet.center = CGPoint(x: playAgainoutlet.center.x, y: playAgainoutlet.center.y + 400)
}
extension ViewController: gameModeDelegate{
func didTapChoice(test: String) {
CommunicationLabel.text = test
}
}
I tried these two methods so far and i keep getting this error.
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
You should not use this approach to achieve the result, you may use two different approaches to achieve the same result.
1- Use a delegate protocol approach:
in secondViewController you should declare a protocol like this
protocol applySelecction {
func applyText(text: String)
}
and in the class declare a variable like this.
var delegate: apply selection?
then in the button action
#IBAction func saveButtom(sender: UIButton){
//print(selected)
delegate?.applySelection(text: text) //text is the value select from UILAbel o the option the user select
self.dismiss(animated: true, completion: nil)
}
then in firstViewController conforms to applySelection protocol like this
class FirstViewController: UIViewController,applySelection{
func applyText(text: String){
//update the UIlabel here
2- Use a closure.
here in secondViewController you should add a new var like this,
var applyText: ((String) -> Void)?
then in
#IBAction func saveButtom(sender: UIButton){
self.applyText(text) //text is your text to update
}
and in firstViewController in prepare for segue rewrite like this.
let vc = segue.destination as! fisrtViewController)
vc.applyText = { [weak self] data in
guard let self = self else {return}
self.text = text //this is assigning the text to self-text supposing text is a UILabel in this viewController
}
You may try one of the two approaches which may seem right for you.
EDIT.
try this.
class ViewController2: UIViewController {
var selectionDelegate:gameModeDelegate!
#IBAction func chooseButton(_ sender: Any) {
selectionDelegate.didTapChoice(test: "TEST")
//if segue is a show segue
self.navigationController?.popViewController(animated: true)
//else is a modal segue.
dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
PD. you dont have to present a viewcontroller already present in the view stack, only dissmis it. Good luck
I'm getting data from second VC to first VC using protocol or delegates, Data is receiving in first VC but the problem is that Data is not showing in Textfield. Here is my Complete Code for understanding. Any Effort is appreciated.
FirstVC class
import UIKit
class firstViewController: UIViewController, UITextFieldDelegate, MyProtocol {
var valueSentFromSecondViewController : String?
#IBOutlet weak var myTextField : UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func myTextFieldACTIONWhenEditingDidBegin(_ sender: Any) {
myTextField.isUserInteractionEnabled = false
let secondVC = self.storyboard?.instantiateViewController(withIdentifier: "secondViewController") as! secondViewController
secondVC.delegate = self
self.navigationController?.pushViewController(secondVC, animated: true)
}
func setResultsAfterEvaluation(valueSent: String) {
self.valueSentFromSecondViewController = valueSent
print(valueSentFromSecondViewController!) // Ahtazaz(DATA showing here)
myTextField.text = valueSentFromSecondViewController //This's the problem, Why not showing here in this this TextField
}
}
Now, SecondVC Class
import UIKit
protocol MyProtocol {
func setResultsAfterEvaluation(valueSent: String)
}
class secondViewController: UIViewController {
var delegate : MyProtocol?
var sentValue : String?
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func btn(_ sender: Any) {
let firstVC = self.storyboard?.instantiateViewController(withIdentifier: "firstViewController") as! firstViewController
self.navigationController?.pushViewController(firstVC, animated: true)
sentValue = "Ahtazaz"
delegate?.setResultsAfterEvaluation(valueSent: sentValue!)
}
}
You are pushing SecondVC. Then in SecondVC you are pushing again FirstVC.
I think this is where you are making mistake.
let firstVC = self.storyboard?.instantiateViewController(withIdentifier: "firstViewController") as! firstViewController
You are creating a new instance of FirstVC. Then you push it which is wrong. Call your delegate and then Pop back to previous(FirstVC) controller
Try this code in your button action
#IBAction func btn(_ sender: Any) {
sentValue = "Ahtazaz"
delegate?.setResultsAfterEvaluation(valueSent: sentValue!)
self.navigationController?.popViewController(animated: true)
}
This should be the correct approach rather than pushing the controller again.
The steps are Simple to use Delegates for passing the data to previous VC
Second VC:
At the top of VC declare the protocol as follows:
protocol MenuListingDelegate {
func callBackOfMenuSelected(arrSelectedCategory:[Int],isFromWhichPopup:Int)
}
Then inside that define the variable like this
var delegate:MenuListingDelegate?
And then provide the data to the delegate like this. In my case i provide that on click of button before pop View Controller
self.delegate?.callBackOfMenuSelected(strToPass: "Hello")
Now in First VC:
At the top define the Delegate method like this:
class DayDetailVC: UIViewController,MenuListingDelegate {}
And fetch the Data like this
//MARK:- Menu Listing Delegate
func callBackOfMenuSelected(strToPass: String) {
print(strToPass)
}
Note:- Do not forget to declare the delegate of the secondVC where we use this. secondVC.delegate = self.
Edit Check the following cases
Case 1:- Check the outlets of the myTextField i guess the issue is there. If everything is correct remove the Outlet and the set that again
Case 2:- Still if doesnt work then try setting like this
func setResultsAfterEvaluation(valueSent: String) {
myTextField.text = "\(valueSent)"
}
Hope this helps.
Edit 2
I have seen you have used pushViewController in the following lines:
So you can simply use the following line of code to pass the data to firstVC
In SecondVC add following Code:
let firstVC = self.storyboard?.instantiateViewController(withIdentifier: "firstViewController") as! firstViewController
firstVC.valueSentFromSecondViewController = "Hello World"
self.navigationController?.pushViewController(firstVC, animated: true)
Now in FirstVC
Use like in viewDidLoad() or anywhere you want
print(valueSentFromSecondViewController) //Hello World
Cheers it Done.
Choose the way you want.
Note:- But i will suggest you to use popViewController instead of
pushViewController when returning back from SecondVC -> FirstVC. Rest depends upon your requirements.
Hope this helps.
I am trying to pass data back from the second viewController.
I can do that without NavigationController. But now I need to use NavigationController. Then my code does work as before. The data wont pass.
Here is the simple code:
In first viewController
class ViewController: UIViewController, backfromSecond {
#IBOutlet weak var text: UILabel!
var string : String?
override func viewDidLoad() {
super.viewDidLoad()
self.string = "Start here"
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
self.text.text = self.string
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationViewController = segue.destination as? secondViewController{
destinationViewController.delegate = self
}
}
func back(text: String) {
self.string = text
print(text)
}
}
And Second viewController:
protocol backfromSecond {
func back(text: String)
}
class secondViewController: UIViewController {
var string : String = "nothing here"
var delegate : backfromSecond?
override func viewDidLoad() {
super.viewDidLoad()
delegate?.back(text: string)
// Do any additional setup after loading the view.
}
}
What is wrong here?
Suppose A & B are two controllers and you first navigated from A to B with some data. And now you want to POP from B to A with some data.
Unwind Segues is the best and recommended way to do this.
Here are the steps.
Open A.m
define following method
#IBAction func unwindSegueFromBtoA(segue: UIStoryNoardSegue) {
}
open storyboard
Select B ViewController and click on ViewController outlet. press control key and drag to 'Exit' outlet and leave mouse here. In below image, selected icon is ViewController outlet and the last one with Exit sign is Exit Outlet.
You will see 'unwindSegueFromBtoA' method in a popup . Select this method .
Now you will see a segue in your view controler hierarchy in left side. You will see your created segue near StoryBoard Entry Piont in following Image.
Select this and set an identifier to it. (suggest to set the same name as method - unwindSegueFromBtoA)
Open B.m . Now, wherever you want to pop to A. use
self.performSegueWithIdentifier("unwindSegueFromBtoA", sender: dataToSend)
Now when you will pop to 'A', 'unwindSegueFromBtoA' method will be called. In unwindSegueFromBtoA of 'A' you can access any object of 'B'.
That's it..!
I think your problem is in the prepare for segue method. If the view controller is on a navigation stack i think your code should be something like
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationViewController = segue.destination as? UINavigationController).topViewController as! secondViewController{
destinationViewController.delegate = self
}
}
You can use unwind segues to pass data back.
Here's a tutorial
https://spin.atomicobject.com/2014/10/25/ios-unwind-segues/
This works me well.
1st VC
class ViewController: UIViewController, backfromSecond {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func Passingfrom1stVCTo2ndVC(_ sender: AnyObject) {
if let vc = self.storyboard?.instantiateViewController(withIdentifier: "ViewController3") as? ViewController3{
vc.dataFrom1StVC = "message send from 1st VC"
vc.delegate = self
self.navigationController?.pushViewController(vc, animated: true)
}
}
func back(text: String) {
print("data\(text)")
}
}
2nd VC.
protocol backfromSecond: class {
func back(text: String)
}
class ViewController3: UIViewController {
var dataFrom1StVC : String? = nil
week var delegate : backfromSecond?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func DataSendFrom2ndVCTo1stVC(_ sender: AnyObject) {
self.delegate?.back(text: "Message Send From 2nd vc to 1st VC")
self.navigationController?.popViewController(animated: true)
}
}
I hope it will work you. If any problem then ask me i will help you.