Button title set only for the second tab - swift

In my project there is a Segue Show(e.g. Push). After clicking the mainButton, TabBarController is presented. When I choose the second tab, the method setButtonsTitle is called and when I swipe the TabBarController down, I can see "item 2" instead of the "Button" title (default title). But when I click the first tab afterwards and swipe the TabBarController down, it remains "Button". My first thought was that the method does not get called, but this is not true (checked in debugger). Also, print(title) shows "Item 1".
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var mainButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destination = segue.destination as! TabBarController
destination.selectionDelegate = self
}
}
extension ViewController : ButtonThings {
func setButtonsTitle(title: String){
mainButton.titleLabel?.text = title
print(title)
}
}
And the TabBarController:
import UIKit
class TabBarController: UITabBarController {
weak var selectionDelegate: ButtonThings?
override func viewDidLoad() {
super.viewDidLoad()
}
//MARK: UITabBarController
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
selectionDelegate?.setButtonsTitle(title: item.title ?? "no title")
}
}
protocol ButtonThings : class {
func setButtonsTitle(title: String)
}

I was able to reproduce that issue that you had mentioned. I then set the button type to custom and the button title started showing correctly.
Hope this helps.

Related

View Controller shows as pop-up

I want a normal segue, so no pop-up. But when I push the button to go to the next ViewController, it shows as ab pop-up. The buttons to the next ViewController is in a different storyboard than the ViewController I'd like to go to.
This is the code of the first ViewController:
import UIKit
//some other code
class LoginViewController: UIViewController {
//some other code
override func viewDidLoad() {
super.viewDidLoad()
//some other code
}
#objc private func ProceedTapped(){
//Door naar storyboard van de daadwerkelijke app
print("Proceed clicked")
performSegue(withIdentifier: "goToAppIdentifier", sender: self)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
}
And this is the code of the ViewController I'd like to go to:
import UIKit
class HomeScreen: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("Home has loaded")
}
}
The kind of the segue of the "Storyboard Segue" is "Show (e. g. push)"
You just have to go to story board select the 2nd view controller and change presentation style to "Full Screen". Please check below image for reference

i cannot able to pass data between viewcontrollers via protocols

View controller A
class ViewController: UIViewController {
var delegate: server?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func ok(_ sender: Any) {
delegate?.datum(data: "sd")
performSegue(withIdentifier: "goingB", sender: self)
}
}
View controller B
protocol server {
func datum(data: String)
}
class ViewControllerB: UIViewController, server {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
func datum(data: String) {
self.label.text = data
print(data)
}
}
I need to pass the data via view controllers but I cannot able to pass however I know we can pass data through protocols, but anyhow I end up with error when try to run the program
If you need to pass data from one view controller to another and you're using segue for presenting new view controller, you can just override prepare(for:sender:), there is no need to using delegates. Here you can get reference for controller which will be presented and you can assign its variable.
So, first create variable in second view controller and declare that if you assign it with new value, it changes text of your label
class ViewControllerB: UIViewController {
#IBOutlet weak var label: UILabel!
var variable: String? {
didSet {
label.text = variable
}
}
}
Now in first view controller override prepare(for:sender:) and if segue is segue which you've performed, downcast destination view controller and assign its variable
class ViewController: UIViewController {
#IBAction func ok(_ sender: Any) {
performSegue(withIdentifier: "goingB", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goingB" {
let destinationVC = segue.destination as! ViewControllerB
destinationVC.variable = "sd"
}
}
}
Anyway, if you want to use your code with delegate, you have to set delegate of first view controller as second view controller which will be presented. For this purpose you can also use prepare(for:sender:) where you can get reference for destination of segue and then you can call your method on delegate
class ViewController: UIViewController {
var delegate: server?
#IBAction func ok(_ sender: Any) {
performSegue(withIdentifier: "goingB", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goingB" {
let destinationVC = segue.destination as! ViewControllerB
delegate = destinationVC
delegate?.datum(data: "sd")
}
}
}
Notes:
Name protocol with big capital letter Server and we are talking about delegates, add delegate word: ServerDelegate
Constrain protocol for just for classes
Make then your delegate variable weak
protocol ServerDelegate: class {
func datum(data: String)
}
...
weak var delegate: ServerDelegate?
The simplest here is to to set the property directly in prepare.
However, if you want to use delegate, you can. Your problem is that you have mixed between A and B.
The way you wrote it, when you call delegate?.datum, delegate is not defined and we can't access datum.
What do you want to do ? Go from A to B, and when in B, update a label in B with data received from A.
Here just to show how to use (but clearly too complex compared with direct assignment).
protocol Server {
func datum() -> String
}
class ViewControllerB: UIViewController {
#IBOutlet weak var label: UILabel!
var delegate: Server?
override func viewDidLoad() {
super.viewDidLoad()
let data = delegate?.datum()
self.label.text = data
}
}
class ViewControllerA: UIViewController, Server {
override func viewDidLoad() {
super.viewDidLoad()
}
var data = "sd"
func datum() -> String {
return data
}
#IBAction func ok(_ sender: Any) {
performSegue(withIdentifier: "goingB", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destVC = segue.destination as? ViewControllerB {
destVC.delegate = self
}
}
}

