Using functions from delegate in SwiftUIView - ios

Quick question for anyone feeling up for helping a noob.
So I have a protocol in my main class that has a function that I'd like to access from a UIView. This works for other view controllers. However, in my prepare function, I try this:
if let destination = segue.destination as? SwiftUIView {
destination.delegate = self
}
But I get, "Cast from 'UIViewController' to unrelated type 'SwiftUIView' always fails"
and "Cannot assign to property: 'destination' is a 'let' constant".
This is my whole prepare function:
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.destination is ClientView
{
let vc = segue.destination as? ClientView
vc?.username = thetitle;
}
if let destination = segue.destination as? CreateClientView {
destination.delegate = self
}
if let destination = segue.destination as? SwiftUIView {
destination.delegate = self
}
}
and it works in the CreateClientView. But not for my SwiftUIView. Any idea how to fix this? I am accessing the function in SwiftUIView the same way I do in CreateClientView. Thanks everybody.
SwiftUIView is just type View and looks like this (abridged):
struct SwiftUIView: View {
#State var username: String = ""
#State var address: String = "";
#State var notificationsEnabled: Bool = false
#State var note: String = "";
var delegate:ClientDelegate?;
and SwiftUIView is a child of another view which looks like this:
class CreateClientView: UIViewController {
let contentView = UIHostingController(rootView: SwiftUIView());
var delegate:ClientDelegate?;
var mainView : ViewController?;
override func viewDidLoad() {
super.viewDidLoad()
addChild(contentView);
view.addSubview(contentView.view);
setupConstraints()
// Do any additional setup after loading the view.
}

As the error message states, you're trying to cast a SwiftUI view into a ViewController. which you cant.
For a quick fix to your problem you can try this
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.destination is ClientView
{
let vc = segue.destination as? ClientView
vc?.username = thetitle;
}
if let destination = segue.destination as? CreateClientView {
destination.delegate = self
}
if let destination = segue.destination as? CreateClientView {
destination.delegate = self
}
}
class CreateClientView: UIViewController, SwiftUIViewDelegate {
let contentView = UIHostingController(rootView: SwiftUIView(delegate: self));
var delegate:ClientDelegate?;
var mainView : ViewController?;
override func viewDidLoad() {
super.viewDidLoad()
addChild(contentView);
view.addSubview(contentView.view);
setupConstraints()
// Do any additional setup after loading the view.
}
// MARK: SwiftUIViewDelegate
// call ClientDelegate here whereever SwiftUIViewDelegate methods are implemeted
}
protocol SwiftUIViewDelegate {...}
struct SwiftUIView {
let delegate: SwiftUIViewDelegate
}

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

Passing data forward from ViewController to ContainerView

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
}

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.

Pass text to TextView via Segue

I'm trying to pass text to TextView from other ViewController:
if (segue.identifier == "HomeToDetails") {
let nav = segue.destination as! UINavigationController
let nextVC = nav.topViewController as! DetailsViewController
nextVC.infoTextView.text = "TESTING"
}
But it crashes:
fatal error: unexpectedly found nil while unwrapping an Optional value
setting text on UITextField of DestinationViewController is not possible in the prepareForSegue:sender, because all view components of the recently allocated controller are not initialized before the view is loaded (at this time, they are all nil), they only will be when the DestinationViewController view is loaded.
You need to use optional variable infoString in DetailsViewController which you can set in prepareForSegue method
if (segue.identifier == "HomeToDetails") {
let nav = segue.destination as! UINavigationController
let nextVC = nav.topViewController as! DetailsViewController
nextVC.infoString = "TESTING"
}
in DetailsViewController.swift
class DetailViewController: UIViewController {
var infoString: String?
override func viewDidLoad() {
super.viewDidLoad()
if let info = infoString {
self.infoTextView.text = info
}
}
}
if value is nil and you unwrap it, then the app will crash, you should use optional binding to avoid crash.
if (segue.identifier == "HomeToDetails") {
if let nav = segue.destination as? UINavigationController { //should use optional binding to avoid crash
if let nextVC = nav.topViewController as? DetailsViewController {
nextVC.infoTextView.text = "TESTING"
}
}
}
Check this:
if (segue.identifier == "HomeToDetails") {
// get a reference to the second view controller
let secondViewController = segue.destination as! DetailsViewController
// set a variable in the second view controller with the String to pass
secondViewController.infoTextView.text = "TESTING"
}
Try this code:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let nav = segue.destination as? UINavigationController {
if let nextVC = nav.topViewController as? DetailsViewController {
nextVC.infoTextView.text = "Data you want to pass"
}
}
}
this is because when you are assigning value to text view, the text view not initialized that time as viewDidLoad of next class is not called yet. So pass only string, then in viewDidLoad of next class, set the text.
You cannot pass a text directly to TextView in another view before it's created.
First ViewController:
if (segue.identifier == "HomeToDetails") {
let secondViewController = segue.destination as! DetailsViewController
secondViewController.myText = "TESTING"
}
DetailsViewController:
var myText = ""
override func viewDidLoad() {
super.viewDidLoad()
infoTextView.text = myText
}
All view components of the DetailsViewController are not initialized before the view is loaded. You can load that view before assigning values to the components of that view
Consider vC is the view controller which contains the view that you want to load. Then
if let _ = vC.view {
//Assign value here
}
Once the view is loaded, we can set the value to the components in that view. In SWIFT 3
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "HomeToDetails" {
let nav = segue.destination as! UINavigationController
let nextVC = nav.topViewController as! DetailsViewController
if let _ = nextVC.view {
nextVC.infoTextView.text = "TESTING"
}
}
}

how to pass json data other view controller

Hi all am getting a data from server i.e:
{
error = 0;
"error_msg" = "Successfully Login";
success = 1;
user = {
DriverID = 35;
DriverName = "Home nath";
"com_id" = 2;
};
}
now i need to pass data of DriverName to other view controller in label box.how to do this one??
create a variable of type string in your destination view controller.
DestinationViewController.Swift
class DestinationViewController: UIViewController {
var driverName: String?
override func viewDidLoad() {
super.viewDidLoad()
print(driverName)
}
}
CurrentViewController.swift
In your CurrentViewController override prepareForSegue:sender: method.
class CurrentViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destnationVC = segue.destination as! DestinationViewController
destnationVC.driverName = "Home nath"
}
}
learn how to pass data from CurrentViewController to DestinationViewController and DestinationViewController to OrignViewController it ll help you lot.
refer Passing Data between View Controllers.

Resources