accessing property from another viewController's class in Swift? - ios

Working with a split view controller...I have a variable that gets a value in my settingsViewController class, and now in my main view controller I need to access the valuable that variable. How can I get to settingsViewController.selectedCounty?
class settingsViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
let titleData = TitleData()
var selectedCounty = String?("Allegany")
trying to grab this value to place in:
class ViewController: UIViewController {
let settings = settingsViewController()
let selectedCounty = settings.selectedCounty
returns "settingsViewController.type" does not have a member named selectedCounty?

I ended up figuring it out, I needed to call prepareForSegue on settingsViewController to be able to pass this to my other ViewController (note I changed it to FirstViewController to avoid confusion:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var destViewController: FirstViewController = segue.destinationViewController as! FirstViewController
destViewController.selectedCounty = selectedCounty
}

In Swift initial value of a (stored)property can not be dependent on other property(s).
Here your ViewController's property(i.e. selectedCounty) depends on settingsViewController's property(i.e. selectedCounty).
Solution:
You can assign it later in init()
let selectedCounty:String
init(){
selectedCounty = settings.selectedCounty!
}

The line
let settings = settingsViewController()
creates a constant of the name settings of type settingsViewController. Then in the line
let selectedCounty = settingsViewController.selectedCounty
this constant is not accessed. Rather a type property selectedCounty of the type settingsViewController is accessed. Since there is no such type property, this is an error.
Access the property as follows:
let selectedCounty = settings.selectedCounty
or make it a type property:

Related

SWIFT: Is it possible to access stored properties of a class inside its extension?

class BibliothequesViewController: UIViewController {
static let sharedInstance = BibliothequesViewController()
var presentedBy: UIViewController?
}
I tried to access, both sharedInstance and presentedBy inside the extension:
extension BibliothequesViewController: UITableViewDelegate, UITableViewDataSource {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//Trying to present by using presentBy property
let vc = segue.destination as! presentedBy
// This throws this error: Use of undeclared type 'presentedBy'
let vc = segue.destination as! BibliothequesViewController.sharedInstance.presentedBy
// This throws this error: Static property 'sharedInstance' is not a member type of 'BibliothequesViewController'
}
}
The first error makes sense.
The second one doesn't as I have used BibliothequesViewController.sharedInstance.presentedBy in other areas in the app and it works fine.
The point is I want to know is there a way to access sharedInstance or presentedBy inside the extension?
The target of as is a type, not a variable. The only thing you know about presentedBy is that it's of type Optional<UIViewController>. And segue.destination is of type UIViewController. Since every type can be promoted to the optional of its type, your as isn't doing anything. When you're done, you know it's a UIViewController. You're good to go. You can call any UIViewController methods you want, but you could do that anyway.
In short: your as! isn't doing anything. Just get rid of it.
(As #Runt8 notes, yes, you can definitely access stored properties from an extension, but that has nothing to do with your actual question.)

Transferring an object from a Realm database to another UITabBar controller

Tell me, please, I'm trying to solve the problem of transferring an instance of a class to another controller using the Realm database.
I have a main controller that stores objects according to the model the following data:
class Route: Object {
#objc dynamic var routeImage: Data?
#objc dynamic var routeName: String?
#objc dynamic var numberOfPersons = 0.0
#objc dynamic var dateOfDeparture: String?
#objc dynamic var dateOfArrival: String?
let placeToVisit = List<Place>()
let person = List<Person>()
}
In the controller to which I need to transfer this data, I created
var currentRoute: Route!
In the Storyboard, I specified the identifier "showDetail" from the controller cell to the UITabBar, and in the main controller, I created a method:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
guard let indexPath = tableView.indexPathForSelectedRow else {return}
let newPlaceVC = segue.destination as! InformationViewController
newPlaceVC.currentRoute = routes[indexPath.row]
}
}
Error I got:
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
Could not cast value of type 'UITabBarController' (0x111ed8b10) to 'Organizer_Tourist.InformationViewController' (0x108dd0a70).
2019-10-07 14:30:35.626853+0800 Organizer Tourist[5467:2618892] Could not cast value of type 'UITabBarController' (0x111ed8b10) to 'Organizer_Tourist.InformationViewController' (0x108dd0a70).
(lldb)
But it is not valid, the application crashes by tap on the cell. I suppose this would work if there was not a tabBar, but a regular table, view controllers. I was looking for solutions and all I came across was implementation through singleton. Now I have a lot of questions, but will this really be the right decision? People say this violates the "modularity" of the application and carries its own problems. The question is how is this done through singleton? What to consider, where to start? Which method is worth editing?
Error said what is happening:
Could not cast value of type 'UITabBarController'
You are trying to cast segue.destination to typo InformationViewController which is not.
If you embed your InformationViewController in UITabBarController so you need to cast to your UITabBarController rather than InformationViewController.
Try something like this:
if segue.identifier == "showDetail" {
guard let indexPath = tableView.indexPathForSelectedRow else { return }
let tabBarController = segue.destination as! UITabBarController
UserSelectedRoute.shared.selectedRoute = routes[indexPath.row]
}
If you want to pass current selected route to InformationViewController you can create singleton object which will be hold current route
final class UserSelectedRoute {
private init() { }
static var shared = UserSelectedRoute()
var selectedRoute: Route?
}
And then in your InformationViewController you can have something like:
var currentRoute = UserSelectedRoute.shared.selectedRoute
Hope this will help you!

