action without button on swift - ios

I’m new at this and I’m wondering if it’s possible in swift or objective-C to make an action without the interaction of a button or any type of user interaction? So far i can send an array to a second ViewController but I need to process the data and show it in a UIPickerview and I don’t know how...
i pass the data with this:
func pepareSegueWithIdentifier(segue:UIStoryboardSeque, senderAnyObject?){
var detailVC = segue.destinationViewController as ViewNotLogged;
detailVC.pickerData1 = pickerData
}
i creater the var in the second viewcontroller
class ViewNotLogged: UIViewcontroller, UIPickerViewDataSource, UIPickerDataDelegate{
var pickerData1=[String]()
}
but after this i dont nowaht to do to modify the data or even check it with println()

For my comment above - you could do something like this:
class ViewNotLogged: UIViewController {
var pickerData1 = [String]()
override func viewDidLoad() {
super.viewDidLoad()
println("My data \(pickerData1)")
}
}
And try not to implements UIPickerViewDataSource, UIPickerViewDelegate before VC don't ready
UPD:
Emulate with my data and it works.
Here my code:
class ViewController: UIViewController {
var pickerData = ["Sw", "Sw"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var detailVC = segue.destinationViewController as ViewNotLogged;
detailVC.pickerData1 = pickerData
}
}
class ViewNotLogged: UIViewController {
var pickerData1 = [String]()
override func viewDidLoad() {
super.viewDidLoad()
println("My data \(pickerData1)")
}
}
In IB I just have two VC first is ViewController and second is ViewNotLogged. Also I have button at first, that make modal storyboard segue.

Related

Delegate between TableViewController and ViewController

I'm in my first week of developing in iOS and have become stuck on an issue with passing data between view controllers. My set up consists of a view with VC having a button in it and also a container view (no associated view controller). The container view has an embedded Segue to a TableView with TableViewController. The table has 6 rows and each row has a stepper that can change the value of a text view on the associated row. What I would like to do is collect the values of all the textviews when I press the button on the main view.
I am trying to use delegate to do this but when I press the button the returned value is always nil. I believe the problem is to do with the fact the VC is not being passed to the table view controller via the prepareForSegue function but I'm not sure why? Could be to do with the load order of the controllers?
import UIKit
class PredictionViewController: UIViewController, PredictionDelegate {
var predictionData: String!
#IBOutlet weak var messageTextBox: UITextView!
#IBOutlet weak var predictionSubmitButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.messageTextBox.isEditable = false
}
override func viewDidAppear(_ animated: Bool) {
}
func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "predictionSegue") {
// pass data to next view
let vc = segue.destination as! PredictionsTableViewController
vc.predictionHomeDelegate = self
}
}
func receiveData(with data: String) {
predictionData = data
print(predictionData)
}
#IBAction func predictionSubmitButtonAction(_ sender: UIButton) {
print(predictionData)
}
}
TableViewController: (stripped to minimum)
import UIKit
protocol PredictionDelegate: class {
func receiveData(with data: String)
}
class PredictionsTableViewController: UITableViewController, PredictionDelegate {
weak var predictionHomeDelegate: PredictionDelegate?
#IBOutlet weak var homeTeamScore1: UITextView!
#IBOutlet weak var homeTeamStepper1: UIStepper!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
}
func getPredictionList() {
//does some stuff
self.passDataBackwards()
}
func receiveData(with data: String) {}
func passDataBackwards() {
let data = "{\"score\":\"1\"}"
predictionHomeDelegate?.receiveData(with: data)
}
#IBAction func homeTeamStepper1Action(_ sender: UIStepper) {
let score = Int(sender.value).description
homeTeamScore1.text = score
self.passDataBackwards()
}
}
Any help gratefully received!
Edit:
After comments...
You have the wrong idea about Protocols and Delegates. They are not needed here.
Instead, in your "home" VC, get a reference to the embedded VC. Then, add a function in your embedded VC that you can call to get its data.
// "home" view controller
class PredictionViewController: UIViewController {
// this will be a reference to the embedded view controller
var embeddedVC: PredictionsTableViewController?
#IBAction func getDataButtonTapped(_ sender: Any) {
if let tableData = embeddedVC?.getMyData() {
print("Result: \(tableData)")
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "predictionSegue") {
if let vc = segue.destination as? PredictionsTableViewController {
// get a reference to the embedded VC
self.embeddedVC = vc
}
}
}
}
// embedded view controller
class PredictionsTableViewController: UIViewController {
var numTaps = 0
func getMyData() -> String {
return "\(numTaps)"
}
#IBAction func didTap(_ sender: Any) {
numTaps += 1
}
}
You're close, but a couple mistakes...
Here is a very, very simple example. View controller with container, which has a view controller with a button.
Code:
import UIKit
// your protocol
protocol PredictionDelegate: class {
func receiveData(with data: String)
}
// embedded view controller
// NOTE: this should NOT include PredictionDelegate
class PredictionsTableViewController: UIViewController {
weak var predictionHomeDelegate: PredictionDelegate?
#IBAction func didTap(_ sender: Any) {
// on button tap, "send data back"
predictionHomeDelegate?.receiveData(with: "Test")
}
}
// "home" view controller
// NOTE: this DOES include PredictionDelegate
class PredictionViewController: UIViewController, PredictionDelegate {
func receiveData(with data: String) {
print(data)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "predictionSegue") {
if let vc = segue.destination as? PredictionsTableViewController {
// set self as the delegate of the embedded PredictionsTableViewController
vc.predictionHomeDelegate = self
}
}
}
}
Notes:
Do NOT include func receiveData(with data: String) {} in your embedded view controller
Do NOT assign PredictionDelegate to your embedded view controller
You need to hook your segue to the vc itself instead of the cell and use
self.performSegue(withIdentifier: "predictionSegue", sender: nil)
Since these are two separate screens I am not too sure it makes too much sense to have the button on the first view controller that submits data from the second, can the button not just be on the second. If for whatever reason it can't you could pass the data back to the first view controller on segueing back by adding a public variable to the first view controller and adding another prepare method to the second to pass the data back like you have done when adding the delegate.

