I have two UIViewControllers, A and B, I connect them within a UIPageViewController:
Here is how it looks in the Storyboard:
I don't know how to pass data to B from A.
Well assume you have some class (which you should have provided) like:
class MyModel {
var dataFromFirstController: Any?
var dataFromSecondController: Any?
var sharedData: Any?
}
Now you need a subclass of page view controller which is the one that controls the data so override view did load to create a model:
var myModel: MyModel!
override func viewDidLoad() {
super.viewDidLoad()
self.myModel = MyModel()
}
Now when you generate or fetch view controllers you simply assign the same model to them:
func getFirstViewController() -> UIViewController {
let controller = MyFirstController.generate()
controller.myModel = self.myModel
return controller
}
func getSecondViewController() -> UIViewController {
let controller = MySecondController.generate()
controller.myModel = self.myModel
return controller
}
Now all 3 view controllers share the same model. This is probably the easiest way of doing it but there are very many ways. The cleanest is probably using delegates which would report back to page controller that would then report back to given view controllers.
I had some difficulty finding a solution to this and came up with something myself using delegation. Suggestions are welcome
In the ViewController sending the data, define a delegate as follows:
protocol FirstVCDelegate {
func foo(someData: String)
}
class FirstViewController: UIViewController {
var delegate: FirstVCDelegate?
....
func someMethod() {
delegate?.foo("first VC")
}
}
In the PageViewController set up your View Controllers as follows:
class PageViewController: UIPageViewController, ... {
var myViewControllers = [UIViewController]()
override func viewDidLoad() {
let firstVC = storyboard?.instantiateViewController(withIdentifier: "FirstViewController") as! FirstViewController
let secondVC = storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
firstVC.delegate = secondVC
myViewControllers.append(firstVC)
myViewControllers.append(secondVC)
}
// MARK: - PageVC Delegate / Datasource
....
and finally, the receiving ViewController implements the delegate as follows:
class SecondViewController: UIViewController, FirstVCDelegate {
....
func foo(data: String) { // This method is triggered from FirstViewController's delegate?.foo("first VC")
print(data) // "first VC" will be printed
}
}
Good luck,
Aaron
Related
I have UIViewController and how I can to convert UIViewController to Class.swift. Class is initialized...
MainController.staticInstance.viewControllers?[1] as! Destination
MainController is class which extending UITabBarController. I want to get child controller from UITabBar and convert it to Class which parent.
Clearly example:
class MainController: UITabBarController {
override func viewDidLoad() {
(self.viewControllers?[1] as! Destination).itsMyFunction();
}
}
MAXIMUM DETAIL:
1 class
class First: UIViewController {
func itsMyFunction() {
print("Hello world!")
}
}
this is Class I attach to class in STORYBOARD!
2 class
class MainController: UITabBarController {
func override viewDidLoad() {
// Here I set index UITabBar ITEM which attach to MAIN UiTabBarController
self.selectedIndex = 0
// NOW I want to get INSTANCE CLASS First
(self.viewControllers?[1] as! First).itsMyFunction();
}
}
Can you please follow this setup example?
class TabBarViewController: UITabBarController {
let firstVC = First()
let secondVC = SecondViewController()
override func viewDidLoad() {
super.viewDidLoad()
firstVC.tabBarItem = UITabBarItem(tabBarSystemItem: .search, tag: 0)
secondVC.tabBarItem = UITabBarItem(tabBarSystemItem: .more, tag: 1)
let tabBarList = [firstVC, secondVC]
viewControllers = tabBarList
if let destination = viewControllers?.first as? First {
destination.itsMyFunction()
}
}
}
It seems that you fail to get the Class instance of the controller because your viewControllers array is empty based on the code that you posted. Let me know if it worked.
That way you get a crash if the type is something different. Try to avoid using ! whenever you can.
Use a weak cast and evaluate the unwrapped type if its yours.
if let destination = destMainController.staticInstance.viewControllers?.first as? Destination {
destination.itsMyFunction()
}
I've got two View Controllers. Main and Temporary one. The second one performs an action on the different screen (is called by pushViewController) and then I'm popping (popViewController) and would like to present the returned value which is String.
I've tried using protocol but it's nil.
Here is my code:
SecondVC.swift:
protocol ValueDelegate {
func append(_ text: String)
}
class SecondViewController: UIViewController{
var delegate: ValueDelegate!
...
...
private func function(){
if let delegate = self.delegate{
delegate.append(value.stringValue)
}
navigateBack()
}
private func navigateBack(){
if let navigation = self.navigationController{
navigation.popViewController(aniamted: true)
}
}
MainVC.swift:
class MainViewController: UIViewController, ValueDelegate {
var secondVC = SecondViewController()
...
func append(_ value: String) {
textField.text?.append(barcode)
}
...
override func viewDidLoad(){
super.viewDidLoad()
self.secondVC.delegate = self
}
}
Use these links to understand exactly how to use Protocols in swift:
Passing data between two ViewControllers (delegate) - Swift
Passing Data between View Controllers
You have to implement below line of code in first view controller :-
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showSecondViewController" {
let secondViewController = segue.destination as! SecondViewController
secondViewController.delegate = self
}
}
I've tried using protocol but it's nil.
Because you never set it to anything. It was your job, when you pushed the SecondViewController, to set its valueDelegate to the MainViewController. But you didn't.
What you did do was set the valueDelegate of another SecondViewController to the MainViewController:
var secondVC = SecondViewController()
self.secondVC.delegate = self
That was silly, because secondVC is a different, newly made instance of SecondViewController having nothing at all to do with your real interface. In particular, it is not the SecondViewController instance that gets pushed. But that is the instance you need to set the delegate of.
I have a UIPageViewcontroller containing three ViewControllers. One Viewcontroller is my ProfileViewcontroller. I have a button in my ProfileViewController, which should tell the UIPageViewCongtroller when pressed, to switch to the next Viewcontroller.
It's my first time implementing a delegate, but I just can't figure out why its not working.
UIPageViewController class:
class PageViewController: UIPageViewController, ProfileViewControllerDelegate {
private(set) lazy var orderedViewControllers: [UIViewController] = {
// The view controllers will be shown in this order
return [self.newColoredViewController("Search"),
self.newColoredViewController("Menu"),
self.newColoredViewController("Profile"),
self.newColoredViewController("Offer")]
}()
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
if orderedViewControllers.count >= 2 {
scrollToViewController(orderedViewControllers[1])
}
}
// MARK: ProfileViewControllerDelegate
func profileViewControllerDidTouchOffer(viewController:ProfileViewController, sender: AnyObject) {
scrollToNextViewController()
print("I'm pressing the offer Button")
}
ProfileViewController class:
protocol ProfileViewControllerDelegate : class {
func profileViewControllerDidTouchOffer(controller: ProfileViewController, sender: AnyObject)
}
class ProfileViewController: UIViewController {
weak var delegate: ProfileViewControllerDelegate?
#IBOutlet var profileImageView: SpringImageView!
#IBOutlet var offerButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func offerButtonTouchUpInside(sender: AnyObject) {
delegate?.profileViewControllerDidTouchOffer(self, sender: sender)
}
Answer
I updated the PageViewController class by changing the way I add the ViewControllers in orderderedViewControllers:
private(set) lazy var orderedViewControllers: [UIViewController] = {
// The view controllers will be shown in this order
let profileVC = UIStoryboard(name: "Main", bundle: nil) .instantiateViewControllerWithIdentifier("ProfileViewController") as! ProfileViewController
profileVC.delegate = self
return [self.newColoredViewController("Search"),
self.newColoredViewController("Menu"),
profileVC,
self.newColoredViewController("Offer")]
}()
Looks like you're using the InterfaceBuilder so I'm not sure how it's done there (probably in prepareForSegue), but a typical pattern is something like this:
let vc = ProfileViewController()
vc.delegate = self // Or whatever you want to act as the delegate
// Now show the view controller.
The key is that before using the view controller, you make sure it's delegate property is set to the thing that is the delegate and conforms to the protocol. Typically the calling controller would be the delegate.
EDIT: If you're using the UIPageViewController, then you should add the delegate to the viewController before passing it to setViewControllers https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIPageViewControllerClassReferenceClassRef/#//apple_ref/occ/instm/UIPageViewController/setViewControllers:direction:animated:completion:
I need to send some data back from secondView to First View by popView.
How can i send back the data by popViewControllerAnimated?
Thanks!
You can pass data back using delegate
Create protocol in ChildViewController
Create delegate variable in ChildViewController
Extend ChildViewController protocol in MainViewController
Give reference to ChildViewController of MainViewController when navigate
Define delegate Method in MainViewController
Then you can call delegate method from ChildViewController
Example
In ChildViewController: Write code below...
protocol ChildViewControllerDelegate
{
func childViewControllerResponse(parameter)
}
class ChildViewController:UIViewController
{
var delegate: ChildViewControllerDelegate?
....
}
In MainViewController
// extend `delegate`
class MainViewController:UIViewController,ChildViewControllerDelegate
{
// Define Delegate Method
func childViewControllerResponse(parameter)
{
.... // self.parameter = parameter
}
}
There are two options:
A) with Segue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
let goNext = segue.destinationViewController as ChildViewController
goNext.delegate = self
}
B) without Segue
let goNext = storyboard?.instantiateViewControllerWithIdentifier("childView") as ChildViewController
goNext.delegate = self
self.navigationController?.pushViewController(goNext, animated: true)
Method Call
self.delegate?.childViewControllerResponse(parameter)
If you want to send data by popping, you'd do something like:
func goToFirstViewController() {
let a = self.navigationController.viewControllers[0] as A
a.data = "data"
self.navigationController.popToRootViewControllerAnimated(true)
}
Extending Dheeraj's answer in case your ViewController is not first VC in the stack, here is the solution:
func popViewController() {
guard let myVC = self.navigationController?.viewControllers.first({ $0 is MyViewController }) else { return }
myVC.data = "data"
self.navigationController?.popViewController(animated: true)
}
However, this solution will break if you have 2 or more than 2 MyViewController in the stack. So, use wisely.
Answer given here is a little complex, quite simple to just use UINavigationControllerDelegate
class FirstNavigationController: UIViewController {
var value: String?
}
class SecondNavigationController: UIViewController, UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
guard let vc = navigationController.topViewController as? FirstNavigationController else { return }
vc.value = "Hello again"
}
}
self.navigationController?.popViewController(animated: true)
let vc = self.navigationController?.viewControllers.last as! MainViewController
vc.textfield.text = "test"
this popviewcontroller solutions
I have two controllers and i need call up function the first controller to second controller:
In second controller I have created protocol and init delegate in class:
protocol testProtocol {
func testDelegate() // this function the first controllers
}
class SecondViewController: UIViewController {
var delegate: testProtocol?
....
}
#IBAction func testDelegateClicked(sender : AnyObject) {
delegate?.testDelegate()
}
First Controller
class ViewController: UIViewController, testProtocol {
var secondController: SecondViewController = SecondViewController()
override func viewDidLoad() {
super.viewDidLoad()
secondController.delegate = self
}
func testDelegate() {
println("Hello delegate")
}</pre>
But function not getting called
I am going to make an assumption you are using storyboards. If I am correct, then your issue is that your secondController, created in your First Controller, is not the actual one you are presenting. You will need to set secondController in your prepareForSegue:
Second Controller
Unchanged
First Controller
class ViewController: UIViewController, testProtocol {
// you will want to add the ? since this variable is now optional (i.e. can be nil)
var secondController: SecondViewController? // don't assign it a value yet
// ...
// implementation of the protocol
func testDelegate() {
println("Hello delegate")
}
// your prepare for segue
override func prepareForSegue(segue: UIStoryboardSegue?, sender: AnyObject?) {
// get the controller that storyboard has instantiated and set it's delegate
secondController = segue!.destinationViewController as? SecondViewController
secondController!.delegate = self;
}
}