Delegate using Container View in Swift

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.

Need to free memory after Modal Segues, but I need both my Segues to pass data from A to B and B to A

I am making a game for iOS with SpriteKit.
I have 2 Viewcontrollers. One is the GameViewController and the other one is the MenuViewController. Let's call them A and B respectively.
When the player dies, a function is called in GameScene.swift that launches a modal "Lost" Segue to B. There, the player can restart the game or buy a life and a "Back" Segue is called to A.
I need to dismiss the additional Views that get created each time I call a segue.
Problem is: I need the "Lost" Segue to send data about the Score to View B and I need the "Back" Segue to send data to View A about wether or not the player used a life.
I have implemented all this. But now I need to find how to dismiss old views that keep eating the device's memory, thus leading to lag and crash.
I have googled for hours and hours. No solution was adapted to my situation.
The solutions I found either caused my app to bug, data not to be passed or views not to be generated.
I will not add code here since there is a LOT. But I am sure the answer is actually really easy, just not for a beginner like me.
I think a possible solution would be an unwind segue from B to A ?
But do unwind segues pass data along ?
Moreover, I found no answer I could understand on how to use an unwind segue.
I exhausted all my possibilities. Stack Exchange is my last chance.
You definitely should use an unwind segue to return to the previous viewController, otherwise as you have found your memory usage increases until your apps quits.
I created the following example from your description. It uses a standard segue to move from the GameViewController to the MenuViewController and it uses an unwind segue to move from the MenuViewController back to the GameViewController.
The GameViewController has a Player Dies UIButton, a UITextField for entering a score, and a UILabel for displaying the lives.
The MenuViewController has a UILabel for showing the score, a Buy a Life UIButton for adding lives, and a Restart UIButton for returning to the GameViewController.
Here's the code:
GameViewController.swift
import UIKit
class GameViewController: UIViewController {
#IBOutlet weak var scoreTextField: UITextField!
#IBOutlet weak var livesLabel: UILabel!
var lives = 3
func updateLivesLabel() {
livesLabel.text = "Lives: \(lives)"
}
override func viewDidLoad() {
super.viewDidLoad()
updateLivesLabel()
}
// This is the function that the unwind segue returns to.
// You can call it anything you want, but it has to be in
// the viewController you are returning to, it must be tagged
// with #IBAction and it must take a UIStoryboardSegue as its
// only parameter.
#IBAction func returnFromMenu(segue: UIStoryboardSegue) {
print("We're back in GameViewController")
// Update the lives label based upon the value passed in
// prepareForSegue from the MenuViewController.
updateLivesLabel()
}
#IBAction func goPlayerDies(sender: UIButton) {
lives--
self.performSegueWithIdentifier("Lost", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Lost" {
let destinationVC = segue.destinationViewController as! MenuViewController
destinationVC.score = Int(scoreTextField.text ?? "") ?? 0
destinationVC.lives = lives
}
}
}
MenuViewController.swift
import UIKit
class MenuViewController: UIViewController {
#IBOutlet weak var scoreLabel: UILabel!
var score = 0
var lives = 0
override func viewDidLoad() {
super.viewDidLoad()
scoreLabel.text = "Score: \(score)"
}
#IBAction func buyLife(sender: UIButton) {
lives++
}
#IBAction func goRestart(sender: UIButton) {
self.performSegueWithIdentifier("Back", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Back" {
let destinationVC = segue.destinationViewController as! GameViewController
destinationVC.lives = lives
}
}
}
This is how you wire up the forward segue to be called programmatically:
Control-drag from ViewController icon to the MenuViewController:
Select Present Modally from the pop-up:
Click on the segue arrow between the viewControllers and give it an identifier in the Attributes Inspector:
This is how you wire up the unwind segue to be called programmatically:
Control-drag from ViewController icon to Exit icon:
Choose returnFromMenu from pop-up:
Click on the Unwind Segue in the Document Outline and give it the identifier "Back" in the Attributes Inspector on the right:
Alternate Answer
Instead of using segues, you can present and dismiss viewControllers manually. The advantage for your app is that the MenuViewController will be allocated only once and will persist for the life of the app. This same viewController will be presented and dismissed repeatedly, but it will not be deallocated which I suspect is leading to your crashes.
The GameViewController will be the initialViewController that is created by the Storyboard. The MenuViewController will be loaded in viewDidLoad of the GameViewController.
To make this work, you need to add an identifier to the MenuViewController so that it can be instantiated by name. Click on the MenuViewController in the Storyboard and set its Storyboard ID in the Identity Inspector:
Here is the code. Note that all mention of segues is gone. Note how viewWillAppear is used to update the viewControllers.
GameViewController.swift
import UIKit
class GameViewController: UIViewController {
#IBOutlet weak var scoreTextField: UITextField!
#IBOutlet weak var livesLabel: UILabel!
var menuViewController: MenuViewController?
var lives = 3
func updateLivesLabel() {
livesLabel.text = "Lives: \(lives)"
}
override func viewDidLoad() {
super.viewDidLoad()
menuViewController = self.storyboard!.instantiateViewControllerWithIdentifier("MenuViewController") as? MenuViewController
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
updateLivesLabel()
}
#IBAction func goPlayerDies(sender: UIButton) {
lives--
menuViewController?.score = Int(scoreTextField.text ?? "") ?? 0
menuViewController?.lives = lives
self.presentViewController(menuViewController!, animated: true, completion: nil)
}
}
MenuViewController.swift
import UIKit
class MenuViewController: UIViewController {
#IBOutlet weak var scoreLabel: UILabel!
var score = 0
var lives = 0
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
scoreLabel.text = "Score: \(score)"
}
#IBAction func buyLife(sender: UIButton) {
lives++
}
#IBAction func goRestart(sender: UIButton) {
let destinationVC = self.presentingViewController as! GameViewController
destinationVC.lives = lives
self.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
}
}