Access the same variable before and after value changes in different classes - Swift

I've stucked on a simple concept(I guess), basically I have two ViewControllers on Storyboard also I have 2 classes, ViewController and ViewController2:
I Have a Label whit a default value (0), and when I click on button I want to change the value for this variable to 10, and then I click on the button "Show" and I print this variable, I'm successfully changing the Label and printing the new Value.
The real problem is when I want to get the new variable value from another view, even after I change the value if I try to print the variable on second view the variable always return de default value(0)
ViewController
import UIKit
class ViewController: UIViewController {
var variable = "0"
#IBOutlet var defaultLabel: UILabel!
#IBOutlet var label1Label: UILabel!
#IBAction func setValue(sender: AnyObject) {
setValue()
}
#IBAction func getValue(sender: AnyObject) {
getValue()
}
override func viewDidLoad() {
super.viewDidLoad()
}
func setValue(){
variable = "10"
defaultLabel.text = variable
}
func getValue(){
print(variable)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
ViewController2
import UIKit
class ViewController2: UIViewController {
#IBOutlet var label2Label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func show(sender: AnyObject) {
print(ViewController().getValue())
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
I've found this post:
Access variable in different class - Swift
And I really think this is the way that I will find my solution but I really don't understand how to call the variable on ViewController2.
Thanks.
#IBAction func show(sender: AnyObject) {
print(ViewController().getValue())
}
ViewController() - this is class constructor and each time you call ViewController() it return a new instance/object of ViewController class, with default values of course.
If you show ViewController2 from ViewController you can create a property/variable variable2 like variable in ViewController and set value before display, but after ViewController2 is created. If you use segues you can put this code in ViewController class:
// Put this code in ViewController class
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let viewController2 = segue.destinationViewController as? ViewController2
if viewController2 != nil {
// you can't set the value for label at this time
// because the viewcontroller and all its UI controls aren't loaded
// but you can set a non UI variable
viewController2?.variable2 = self.variable
}
}
After that you can put one line of code in viewDidLoad method from ViewController2 class:
// Put this code in ViewController2 class
var variable2 = "0"
#IBOutlet var label2Label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
self.label2Label.text = variable2
}
Use Delegates!
Here's an example where ViewController1 is the delegate for ViewController2:
Define a protocol:
protocol VariableManager {
func getValue() -> Int
}
Then, in ViewController1, modify the getValue method so that ViewController1 conforms to the protocol:
class ViewController1: VariableManager {
func getValue() -> String {
return variable
}
}
Now define a variable in ViewController2 named delegate:
class ViewController2 {
var delegate: VariableManager?
}
In your prepareForSegue method in ViewController1 :
override func prepareForSegue(segue: UIStoryboardSegue) {
if let identifier = segue.identifier {
switch identifier {
case "MySegueIdentifier":
let destination = segue.destinationViewController as! 'ViewController2'
destination.delegate = self
default:
break
}
}
}
Now in ViewController2, change the show method:
#IBAction func show(sender: AnyObject) {
if let delegate = delegate {
let variable = delegate.getValue()
print(variable)
}
Delegation is a very common, and very important pattern. I suggest you read up on it: https://developer.apple.com/library/ios/documentation/General/Conceptual/DevPedia-CocoaCore/Delegation.html
Trying to instantiate another instant of ViewController1 inside ViewController2 is not good practice.

