How do I access text in UITextField from another ViewController (Swift)? - ios

I have a UITextField on one ViewController and I want to be able to have a Label on the second ViewController = whatever the user enters.
However, I get an error when trying to do this. I know Swift doesn't use import so I'm not sure what to do.
// First View Controller
#IBOutlet weak var textOne: UITextField!
// Second View Controller
#IBOutlet weak var theResult: UILabel!
theResult.text = textOne.text
Error: Unresolved Identifier

It looks like that you in reality want to just access the text from the UITextField and not the field itself. So you should send the text to the second ViewController from your first one by using prepareForSegue.
But before, you have to set the name of your segue so that Swift knows which data to send:
As you see, we name the segue segueTest.
So now we can implement the prepareForSegue-method in the FirstViewController and set the data which should be sent to the second.
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "segueTest") {
var svc = segue!.destinationViewController as secondViewController;
svc.theText = yourTextField.text
}
}
That you can do that, you have to set a variable in your SecondViewController of course:
var theText:String!

You need to pass a reference to your FirstViewController instance to the SecondViewController instance at prepearForSegue...
Then do something like this
//FirstViewController
#IBOutlet weak var textOne: UITextField!
func getTextOne () -> String? {
return textOne.text
}
//SecondViewController
#IBOutlet weak var theResult: UILabel!
var firstViewControllerInstance: FirstViewController?
func showResult () {
theResult.text = firstViewControllerInstance?.getTextOne()
}
And you are done

Related

Swift 3 pass values to Controller on UICollectionViewCell button click

I am beginner in iOS development and I have implemented following screen using UICollectionView :
CollectionViewCell File Code is:
import UIKit
class EventCell: UICollectionViewCell{
var classEvent: Event?
#IBOutlet weak var eventTitle: UILabel!
#IBOutlet weak var eventTeams: UILabel!
#IBOutlet weak var eventTime: UILabel!
#IBOutlet weak var eventTeamOneImage: UIImageView!
#IBOutlet weak var eventTeamTwoImage: UIImageView!
#IBOutlet weak var leaderboardButton: UIButton!
var datasourceItem: Any?{
didSet{
guard let event = datasourceItem as? Event else { return }
classEvent = event
eventTitle.text = "Match \(event.matchNo) (\(event.matchStage))"
eventTeams.text = "\(event.teamOne.nameAttr) vs \(event.teamTwo.nameAttr)"
eventTime.text = "\(event.getEventLockTimeAsString())"
eventTeamOneImage.loadImageUsingCache(withUrl: event.teamOne.flagPhoto)
eventTeamTwoImage.loadImageUsingCache(withUrl: event.teamTwo.flagPhoto)
leaderboardButton.addTarget(self, action: #selector(handleLeaderBoardClick), for: .touchUpInside)
}
}
#IBAction func leagueButton(_ sender: Any) {
}
weak var delegate: HomeControllerDelegate?
func handleLeaderBoardClick() {
if let matchId = classEvent?.id {
print(matchId)
delegate?.clickOnLeaderBoard(matchId: matchId)
}
}
}
Now on click on Leaderboard button(icon with 1,2,3) I would like to open new LeaderBoard Controller and pass matchId which is classEvent.id
How can I pass values to the new controller? And what is the best way to do that.
You can pass the match Id via segue:
In LeaderBoard Controller set a property:
var matchId:Int?
Set a segue between the controller and add an identifier:
On Click leaderboard button:
self.performSegueWithIdentifier("yourSegueIdentifier", sender: self)
Add the segue method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue,identifier == "yourSegueIdentifier" {
let destinationVC = segue.destinationViewController as LeaderbardController
destinationVC.matchId = classEvent.id
}
}
}
Three easy steps to get what u want:
Make a BaseViewController class a subclass of UiViewController. This class would be the alternate of UiViewcontroller in your project,it means while creating any viewcontroller BaseViewController will be the parent class.
Declare a variable in BaseViewController.e.g- var data: Any?
Then while moving from a viewcontroller to another , simply assign any type of data to that variable declared in BaseViewController.
And in any lifecycle method of your new viewcontroller you will get that data using self.data.

Swift: set variable before performing segue

