I have a custom class segmented control that is communicating with a view controller every time a different item in the segmented control is selected. I'm able to pass data to the view controller just fine.
#IBDesignable class SegmentedControlLeft: UIControl {
var selectedIndex: Int = 0 {
didSet {
displayNewSelectedIndex()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let messagesViewController: MessagesViewController = storyboard.instantiateViewController(withIdentifier: "PurpleVC") as! MessagesViewController
messagesViewController.animateViews(selectedIndex: selectedIndex)
}
}
...some more code
}
However, when I try to use some basic logic every time the passed variable is updated, all the items inside the "animateViews" function apparently turn nil. I receive the infamous "unexpectedly found nil" error. This only happens when i try to use the variable I'm passing, everything runs as expected otherwise outside of the function.
import UIKit
class MessagesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var messageTableView: UITableView!
#IBOutlet weak var boostCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
...all cells, delegates, and datasource are registered and setup in here.
}
func animateViews(selectedIndex: Int) {
if selectedIndex == 0 {
print("LEFT") // prints as expected
self.messagesTableView.isHidden = false // unexpected found nil error on each of these items.
self.boostCollectionView.isHidden = true
} else if selectedIndex == 1 {
print("RIGHT") // prints correctly when updated
self.messageTableView.isHidden = true
self.boostCollectionView.isHidden = false
}
}
}
I've been playing around with this for far too long, how do I get views in the animateViews function to hide and unhide using the variable I'm passing from the segmented Control?
There is probably an amazingly simple fix that I'm not getting here, but I appreciate you for getting your eyeballs this far. Thank you!
This is happening because by the time you call
messagesViewController.animateViews(selectedIndex: selectedIndex), the MessagesViewController is not instantiated. That also means that the Table view and Collection View are not drawn yet and you are trying to use it before it is being created.
The viewDidLoad() method has to be called once to use the UI Elements of the View Controller.
Each time this line is called let messagesViewController: MessagesViewController = storyboard.instantiateViewControl, a new instance of the view controller is created.
Suggestion:
My suggestion will be to create an outlet of the segmented controls(seems like you are having multiple Segmented Controls in View Controller), and create a method like #IBAction func indexChanged(_ sender: AnyObject) { and assign this action to the value changed property of the segmented controls.
You can differentiate among the segmented controls using the sender parameter.
Related
So all my controller's are done programmatically to avoid segues and that sort of complicated stuff.
I have a viewcontroller (Call it ProfileViewController) that downloads data from the network.
So I have a method in ProfileViewController that instantiates a single storyboard file with a static tableview with cells that have textfields in them. Here is the method:
ProfileViewController:
func userSelectedUpdateProfile() {
// Obtain reference to the only storyboard file named EditProfileSB
let storyboard = UIStoryboard(name: "EditProfileSB", bundle: nil)
// Since the Tableview is embedded in a navigation controller (with ID set to "navigationID")
if let parentNavigationController = storyboard.instantiateViewController(withIdentifier: "navigationID") as? UINavigationController {
// Now find the embedded TableViewController and access it's properties to pass to.
if let childEditController = parentNavigationController.topViewController as? EditProfileTableViewController {
// ! Error here ! Found nil when doing this.
childEditController.nameTextfield.text = "Passed this to static cell"
}
present(parentNavigationController, animated: true, completion: nil)
}
}
So the code itself is self-explanatory to what I am trying to achieve here. The TableView is embedded in a Navigation (done on storyboard with "Editor > Embed In") so on the 2nd nested if let statement I am now checking to find that Edit controller and access its properties (nameTextfield).
I get a crash when I attempt to access the nameTextField.text property. This textfield is set using storyboard. Here is that EditProfileTableViewController:
class EditProfileTableViewController: UITableViewController {
#IBOutlet weak var nameTextfield: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
// Other methods ...
}
Here is the storyboard flow layout.
Am I missing something here? I keep getting a crash on childEditController.nameTextfield.text = "Passed this to static cell" on the method userSelectedUpdateProfile().
If your View Controller still not call viewDidLoad().
your textfield is not create.
#IBOutlet weak var nameTextfield: UITextField!
you can see it's attribute is weak here.
Try create a value and pass text to the value. Then in viewDidLoad(), you can set the value to your textField
I'm getting nil when unwrapping an optional value with GADBannerView..
I setup my ad banner like this, in FlashViewController.swift..
class FlashViewController: UIViewController {
#IBOutlet weak var bannerView: GADBannerView!
and then in ViewDidLoad:
func initAdMobBanner() {
bannerView.adUnitID = "ca-app-pub-3940256099942544/2934735716"
bannerView.rootViewController = self
bannerView.load(GADRequest())
}
bannerView has an outlet in storyboard to Root View Controller, which is class FlashViewController.
Then in TableViewController.swift I have my purchase button. Purchase button runs:
FlashViewController().HideMyBanner();
The function HideMyBanner is in FlashViewController and will run this code:
if bannerView != nil {
print("bannerview Contains a value!")
bannerView.isHidden = true
} else {
print("bannerview Doesn’t contain a value.")
}
The issue is, if I create a button directly in FlashViewContorller.swift and run the same function, bannerView contains a value and can be hidden.. If I call the function from TableViewController.swift, it returns nil, (or crashes if I try to hide bannerView... I feel like I missing something easy here, but already spent a long time trying to figure it out...
By using this line FlashViewController().HideMyBanner(); you are creating new object of FlashViewController. so it will crash.you need to use the object of FlashViewController which is already created and loaded in memory.
I think you need to pass the reference of FlashViewContorller to TableViewController
If your TableViewController is load from FlashViewContorller than you need to create reference FlashViewContorller in TableViewController like this way.
class TableViewController: UIViewController {
var objFlashViewContorller : FlashViewContorller?
override func viewDidLoad() {
super.viewDidLoad()
//This is the UILabel
}
func purchasebuttonClick() {
objFlashViewContorller?.HideMyBanner();
}
}
While setup Navigation FlashViewContorller to TableViewController you need to pass reference.
let tableViewController = self.storyboard!.instantiateViewController(withIdentifier: "TableViewController") as! TableViewController
tableViewController.objFlashViewContorller = self
self.navigationController?.pushViewController(tableViewController, animated: true)
OR
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "TableViewController" {
let vc = segue.destinationViewController as! TableViewController
vc.objFlashViewContorller = self
}
Don't use FlashViewController().HideMyBanner();
I think you need to use this in in TableViewController.swift -> my purchase button method.
self.revealViewController.frontViewController.HideMyBanner()
I used Notifications to finally work this out... Controller 2 sends a notification that a purchase has been made, and Controller 1 observes and waits for this notification, then takes care of hiding the banner in Controller 1.
https://blog.bobthedeveloper.io/pass-data-with-nsnotification-in-swift-3-73743723c84b
Quick note before my actual question: I am very new (a few days in) to XCode and Swift. I have programming experience in scientific platforms such as matlab, mathematica, and IGOR.
My question:
In my main view controller I have an IBOutlet for a UIButton which I have implemented in the main story board.
class ViewController: UIViewController {
....
#IBOutlet weak var chip_a1: UIButton!
....
}
Also in my main view controller, and within an IBAction method, I'm able to access the hidden attribute as follows:
#IBAction func chip_a1_touchupinside(sender: UIButton) {
self.chip_a1.hidden = true
}
But when I try to access this property from within a different view controller class as follows:
class endgame: UIViewController {
....
#IBAction func playagain_touchupinside(sender: UIButton) {
var handle = ViewController()
// make the states of all the chips visible
handle.chip_a1.hidden = false // this is the line that gives me an ERROR
dismissViewControllerAnimated(true, completion: nil)
}
....
}
The error is this:
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_1386_INVOP, subcode=0x0)
Any help with understanding/fixing this issue would be much appreciated. Happy to provide more detail if I need to.
Best,
Alex.
You create UIViewcontroller with xib File
You have to Change
var handle = ViewController()
to
var handle = ViewController(nibName: "ViewControllerNibfileName", bundle: nil)
as Button is IBOutlet and need the nibfile loaded with viewcontroller
I'm trying to wrap my head around protocols and delegates, but seems to be having some issues. I have 2 ViewControllers that I'm trying to pass data from. ViewController A has a text field that I want to be optionally populated from ViewController B. So there is a button on ViewController A that segues you over to ViewController B This is how I have B set up.
protocol AddPlayersViewControllerDelegate{
var playersName:String? { set get }
}
class B-Controller: UIViewController, UITableViewDelegate, UITableViewDataSource{
var addPlayerDelegate:AddPlayersViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
..etc
}
I'm using this code in my viewControllers B class to dismiss the currentView when a cell is selected
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("did select")
let cell = playerTableView.cellForRowAtIndexPath(indexPath)
addPlayerDelegate?.playersName? = "New Name"
if let navController = self.navigationController {
navController.popViewControllerAnimated(true)
}
}
It's not allowing me to set the players Name property inside the protocol here. It keeps returning me Nil when I check it from ViewController A.
View Controller A looks like this:
class A-ViewController: UIViewController, UITextFieldDelegate, AddPlayersViewControllerDelegate{
var addPlayerDelegate:AddPlayersViewControllerDelegate?
}
//then I'm just trying to print out the new name the was set in ViewController B
override func viewWillAppear(animated: Bool) {
println("this is the selected players name \(addPlayerDelegate?.playersName)") - returns nil
}
I'm sure I'm not fully understanding something, but I feel that I just keep reading and trying out examples only to end back up here where I started from.
//************************* UPDATE *************************//
I'm going to try and simplify my set up. I have 2 View Controllers, VC-A, and VC-B.
VC-A has a text field and a button. VC-B has a tableview. I want the option to have the textField to be populated from the cell.text from CB-B, but only if the user taps the button to view VC-B. So the first time that VC-A loads, it should being back nil from my playersName string from the protocol, because VC-B has never been called as of yet. But once the user taps the button inside VC-A to view VB-B and then selected a cell, which would dismiss VC-B and populate the playersName string inside the protocol on the VC-B class, then I'm using the viewWillAppear method to check to see if playersName has been set and if so use it. Here is my updated code from the help you have given me.
VC-A
class FirstViewController: AddPlayersViewControllerDelegate{
var playersName:String?
let svc = LookUpViewController()
override func viewDidLoad() {
super.viewDidLoad()
svc.addPlayerDelegate = self
}
}
VC-B
protocol AddPlayersViewControllerDelegate{
var playersName:String? { set get }
}
class LookUpViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
var addPlayerDelegate: AddPlayersViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = playerTableView.cellForRowAtIndexPath(indexPath)
addPlayerDelegate?.playersName = "Ziggy"
println("the name to be pass is \(addPlayerDelegate?.playersName)")
if let navController = self.navigationController {
navController.popViewControllerAnimated(true)
}
}
It seems that I'm still getting nil even when I got back to VC-A from VC-B. All I want is to be able to get some data (string) from VC-B and use it in VC-A, but only after the user uses the VC-B class. Does that make sense?
You have shown that in the BController you have a property addPlayerDelegate:
var addPlayerDelegate:AddPlayersViewControllerDelegate?
And you have shown that in the BController you talk to that property:
addPlayerDelegate?.playersName? = "New Name"
But you have not shown that at any point in the lifetime of this controller, its addPlayerDelegate property is ever set to anything. For example I would want to see code like this:
someBController.addPlayerDelegate = someAController
If that doesn't happen, then that property remains at its initial value of nil.
Another problem with your code is that this line makes no sense:
class A-ViewController : // {
var addPlayerDelegate:AddPlayersViewControllerDelegate?
}
The AController doesn't need this property. They don't both need delegates! What the AController needs is a playersName property. Without it, it doesn't conform to the AddPlayersViewControllerDelegate protocol. In fact, I'm surprised that without that property your code even compiles. Are you sure you are reporting it correctly?
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()