How to pass a data to a nested controller (UIContainerView)?

I've searched everywhere for a solution but nothing. I have two view controllers and I want to pass data from viewController.swift to resultViewController.swift (the container view), i've succeeded to send data when I run it. but when i clicked increase button I can't send data again.
ViewController.swift:
class ViewController: UIViewController {
var result: Int = 1
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// performSegueWithIdentifier("sendResult", sender: <#AnyObject?#>)
}
#IBAction func increas(sender: AnyObject) {
result++
performSegueWithIdentifier("sendResult", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "sendResult"{
var resultVC = segue.destinationViewController as! ResultViewController
resultVC.result = self.result
}
}
}
ResultViewController.swift:
class ResultViewController: UIViewController {
var result: Int!
#IBOutlet weak var resultLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
resultLabel.text = "\(result)"
}
}
Ok. Here's some working code.
First, remove your increase method and unhook it from your button in the Connections Inspector. Then delete your old segue in Interface Builder and create a new segue by dragging from the button to ResultsViewController. Make sure you give the segue a "sendResult" identifier in Interface Builder. In ViewController:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "sendResult" {
result++
var rvc = segue.destinationViewController as! ResultViewController
rvc.result = result
}
}
And then in ResultsViewController:
#IBOutlet weak var resultLabel: UILabel!
var result : Int = 0
override func viewDidLoad() {
super.viewDidLoad()
resultLabel.text = "\(result)"
// Do any additional setup after loading the view.
}
Nested ViewControllers are a bit tricky to use because from the parent controller point of view, all they see is a UIView (there is no rootViewController or nestedViewController property, which would be very nice to have).
The best way I have found to send/read data to/from the nested controller is by adding a reference to it on the parent controller.
It turns out that prepareForSegue is called after viewDidLoad for all nested view controllers. Then it is never called again, unless you call it manually. But you shouldn't and will not need to do that.
Here is how you can get a reference to the nested controller to use later in your code:
class ViewController: UIViewController {
var result: Int = 1
private var resultVC: ResultViewController? // Keep it optional
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func increas(sender: AnyObject) {
result++
// Update result value, keep optional reference for security
// Here you could write & read values, call methods, etc.
resultVC?.result = result
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "sendResult" {
// Save reference to child view controller (use optional)
resultVC = segue.destinationViewController as? ResultViewController
}
}
}
Once you have the reference to the nested view controller, you can use it anywhere in the code. To be safe, always use it with ?.

Passing date back from view controller