I've a tableView with a cell for each pokemon in the "pokedex". When I press a button in the Cell, I want to show a view with details about this creature.
What I do is that I have a global variable "currentPokemon", that I set to the requested pokemon when the button is pressed. Here's my code for the Cell :
class PokemonTableViewCell: UITableViewCell {
var pokemon: Pokemon!
#IBOutlet weak var pokemonImage: UIImageView!
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var releaseDateLabel: UILabel!
#IBAction func setPokemon(sender: UIButton) {
currentPokemon = self.pokemon
}
}
When I try to access the currentPokemon var in the details view, I get a fatal error because currentPokemon is nil. How could I get this code to be executed before the segue ?
You need to add store for selected Pokemon with singleton instance like:
struct Pokemon {
let name: String
}
class PokemonStore {
static let instance = PokemonStore()
var currectPokemon: Pokemon?
}
Later you can get saved pokemon in any place of your code with PokemonStore.instance.currectPokemon
If you are using segue, you should pass data to detail controller in this UIViewController method:
func performSegue(withIdentifier identifier: String, sender: Any?)

Making a randomizer picking words from text field (Swift)

I'm trying to make app that takes info from the two text fields and randomly selects one of the sentences and places it in a label on another view controller. I'm a student in the Mobile Apps 1 class so I'm new to this. If you could explain it as much as possible it will be greatly appreciated. Happy new year!
My code:
class twoIdeasViewController: UIViewController {
#IBOutlet weak var twoIdeaContinueButton: UIButton!
#IBOutlet weak var twoIdea2TextField: UITextField!
#IBOutlet weak var twoIdea1TextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Enter Ideas"
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
let twoIdea1:String = twoIdea1TextField.text!
let twoIdea2:String = twoIdea2TextField.text!
return true
}
func prepareForSegue(segue: UIStoryboardSegue, Object: AnyObject?){
let twoIdeaFinal = segue.destinationViewController as! twoFinalViewController
twoIdeaFinal.twoIdea = //the variable that will contain the randomizer
}
}
Make use of arc4random_uniform() to generate a random number that controls which of the two text fields you wish to extract and send text from. Also, you seem to need to fix up your prepateForSegue method: you need to match the segue identifier with the identifier of your 2nd view controller (set in attributes inspector while selecting this other view controller in your storyboard).
#IBOutlet weak var twoIdea2TextField: UITextField!
#IBOutlet weak var twoIdea1TextField: UITextField!
// ...
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
/* Get the new view controller using segue.destinationViewController.
Pass the randomly chosen text view text to the UILabel of the
new view controller. */
/* Here: you need to match with the identifier of your
VC 'twoFinalViewController' (set in attributes inspector) */
if segue.identifier == "twoFinalVC" {
let viewController = segue.destinationViewController as! ViewController
let random = arc4random_uniform(2)
viewController.twoFinalLabel.text = (random == 0) ? (twoIdea1TextField.text ?? "") : (twoIdea2TextField.text ?? "")
}
}
For a detailed description covering segue communication between two view controllers (UITableViewController and UIViewController), see the following thread
Global variable and optional binding in Swift
You can use something like that
func getRandomString() -> String
{
let randomNumber = arc4random_uniform(2) + 1
switch randomNumber
{
case 1:
return twoIdea1TextField.text!
case 2:
return twoIdea2TextField.text!
default:
return ""
}
}
I have no time, but I think that with an enum is simpler than what I did.

Sending value back from detail to master

I am using a master-detail model in Swift.
However, I want to send a class object created in detail view back to master view. I wrote a unwind function in the master view, but I cannot see the back button in the detail view so I cannot ctrl+drag it to the exit.
Does anyone know how to set the back button to make it visible?
Rather than worrying about hooking up something to the back button, you can update the model directly as the user updates the fields in the detail view controller. To do this you can pass a reference to some model object that contains the properties to be updated (make sure that's a reference type, e.g., a class, and not a struct, though).
For example:
class Person {
var firstName: String?
var lastName: String?
}
class MasterViewController: UIViewController {
#IBOutlet weak var firstNameLabel: UILabel!
#IBOutlet weak var lastNameLabel: UILabel!
var person = Person()
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let destination = segue.destinationViewController as? DetailViewController {
destination.person = person
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
firstNameLabel.text = person.firstName
lastNameLabel.text = person.lastName
}
}
class DetailViewController: UIViewController,UITextFieldDelegate {
var person: Person?
#IBOutlet weak var firstNameTextField: UITextField!
#IBOutlet weak var lastNameTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
firstNameTextField.text = person?.firstName
lastNameTextField.text = person?.lastName
}
// Note, I specified the detail view controller to be the delegate
// for the two text fields in IB: I then can detect when editing is
// done and act accordingly.
func textFieldDidEndEditing(textField: UITextField) {
switch textField {
case firstNameTextField:
person?.firstName = textField.text
case lastNameTextField:
person?.lastName = textField.text
default:
assert(false, "unidentified textField \(textField)")
}
}
}
You can have master view controller update itself in viewDidAppear, like I did above, or, better, you could add observers for the model properties. But hopefully it illustrates the basic idea.