Accessing ViewController's variables through another ViewController

On my iOS app written in Swift, I have a variable which is initialized on FirstViewController.swift.
I want to assign its value to a label on SecondViewController.swift.
At first I've tried to do it like this on SecondViewController.swift:
var firstViewController: FirstViewController = FirstViewController(nibName: nil, bundle: nil)
var name = firstViewController.name
After the didn't work, I tried to do it using a struct:
// FirstViewController.swift
struct GlobalVariables {
var name: String = "test"
}
// SecondViewController.swift
var name = FirstViewController.GlobalVariables.name
But that didn't work either. After both methods I'm printing the new variable to the console and assign its value to the label, but all I see is nothing on the label and nil on the console.
Can you please help me with that? How can I access to a variable on FirstViewController.swift through SecondViewController.swift?
To pass arguments between View Controllers, you can use segues.
First you have the variable in FirstViewController
FirstViewController: UIViewController {
var name: String = "test"
...
}
Then you have a variable of the same type in SecondViewController
SecondViewController: UIViewController {
var name: String = ""
...
}
To move from FirstViewController, you use a programmatic segue.
self.performSegue(withIdentifier: "Indentifier", sender: nil)
Then, in FirstViewController, define prepareForSegue:sender.
You get a reference to the destination view controller, then set the variable to the one from sending view controller
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! SecondViewController
vc.name = name
}
EDIT:
If you need it to be accessible in all view controllers, define a Global class. You stated in your question you tried a struct. Instead, try static variables
class Global {
static var name: String?
}
EDIT 2:
Another way to have global variables is with a singleton class
class Global {
static let sharedInstance = Global()
var name: String?
}
Here's a barebones idea of how to do it with a separate file to hold data (values) for your labels to display. Disclaimer: it might not be the best idea (you probably don't want to bind your ViewControllers to this single data file like this), but it should do the trick before you learn other better ways. So, here we go.
You want to use a separate file (name it for example MyTextValuesModel.swift) for all the values you'd like your FirstViewController and SecondViewController to access.
You select File > New > File... from the menu, and choose Swift File.
Then you declare your class:
import Foundation
class ValuesForLabels {
var textForLabel1 = "Label1"
var textForLabel2 = "Label2"
var textForLabel3 = "Label3"
// etc...
}
Now in your FirstViewController and SecondViewController you declare a variable to refer to this class:
var textsForLabels = ValuesForLabels()
Now you can access (read/write) the labels from any view controllers like this:
textsForLabels.textForLabel1 = "NewLabel1Text"
// you've just set a new value for Label1
label1.text = textsForLabels.textForLabel1
// your label1 text is now set to textForLabel1
If you'd want to access label values from another view controller, add a new variable to your new view controller to reference the labels.

Can't pass data to another view controller in when preparing for segue

