Swift Custom UIViewController's label is nil [duplicate] - ios

This question already has an answer here:
IBOutlet of another view controller is nil
(1 answer)
Closed 7 years ago.
I customized a viewcontroller with xib, and this viewcontroller has a label.
I have already connected the label with xib.
But when I use this label, it is nil.
What's wrong I did?
below is my code.
import UIKit
class MyViewController: UIViewController {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
code to use this viewcontroller
let myViewController = MyViewController(nibName: "MyViewController", bundle: nil)
myViewController.label.text = "test"
presentViewController(myViewController, animated: true) { () -> Void in
println("ok")
}
Thank you.

The issue is you are trying to access the label before it is loaded.
The following changes may solve your problem;
import UIKit
class MyViewController: UIViewController {
var labelText: String!
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
//Setting Label text here
label.text = labelText;
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
and
let myViewController = MyViewController(nibName: "MyViewController", bundle: nil)
myViewController.labelText = "test"
presentViewController(myViewController, animated: true) { () -> Void in
println("ok")
}

From the documentation:
The nib file you specify is not loaded right away. It is loaded the first time the view controller's view is accessed. If you want to perform additional initialization after the nib file is loaded, override the viewDidLoad method and perform your tasks there.
You may access the label from viewDidLoad. If you need to access the label externally, make sure you have accessed the view property first to force the view to be loaded.

The view of the view controller is not loaded. You can either use the viewDidLoad() method inside your view controller or you load it by accessing the view property. It is lazy loaded and will load your view.
So before accessing viewController.view.label write print(viewController.view) to lazy load the view

Related

Can't add functionalities to my UIPageViewController's UIViewControllers

I've made a UIPageViewController, which will administrate 3 UIViewControllers.
It all seems to work just fine, right until I add a button in the storyboard, and hook it up with a IBOutlet - then my app crashes with:
"libc++abi.dylib: terminating with uncaught exception of type NSException"
Also, if I hook the button up with an IBAction, the app crashes when tapping the button.
class PostPageViewController: UIPageViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("IN PAGE VC!!")
self.dataSource = self
if let firstViewController = orderedViewControllers.first {
print("IN FIRST!! \(firstViewController)")
setViewControllers([firstViewController], direction: .Forward, animated: true, completion: nil)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
private(set) lazy var orderedViewControllers: [UIViewController] = {
return [self.newViewController("1"), self.newViewController("2"), self.newViewController("3")]
}()
private func newViewController(number: String) -> UIViewController {
return self.storyboard!.instantiateViewControllerWithIdentifier("NewPostVC\(number)")
}
}
I have an extension controlling the pages as well, but I don't think that's interesting in this situation.
Here's my first view controller, thats causing the problem:
class NewPostVC1: UIViewController {
#IBOutlet weak var cancelButton: UIButton!
#IBAction func cancelButtonTapped(sender: AnyObject) {
print("Cancel button tapped!")
}
override func viewDidLoad() {
super.viewDidLoad()
print("NEWPOSTVC 111111")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
So, as said, the app creashed when tapping the button - I guess it's something about the UIPageViewController not being able to figure out which UIViewController is interacting, but I haven't been able to find anything that confirms this. Does anyone have an idea about this issue?
UPDATE: I just figured out that mu first viewController isnt instanciated, but number 2 and 3 are.. So right now my issue is that I have to load my viewcontroller somehow, before its shown - OR simply just dont have any functionality on the first page, and only on number 2 and 3.. Does anyone have a suggestion for instanciate the first viewcontroller before its shown? I've tried storyboard.instantiateViewControllerWithIdentifier("NewPostVC1"), but it doesn't do the job!
Those kinds of error usually mean you have hooked something up incorrectly on the storyboard. E.g. you've added an IBAction, hooked it up to the button action but then gone back to the code and changed the name of the method (by accident or on purpose).

Delegation from ContainerView to parent ViewController

I have the following setup:
StartViewController has a ContainerView that contains ContainerViewController
I try to find a way to hidden an element in StartViewController after a task is performed in ContainerViewController.
For this I try to use delegation method like this:
StartViewController
class StartViewController: UIViewController, showBannerAdDelegate {
#IBOutlet weak var bannerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
bannerView.hidden = false
}
func bannerAdHidden(status: Bool) {
bannerView.hidden = status
}
}
ContainerViewController
protocol showBannerAdDelegate: class {
func bannerAdHidden(status: Bool)
}
class ContainerViewController: UIViewController {
weak var delegate: showBannerAdDelegate! = nil
#IBAction func buttonPressed(sender: UIButton) {
delegate.bannerAdHidden(true)
}
}
If I presented the ContainerViewController I could do in prepareForSegue
let destination = segue.destinationViewController as! ContainerViewController
destination.delegate = self
But in this case both View Controller are always present.
What code should I add to the View Controller to make it work?
Thank you,
If one of the view controllers is inside a container view then it is loaded with an embed segue, which fires when the containing view controller is first loaded. The prepareForSegue method still gets called, so you can set up a delegate exactly as you've described. I always thought embed segues were a little odd (it's not really a segue, more like loading a child view controller) but that's how it works.