IBoutlet and IBAction in a separated class to decompose an UIViewController

In my storyboard I've a ViewController linked with a class named MyViewController.
In this view, there are a lot of #IBOutlet and #IBAction and I would know if it's possible to decompose this ViewController with separated classes which contains #IBOutet and #IBAction.
For example:
class MyViewController
{
#IBOutlet var button1 : UIButton!
#IBOutlet var button2 : UIButton!
...
#IBOutlet var button10 : UIButton!
#IBAction func clickOnButton1(sender : AnyObject!)
{
...
}
#IBAction func clickOnButton2(sender : AnyObject!)
{
...
}
...
#IBAction func clickOnButton10(sender : AnyObject!)
{
...
}
}
Will be...
class MyViewController
{
let buttonsManager = ButtonsManager()
}
class ButtonsManager
{
#IBOutlet var button1 : UIButton!
#IBOutlet var button2 : UIButton!
...
#IBOutlet var button10 : UIButton!
#IBAction func clickOnButton1(sender : AnyObject!)
{
...
}
#IBAction func clickOnButton2(sender : AnyObject!)
{
...
}
...
#IBAction func clickOnButton10(sender : AnyObject!)
{
...
}
}
But the problem is, how can I link buttons IBOutlet and IBAction to ButtonsManager class because the owner of the view inside my Storyboard is MyViewController class?
Thanks!
Disclaimer: I don't know if this is a recommended way of doing this, but I thought it was an interesting question. Either way, I came up with this solution, which at least works. Decide for yourself if it's something you want to do.
First have your ButtonsManager be a subclass of NSObject. That way we can make an instance of ButtonsManager be an IBOutlet itself.
class ButtonsManager: NSObject {
#IBOutlet var button1 : UIButton!
#IBAction func clickOnButton1(sender : AnyObject!) {
print("hello world!")
}
}
I added an outlet for the button, and one action which prints "hello world!". Essentially, you would move all your #IBOutlets and #IBActions into this class.
In your view controller class, add a single #IBOutlet of type ButtonsManager:
class MyViewController {
#IBOutlet var buttonsManager: ButtonsManager?
// rest of class
}
What remains is to hook things up in Interface Builder:
Add a generic "Object" item from the Object Library to the scene.
In the Identity Inspector, set its custom class to "ButtonsManager"
Select the View controller in the scene (e.g. MyViewController) and go to the Connections Inspector.
Make a connection from the buttonsManager Outlet to the ButtonsManager object in the scene
Select the ButtonsManager object again in the scene and go to the Connections Inspector.
You'll find all the outlets and received actions here. Hook them up to your view like you would normally. I.e. make connections by dragging from the outlet/action to the respective view elements.
Compile!
EDIT:
A better solution would probably be to have a reference to the view controller in the ButtonsManager class, rather than the other way around. That would provide much easier access to the view controller class from your IBAction methods. As a bonus, you would have no #IBOutlet keywords in your view controller class.
Remember to change the connections in Interface Builder. That is, remove the connection on MyViewController, and add a connection on ButtonsManager from the myViewController outlet to the MyViewController object in the scene.
class MyViewController {
var testString = "Hello world!"
func testMethod() {
print("Printing some other string here!")
}
// rest of class
}
class ButtonsManager: NSObject {
#IBOutlet var myViewController : MyViewController!
#IBOutlet var button1 : UIButton!
#IBAction func clickOnButton1(sender : AnyObject!) {
print(self.myViewController.testString)
self.myViewController.testMethod()
}
}

Resources