I am trying to pass an object to another view controller but I get an error when I try to set the property of the view controller object.
The error is pretty informative. It says the class ViewController has no public or internal property called data. You'll have to declare a property called data in class ViewController.
class ViewController: UIViewController {
var data: String?
}
the class you have that is named ViewController needs to have a public variable named data.
Your ViewController class could look something like this:
class ViewController: UIViewController {
// This is your public accessible variable you can set during a seque
var data: String?
override func loadView() {
super.loadView()
print(self.data)
}
}
Also, your prepareForSegue function can be simplified like this
if let displayTodoVC = segue.destinationViewController as? ViewController {
displayTodoVC.data = "Hello World"
}
The ViewController is obviously missing a string variable named data.
class ViewController : UIViewController {
var data: String? // Make sure you have this defined in your view controller.
}
I would also suggest that you use a conditional unwrapping of the destinationViewController in your prepareForSegue.
prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let viewController = segue.destinationViewController as? ViewController {
viewController.data = "Hello World"
}
}
For future posts, please refrain from posting images of code. You should include code as text in your questions.
Happy coding :)
The Basics

Property value prints but when assigned to label it shows nil

Currently working with two view controllers and a swift file dealing with the details of a store such as the phone number. There is a main ViewController and a DetailsViewController.
I currently acquire data from google places api and am able to successfully store the values in a PlaceDetails Class. Testing out the data - I am able to print to the console. However, when I try to assign a value taken from the API to a UILabel the application crashes and shows that the value of the property is nil. I am not sure why this is the case. I feel in the two view controllers I am instantiating the PlaceDetails class correctly and accessing it's properties correctly but there is something I am not aware of that is going wrong.
class ViewController: UIViewController
{
let placeDetails = PlaceDetails()
let detailsVC = DetailsViewController()
func tapLabel( sender: UITapGestureRecognizer )
{
// print statement successfully prints out the stored value as - Optional("1 888-555-5555")
print(placeDetails.phoneNumber)
// assigning value to label causes a crash stating value is nil
detailsVC.phoneNumberLabel.text = placeDetails.phoneNumber!
self.performSegueWithIdentifier("showDetailsVC", sender: self)
}
}
class DetailsViewController: UIViewController
{
#IBOutlet weak var phoneNumberLabel : UILabel!
let placeDetails = PlaceDetails()
override func viewDidLoad()
{
super.viewDidLoad()
//This approach does not cause a crash but outputs nil to the console for both the print statement and the assignment statement
print(placeDetails.phoneNumber)
phoneNumberLabel.text = placeDetails.phoneNumber!
}
}
class PlaceDetails
{
override init()
{
super.init()
}
var phoneNumber : String? //viewcontroller actions give this class property its value
}
You need to assign placeDetails to your destination view controller in prepareForSegue. I know you aren't doing this as you have created placeDetails as a let constant rather than a variable so it can't ever change from the empty PlaceDetails you originally assign.
You should declare it as an optional variable and then unwrap it properly when you use it;
class ViewController: UIViewController
{
let placeDetails = PlaceDetails()
func tapLabel( sender: UITapGestureRecognizer )
{
self.performSegueWithIdentifier("showDetailsVC", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "showDetailsVC") {
let destVC = segue.destinationViewController as! DetailsViewController
destVC.placeDetails = self.placeDetails
}
}
}
class DetailsViewController: UIViewController
{
#IBOutlet weak var phoneNumberLabel : UILabel!
var placeDetails: PlaceDetails?
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
if let placeDetails = self.placeDetails {
if let phoneNumber = placeDetails.phoneNumber {
self.phoneNumberLabel.text = phoneNumber
}
}
}
}
You can't use the value in viewDidLoad as this method will execute before the property has been set; the view controller is loaded before prepareForSegue is called, so viewWillAppear is a better place.
Try to cast your phoneNumber in a string.
detailsVC.phoneNumberLabel.text = String(placeDetails.phoneNumber!)
Also, the nil value could come from the encoding method of the response of the API.
EDIT
I think you have to set the text of your UILabel in the viewDidLoad() method of your showDetailsVC. Declare a string variable in showDetailVC, and then pass your placeDetails.phoneNumber variable to the string you just declare. (Instead of directly set the text in the tapLabel() method). Then in your
showDetailVC set the text to your UILabel in the viewDidLoad()

Resources