Accessing methods, actions and/or outlets from other controllers with swift - swift

I'm working on a macOS project where I have a split view containing 2 other ViewControllers and I can't figure out how to access the ViewControllers from my primary window's ViewController.
this is the setup:
Basically what I'm trying to do is use the Button in my ViewController on the top-left to access the Label in my SectionController on the right, which is embedded in my split view.
Since I can't create an IBAction or IBOutlet for a control in a different ViewController, I can't figure out how to get these to be connected. My current workaround has been to have a property on my AppDelegate and then access the main shared application delegate, but that feels hacky and won't scale. I'm completely lost as to how to proceed. I'm ok with using a function to pass data or whatever to the other ViewController(s).
I'm using Swift 4 with Xcode 9 (beta).
Any ideas?

Of course you can't create IBAction or IBOutlet for a control in a different ViewController!!
But simply each view controller in the hierarchy has a reference for its child view controllers.
Method 1:
#IBAction func buttonTapped(_ sender: Any) {
let splitViewController = self.childViewControllers[0] as! YourSplitViewController
let targetViewController = splitViewController.childViewControllers[0] as! YourTargetViewController
targetViewController.label.text = "Whatever!"
}
Method 2:
It may be better if you took a reference for each child controller in your "prepare for segue" method
ContainerViewController:
var mySplitViewController: YourSplitViewController?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "splitViewSegue" {
self.mySplitViewController = segue.destination as! YourSplitViewController
}
}
YourSplitViewController:
var aViewController: YourFirstViewController?
var bViewController: YourSecondViewController?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "aViewSegue" {
self.aViewController = segue.destination as! YourFirstViewController
} else if segue.identifier == "bViewSegue" {
self.bViewController = segue.destination as! YourSecondViewController
}
}
So you can access it like that in your container view controller:
#IBAction func buttonTapped(_ sender: Any) {
self.mySplitViewController.firstViewController.label.text = "Whatever!"
}

Related

connect segue to Navigation control

Before I used a Segue to transfer data between to view controllers.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Edit List" {
if let currentTodo = todoReadyForEditing, let editControler = segue.destination as? ListPageController {
editList.prepare(for: currentList)
}
Now the plan is changed I should transfer the data from the first view controllers to a tableViewController. Since I want to have a navigation bar to the tableViewController page, I added a navigationBarController before it.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Edit List" {
if let currentTodo = todoReadyForEditing, let editControler = segue.destination as? ListTableViewController {
editList.prepare(for: currentList)
}
The problem is when I connected segue directly to UITableViewController, everything is work, same as before that I have two view controllers, but the navigation bar is hidden, which I don't want. As I read, apparently i have to connect the segue first to the navigationViewController, but when I did not, the data won't transfer to the table view controller.
Is there any suggestion? Thanks for your help in advance
Destination of segue is UINavigationController, but you can work with its topViewController property which is your controller
guard let navCon = segue.destination as? UINavigationController,
let controller = navCon.topViewController as? ListTableViewController else { fatalError("Segue wasn’t set right") }
Anyway, I would suggest you don’t use segues and create your UINavigationController with embedded controller programmatically

Value not passing between viewControllers

Struggling to get my viewControllers to send value from the main viewController to a second. I want it to happen on a button click, I'm going to get the value from the button and pass it to the new form. But it just isn't working.
Code for main ViewController
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func butClick(_ sender: UIButton) {
NSLog("Button Pressed : %#",[sender .currentTitle])
//var tt = [sender .currentTitle]
// Create the view controller
let vc = TimesTablesViewController(nibName: "TimesTablesViewController", bundle: nil)
vc.passedValue = "xx"
self.performSegue(withIdentifier: "pushSegue", sender: nil)
}
}
Code for second viewController called TimesTablesViewController:
class TimesTablesViewController: UIViewController {
#IBOutlet weak var titleLabel: UILabel!
var passedValue:String = ""
override func viewDidLoad() {
super.viewDidLoad()
titleLabel?.text = "\(passedValue) Times Table"
}
}
I've followed tutorials but can't seem to solve the problem! Thanks for any help!
Replace
self.performSegue(withIdentifier: "pushSegue", sender: nil)
with
self.present(vc,animated:true,completion:nil)
or ( if the current vc is inside a naigation )
self.navigationController?.pushViewController(vc,animated:true)
Using
self.performSegue(withIdentifier: "pushSegue", sender: nil)
is fit with storyboards not xibs and if this your case then you need to use the above line only inside the button action with implementing this method inside the source vc
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "pushSegue" {
if let nextViewController = segue.destination as? TimesTablesViewController{
nextViewController.passedValue = "xx"
}
}
}
I’m assuming that the new view controller is appearing, but you’re simply not seeing the data. If so, you’re evidently using storyboards. The TimesTablesViewController(nibName:bundle:) only works if you’re using XIB/NIBs and manually presenting new view controller.
If you’re really using storyboards, simplify your butClick method:
#IBAction func butClick(_ sender: UIButton) {
NSLog("Button Pressed")
performSegue(withIdentifier: "pushSegue", sender: self)
}
But implement prepare(for:sender:):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? TimesTablesViewController {
destination.passedValue = "xx"
}
}
Assuming the above fixes your problem, I might suggest a further simplification. Notably, if your butClick(_:) method is really only calling performSegue, you can segue to this next scene without any #IBAction method at all:
remove butClick(_:) entirely;
remove the connection between the button and the butClick method in IB, on the “Connections Inspector” tab in the right panel; and
control-drag from the button previously hooked up to butClick(_:) to the scene for TimesTablesViewController.
That will simplify your code further.

