Pass messages and/or objects between different storyboards swift - ios

I've two storyboards and need to pass messages and objects. I know how to do it in the same storyboard and with .xib files, but not with two different storyboards.
My code is:
var storyboard = UIStoryboard(name: "RecibosStoryboard", bundle: nil)
var controller = storyboard.instantiateViewControllerWithIdentifier("RecibosStoryboard") as! UINavigationController
self.presentViewController(controller, animated: true, completion: nil).
// If i do: var controller = storyboard.instantiateViewControllerWithIdentifier("RecibosStoryboard") as! = TableRecibosViewController -> fails ->cannot convert TablaRecibosViewController to UINavigationController
// If i do:
/* var controller = storyboard.instantiateViewControllerWithIdentifier("RecibosStoryboard") as! UINavigationController
let vartest: TablaRecibosTableViewController = TablaTablaRecibosTableViewController()
prueba.inicioPrueba(str, strPrueba2: str2) -> two objects are nill
self.presentViewController(controller, animated: true, completion: nil).
Two objects are nill*/
My second storyboard is "RecibosStoryboard" and only has 1 view who class is TablaRecibosViewController and has a contructor method:
class TablaRecibosTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tablaRecibos: UITableView!
var pruebaMansajes: String?
var pruebaMansajes2: String?
var arrayOfObjectsCellRecibos: [ObjectTableRecibos] = [ObjectTableRecibos] ()
override func viewDidLoad() {
super.viewDidLoad()
tablaRecibos.dataSource = self
tablaRecibos.delegate = self
println("Pruebas satisfactorias1 \(pruebaMansajes) \(pruebaMansajes2)")
}
func inicioPrueba(strprueba1:String, strPrueba2:String){
pruebaMansajes = strprueba1
pruebaMansajes2 = strPrueba2
}
When i execute the App or crash or print two objects = nil
I don't find the way to do. Thanks a lot.

The problem is the following "cannot convert TablaRecibosViewController to UINavigationController". Your TablaRecibosViewController is embedded inside a UINavigationController. Try the following:
if let recibosViewController = (storyboard.instantiateViewControllerWithIdentifier("RecibosStoryboard") as? UINavigationController).viewControllers.first as? TablaRecibosTableViewController {
recibosViewController.pruebaMensajes = "Hola mundo"
}
You were creating a new instance of TablaTablaRecibosTableViewController() which was not associated to the Storyboard, therefore the table view was nil and it crashed.

Related

How to present a new screen without a UIViewController in Swift 4?

I have not implemented UIViewController because I have already inherited from another class, and it gives the error that present is not a member of this class
func shareAppLink() {
let name = "http://aijaz.com"
let items = [name] as [Any]
let ac = UIActivityViewController(activityItems: items, applicationActivities: nil)
present(ac, animated: true)
}
You can also use Respoder Chain to get the parent view controller for a view
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.next
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}
And declare your shareAppLink function like
func shareAppLink(sender : UIView) {
let name = "http://aijaz.com"
let items = [name] as [Any]
let ac = UIActivityViewController(activityItems: items, applicationActivities: nil)
sender.parentViewController(ac, animated: true)
}
then in didSelectRowAt, you can call it as:
self.shareAppLink(sender : cell)
present(_:animated:completion:) is a method of UIViewController, so it must be called on some type of UIViewController.
If your class is initially created by a view controller, then you could try passing in a reference using the delegation pattern:
Delegation is a simple and powerful pattern in which one object in a
program acts on behalf of, or in coordination with, another object.
The delegating object keeps a reference to the other object—the
delegate—and at the appropriate time sends a message to it. The
message informs the delegate of an event that the delegating object is
about to handle or has just handled.
If you created a protocol for your custom class something like this:
protocol MyClassDelegate {
func shareAppLink()
}
Then you could conform to that protocol in your View Controller and call the method something like this: delegate.shareAppLink()
You have to inherit UIViewController subclass or UIViewController class. By doing that an error should be resolved.

iOS (Swift) - Array of UIViewControllers