I've been following this thread at the end the code doesn't show any errors but when I run it on my phone, the application crashes and I get a signal SIGABRT error, what could be causing it? This is my code:
FirstViewController.swift :
class FirstViewController: UIViewController, UITextFieldDelegate, setDateValueDelegate{
func setDate(value: String) {
self.receivedDate = value
}
#IBOutlet weak var dateButton: UIButton!
var receivedDate:String = ""
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var secondVC = (segue.destinationViewController.visibleViewController as SecondViewController)
secondVC.delegate = self
}
SecondViewController.swift:
protocol setDateValueDelegate {
func setDate(value: String)
}
class SecondViewController: UIViewController {
var delegate: setDateValueDelegate?
#IBOutlet weak var datePicker: UIDatePicker!
var strDate:String = ""
func datePickerChanged(datePicker:UIDatePicker) {
var dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = NSDateFormatterStyle.ShortStyle
dateFormatter.timeStyle = NSDateFormatterStyle.ShortStyle
strDate = dateFormatter.stringFromDate(datePicker.date)
dateLabel.text = strDate
delegate?.setDate(strDate)
}
Any help is appreciated! :)
Since it's not actually "visible" yet, your segue.destinationViewController.visibleViewController line doesn't work to get an instance of the soon-to-be-active SecondViewController instance; and as such, I believe the problem is that you never actually set the delegate.
Try updating your prepareForSegue method by removing the visibleViewController property and simply get the segue's destinationViewController, like so:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var secondVC = segue.destinationViewController as SecondViewController
secondVC.delegate = self
}
Edit: Also try setting your delegate's protocol at the top of SecondViewController if you haven't already:
// set up the setDateValueDelegate protocol with the set date function
protocol setDateValueDelegate {
func setDate(value: String)
}
class SecondViewController: UIViewController {
var delegate: setDateValueDelegate?
So, Im going to answer my own question and at the same time answer it to any other person who want to pass data from ViewController B to ViewController A. I will try to describe every detail for making it more simple step by step.
In your storyboard, give an identifier to your segue, this is done by clicking on the segue and going to the Attributes Inspector.
Create a file called "protocol.swift" with the following code inside.
protocol.swift :
protocol setDateValueDelegate {
func setDate(toValue: String)
}
The "setDateValueDelegate" and "setDate" can be replaced to whatever you want but they must be consistent within the entire code.
In your FirstViewController class, implement the protocol.
FirstViewController.swift:
class FirstViewController: UIViewController, setDateValueDelegate {
(The protocols name, in my case "setDateValueDelegate" must be the same as the one in protocol.swift)
Add the function to your first view controller.
func setDate(toValue:String) { //setDate must be replaced to the name of your function in the protocol.swift file.
var date = toValue // The value that is being received from the SecondViewController.
}
Add prepareForSegue function to your FirstViewController:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "DueDate") {
var secondVC: SecondViewController = (segue.destinationViewController as SecondViewController)
secondVC.delegate = self
}
(SecondViewController in "var secondVC: SecondViewController" and in "as SecondViewController" must be replaced to your Second View Controller's name.
Add the delegate in SecondViewController
var delegate:setDateValueDelegate?
/* As before setDateValueDelegate must be replaced to the name that
you gave to your delegate */
In your Second View Controller's ViewDidLoad call the delegate:
delegate?.setDate(/* The value that you want to send to the FirstViewController*/)
/* setDate must be changed to the function's name in protocol.swift */
Hope this helped to you all! :)
I did it like this
FirstViewController:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
var SecondVC: SecondViewController = segue.destinationViewController as SecondViewController
if segue.identifier == "Player2Button"{
SecondVC.NumberOfPlayers = 2
}else{
SecondVC.NumberOfPlayers = 1
}
}
SecondViewController
Just use NumberOfPlayers to do whatever you want. Hope this helps

Sending data with Segue with Swift