Difference Between 2 Ways of Declaring a ViewController Object

So I experimented with these 2 different ways of declaring a ViewController variable and it seemed to offer me the same results. However, I do feel there must be a difference between setting the destinationVC variable because if not, won't people use the more straightforward way of just declaring a new object?
[using segue.destination as! ViewControllerName]
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "changeCityName" {
let destinationVC = segue.destination as! ChangeCityViewController
destinationVC.delegate = self
}
}
[using ViewControllerName()]
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "changeCityName" {
let destinationVC = ChangeCityViewController()
destinationVC.delegate = self
}
}
In the prepareForSegue method, these two methods of creating a new VC differs greatly.
If you use segue.destination, you refer to the specific VC that the segue is going to, i.e. the one in your storyboard that the segue is connected to. If you create a new VC, then the VC you created won't be the same one as the segue is going to. i.e. you are dealing with a separate VC. Setting the delegate of the newly created VC won't do anything to the VC that is actually being presented.
If you are talking about the difference between using a segue to present a VC and this:
let vc = SomeViewController()
self.present(vc, animated: true)
Then the difference is less. If you use segues, then the views in the view controller will be read from the storyboard (NIB) file. If you create the VC by calling the initializer, you will have to handle adding the views in your view controller class.
Result may be visually same but its not true.
If you don't put any code inside prepare(for segue) still you will get same result(visually)
prepare(for segue) is called when UIViewControllers are connected through storyboard.
Since UIViewControllers are already connected in storyboard, so the destination UIViewController is called on your desired event.
In your first case using (segue.destination as! ViewControllerName) which is correct way of using segue.
Before going further one more thing is to be discussed about and that is
Why we are required to write code inside prepare(for segue) if its already connect through storyboard
1.From one button action you can connect several segues depending on your requirements, but each time button is pressed same prepare(for segue) method will be called, so to differentiate which UIViewController is to be called we do something like this
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "FirstViewControllerIdentifier")
{
}else if(segue.identifier == "SecondViewControllerIdentifier"){
}else if(segue.identifier == "ThirdViewControllerIdentifier"){
}else{
// and so no
}
}
Now here we get object of destination controller(UIViewController) already being prepared.So we are not required to make a new object of destination controller
2.We can pass data to destination controller and also we can set delegate
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "FirstViewControllerIdentifier")
{
// here we get object of first view controller and set delegate
let firstVC = segue.destination as! FirstViewController
firstVC.delegate = self
}else if(segue.identifier == "SecondViewControllerIdentifier"){
// here we get object of second view controller and pass some data to it
let secondVC = segue.destination as! SecondViewController
secondVC.someData = someData
}else if(segue.identifier == "ThirdViewControllerIdentifier"){
}else{
// and so no
}
}
Now in your second case using ViewControllerName() (the wrong code)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "FirstViewControllerIdentifier")
{
// here we create object of first view controller.This object is different from the destination view controller
//you create an object and set a delegate but after that you are not using that object and that object is totallu useless.
let firstVC = FirstViewController()
firstVC.delegate = self
// above code does not affect any thing but the contoller which is to be presented is destination view controller which is connected through storyboard
}
}
Hope you understand how to use segue and let me know if there is any problem

How to programmatically seque to multiple view controllers from a button

I have a view controller with a lot (36) of buttons, and I want each of these buttons to segue to a particular view controller based on a variable that was set earlier in the program. In other words, any button could potentially go to 15 different view controllers based on a variable that was sent to the viewcontroller containing the buttons...
I think I can make this work if I click and drag each button to every viewcontroller... but it seems silly and messy.
I tried doing something like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if variable == "Whatever" {
let send = segue.destination as! AViewController
send.variablesent = (sender as! UIButton).title(for: .normal)!}
}
But this only works if I click and drag the button in the storyboard to the "AViewController".
Any help is appreciated, thanks!!
For that you can make segue from SourceViewController to DestinationViewController instead of from Button to Controller, After that when you call perfromSegue in your button action then pass button reference as sender in perfromSegue call.
#IBAction func buttonAction(_ sender: UIButton) {
self.performSegue(withIdentifier: "segueIdentifier", sender: sender)
}
Now in prepareForSegue cast sender to UIButton and set title according to it.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if variable == "Whatever" {
let send = segue.destination as! AViewController
if let button = sender as? UIButton {
send.variablesent = button.titleLabel?.text ?? "Default value"
}
}
}

Access a variable in another view controller - Xcode 8.0 Swift 3.0

How do I set a variable on one VC and then access it on another VC? I know that you need to use prepare for segue but I want to access the variable when the VC appears. This is my code so far:
override func viewDidAppear(_ animated: Bool) {
print("viewDidAppear")
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("prepare for segue")
let variableUnwind = ("StackOverFlow")
if segue.identifier == "toFirstViewController" {
let hello = segue.destination as! AddItemViewController
print(hello.username)
}
}
}
I know that prepare for segue means that the actions in that function will happen before a segue but I don't know how to eliminate prepare for segue without losing what will happen in that function.
Basically, I want to set a variable on VC2 and the access it on VC1 when VC1 has loaded. If It helps I am using an unwind segue to go to VC1 from VC2.
You need to do
hello.whatever your variable name is = the new variable you created on VC2
after you declare the hello variable
hope this helps
override func viewDidAppear(_ animated: Bool) {
print("viewDidAppear")
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("prepare for segue")
let variableUnwind = ("StackOverFlow")
if segue.identifier == "toFirstViewController" {
let hello = segue.destination as! AddItemViewController
hello.variableName = newVariableName
}
}
}