Hello I want to know what is the best way to call a func after performing a segue on swift. I want to click on a button and perform a segue and start my viewDidLoad() with a new func.
//FIRST VIEW CONTROLLER
import UIKit
class CelebritiesViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
questionsLevel1()
pickingRandomQuestion()
hide()
finishGame.hidden = true
nextLevel.hidden = true
if isButtonClick == true {
questionsLevel2()
}
}
}
//SECOND VIEW CONTROLLER
import UIKit
class Level2Section: UIViewController {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "level2" {
if let destinationLevel2 = segue.destinationViewController as? CelebritiesViewController {
destinationLevel2.isButtonClick = true
}
}
}
}
PS: After finish first section of questions user can choose to finish the game or go to next level, next level is a new view controller so we performing this segue in a second view controller not in the same one.
ViewController1:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "SegueIdentifierHere" {
if let destVC = segue.destinationViewController as? ViewController2 {
destVC.isButtonClick = true
}
}
}
ViewController2:
Define a Bool:
var isButtonClick:Bool!
ViewDidLoad():
override func viewDidLoad() {
super.viewDidLoad()
questionsLevel1()//This is my first section of questions
pickingRandomQuestion()//My function to make a random number of questions
if isButtonClick == true {
isButtonClick = !isButtonClick
callSecondFunction()
}
}
Related
I have 2 Controllers: MainVC and SideMenuVC.
I wanted to modify MainVC using SideMenuVC, so created delegate of SideMenuVC ( as well, there's Emdebed segue "name..." to "Side Menu View Controller" on storyboard because MainViewController has subView, which contains ContainerView - this container is our SideMenuVC. And this delegate works as he should.
However, due to logic in the app, I also need to send data from MAINVC to SIDEMENUVC.
So i did the same - created another delegate of second VC... But I turned out, MainViewControllerDelegate is not responding in SideMenuViewController. And i'm absolutely clueless...
Yes, i do implement necessary protocols in both classes, in extension!
Code of both VCs below, screens of storyboard in the attachment
MainViewController + MainViewControllerDelegate
protocol MainViewControllerDelegate{
func isImageLoaded(_ isLoaded:Bool)
}
class MainViewController: UIViewController {
/* ... */
var delegate: MainViewControllerDelegate?
var sideMenuViewController: SideMenuViewController?
private var isSideMenuPresented:Bool = false
private var isImageLoaded:Bool = false
override func viewDidLoad() {
super.viewDidLoad()
self.isImageLoaded = false
self.setupUI()
self.delegate?.isImageLoaded(false)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "MainVC_SideMenuVC_Segue")
{
if let controller = segue.destination as? SideMenuViewController
{
self.sideMenuViewController = controller
self.sideMenuViewController?.delegate = self
}
}
}
/* ... */
//I'm using PHPicker, and when new image is selected, i want to send "true" via delegate
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
dismiss(animated: true)
if let itemProvider = results.first?.itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self){
let previousImage = self.presentedImage.image
itemProvider.loadObject(ofClass: UIImage.self){ [weak self] image, error in
DispatchQueue.main.async {
guard let self = self, let image = image as? UIImage, self.presentedImage.image == previousImage else {
return
}
self.presentedImage.image = image
self.isImageLoaded = true;
self.delegate?.isImageLoaded(true)
}
}
}
}
SideMenuViewController + SideMenuViewControllerDelegate
protocol SideMenuViewControllerDelegate{
func hideSideMenu()
func performAction(_ type:OperationType)
}
class SideMenuViewController: UIViewController {
/*...*/
var delegate: SideMenuViewControllerDelegate?
var mainViewController: MainViewController?
private var menuData: [ExpandingCellModel] = []
private var isImageLoaded: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
// self.mainViewController?.delegate = self
menuData = setupData()
setupUI()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let controller = segue.destination as? MainViewController {
controller.delegate = self
}
}
}
/* ... */
Here is what I think is happening.
There is segue happening from MainVC to SideMenuVC but there is no segue actually happening between SideMenuVC to MainVC in my opinion.
Happening is keyword because there is an EmbedSegue from MainVC to SideMenuVC but where is the segue from SideMenuVC to MainVC ? You did some connection in storyboard but nothing is happening in my opinion.
That is why in override func prepare is being called as planned in MainViewControllerDelegate and the delegate is getting set but it is not getting set in SideMenuViewController since override func prepare doesn't get called as no segue happens.
What you can do instead which might work is set both the delegates inside prepare in MainViewControllerDelegate
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "MainVC_SideMenuVC_Segue")
{
if let sideMenuVC = segue.destination as? SideMenuViewController
{
sideMenuVC.delegate = self
// assign MainViewControllerDelegate here
self.delegate = sideMenuVC
}
}
}
Check now if data is sent back to main view controller also.
If your issue is still not yet solved, please have a look and try this small example I set for you in github to show passing data between mainVC and embeddedVC and it might give you some hints.
You can see the result of this in action here: https://youtu.be/J7C7SEC04_E
I'm trying to run a function in a container view that I added in the storyboard editor but when I call it in the parent view controller, nothing happens. I want to be able to call a function and change a property of the child view controller triggered from the parent. Am I doing something wrong or should I just be doing this another way?
TLDR: Trigger a function in a child view controller from parent
Parent View Controller:
// ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var routeConfirmationView: UIView! //This is the container view that I'm trying to work with
var RouteSelectionViewController: RouteSelectionViewController?
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(routeConfirmationView)
self?.RouteSelectionViewController?.getRidOfLoadingCover(isHidden: true) //The code that isn't doing anything
}
}
Container View Controller:
import UIKit
class RouteSelectionViewController: UIViewController {
#IBOutlet weak var loadingCoverView: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad(
}
//The function that I want to trigger from the other view controller:
func getRidOfLoadingCover (isHidden: Bool){
if (isHidden == true) {
loadingCoverView.alpha = 0
}
else if (isHidden == false) {
loadingCoverView.alpha = 100
}
}
}
Storyboard:
To call functions, or access properties, in a View Controller embedded in a Container View, you need to get and keep a reference to that controller.
When the Container View loads the embedded VC, it calls Prepare For Segue. Grab your reference there:
class WithContainerViewController: UIViewController {
var routeSelectionVC: RouteSelectionViewController?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? RouteSelectionViewController {
// save reference to VC embedded in Container View
self.routeSelectionVC = vc
}
}
#IBAction func didTap(_ sender: Any) {
if let vc = routeSelectionVC {
vc.getRidOfLoadingCover(isHidden: true)
}
}
}
class RouteSelectionViewController: UIViewController {
#IBOutlet weak var loadingCoverView: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
}
//The function that I want to trigger from the other view controller:
func getRidOfLoadingCover (isHidden: Bool){
if (isHidden == true) {
loadingCoverView.alpha = 0
}
else if (isHidden == false) {
loadingCoverView.alpha = 100
}
}
}
You will likely next ask about calling a function in the "parent" VC from the embedded VC. This can be done with protocol / delegate pattern, or with closures. Again, you can set that up in prepare for segue:
class WithContainerViewController: UIViewController {
var routeSelectionVC: RouteSelectionViewController?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? RouteSelectionViewController {
// set the closure in the VC embedded in Container View
vc.callbackClosure = {
self.routeSelectionButtonTapped()
}
// save reference to VC embedded in Container View
self.routeSelectionVC = vc
}
}
#IBAction func didTap(_ sender: Any) {
if let vc = routeSelectionVC {
vc.getRidOfLoadingCover(isHidden: true)
}
}
func routeSelectionButtonTapped() -> Void {
print("Button in RouteSelectionViewController in Container View was tapped!")
}
}
class RouteSelectionViewController: UIViewController {
#IBOutlet weak var loadingCoverView: UIActivityIndicatorView!
var callbackClosure: (() -> ())?
override func viewDidLoad() {
super.viewDidLoad()
}
//The function that I want to trigger from the other view controller:
func getRidOfLoadingCover (isHidden: Bool){
if (isHidden == true) {
loadingCoverView.alpha = 0
}
else if (isHidden == false) {
loadingCoverView.alpha = 100
}
}
#IBAction func didTap(_ sender: Any) {
callbackClosure?()
}
}
I guess you did not set the RouteSelectionViewController variable (btw., variable names should start with a small letter), so it is nil and nothing happens.
If you want you embed a view controller into another, you need to implement the view controller containment requirements (see documentation).
I'm in my first week of developing in iOS and have become stuck on an issue with passing data between view controllers. My set up consists of a view with VC having a button in it and also a container view (no associated view controller). The container view has an embedded Segue to a TableView with TableViewController. The table has 6 rows and each row has a stepper that can change the value of a text view on the associated row. What I would like to do is collect the values of all the textviews when I press the button on the main view.
I am trying to use delegate to do this but when I press the button the returned value is always nil. I believe the problem is to do with the fact the VC is not being passed to the table view controller via the prepareForSegue function but I'm not sure why? Could be to do with the load order of the controllers?
import UIKit
class PredictionViewController: UIViewController, PredictionDelegate {
var predictionData: String!
#IBOutlet weak var messageTextBox: UITextView!
#IBOutlet weak var predictionSubmitButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.messageTextBox.isEditable = false
}
override func viewDidAppear(_ animated: Bool) {
}
func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "predictionSegue") {
// pass data to next view
let vc = segue.destination as! PredictionsTableViewController
vc.predictionHomeDelegate = self
}
}
func receiveData(with data: String) {
predictionData = data
print(predictionData)
}
#IBAction func predictionSubmitButtonAction(_ sender: UIButton) {
print(predictionData)
}
}
TableViewController: (stripped to minimum)
import UIKit
protocol PredictionDelegate: class {
func receiveData(with data: String)
}
class PredictionsTableViewController: UITableViewController, PredictionDelegate {
weak var predictionHomeDelegate: PredictionDelegate?
#IBOutlet weak var homeTeamScore1: UITextView!
#IBOutlet weak var homeTeamStepper1: UIStepper!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
}
func getPredictionList() {
//does some stuff
self.passDataBackwards()
}
func receiveData(with data: String) {}
func passDataBackwards() {
let data = "{\"score\":\"1\"}"
predictionHomeDelegate?.receiveData(with: data)
}
#IBAction func homeTeamStepper1Action(_ sender: UIStepper) {
let score = Int(sender.value).description
homeTeamScore1.text = score
self.passDataBackwards()
}
}
Any help gratefully received!
Edit:
After comments...
You have the wrong idea about Protocols and Delegates. They are not needed here.
Instead, in your "home" VC, get a reference to the embedded VC. Then, add a function in your embedded VC that you can call to get its data.
// "home" view controller
class PredictionViewController: UIViewController {
// this will be a reference to the embedded view controller
var embeddedVC: PredictionsTableViewController?
#IBAction func getDataButtonTapped(_ sender: Any) {
if let tableData = embeddedVC?.getMyData() {
print("Result: \(tableData)")
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "predictionSegue") {
if let vc = segue.destination as? PredictionsTableViewController {
// get a reference to the embedded VC
self.embeddedVC = vc
}
}
}
}
// embedded view controller
class PredictionsTableViewController: UIViewController {
var numTaps = 0
func getMyData() -> String {
return "\(numTaps)"
}
#IBAction func didTap(_ sender: Any) {
numTaps += 1
}
}
You're close, but a couple mistakes...
Here is a very, very simple example. View controller with container, which has a view controller with a button.
Code:
import UIKit
// your protocol
protocol PredictionDelegate: class {
func receiveData(with data: String)
}
// embedded view controller
// NOTE: this should NOT include PredictionDelegate
class PredictionsTableViewController: UIViewController {
weak var predictionHomeDelegate: PredictionDelegate?
#IBAction func didTap(_ sender: Any) {
// on button tap, "send data back"
predictionHomeDelegate?.receiveData(with: "Test")
}
}
// "home" view controller
// NOTE: this DOES include PredictionDelegate
class PredictionViewController: UIViewController, PredictionDelegate {
func receiveData(with data: String) {
print(data)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "predictionSegue") {
if let vc = segue.destination as? PredictionsTableViewController {
// set self as the delegate of the embedded PredictionsTableViewController
vc.predictionHomeDelegate = self
}
}
}
}
Notes:
Do NOT include func receiveData(with data: String) {} in your embedded view controller
Do NOT assign PredictionDelegate to your embedded view controller
You need to hook your segue to the vc itself instead of the cell and use
self.performSegue(withIdentifier: "predictionSegue", sender: nil)
Since these are two separate screens I am not too sure it makes too much sense to have the button on the first view controller that submits data from the second, can the button not just be on the second. If for whatever reason it can't you could pass the data back to the first view controller on segueing back by adding a public variable to the first view controller and adding another prepare method to the second to pass the data back like you have done when adding the delegate.
This question already has answers here:
Swift pass data through navigation controller
(8 answers)
Closed 4 years ago.
I need to send bool value to Second viewController based on some condition.
but in Second ViewController the value of bool variable is always false.
This is First ViewController code:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func moveTo(_ sender: Any) {
self.performSegue(withIdentifier: "Move2", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "Move2"
{
let secondVC = segue.destination as? SecondViewController
secondVC?.second = true
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The SecondViewController code is
import UIKit
class SecondViewController: UIViewController {
var second = Bool()
override func viewDidLoad() {
super.viewDidLoad()
print(second)
// Do any additional setup after loading the view.
}
But this Value of second variable is false.
Whats wrong in my code. need help. TIA..
EDIT : In both ViewController i have navigation controller. That's problem. SecondVC is SecondViewController's navigation controller, so that i can't pass data to that secondVC.
Go to your Storyboard and select SecondViewController (the yellow circle on the top left. Go to identity inspector and select SecondViewController from the Class drop down.
See image below:
Keep things simple and clean :
FirstVC :
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "Move2"
{
let secondVC = segue.destination as? SecondViewController
secondVC.second = true
}
}
SecondVC :
class SecondViewController: UIViewController {
var second: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
print(second)
}
}
Just tested this myself. true is printed.
I cannot get my delegate protocol to work. I used this stack overflow questions as a guide dispatch event to parent ViewController in swift . I don't know if things have changed in Swift 3 since this post, but my function in my parentViewController is never getting called. Here is my setup.
//PROTOCOL
protocol PDPPropDetailsDelegate {
func buttonPressed(PropDetailsVC: propertyDetailsVC)
}
// CHILD VIEW CONTROLLER
class propertyDetailsVC: UIViewController {
var delegate: PDPPropDetailsDelegate?
#IBAction func emailButton(_ sender: AnyObject) {
self.delegate?.buttonPressed(PropDetailsVC: self)
}
}
The Button is getting called in Child View Controller.
// PARENT VIEW CONTROLLER
class ImageDetailsVC: UIViewController, PDPPropDetailsDelegate {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "container"{
container = segue.destination as! ContainerViewController
}
}
#IBAction func segmentControlAct(_ sender: Any) {
switch segmentControllerView.selectedIndex {
case 0: print("case 1")
container!.segueIdentifierReceivedFromParent("first")
case 1: print("case 2")
container!.segueIdentifierReceivedFromParent("second")
PropertyDetailsVC.delegate = self // **WHERE I SET DELEGATE**
setUpPropertyDetailsUI(property: filterImages)
default: print("default")
}
}
func buttonPressed(PropDetailsVC: propertyDetailsVC) {
print("BUTTON PRESSED")
}
}
Button Pressed is never called. I assume it has to do with the delegate not getting set properly. Not exactly sure why that would be the case though. My setUpPropertyDetailsUI(property: filterImages) takes the Outlets from that VC and sets that works just fine. I did a breakpoint and it is called when I segment over to the PropertyDetailsVC. Any advice or suggestions?
import UIKit
open class ContainerViewController: UIViewController {
//Manipulating container views
fileprivate weak var viewController : UIViewController!
//Keeping track of containerViews
fileprivate var containerViewObjects = Dictionary<String,UIViewController>()
/** Specifies which ever container view is on the front */
open var currentViewController : UIViewController{
get {
return self.viewController
}
}
fileprivate var segueIdentifier : String!
/*Identifier For First Container SubView*/
#IBInspectable internal var firstLinkedSubView : String!
override open func viewDidLoad() {
super.viewDidLoad()
}
open override func viewDidAppear(_ animated: Bool) {
if let identifier = firstLinkedSubView{
segueIdentifierReceivedFromParent(identifier)
}
}
override open func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func segueIdentifierReceivedFromParent(_ identifier: String){
self.segueIdentifier = identifier
self.performSegue(withIdentifier: self.segueIdentifier, sender: nil)
}
override open func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == segueIdentifier{
//Remove Container View
if viewController != nil{
viewController.view.removeFromSuperview()
viewController = nil
}
//Add to dictionary if isn't already there
if ((self.containerViewObjects[self.segueIdentifier] == nil)){
viewController = segue.destination
self.containerViewObjects[self.segueIdentifier] = viewController
}else{
for (key, value) in self.containerViewObjects{
if key == self.segueIdentifier{
viewController = value
}
}
}
self.addChildViewController(viewController)
viewController.view.frame = CGRect(x: 0,y: 0, width: self.view.frame.width,height: self.view.frame.height)
self.view.addSubview(viewController.view)
viewController.didMove(toParentViewController: self)
}
}
}
import UIKit
class EmptySegue: UIStoryboardSegue{
override func perform() {
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
You seem to be a little confused at the flow of the app. Here's an answer I wrote for someone else's question about this same topic:
https://stackoverflow.com/a/45312362/3832646
Your protocol and Child view controller look great, but there are quite a few things amiss with the rest of your code here:
your prepare(for segue:_, sender:_) is typically where you would set the delegate for the destination (child) view controller.
PropertyDetailsVC.delegate = self won't do anything - you need an instance of the view controller to set its delegate.
It looks like you're using some sort of container global variable that I'm not sure what it would be for.
Take a look at the answer I posted and have another go. It's in Swift 3.