I'm following this tutorial link
But with one change that I made since the beginning.
I Added a ViewController (and made it the Initial View Controller) and Added a Container View in it.
Then I embed seagued between my View Container and the tutorial's SplitViewController.
And all worked well, until I got to this step:
Go to to AppDelegate.swift and replace the implementation of application(_:didFinishLaunchingWithOptions:) with the following:
guard let splitViewController = window?.rootViewController as? UISplitViewController,
let leftNavController = splitViewController.viewControllers.first as? UINavigationController,
let masterViewController = leftNavController.topViewController as? MasterViewController,
let detailViewController = splitViewController.viewControllers.last as? DetailViewController
else { fatalError() }
let firstMonster = masterViewController.monsters.first
detailViewController.monster = firstMonster
return true
How this code should be modified to work with my case?
In your case you can't get access to uisplitviewcontroller in AppDelegate. Since you are using Container Embedded view like in image below. You get reference of the UISplitviewcontroller from prepareFor segue of your first viewController. So instead of writing code in AppDelegate, try do that in prepare for segue in your initial viewController.
Initial ViewController
class ViewController: UIViewController {
#IBOutlet weak var containerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "embedseg" {
guard let splitViewController = segue.destination as? UISplitViewController,
let leftNavController = splitViewController.viewControllers.first as? UINavigationController,
let masterViewController = leftNavController.topViewController as? MasterViewController,
let rightNavController = splitViewController.viewControllers.last as? UINavigationController,
let detailViewController = rightNavController.topViewController as? DetailViewController
else { fatalError() }
let firstMonster = masterViewController.monsters.first
detailViewController.monster = firstMonster
masterViewController.delegate = detailViewController
detailViewController.navigationItem.leftItemsSupplementBackButton = true
detailViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem
}
}
}
Related
About
I'm developping ios app with MXParallaxHeader.
I use it in order to create twitter-like UI.
However, I don't know how to pass data from MXScrollView to childViewController when I go MXScrollView page.
Question
How could I pass data from MXScrollView to childViewController?
Sample Code
PreviousViewController
//MXScrollViewController=ParentVC of MarkerInfoTabViewController
class PreviousViewController:UIViewController {
.
.
#IBAction func goMXScrollView(_ sender: Any) {
let nextView = self.storyboard?.instantiateViewController(withIdentifier: "MXScrollViewController") as! MXScrollViewController
self.navigationController?.pushViewController(nextView, animated: true)
}
}
childViewController
//MarkerInfoTabViewController(=childVC of MXScrollView)
import Tabman
import UIKit
import MXParallaxHeader
class MarkerInfoTabViewController: TabmanViewController {
var marker:Marker!
private var viewControllers = [UIViewController?]()
override func viewDidLoad() {
super.viewDidLoad()
let leftView = self.storyboard?.instantiateViewController(withIdentifier: "MarkerInfoLeftViewController") as! MarkerInfoLeftViewController
let rightView = self.storyboard?.instantiateViewController(withIdentifier: "MarkerInfoRightViewController") as! MarkerInfoRightViewController
//↓I want to pass data here
leftView.marker = marker
viewControllers = [leftView, rightView]
self.dataSource = self
// Create bar
let bar = TMBar.ButtonBar()
bar.layout.transitionStyle = .snap // Customize
// Add to view
addBar(bar, dataSource: self, at: .top)
}
}
My App Image
・storyboard
・segue to header
・segue to childVC
Reference
My app is mostly the same as this app.
Just define a property in the child controller class
class ChildViewController {
var passedData: MY_DATA_TYPE
...
and set it after the instantiation
let nextView = self.storyboard?.instantiateViewController(withIdentifier: "ChildViewController") as! ChildViewController
nextView.passedData = MY_PASSED_DATA
self.navigationController?.pushViewController(nextView, animated: true)
I'm trying to update UILabel from appDelegate but the label never change the text. Here is my code:
ViewController:
#IBOutlet weak var lblToUpdate: UILabel?
Here is my app delegate:
DispatchQueue.main.async{
let storyboard = UIStoryboard(name:"Main", bundle:nil)
let myVC = storyboard.instantiateViewController(withIdentifier: "myVC") as! myViewController
if let text = newText{
myVC.text?.text = text
}
}
I have no errors of any kind but the the label never updates.
Any of you knows why the label never gets updated?
I'll really appreciate your help.
The problem is that this VC
let myVC = storyboard.instantiateViewController(withIdentifier: "myVC") as! myViewController
is not the presented one you have to query the window for it and change it's label as you currently play with another one
its not working because the label doesn't exists in the time, just pass the value to a variable in your next controller and then in some of the methods for example :
viewdidload you can update your label with the value of the variable for example :
DispatchQueue.main.async{
let storyboard = UIStoryboard(name:"Main", bundle:nil)
let myVC = storyboard.instantiateViewController(withIdentifier: "myVC") as! myViewController
if let text = newText{
myVC.theVariable = text
}
}
class NextViewController: UIViewController {
var theVariable = ""
override func viewDidLoad() {
super.viewDidLoad()
lblToUpdate.text = theVariable
}
You shouldn't create new instance because is other viewcontroller, this already exist.
You should access to rootViewcontroller and follow window hierarchy
DispatchQueue.main.async{
if let vcroot = self.window!.rootViewController {
if let text = newText{
vcroot.text?.text = text
}
}
}
This code above is example to refer you question.
If you have navigationcontroller is for example:
DispatchQueue.main.async{
if let navroot = self.window!.rootViewController as? NavigationController{
if let childvc = navroot.childviewcontrollers.first {
if let text = newText{
childvc.text?.text = text
}
}
}
}
If have severals viewcontroller in navigation controller you will access using for loop and check and arrive to the viewcontroller that wish change value property or set values
app is starting in detailViewController instead of MasterViewController.
how to fix that in the iPhone?
(its a universal app, and don't want changes when it runs in the iPad)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "embedseg" {
guard let splitViewController = segue.destination as? UISplitViewController,
let leftNavController = splitViewController.viewControllers.first as? UINavigationController,
let masterViewController = leftNavController.topViewController as? MasterViewController,
let rightNavController = splitViewController.viewControllers.last as? UINavigationController,
let detailViewController = rightNavController.topViewController as? DetailViewController
else { fatalError() }
let firstMonster = masterViewController.monsters.first
detailViewController.monster = firstmonster
masterViewController.delegate = detailViewController
detailViewController.navigationItem.leftItemsSupplementBackButton = true
detailViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem
}
}
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"
}
}
}
I currently have a DetailViewController segued from a ViewController, which is embedded in a UINavigationViewController, which I want to embed in a UITabBarController. When I first did it on my storyboard, my app crashed with the error:
"Could not cast value of type 'UITabBarController' (0x10badf258) to 'UINavigationController' (0x10badf208)".
After research, I added the first two lines (let tabVc =, and let navVc = ) and still crashed. What am I missing to create a successful TabBarController?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let tabVc = segue.destinationViewController as! UITabBarController
let navVc = tabVc.viewControllers!.first as! UINavigationController
if segue.identifier == "ShowItem" {
if let row = tableView.indexPathForSelectedRow?.row {
let item = itemStore.allItems[row]
let detailViewController = segue.destinationViewController as! DetailViewController
detailViewController.item = item
detailViewController.imageStore = imageStore
}
}
}
UPDATE: After applying changes, my error has changed to
"Could not cast value of type 'UITabBarController' (0x103ff6258) to 'Photomania.ItemsViewController' (0x1029520d0)."
Error
I think you are approaching the problem in a wrong way. You can set the imageStore and itemStore of ItemsViewController in it's viewDidLoad.
override func viewDidLoad() {
super.viewDidLoad()
self.itemStore = ItemStore()
self.imageStore = ImageStore()
}
But if you wish to do it in didFinishLaunchingWithOptions of AppDelegate then this is how you would do it.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let rootController = window?.rootViewController
if rootController is UITabBarController {
let firstTabItem = (rootController as! UITabBarController).viewControllers?[0]
if firstTabItem is UINavigationController {
let firstController = (firstTabItem as! UINavigationController).viewControllers.first as! ItemsViewController
firstController.itemStore = ItemStore()
firstController.imageStore = ImageStore()
}
}
}
As the error clearly states, it's this line of code that is failing:
let navVc = tabVc.viewControllers!.first as! UINavigationController
according to the error the first viewController in the tabVC is not a navigationVC.
So make sure your tab bars are arranged in the correct order and be certain that the first viewController of your tabVC is indeed a navigation view controller.