Access Variables of ViewController Before Popping To It

I have a home button which triggers this code when pressed.
I need to change a variable within the viewController I am popping to before I pop to it. Is there any way to do this?
func goHome(){
let switchViewController = self.navigationController?.viewControllers[1] as! UIViewController
self.navigationController?.popToViewController(switchViewController, animated: true)
}
This is how I thought you would go about it but no variables of the viewController appear in the autocomplete window.
switchViewController.x = 5
Any information on how to go about this and/or why this isn't working as is would be greatly appreciated.
You're setting switchViewController as a generic UIViewController, which has no variable .x.
You should set it as the correct class, which in this case would be whatever you named the class that has the 'x' variable:
let switchViewController = self.navigationController?.viewControllers[1] as! SwitchViewController
switchViewController.x = 5
In this case I've used the class name SwitchViewController, which means you'd need a .Swift class file like so:
import UIKit
class SwitchViewController: UIViewController {
var x: Int!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
What type of UIViewController your switchViewController is?
In order to have custom variables in the the UIViewController, you would need to subclass it, and when getting it back, you would do it like this:
func goHome(){
let switchViewController = self.navigationController?.viewControllers[1] as! YourViewController
self.navigationController?.popToViewController(switchViewController, animated: true)
}
Taking x as the example variable, UIViewController has no variable of this type, but your subclass (which i expect you have one) of UIViewController might have, and then auto-complete would kick in.

swift delegate beetween two view controller without segue

My delegate protocol never called
My first controller - ViewController
class ViewController: UIViewController,testProtocol {
#IBAction func btInit(sender: AnyObject) {
println("Bt Init")
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initViewController: UIViewController = storyBoard.instantiateViewControllerWithIdentifier("viewTarget") as targetViewController
self.presentViewController(initViewController,animated: false, nil)
}
var targetController = targetViewController();
override func viewDidLoad() {
super.viewDidLoad()
self.targetController.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func testDelegate(){
println(" in my view controller delegate ")
}
}
In my second view controller - targetViewController
protocol testProtocol {
func testDelegate() // this function the first controllers
}
class targetViewController: UIViewController {
#IBAction func BtTarget(sender: AnyObject) {
println("bt target pressed")
delegate?.testDelegate()
}
var delegate : testProtocol?
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func testDelegate(){
println(" in my target view controller delegate ")
}
}
Why is testDelegate() never called on ViewController? What am I doing wrong? Thanks.
I have read a lot of posts about this, but all of the examples are given with segue transition, and I don't want use a segue.
Typically you set a new view controller's delegate property in prepareForSegue:. You said you're not using a segue, so you'll need to instantiate the second view controller and present it somehow. You can do this by doing something like:
let storyboard = UIStoryboard(name: "AStoryboardName", bundle: nil)
let secondVC = storyboard.instantiateViewControllerWithIdentifier(anIdentifier) as! targetViewController
secondVC.delegate = self
presentViewController(secondVC, animated: true, completion: nil)
You have a testDelegate() method in both view controllers, but you only want it in the first view controller. Then your second view controller can call delegate?.testDelegate() at the appropriate time.
Finally, you typically want to make delegate properties weak, so I would recommend changing var delegate : testProtocol? to weak var delegate: testProtocol?
I would read up on delegation. Here is a relatively simple 5 step process to delegation that may help you:
Delegation in 5 Steps:
object A is the delegate for object B, and object B will send out the messages:
Define a delegate protocol for object B.
Give object B an optional delegate variable. This variable should be weak.
Make object B send messages to its delegate when something interesting happens, such as the user pressing the Cancel or Done buttons, or when it needs a piece of information.
Make object A conform to the delegate protocol. It should put the name of the protocol in its class line and implement the methods from the protocol.
Tell object B that object A is now its delegate (in prepareForSegue(sender)).

Passing data between view in swift

I am trying to familiarize with swift but I can't find how to pass data between views using Swift.
class ViewController: UIViewController {
#IBOutlet var field: UITextField
#IBOutlet var butt: UIButton
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if let vc = segue.destinationViewController as? secondViewController {
if(vc.lab != nil){
vc.lab.text=self.field.text
println(self.field.text)
}
}
and second view controller:
class secondViewController: UIViewController {
#IBOutlet var lab: UILabel
override func viewDidLoad() {
super.viewDidLoad()
}
What I want to do is simply change the label in the second view with the text of the textfield of the first view.
In this way does not give me any error but I do not change the label.
To me, this doesn't look like a Swift problem. It looks like a view lifecycle problem. At the time prepareForSegue: is called, the secondViewController has not loaded it's IBOutlets from the storyboard yet. A better solution would be to set some type of property on the file, like
vc.myLabelString = self.field.text
then in viewDidLoad of secondViewController assign the text to your label.
FYI: You can always check if a view controller has loaded it's view with vc.isViewLoaded()

Resources