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.
Related
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!"
}
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"
}
}
}
I have read through a lot of different posts regarding this issue, but none of the solutions seemed to work for me.
I started a new app and I placed the initial ViewController inside a navigation controller. I created a second view and linked them together on the storyboard with a segue. The segue works successfully, and I can see the data I am transferring in a print statement from the second screen, but the screen shows black.
WelcomeScreen:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "segueToTraits"{
if let gender = self.selectedGender{
let traitVC = segue.destinationViewController as? TraitViewController
traitVC!.gender = gender
}
}
}
func sendGenderToTraitsView(gender : String?){
performSegueWithIdentifier("segueToTraits", sender: self)
}
#IBAction func button1(sender: UIButton) {
selectedGender = boyGender
self.sendGenderToTraitsView(selectedGender)
}
#IBAction func button2(sender: UIButton) {
selectedGender = girlGender
self.sendGenderToTraitsView(selectedGender)
}
Storyboard:
Link to image of my storyboard
My segue is set as follows:
Link to image of my segue information
Also, my viewControllers are named WelcomeViewController and TraitViewController. They have storyboard id's of welcomeVC and traitsVC.
Any help would be incredibly appreciated. Let me know if you need any other information.
By following the steps that #ronatory had laid out so well for me in the accepted answer, I was able to see that I had set up my TraitViewController as a UIPageViewController instead of UIViewController.. and so it didn't generate any errors, but it just took me to a black screen. Feel silly that I read through my code so many times and never noticed this.
Main point: If you're getting a black screen on a ViewController randomly, make sure your class is extending the correct parent class.
in my case:
class TraitViewController: UIPageViewController {
needed to be
class TraitViewController: UIViewController {
I've build your app and everything works, maybe you've missed something, here is my solution (Note: Code is in Swift 3.0, but should be easy to adopt it to Swift 2.*):
The storyboard:
Set the segueToTraits identifier:
Set the TraitViewController class as custom class in the storyboard:
The view controller with the buttons:
import UIKit
class ViewController: UIViewController {
let boyGender = "boy"
let girlGender = "girl"
var selectedGender: String?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segueToTraits"{
if let gender = self.selectedGender {
let traitVC = segue.destination as? TraitViewController
traitVC!.gender = gender
}
}
}
func sendGenderToTraitsView(gender : String?){
performSegue(withIdentifier: "segueToTraits", sender: self)
}
#IBAction func button1(sender: UIButton) {
selectedGender = boyGender
self.sendGenderToTraitsView(gender: selectedGender)
}
#IBAction func button2(sender: UIButton) {
selectedGender = girlGender
self.sendGenderToTraitsView(gender: selectedGender)
}
}
The trait view controller:
import UIKit
class TraitViewController: UIViewController {
var gender: String = ""
override func viewDidLoad() {
super.viewDidLoad()
print("gender: \(gender)")
}
}
Result:
You can find the sample project here
I'm developing an app for iPad Pro. In this app, containerView use to add additional views and interact with them.
First, I created a protocol:
protocol DataViewDelegate {
func setTouch(touch: Bool)
}
Then, I created my first view controller
import UIKit
class ViewController: UIViewController, DataViewDelegate {
#IBOutlet var container: UIView!
#IBOutlet var labelText: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
func setTouch(touch: Bool) {
if touch == true {
labelText.text = "Touch!"
}
}
}
And finally, I created a view that will be embedded in containerView.
import UIKit
class ContainerViewController: UIViewController {
var dataViewDelegate: DataViewDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func touchMe(sender: AnyObject) {
dataViewDelegate?. setTouch(true)
}
}
But for some reason, nothing happened, the first view controller receives nothing in setTouch function.
My question is: In this case, using container, how can I make the communication between two ViewsControllers?
Like #nwales said you haven't yet set the delegate. You should do set the delegate in prepareForSegue function on your first viewController (who contain the viewContainer)
First select the embed segue and set an identifier in the attributes inspector.
Then in the parentViewController implement the func prepareForSegue like this:
Swift 4+:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "the identifier") {
let embedVC = segue.destination as! ViewController
embedVC.delegate = self
}
}
Below:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
if (segue.identifier == "the identifier") {
let embedVC = segue.destinationViewController as! ContainerViewController
embedVC.dataViewDelegate = self
}
}
Looks like you defined the delegate, but have not set the delegate. This happens to me all the time.
Say, I have a label show : Loading...
problem: When return from VC(2). The label is not hidden.
How to hide it when return from VC(2) and dont hide it when in navigating to VC(2) and show the message : Loading....
in VC(1)
#IBOutlet weak var lbLoadingMsg
In viewDidLoad() {
lbLoadingMsg.hidden = true
}
-2-- turn it on when prepare to navigate to VC(2)
override func shouldPerformSegueWithIdentifier(identifier: String?, sender: AnyObject?) -> Bool
{
--code--
lbLoadingMsg.hidden = false
}
Override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!){
}
You can use NSNotificationCenter for that.
Follow this simple steps:
1.In your VC(2) add this code into your button from where you are going back:
#IBAction func goBack(sender: AnyObject) {
NSNotificationCenter.defaultCenter().postNotificationName("hide", object: nil)
self.dismissViewControllerAnimated(true, completion: nil)
}
2.In your First View add this code into viewDidLoad method:
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "hideLabel:", name:"hide", object: nil)
}
now this method will call this function:
func hideLabel(notification: NSNotification){
self.lbLoadingMsg.hidden = true
}
And this will hide your label in first view when ever goBack button will pressed from first view.
Hope this will help you.
Write this in VC2
,
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var identifier = segue.identifier
if(identifier! == "yourIdentifier"){
var vc1:VC1 = segue.destinationViewController as! VC1
vc1.lbLoadingMsg.hidden = true
}
}
func viewDidAppear(_ animated: Bool) {
lbLoadingMsg.hidden = true
}
Move
lbLoadingMsg.hidden = true
line from viewDidLoad to viewDidAppear. I think most quicker way.