I have an array UIButtons that have a unique tag value. When a given button is pressed, I want to load and present a UIViewController that is also stored in an array of equal length.
The UIViewControllers to be presented are subclasses of a subclass of UIViewController:
class AViewController: UIViewController {}
class BViewController: AViewController {}
class CViewController: AViewController {}
class DViewController: AViewController {}
// ... etc.
I've tried storing the array of AViewController subclasses using the following:
private var array: [AViewController] = [BViewController.self, CViewController.self, DViewController.self]
but I get the error Cannot convert value of type '[BViewController].Type' to specified type '[AViewController]' for the first element.
I will then present BViewController (for instance) using the following:
let ViewController = array[button.tag].self
var viewController: AViewController
viewController = ViewController.init()
viewController.transitioningDelegate = self
viewController.modalPresentationStyle = .custom
present(viewController, animated: true)
Please let me know if this is the incorrect thought process for doing something like this please and thanks for any help.
You need instances
private var array: [AViewController] = [BViewController(), CViewController(), DViewController()]
if the vcs are in IB , then you would do
let b = self.storyboard!.instantiateViewController(withIdentifier: "bID") as! BViewController
let c = self.storyboard!.instantiateViewController(withIdentifier: "cID") as! CViewController
let d = self.storyboard!.instantiateViewController(withIdentifier: "dID") as! DViewController
Then
array = [b,c,d]
lazy var VCArray :[UIViewController] = {
return [VCinst(name: "firstVC"),VCinst(name: "secondVC"), VCinst(name: "thirdVC")]
}()
func VCinst(name:String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: name)
}
Alternate answer if you want to store array of view controllers as a constant.
struct AppConstant {
static let yourViewControllers: [AnyClass] = [AViewController.self,
BViewController.self, CViewController.self]
}

return UIViewController from a class function

In my Restaurant model I have a function, that returns UINavigationController, I've implemented it to make my code more readable. But I am curious if it is ok to do it this way, or should I make a function of UIViewController, that takes Restaurant as a parameter.
func reserveTable(timeIndex: Int) -> UINavigationController {
let storyBoard = UIStoryboard(name: "Reservations", bundle: nil)
let targetVC = storyBoard.instantiateViewController(withIdentifier: "bookingNC") as! UINavigationController
let destinationVC = targetVC.topViewController as! BookingViewController
destinationVC.availabeleDates = self.schedule
destinationVC.timeRange = self.avaliableTimes
destinationVC.restaurantId = self.id
destinationVC.requestedTimeIndex = timeIndex
destinationVC.reservationType = "Бронирование"
return targetVC
}
I would do this differently, your model is supposed to handle data, not Views.
There are many ways in which you could do this, personally I would have a property on the booking controller for the reservation. Then you could just do something like:
let vc = BookingViewController()
vc.reservation = reservation
self.present(vc, animated: true)
wherever you needed to present the booking controller. Ensuring you have a reservation first.
In your init/viewDidLoad method of the BookingViewController you can do alot of the work above...
override func viewDidLoad() {
super.viewDidLoad()
if let reservation = self.reservation {
self.availabeleDates = reservation.schedule
self.timeRange = reservation.avaliableTimes
self.restaurantId = reservation.id
self.requestedTimeIndex = timeIndex
self.reservationType = "Бронирование"
}
}

Passing data between two view controllers are not working

I am trying to pass some data between two view controllers, but it doesn't work..
This is the data i am trying to pass(these has items from parse.com - the same code is in both view controllers):
var userFile = [PFFile]()
var createdAt = [NSDate]()
var objID = [String]()
This is the button for open the view controller(inside the first view controller i am trying to send data FROM):
#IBAction func openButtonAction(sender: AnyObject) {
let modalVC = ModalViewController(nibName: "ModalViewController", bundle: nil)
modalVC.userFile = self.userFile
modalVC.createdAt = self.createdAt
modalVC.objID = self.objID
print("USERFILE: \(modalVC.userFile.count)")
presentViewController(modalVC, animated: true, completion: nil)
}
The view controller is a ModalViewController.xib connected to ViewStoryModalViewController.swift
This is the viewDidLoad in the view controller i am trying to send data TO:
override func viewDidLoad() {
super.viewDidLoad()
print("USERFILECOUNT: \(self.userFile.count)")
}
My problem is that this is the messages i get in xCode output:
What might be wrong here? Any suggestions?
xCode output tells that an array self.userFile contains zero elements, It doesn't mean that it is passed wrong. It is just empty.
print("USERFILECOUNT: \(self.userFile.count)")
Check if it is empty before passing it to modal vc.
Try this code
You first need to present after that try to set variable.
IBAction func openButtonAction(sender: AnyObject) {
let modalVC = ModalViewController(nibName: "ModalViewController", bundle: nil)
print("USERFILE: \(modalVC.userFile.count)")
presentViewController(modalVC, animated: true, completion: nil)
modalVC.userFile = self.userFile
modalVC.createdAt = self.createdAt
modalVC.objID = self.objID
}

Custom segue to a different storyboard

