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
Related
I am failing to understand the fundamentals of what is needed to add my HomeViewController (UIViewController) as one of the tabs in my homeTabBarController (UITabBarController) using the setViewControllers method.
I have tried initializing this and simply adding it as a param in the method. There seems to be a difference between a view controller created via storyboard and one created programmatically because when I tried adding a viewcontroller:UIViewController programmatically to the setViewControllers method, it worked fine.
My code below compiles however I get a runtime exception Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ff7b8491598) at the line when homeTabBarController.setViewControllers is called
`
func loadTabBar() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let homeViewController = storyboard.instantiateViewController(identifier: Constants.Storyboard.homeViewController) as? HomeViewController
homeViewController!.title = "Home"
homeTabBarController.setViewControllers([homeViewController!], animated: false)
homeTabBarController.modalPresentationStyle = .fullScreen
present(homeTabBarController, animated: true)
}
`
//MARK: - Create the instances of ViewControllers
let grayViewController = HomeViewController()
let blueViewController = FirstViewController()
let brownViewController = SecondViewController()
override func viewDidLoad() {
super.viewDidLoad()
//set title of the viewcontrollers
grayViewController.title = "home"
blueViewController.title = "first"
brownViewController.title = "second"
//Assigne the viewcontrollers to the TabBarViewController
self.setViewControllers([ grayViewController, blueViewController, brownViewController], animated: false)
//set system images to each tabBars
guard let items = self.tabBar.items else {
return
}
let images = ["house.fill", "star.fill", "bell.fill"]
for i in 0...2 {
items[i].image = UIImage(systemName: images[i])
}
self.tabBar.tintColor = .black
}
// You can download the project from the below github link
https://github.com/ahmetbostanciklioglu/AddingTabBarControllerProgrammatically.git
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)
There is such code, if user logged on - ViewController changes :
func ifLogged() {
let preferences = UserDefaults.standard
let token = "token"
if preferences.object(forKey: token) == nil {
// Doesn't exist and stop at the same viewController
} else {
let newViewController = GeneralChooser()
self.navigationController?.pushViewController(newViewController, animated: false)
// push new generalChooser when logged
}
}
But in this case I do not see view elements, just background color from ViewHelper class
class GeneralChooser: UIViewController{
override func viewDidLoad() {
super.viewDidLoad()
ViewHelper.BackGroundColor(view: self.view)
There is BackgroundColor func below
class func BackGroundColor(view: UIView){
let startColor = UIColor(red:0.15, green:0.50, blue:0.70, alpha:1.0)
let endColor = UIColor(red:0.58, green:0.65, blue:0.81, alpha:1.0)
let newLayer = CAGradientLayer()
newLayer.colors = [startColor.cgColor,endColor.cgColor]
newLayer.zPosition = -1
newLayer.frame = view.frame
view.layer.addSublayer(newLayer)
If i do not call this function in GeneralChooser class, the screen is black and there are not view elements, but in initial ViewController i see view elements, just background color is default.
What am I doing wrong. Thanks for all.
The problem is in this line
let newViewController = GeneralChooser()
this line doesn't load xib or reference the storyboard object associated with that class
Use instantiateViewController and give the viewController storyboard ID in storyboard say GeneralChooserView
let vc = self.storyboard?.instantiateViewController(withIdentifier:"GeneralChooserView")
self.navigationController?.pushViewController(vc!, animated: true)
Or if you are using xibs with GeneralChooser as xib file for GeneralChooser class
let vc = GeneralChooser(nibName: "GeneralChooser", bundle: nil)
self.navigationController?.pushViewController(vc, animated: true)
I have an alert view that i made using presentr from GitHub. I used a simple view controller that will overlay over the current view controller. Now i have elements such as a UIImage and a UIlabel from the first view controller that needs to be accessed by the alert view controller. But when I click a unbutton in the alert view controller to access the uiimage and text from uilabel from the firstviewcontroller. Here is the code. Can you show me how I can fix this. I can't segue the data because I'm presenting the view controller and the data I'm trying to access is too much too segue anyway. I keep getting this error "fatal error: unexpectedly found nil while unwrapping an Optional value" overtime i click the save button in alertviewcontroller.
class firstviewcontroller: UIViewController{
var photos2: [ImageSource]?
#IBOutlet weak var Label: UIlabel!
#IBOutlet weak var Image: UIImage!
}
class alertviewcontroller: UIViewController{
#IBAction func Save(_ sender: Any) {
dismiss(animated: true, completion: nil)
let storyboard: UIStoryboard = UIStoryboard.init(name: "Main", bundle: nil)
let firstViewController: firstviewcontroller = storyboard.instantiateViewController(withIdentifier: "firstviewcontroller") as! firstviewcontroller
if let image = firstViewController.Image {
imageview.image = image
}
if let label = firstViewController.Label {
label.text = Label.text
}
}
}
The biggest mistake you're making is that you are creating a new instance of firstviewcontroller instead of just accessing the current one.
class alertviewcontroller: UIViewController{
#IBAction func Save(_ sender: Any) {
dismiss(animated: true, completion: nil)
let storyboard: UIStoryboard = UIStoryboard.init(name: "Main", bundle: nil)
let firstViewController: firstviewcontroller = storyboard.instantiateViewController(withIdentifier: "firstviewcontroller") as! firstviewcontroller // This is the mistake
if let image = firstViewController.Image {
imageview.image = image
}
if let label = firstViewController.Label {
label.text = Label.text
}
}
}
What you should do instead is access the presentingViewController since you presented the alertviewcontroller using the present function
The code would look like this
class alertviewcontroller: UIViewController{
#IBAction func Save(_ sender: Any) {
dismiss(animated: true, completion: nil)
let storyboard: UIStoryboard = UIStoryboard.init(name: "Main", bundle: nil)
// NOTE: only do implicit unwrapping `as!` if you're sure that the value you're unwrapping is not `nil` or it is of the correct `data type` cause it might cause your app to crash
let firstViewController: firstviewcontroller = self.presentingViewController as! firstviewcontroller
if let image = firstViewController.Image {
imageview.image = image
}
if let label = firstViewController.Label {
label.text = Label.text
}
}
}
Tip: Please review Swift coding guidelines since there are some minor mistakes regarding your naming of methods, variables, and classes.
Might I am wrong but after review your code I have found this messing statement
let firstViewController: firstviewcontroller = storyboard.instantiateViewController(withIdentifier: "firstviewcontroller") as! firstviewcontroller
In this statement you are creating a new instance of firstviewcontroller not accessing already created instance of firstviewcontroller.
Next, you said
I used a simple view controller that will overlay over the current
view controller
So I have assumed you didn't present or push alertviewcontroller on current viewController, on that behave I suggest you the following solutions
In alertviewcontroller get the instance of topViewController by using this method
class func topViewController(base: UIViewController? = (UIApplication.shared.delegate as! AppDelegate).window?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(base: nav.visibleViewController)
}
if let tab = base as? UITabBarController {
if let selected = tab.selectedViewController {
return topViewController(base: selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(base: presented)
}
return base
}
then you can transfer your data from alertviewcontroller to topViewController.
Next solution is that you can pass values from alertviewcontroller to firstviewcontroller by using delegates.
I was wondering how I could run a method from another class. For example, something like the following code:
let newVC: ScoreViewController = ScoreViewController()
newVC.makeScore()
The above code won't work for me because in makeScore(), I am changing a label's text, which I can't do with the above code because it is creating a new instance. Is there a way to call a method to be run without creating a new instance so that I can change a label's text in makeScore()?
EDIT:
How ScoreViewController is added to PageViewController:
let vc = storyboard.instantiateViewControllerWithIdentifier("Score") as! ScoreViewController
self.addChildViewController(vc)
self.scrollView.addSubview(vc.view)
I am assuming that you've some method in your FirstViewController where you're changing the score and showing it in your ScoreViewController. The delegation pattern is the possible solution for this problem. In your FirstViewController create a protocol for updating score such as:
protocol FirstVCScoreDelegate:NSObjectProtocol {
func makeScore()
}
Then inside your FirstViewController create a var for this delegate:
var delegate: FirstVCScoreDelegate
Then in your PageViewController, where you are creating the instances of the FirstViewController and ScoreViewController, set the delegate of the FirstViewController to ScoreViewController:
var firstVC: FirstViewController()
var scoreVC: ScoreViewController()
firstVC.delegate = scoreVC
And after this, in your method in the FirstViewController where the score is changing:
#IBAction func scoreChangeAction(sender: AnyObject) {
if delegate.respondsToSelector(Selector("makeScore")) {
delegate.makeScore()
}
}
This will signal the ScoreViewController to update the score. You now have to implement the delegate method inside ScoreViewController:
extension ScoreViewController: ScoreDelegate {
func makeScore() {
//update your label
}
}
I believe this will solve your problem.
UPDATE
Try this in your PageViewController's viewDidLoad: method:
override func viewDidLoad() {
super.viewDidLoad()
let mainStoryboard = UIStoryboard(name: "MainStoryboard", bundle: NSBundle.mainBundle())
let firstVC : FirstViewController = mainStoryboard.instantiateViewControllerWithIdentifier("firstVC") as FirstViewController
let scoreVC : ScoreViewController = mainStoryboard.instantiateViewControllerWithIdentifier("scoreVC") as ScoreViewController
firstVC.delegate = scoreVC
self.addChildViewController(firstVC)
self.addChildViewController(scoreVC)
self.scrollView.addSubview(firstVC.view)
self.scrollView.addSubview(firstVC.view)
}
In the PageViewController, declare a property:
class PageViewController {
var scoreViewController:ScoreViewController
// ....
}
After initializing the ScoreViewController:
let vc = storyboard.instantiateViewControllerWithIdentifier("Score") as! ScoreViewController
Hold it as a property:
self.scoreViewController = vc
Then use
self.scoreViewController.makeScore()