Passing data forward from ViewController to ContainerView - ios

I am using network request to retrieve data from back-end in ViewController and this view contains three containers so, I want to pass these data into containers. that fails while I am using prepare for segue.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ContinueSurvey" {
let continueSurveyVC = segue.destination as! ContinueSurveyVC
continueSurveyVC.notComplatedSurveies = notComplatedSurveies
} else if segue.identifier == "LatestOffers" {
let latestOffersVC = segue.destination as! LatestOffersVC
latestOffersVC.latestOffers = latestOffers
} else if segue.identifier == "LatestSurvey" {
let latestSurveyVC = segue.destination as! LatestSurveyVC
latestSurveyVC.latestSurveies = latestSurveies
}
}

This might help: https://learnappmaking.com/pass-data-between-view-controllers-swift-how-to/
Here’s a view controller MainViewController with a property called
text:
class MainViewController: UIViewController
{
var text:String = ""
override func viewDidLoad()
{
super.viewDidLoad()
}
}
Whenever you create an instance of MainViewController, you can assign
a value to the text property. Like this:
let vc = MainViewController()
vc.text = "Hammock lomo literally microdosing street art pour-over"
This is the code for the view controller:
class SecondaryViewController: UIViewController
{
var text:String = ""
#IBOutlet weak var textLabel:UILabel?
override func viewDidLoad()
{
super.viewDidLoad()
textLabel?.text = text
}
}
Then, here’s the actual passing of the data… Add the following method
to MainViewController:
#IBAction func onButtonTap()
{
let vc = SecondaryViewController(nibName: "SecondaryViewController", bundle: nil)
vc.text = "Next level blog photo booth, tousled authentic tote bag kogi"
navigationController?.pushViewController(vc, animated: true)
}

you can use present ViewController and in presented ViewController in viewWillAppear check what happened:
present VeiwController:
let vc = self.storyboard?.instantiateViewController(withIdentifier: "yourViewControllerIdentifire") as! yourViewController
self.present(vc, animated: true, completion: nil)
& in yourViewContriller use viewWillAppear to pass those data into containers:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//pass those data into containers
}

If you want to update child controllers when they already loaded, you need to store reference on them in parent controller and then in desire moment of time update data as:
weak var continueSurveyVC: ContinueSurveyVC?
weak var latestOffersVC: LatestOffersVC?
weak var latestSurveyVC: LatestSurveyVC?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ContinueSurvey" {
continueSurveyVC = segue.destination as! ContinueSurveyVC
continueSurveyVC?.notComplatedSurveies = notComplatedSurveies
} else if segue.identifier == "LatestOffers" {
latestOffersVC = segue.destination as! LatestOffersVC
latestOffersVC?.latestOffers = latestOffers
} else if segue.identifier == "LatestSurvey" {
latestSurveyVC = segue.destination as! LatestSurveyVC
latestSurveyVC?.latestSurveies = latestSurveies
}
}
/// Call this method anytime you want to update children data
func updateChildData() {
continueSurveyVC?.notComplatedSurveies = notComplatedSurveies
latestOffersVC?.latestOffers = latestOffers
latestSurveyVC?.latestSurveies = latestSurveies
}

Related

Segue and delegates in UIKit

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

Pass Information through TabBarController Swift

Have a Table View Controller that will pass an ID field onto a View Controller in order to retrieve detail however in between the two controllers is a Tab Bar Controller. I am unsure how I am to get the information passed between the two. Was attempting to use a Segue but the value is blank once it gets to the detail controller.
EventBarTableViewCell.swift
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showEventDetail" {
if let IndexPath = self.tableView.indexPathForSelectedRow {
let controller = segue.destination as? eventDetailViewController
controller?.inTradeShowID = (events?[IndexPath.row].tradeshowID!)!
controller?.viaSegue = (events?[IndexPath.row].tradeshowID!)!
//controller?.performSegue(withIdentifier: "tradeShowID", sender: self)
//if shouldShowSearchResults {
// controller?.viaSegue = filteredArray[IndexPath.row].charterNum!
//} else {
// controller?.viaSegue = repositories[IndexPath.row].charterNum!
//}
}
}
}
eventDetailViewController.swift
var viaSegue = ""
var inTradeShowID = ""
override func viewDidLoad() {
super.viewDidLoad()
inTradeShowID = self.viaSegue
}
Could use some help.
You are segueing to a Tab Bar Controller, not to your "display" view, so you need to "drill down" so to speak:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showEventDetail" {
if let IndexPath = self.tableView.indexPathForSelectedRow {
// we're segueing to a Tab Bar Controller
if let tabBarVC = segue.destination as? UITabBarController {
// get the first view controller of the Tab Bar Controller
// *that* is where you want to "pass" your data
if let controller = tabBarVC.viewControllers?.first as? eventDetailViewController {
// either should work
controller.inTradeShowID = (events?[IndexPath.row].tradeshowID!)!
controller.viaSegue = (events?[IndexPath.row].tradeshowID!)!
} //end if let controller = tabBarVC.viewControllers?.first as? eventDetailViewController
} //end if let tabBarVC = segue.destination as? UITabBarController
} //end if let IndexPath = self.tableView.indexPathForSelectedRow
} //end if segue.identifier == "showEventDetail"
}