Question:
How might one write a custom segue that would allow you to embed view controllers from a different storyboard?
Context:
I am trying to write a custom segue with which I can link from one storyboard to another. A good article on atomicobject.com illustrates how to create a segue that originates from a button / event etc. Translated into swift, and allowing for non UINavigationControllers, the code looks like:
public class SegueToStoryboard : UIStoryboardSegue {
private class func viewControllerInStoryBoard(identifier:String, bundle:NSBundle? = nil)
-> UIViewController?
{
let boardScene = split(identifier, { $0 == "." }, maxSplit: Int.max, allowEmptySlices: false)
switch boardScene.count {
case 2:
let sb = UIStoryboard(name: boardScene[0], bundle: bundle)
return sb.instantiateViewControllerWithIdentifier(boardScene[1]) as? UIViewController
case 1:
let sb = UIStoryboard(name: boardScene[0], bundle: bundle)
return sb.instantiateInitialViewController() as? UIViewController
default:
return nil
}
}
override init(identifier: String!,
source: UIViewController,
destination ignore: UIViewController) {
let target = SegueToStoryboard.viewControllerInStoryBoard(identifier, bundle: nil)
super.init(identifier: identifier, source: source,
destination:target != nil ? target! : ignore)
}
public override func perform() {
let source = self.sourceViewController as UIViewController
let dest = self.destinationViewController as UIViewController
source.addChildViewController(dest)
dest.didMoveToParentViewController(source)
source.view.addSubview(dest.view)
// source.navigationController?.pushViewController(dest, animated: true)
}
}
Problem:
The problem that I am having with both their Obj-C and the above Swift code is that when you try to use the via a container view (with semantics of an embed segue - starting with an embed segue, deleting the segue, and then use the above custom segue), it crashes before ever calling the segue code with the following method-not-found error:
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<UIStoryboardSegueTemplate 0x7ffc8432a4f0>
setValue:forUndefinedKey:]: this class is not key value
coding-compliant for the key containerView.'
I have tried to inspect the address listed but get no meaningful results. I do the see the bold statement that it expecting the containerView but don't know how one might isolate, satisfy, and/or work around this problem.
Summary:
My end goal is to embed view controllers defined in separate storyboards to facilitate collaboration and testing without having to write additional code (a non invasive solution). Does anyone have any insight into how to accomplish this greater task? I could fall back to hybrid approach of calling performSegue, but would like to find a single, contained, and complete solution. The above code gets there for event driven (buttons etc) segues, but not with the embed segue.
Any input is appreciated, thanks in advance.
Your approach works fine for custom segues to push / display modally other view controllers but not for embed segues. The reason for this is that the "Embed" segue is not a subclass of UIStoryboardSegue but inherits from UIStoryboardSegueTemplate, which is a private API.
Unfortunately I couldn't find a better way to achieve what you want than with the hybrid approach.
My way is to link the containerView and delete the viewDidLoad segue from it. and manually call the segue on viewdidLoad
public protocol EmbeddingContainerView {
var containerView: UIView! { get set }
}
public class CoreSegue: UIStoryboardSegue {
public static func instantiateViewControllerWithIdentifier(identifier: String) -> UIViewController {
let storyboard = UIStoryboard(name: "Core", bundle: NSBundle(forClass: self))
let controller = storyboard.instantiateViewControllerWithIdentifier(identifier) as! UIViewController
return controller
}
var isPresent = false
var isEmbed = false
override init!(identifier: String?, source: UIViewController, destination: UIViewController) {
if var identifier = identifier {
if identifier.hasPrefix("present ") {
isPresent = true
identifier = identifier.substringFromIndex(advance(identifier.startIndex, count("present ")))
}
if identifier.hasPrefix("embed ") {
isEmbed = true
identifier = identifier.substringFromIndex(advance(identifier.startIndex, count("embed ")))
}
let controller = CoreSegue.instantiateViewControllerWithIdentifier(identifier)
super.init(identifier: identifier, source: source, destination: controller)
} else {
super.init(identifier: identifier, source: source, destination: destination)
}
}
public override func perform() {
if let source = sourceViewController as? UIViewController, dest = destinationViewController as? UIViewController {
if isPresent {
let nav = UINavigationController(rootViewController: dest)
nav.navigationBarHidden = true // you might not need this line
source.presentViewController(nav, animated: true, completion: nil)
} else if isEmbed {
if let contentView = (source as? EmbeddingContainerView)?.containerView {
source.addChildViewController(dest)
contentView.addSubview(dest.view)
dest.view.fullDimension() // which comes from one of my lib
}
} else {
source.navigationController?.pushViewController(destinationViewController as! UIViewController, animated: true)
}
}
}
}
and later in your code:
class MeViewController: UIViewController, EmbeddingContainerView {
#IBOutlet weak var containerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
performSegueWithIdentifier("embed Bookings", sender: nil)
}
}

Resources