Swift: Pass multiple values between views

I have a view that has two text fields and a button.
#IBOutlet var inputURL: UITextField!
#IBOutlet var inputName: UITextField!
#IBAction func submitUrlButton(sender: AnyObject) {
}
and a second view that has two variables:
var submittedURL = ""
var submittedName = ""
println("Name \(submittedName)")
println("URL \(submittedURL)")
In Swift How do I pass the values entered in the two text fields and assign them to those variables in the second view?
Thanks
EDIT FOR THETOM:
import UIKit
class ViewController: UIViewController {
#IBOutlet var inputURL: UITextField!
#IBAction func submitBtn(sender: AnyObject) {
performSegueWithIdentifier("submissionSegue", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
// Create a new variable to store the instance of the next view controller
let destinationVC = segue.destinationViewController as BrandsViewController
destinationVC.submittedURL.text = inputURL.text
}
}
You can use the method prepareForSegue.
In the first view (the one from which the segue is coming from) write the following code :
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
// Create a new variable to store the instance of the next view controller
let destinationVC = segue.destinationViewController as CustomViewController
destinationVC.submittedURL = inputURL.text
destinationVC.submittedName = inputName.text
}
Here CustomViewController is the custom class of the UIViewController to which the segue is going to.
To perform the segue programmatically in your button #IBAction do that :
#IBAction func buttonWasClicked(sender: AnyObject) {
performSegueWithIdentifier("submissionSegue", sender: self)
}
Since your view controllers are linked with segue you can override the prepareForSegue method in first view controller and pass data by doing so
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "secondViewController") { // here secondViewController is your segue identifier
var secondViewController = segue.destinationViewController as SecondViewController // where SecondViewController is the name of your second view controller class
secondViewController.submittedURL = inputURL.text
secondViewController.submittedName = inputName.text
}
}
And to performSegue inside your button action use perfromSegueWithIdentifier method
#IBAction func submitUrlButton(sender: AnyObject) {
//replace identifier with your identifier from storyboard
self.performSegueWithIdentifier("secondViewController", sender: self)
}
The simplest way of accessing values globally not neccessary to pass with segue
First View controller
import UIKit
var submittedURL:NSString? // declare them here
var submittedName:NSString? // Now these two variables are accessible globally
class YourViewController : UIViewController
{
#IBOutlet var inputURL: UITextField!
#IBOutlet var inputName: UITextField!
#IBAction func submitUrlButton(sender: AnyObject) {
if inputURL.text == "" && inputName.text == ""
{
//Show an alert here etc
}
else {
self.submittedURL.text = inputURL.text
self.submittedName.text = inputName.text
}
}
}
SecondView Controller
import UIKit
class SecondviewController: UIViewController
{
//inside viewDidload
println(submittedURL)
println(submittedName)
}