I have two view controllers and two views.
In my first view, I set the variable 'currentUser' to false.
I need to be able to set 'currentUser' to true in the second view controller.
When trying to reference 'currentUser' from the second view it's not picking it up as 'currentUser' is defined in the first view controller.
How do I carry across variables with segue?
Set values from Any ViewController to a Second One using segues
Like this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "yourIdentifierInStoryboard") {
let yourNextViewController = (segue.destinationViewController as yourNextViewControllerClass)
yourNextViewController.value = yourValue
And in your yourNextViewController class.
class yourNextViewControllerClass {
var value:Int! // or whatever
You can call this also programmatically:
self.performSegueWithIdentifier("yourIdentifierInStoryboard", sender: self)
Set values from your DestinationViewController back to your Primary (First) ViewController
1. Implement a protocol, for example create a file called protocol.swift.
protocol changeUserValueDelegate {
func changeUser(toValue:Bool)
}
2. set the delegate on your second View
class yourNextViewControllerClass {
var delegate:changeUserValueDelegate?
3. set the delegate on load (prepareForSegue)
if(segue.identifier == "yourIdentifierInStoryboard") {
var yourNextViewController = (segue.destinationViewController as yourNextViewControllerClass)
yourNextViewController.delegate = self
4. add Function to FirstViewController
func changeUser(toValue:Bool) {
self.currentUserValue = toValue
}
5. call this function from your SecondViewController
delegate?.changeUser(true)
6. Set the delegate in your FirstViewController
class FirstViewController: UIViewController, ChangeUserValueDelegate {
The problem here is that your currentUser variable is of type Bool, which is a value type. So passing it from your first view controller to your second view controller will in fact create a new Bool instance. What you need is to pass a reference from your first view controller to your second view controller (see Value and Reference Types for more details on value and reference with Swift).
Thereby, according to your needs/preferences, you may choose one of the three following examples.
1. The boxing style
Here, we "box" our Bool inside a class and pass a reference of that class instance to the second view controller.
1.1. Create a CurrentUser class:
class CurrentUser {
var someBooleanValue = true {
didSet {
print(someBooleanValue)
}
}
}
1.2. Create a UIViewController subclass for the first view controller:
import UIKit
class ViewController1: UIViewController {
let currentUser = CurrentUser()
override func viewDidLoad() {
super.viewDidLoad()
currentUser.someBooleanValue = false
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let viewController2 = segue.destinationViewController as? ViewController2 {
viewController2.currentUser = currentUser
}
}
}
1.3. Create a UIViewController subclass for the second view controller:
import UIKit
class ViewController2: UIViewController {
var currentUser: CurrentUser?
// Link this IBAction to a UIButton or a UIBarButtonItem in the Storyboard
#IBAction func toggleBoolean(sender: AnyObject) {
if let currentUser = currentUser {
currentUser.someBooleanValue = !currentUser.someBooleanValue
}
}
}
2. The closure style
Here, we get a weak reference of our first view controller in a closure and pass this closure to the second view controller.
2.1. Create a UIViewController subclass for the first view controller:
import UIKit
class ViewController1: UIViewController {
var currentUser = true {
didSet {
print(currentUser)
}
}
override func viewDidLoad() {
super.viewDidLoad()
currentUser = false
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let viewController2 = segue.destinationViewController as? ViewController2 {
let closureToPerform = { [weak self] in
if let strongSelf = self {
strongSelf.currentUser = !strongSelf.currentUser
}
}
viewController2.closureToPerform = closureToPerform
}
}
}
2.2. Create a UIViewController subclass for the second view controller:
import UIKit
class ViewController2: UIViewController {
var closureToPerform: (() -> Void)?
// Link this IBAction to a UIButton or a UIBarButtonItem in the Storyboard
#IBAction func toggleBoolean(sender: AnyObject) {
closureToPerform?()
}
}
3. The protocol-delegate style
Here, we make our first view controller conform to some protocol and pass a weak reference of it to the second view controller.
3.1. Create a custom protocol:
protocol MyDelegate: class {
func changeValue()
}
3.2. Create a UIViewController subclass for the first view controller and make it conform to the previous protocol:
import UIKit
class ViewController1: UIViewController, MyDelegate {
var currentUser = true {
didSet {
print(currentUser)
}
}
override func viewDidLoad() {
super.viewDidLoad()
currentUser = false
}
func changeValue() {
currentUser = !currentUser
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let viewController2 = segue.destinationViewController as? ViewController2 {
viewController2.delegate = self
}
}
}
3.3. Create a UIViewController subclass for the second view controller:
import UIKit
class ViewController2: UIViewController {
weak var delegate: MyDelegate?
// Link this IBAction to a UIButton or a UIBarButtonItem in the Storyboard
#IBAction func toggleBoolean(sender: AnyObject) {
delegate?.changeValue()
}
}
Add an attribute currentUserSecondVC in the destination view controller, and use prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Name Of Your Segue" {
var vc = segue.destinationViewController as NameOfTheSecondViewController
vc.currentUserSecondVC = !currentUser //you can do whatever you want with it in the 2nd VC
}
}
The function that should be defined as override is:
open func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "Segue Name Defined In Storyboard") {
//set the property of the designated view controller with the value you need
}
}
Since you're using same variable across the two Viewcontrollers, namely currentUser (type Bool).
So its better to make it a global variable in both classes.
When coming to global variable concept in swift.
Everything by default in swift is public, and thus if you declare something like this:
class FirstViewController: UIViewController {
var someVariable: Boll = YES
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
}
You can access it and set values as long as you have an instance of it:
var MySecondViewController: FirstViewController = FirstViewController(nibName: nil, bundle: nil)
var getThatValue = MySecondViewController.someVariable

Resources