Controlling Nav Controller from another

My view controllers are sitting inside of view containers.
I'm trying to change the nav controller in the green area with the buttons from the purple area.
In my Main View controller I have:
class MainViewController: UIViewController,Purpleprotocol,Greenprotocol {
weak var infoNav : UINavigationController?
weak var greenVC: GreenVC?
weak var purpleVC: PurpleVC?
weak var peachVC: PeachVC?
func changenav(whichbutton: String) {
print ("changenav")
print(whichbutton + "whichbuttonstring")
if(whichbutton == "1"){
print("change one")
let svc = storyboard?.instantiateViewController(withIdentifier: "rootController") as! GreenVC
self.infoNav?.pushViewController(svc, animated: true)
}
if(whichbutton == "2"){
print("change two")
let svc = storyboard?.instantiateViewController(withIdentifier: "secondController") as! SecondViewController
self.infoNav?.pushViewController(svc, animated: true)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "contentSegue" {
print("contentSegue")
let infoNav = segue.destination as! UINavigationController
let greenVC = infoNav.viewControllers[0] as! GreenVC
greenVC.delegate = self
}
if segue.identifier == "menuSegue" {
let dvmenu = segue.destination as! PurpleVC
purpleVC = dvmenu
dvmenu.delegate = self
}
}
This function does work and I can see the "change one" etc being called but the navigation controller is just not changing..not sure how to get to it I guess correctly.

Calling child view controller using segmented control action?

I have two child view controllers in my parent view controller, I want to call them upon a value change in the segmented control, and want to set the value of the parent imageView through child view controllers.
protocol UserEdittedPhoto {
func UserIsDone(image:UIImage)
}
class ControllerFinal:UIViewController, UserEdittedPhoto{
func UserIsDone(imageEditted: UIImage){
self.usedImage=imageEditted
self.imageView.image=self.usedImage
}
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func segmentAction(sender:UISegmentedControl){
if (segmentedControl.selectedSegmentIndex==0){
performSegueWithIdentifier("EditIAm", sender: nil)
}
else if (segmentedControl.selectedSegmentIndex==1){
performSegueWithIdentifier("EditIAm", sender: nil)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "EditIAm"{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewControllerWithIdentifier("ControllerEdit")
self.presentViewController(controller, animated: true, completion: nil)
let nextView = segue.destinationViewController as! ControllerEdit
nextView.originalImage=self.imageView.image!
nextView.delegate=self
}
else if segue.identifier == "FilterIAm"{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewControllerWithIdentifier("ControllerFilters")
self.presentViewController(controller, animated: true, completion: nil)
let nextView = segue.destinationViewController as! ControllerFilters
nextView.toBeFilter=self.imageView.image!
}
}
class ControllerEdit:UIViewController{
var delegate: UserEdittedPhoto? = nil
if (delegate != nil){
self.originalImage = UIImage(CIImage: CIImage(image: self.originalImage)!.exposureAdjustFilter(sliderValue.value)!)
self.delegate!.UserIsDone(self.originalImage)
print("I am Called!")
}
}
class ControllerFilters:UIViewController{
override func viewDidLoad() {
print("SAHASHhqdwiuhiuhsaiuhsaiudhiuash")
controllerFinal?.imageView.image=toBeFilter
print("SAHASHhqdwiuhiuhsaiuhsaiudhiuash")
super.viewDidLoad()
}
}
UPDATE
To reflect our discussion in the comments below, I don't think you really need Containers and View Controllers to manage your custom controls (Edit / Filters). It's overkill.
Instead, I think you should be creating custom Views, and then adding them to your main Storyboard.
Then you could simply hide/show your custom Views when users tap on the Segmented Control as well as passing values to them, for example:
CustomEditView.valueY = newValueY
CustomFiltersView.valueX = newValueX
Regarding:
I need to call it forcefully through segmentedControl action, so that
my values in the childView be updated
Then you need to map the target View Controllers to local variables and use them to update the target View Controller variables when users presses the segments.
I've update the code and "demo" in my answer to reflect that.
(Notice that I'm just putting random Strings in the labels to make a point.)
Now to the complete answer...
In the setup you described in your other question, which is based on containers, the View Controllers are already there, in the Storyboard. You absolutely don't need to present them again (you can remove performSegueWithIdentifier calls).
If I understood correctly, you just want to show different "controllers" to the user based on what they choose via a Segmented Control.
There are some ways for doing that, but the easiest one would be to hide and to show the containers of the ControllerEdit / ControllerFilters View Controllers -- by changing the containers isHidden variable state.
Like this:
Storyboard setup:
Code (based on my other answer):
import UIKit
protocol UpdateImageProtocol {
func userIsDone(image: UIImage)
}
class ViewController: UIViewController, UpdateImageProtocol {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var changeImageContainer: UIView!
var controllerEdit: ControllerEdit?
#IBOutlet weak var applyFilterContainer: UIView!
var controllerFilters: ControllerFilters?
var image = UIImage(named: "hello")
override func viewDidLoad() {
super.viewDidLoad()
userIsDone(image: image!)
}
func userIsDone(image: UIImage) {
imageView.image = image
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "controllerEdit" {
let nextView = segue.destination as! ControllerEdit
nextView.delegate = self
controllerEdit = nextView
} else if segue.identifier == "controllerFilters" {
let nextView = segue.destination as! ControllerFilters
controllerFilters = nextView
}
}
#IBAction func segmentAction(_ sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0 {
changeImageContainer.isHidden = false
applyFilterContainer.isHidden = true
controllerEdit?.customLabel.text = String(arc4random_uniform(999))
} else {
changeImageContainer.isHidden = true
applyFilterContainer.isHidden = false
controllerFilters?.customLabel.text = String(arc4random_uniform(999))
}
}
}
class ControllerEdit: UIViewController {
#IBOutlet weak var customLabel: UILabel!
var image = UIImage(named: "newHello")
var delegate: UpdateImageProtocol?
#IBAction func changeImage(sender: UIButton) {
delegate?.userIsDone(image: image!)
}
}
class ControllerFilters: UIViewController {
#IBOutlet weak var customLabel: UILabel!
// TODO
}
Put a breakpoint in this function:
#IBAction func segmentAction(sender:UISegmentedControl){
if (segmentedControl.selectedSegmentIndex==0){
performSegueWithIdentifier("EditIAm", sender: nil)
}
else if (segmentedControl.selectedSegmentIndex==1){
performSegueWithIdentifier("EditIAm", sender: nil)
}
}
If it's not getting called, then you probably didn't connect it to an action in IB (is the circle to the left of the the #IBAction filled in?)
If it is getting called, then make sure the segue names are right -- also, fix the one in the else if, because it looks like you want "FilterIAm" there.
Then, put a breakpoint in prepareForSegue:... -- is that getting called? If not, recheck the names are the same as in IB.
EDIT: based on comment
Your prepareForSegue is not supposed to create the ViewController. The destination view controller is created as a consequence of performing the segue and passed to this function.
if segue.identifier == "EditIAm"{
let nextView = segue.destinationViewController as! ControllerEdit
nextView.originalImage=self.imageView.image!
nextView.delegate=self
}
You don't need to present anything -- the destinationViewController is going to be presented. You can set any of its variables (as you have) - that is what is meant by preparing for the segue.

Pass data back from a popover view controller on iPhone

How can I pass data back from a PopoverViewController to the main view controller on an iPhone?
I know I'm doing something terribly wrong but I cannot figure it out.
Here is the code:
PopoverViewController.swift
protocol PopoverViewControllerDelegate {
func messageData(data: AnyObject)
}
class PopoverViewController: UIViewController {
#IBOutlet weak var inputMessage: UITextField!
var delegate: PopoverViewControllerDelegate?
#IBAction func sendData(sender: AnyObject) {
if inputMessage.text != ""{
self.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
self.delegate?.messageData(inputMessage.text!)
}
}
}
Main ViewController.swift:
class ViewController: UIViewController, UIPopoverPresentationControllerDelegate, PopoverViewControllerDelegate {
#IBOutlet weak var showData: UILabel!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// popover segue
if segue.identifier == "popoverSegue" {
let popoverViewController = segue.destinationViewController
popoverViewController.popoverPresentationController!.delegate = self
}
// code to comunicate with data in popoverViewController
let pvc = storyboard?.instantiateViewControllerWithIdentifier("popoverViewController") as! PopoverViewController
pvc.delegate = self
self.presentViewController(pvc, animated:false, completion:nil)
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.None
}
func messageData(data: AnyObject) {
self.showData.text = "\(data)"
}
}
With the code above I can pass data back to the main view controller without a problem, the issue is that the popover doesn't work, it just acts like a regular ViewController occupying the whole screen.
The funny thing is that if I comment the following line of code the popover works but I can no longer pass data back, I can see the popover but the passing data stops working.
// if I comment this line
self.presentViewController(pvc, animated:false, completion:nil)
I don't get any errors, one just stops working.
Any suggestions?
Thanks a lot
In prepareForSegue, the destinationViewController is your PopoverViewController. You need to cast it to that and set the delegate on that so that you can pass back data, and you need to set the popoverPesentationController?.delegate as well. You don't need the rest of the code in prepareForSegue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// popover segue
if segue.identifier == "popoverSegue" {
let popoverViewController = segue.destinationViewController as! PopoverViewController
popoverViewController.delegate = self
popoverViewController.popoverPresentationController?.delegate = self
}
}

Resources