I have been looking all over for an answer for this...I do not know how to pass data through an unwind segue. I know I am supposed to use prepareForSegue but I am not sure what I am supposed to put in. I am trying to pass a variable (myLabel) from a UIPickerView in one class:
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
myLabel.text = pickerData[row]
}
to another class. I have the unwind segue code already but do not know where to put the prepareForSegue and what to put in it.
Let me know if my question needs more clarification.
If I understand your question correctly, you should do something like this:
class MyPickerController : UIViewController {
#IBOutlet weak var myLabel: UILabel!
var pickerData:[String]
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
myLabel.text = pickerData[row]
performSegueWithIdentifier( "mySegueName", sender: self )
}
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let myController = segue.destinationController as? MyDestinationViewController {
myController.text = myLabel.text
}
}
That will pass the label text to the MyDestinationViewController instance.
Note that I don't pass the UILabel object, but the text.
You can not place that UILabel object in the destination controller interface, but you can use the text an set another label with it, for example.
prepareForSegue is called in the class of the view controller that you will be unwinding from (the view controller that was previously pushed or presented, and is now being popped or dismissed). The destination view controller of the segue is the view controller that will appear as a result.
Related
I have a picker view that needs to be presented when UIButton is pressed. The text of the UIButton will change to the text of selected row. But how do I initiate the picker view for a button?
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
override func viewDidLoad() {
super.viewDidLoad()
picker.delegate = self
picker.dataSource = self
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return CountryData.count
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
CountryButton.setTitle(CountryData[row], for: .normal)
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return CountryData[row]
}
#IBAction func Country(_ sender: AnyObject) {
CountryButton.inputView == picker
}
This won't work
Note: when you set the action method, be sure to set the sender to the type of control that's invoking the action, the UIButton or the UIPickerView or whatever. Don't leave it as AnyObject.
It's not completely clear to me what you're trying to do. You say that the picker view needs to change when you press a button, then you say that the text of the button needs to change to the row selected in the picker view.
Are you trying to get the text of the button to change when you select something in the picker view? Or are you trying to instantiate a new picker view when you press the button?
If you're trying to change the text of the button when you select a row in the picker view, then you want to add an onChanged() action method for the picker view and change the button's text in that method.
#IBAction func pickerChanged(_ sender: UIPickerView) {
button.setTitle("New text", for: UIControlState)
}
An easy way to present a new picker view when a button is pressed is to put the picker view in a stack view and set its hide property. Then the button's onPress() method can unhide the picker view in the stack view.
#IBAction func onPress(_ sender: UIButton) {
if weWannaShowthePicker {
pickerStack.arrangedSubviews[pickerPosition].isHidden = false
} else { // hide it
pickerStack.arrangedSubviews[pickerPosition].isHidden = true
}
}
I cannot seem to find the connection to have an external class manage a view in a ViewController. I'm new to iOS and have spent considerable looking for a solution. Simple Example:
Subclass of UIPickerView
I create a file that is a subclass of UIPickerView and have it conform to the PickerView delegate and datasource.
class MyPickerView: UIPickerView, UIPickerViewDelegate, UIPickerViewDataSource {
//In here I conform to all the required methods...no problems there
}
Main View Controller with Outlet for the PickerView
In my MainViewController, I create an outlet for my picker view. Also, in the StoryBoard I hookup the "custom class" for my Picker View to MyPickerView above.
class MainViewController: UIViewController {
#IBOutlet weak var myPickerView: UIPickerView!
override func viewDidLoad() {
//how do I hookup my picker view class
}
}
My Questions:
How do I tell my MainViewController that my subclass "MyPickerView" is managing the picker view for it?
How did I enable communication between the subclass and the view controller?
---------------------
UPDATE: FINAL SOLUTION Incorporating #Oscar's Answer
#Oscar's suggestion below was great. To clarify, I wanted my PickerView subclass to be the UIPickerView Delegate because the Picker will always have the same UI and there are many PickerView delegate methods for UI. (attributedTitleForRow, widthForComponent, rowHeightForComponent, etc) I don't want to call those delegate methods in every ViewController that uses this PickerView.
Now, when the PickerView "didSelectRow" is called, we need to notify our ViewController and pass the value that was selected. To get this to work, I used a Protocol. (summarized below) This topic took me a while to learn, but is crucial, so I suggest spending time with Protocols & Delegation if this doesn't make sense.
Create a protocol in the PickerView with a func that will be used to talk to ViewControllers that present this PickerView:
protocol MyPickerViewProtocol {
func myPickerDidSelectRow(selectedRowValue:Int?)
}
In the ViewController presenting the PickerView, conform to your PickerView protocol. By doing so, you will have to place the func myPickerDidSelectRow somewhere in your ViewController:
class MyViewController: MyPickerViewProtocol {
func myPickerDidSelectRow(selectedRowValue:Int?) {
//do stuff to update your ViewController
}
}
#Oscar's answer below will hookup the picker view to your view controller, but there's one last thing. In order for the PickerView to talk back, you will want a property in your PickerView, that is a reference to the view controller it's contained in. Here's the PickeView and ViewController classes in perspective:
//PickerView Subclass ------------------
protocol MyPickerViewProtocol {
func myPickerDidSelectRow(selectedRowValue:Int?)
}
class MyPickerView: UIPickerView {
//Note: this var is of type your Picker protocol above. Because the ViewController will conform to the protocol, this var will be the reference (or the pointer) to the protocol func you implement in your ViewController...which is myPickerDidSelectRow
var propertyThatReferencesThisViewController:MyPickerViewProtocol?
}
//ViewController Class ----------------
myPicker = MyPickerView()
myPickerView.dataSource = myPicker //note: myPickerView is the outlet of type UIPickerView in your ViewController
myPickerView.delegate = myPicker
//HERE'S THE PROPERTY from our PickerView subclass that will point to this ViewController's protocol methods that we implemented. From the MyPickerViewProtocol
myPicker.propertyThatReferencesThisViewController = self
Now when a row is selected in our PickerView, let's tell the ViewController using our property: propertyThatReferencesThisViewController
class MyPickerView: UIPickerView {
//This Property points to the ViewController conforming to the protocol. This property will only be able to access the stuff you put in the protocol. It won't access everything in your ViewController
var propertyThatReferencesThisViewController:MyPickerViewProtocol?
//didSelectRow UIPickerView Delegate method that apple gives us
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
//get your picker values that you need
let theRowValue = someArray[row]
propertyThatReferencesThisViewController?.myPickerDidSelectRow(theRowValue)
//the ViewController func will be called passing the row value along
}
}
Subclass Pickerview
class MyPickerView: UIPickerView, UIPickerViewDataSource, UIPickerViewDelegate {
var oficinas = ["oficina 1", "Oficinas 2", "Oficina 3"]
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return oficinas.count
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return oficinas[row]
}
}
Main View Controller with Outlet for the PickerView
class MainViewController: UIViewController {
#IBOutlet weak var myPickerView: UIPickerView!
var pickerOficinas: MyPickerView!
override func viewDidLoad() {
//how do I hookup my picker view class
pickerOficinas = MyPickerView()
myPickerView.delegate = pickerOficinas
myPickerView.dataSource = pickerOficinas
}
}
I think you may have got hold of the wrong end of the stick!
Why do you want to make the picker its own delegate? The point of having a delegate is that it can tell its delegate what has been selected etc.
I would think that what you should be doing is making your view controller conform to UIPickerViewDelegate and make it the delegate of the picker and put the logic for whatever you want to happen when an item is picked in those delegate methods. I can't see any other way of 'telling' your view controller about the picker.
Also, if you reference to your picker is weak, then unless you are holding a strong reference to it somewhere else, at all times (eg it is part of the view hierarchy) it will be deallocated.
I am currently working on a small project and i have a viewController that has 4 textFields which 3 work ok. They take String objects. However, the 4th textField is supposed to bring up a UIPickerView with 4 selectable items.
So far this is what i have in my controller that implements this:
#IBOutlet var pickerTextfield: UITextField!
#IBOutlet var itemPicker: UIPickerView! = UIPickerView()
The pickerTextfield is the UITextField object that is the 4th field.
The itemPicker is an unlinked UIPickerView that i want to create programatically.
Right below these properties, i have an array of items for the UIPickerView object:
var seasonalItems = ["Spring", "Summer", "Fall", "Winter"]
In my viewDidLoad method i have this as follow:
itemPicker.hidden = true;
pickerTextfield.text = seasonalItems[0]
pickerTextfield.delegate = self
And the rest of the implementation:
// Below these lines is the implementation of the Picker
func numberOfComponentsInPickerView(pickerView: UIPickerView!) -> Int{
return 1
}
// returns the # of rows in each component..
func pickerView(pickerView: UIPickerView!, numberOfRowsInComponent component: Int) -> Int{
return seasonalItems.count
}
func pickerView(pickerView: UIPickerView!, titleForRow row: Int, forComponent component: Int) -> String! {
return seasonalItems[row]
}
func pickerView(pickerView: UIPickerView!, didSelectRow row: Int, inComponent component: Int)
{
pickerTextfield.text = seasonalItems[row]
itemPicker.hidden = true;
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
itemPicker.hidden = false
return false
}
So the end result from this is when i tap the pickerTextfield object in the app, it shows the first item of the array (Spring) but in text within the UITextField object but it does not show the UIPickerView object with the other selectable items where i could select one and then hide it when selected.
My question is, where or what am i doing wrong here? i been trying to figure this out on my own but i do not seem to get good clear examples with Swift and storyboards. I much rather not drag a UIPickerView in the storyboard but rather the way i attempted to implement. Thanks
You can give UIPickerView as inputView for your TextField in which you want to show picker view.
You also do not need to initially hide picker view in this case.
pickerTextfield.inputView = itemPicker
When you use UIPickerView as inputView of any UITextField then when you tap on the TextField instead of default keypad PickerView will show.
I'm new to Swift and Xcode in general. I'm trying to make an app similar in essence to Twitter. My issue is that after my Table View Controller for my timeline I have two pages/View Controllers: one with a picker to choose a specific house and the next to make a text post.
On the Second View Controller's code with the text, I try to save an object with the post's content and the selected house to Parse. The content is saved just fine. But I get errors when I try to do the same to the house (because it's from a different page)
I'm positive this is a very simple, obvious thing I am missing....
Thanks!
Edit: I'm doing anonymous users btw if that's relevant
code for the picker from the first view controller
#IBOutlet weak var label: UILabel!
#IBOutlet weak var picker: UIPickerView!
let pickerData = ["Adams","Cabot","Currier","Dunster","Eliot","Kirkland","Leverett","Lowell","Mather","Pforzheimer","Quincy","Winthrop"]
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView,
numberOfRowsInComponent component: Int) -> Int {
return pickerData.count
}
func pickerView(pickerView: UIPickerView,
titleForRow row: Int,
forComponent component: Int) -> String!{
return pickerData[row]
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int){
self.label.text = pickerData[row]
}
override func viewDidLoad() {
super.viewDidLoad()
// label is "Adams" when first loaded
label.text = pickerData[0]
picker = UIPickerView()
}
code to from the second view control to Save it to Parse
#IBAction func PostText(sender: AnyObject) {
// establish object to save in Parse CHANGE USER TO HOUSE
var post:PFObject = PFObject(className: "Posts")
post["content"] = PostTextView.text
post["user"] = PFUser.currentUser()
post["house"] = pickerData[row].text
post.saveInBackgroundWithTarget(nil , selector: nil)
// return to navigation screen
self.navigationController?.popToRootViewControllerAnimated(true)
}
error is unresolved identifier (because they're defined in another view controller?)
Neither pickerData nor row belong to Second View Controller (i.e. they're unresolved identifiers) so you have to get an instance of First View Controller to access the pickerData variable and to find picker's currently selected "row."
As long as you're using a UINavigationController you can find any previous view controller in the stack. If FirstViewController is immediately preceding the current SecondViewController, you can find it one index behind on the navigation stack, ex:
let navigationController: UINavigationController = self.navigationController!
let numberOfViewControllers = navigationController.viewControllers.count
var firstViewController: FirstViewController = navigationController.viewControllers[numberOfViewControllers - 2] as FirstViewController
let row = firstViewController.picker.selectedRowInComponent(0)
post["house"] = firstViewController.pickerData[row]
But generally speaking, it's probably cleaner to send your view controller object forward instead of pulling it from the nav stack.
If you're using a Storyboard, for example, you can update the prepareToSegue method in your first view controller to share your current FirstViewController instance with your second view controller:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// (Uncomment the if block and specify a segue if this isn't the
// segue leading from your view controller.)
//if segue.identifier == "yourSegueName" {
let secondViewController = segue.destinationViewController as SecondViewController
secondViewController.firstViewController = self;
//}
}
Then in your second view controller, add a corresponding class variable to hold the first view controller object:
class SecondViewController: UIViewController {
var firstViewController: FirstViewController!
That way you can access its values within FirstViewController like so:
let row = firstViewController.picker.selectedRowInComponent(0)
post["house"] = firstViewController.pickerData[row]
If you're using a UINavigationController without a storyboard, you can similarly set the SecondViewController's FirstViewController variable upon the SecondViewController's creation, ex:
SecondViewController *secondViewController = [[SecondViewController alloc] init];
secondViewController.firstViewController = self;
[self.navigationController pushViewController:secondViewController animated:YES];
I'm new in swift and programming. I have a UIPickerView and UIbutton(by default button is invisible ) in my storyBoard and i want to show myButton when I change row in a UIPickerView (PickerView.selectedRowInComponent(0) == 1) and when press MyButton do something. Here is my code: MyButton and PickerView.
How can i show myButton?
#IBAction func myButton(sender: UIButton) {
println("Button was clicked", sender)
//here i will do something
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if PIckerView.selectedRowInComponent(0) == 1 {
//how to set UIButton property visible, here ???
}
Try this:
myButton.hidden = false
To get access to your button in code you need to create an IBOutlet: In InterfaceBuilder ctrl-drag from the button to your class file and in the popup menu choose outlet. Give that outlet a descriptive name i.e. myButton. Also give your #IBAction a descriptive name, not myButton
So your class will look similar like this:
#IBOutlet weak var myButton: UIButton!
#IBAction func myButtonClick(sender: UIButton) {
println("Button was clicked", sender)
// here i will do something
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if PIckerView.selectedRowInComponent(0) == 1 {
myButton.hidden = false
}
}
And don't forget to assign your custom class in InterfaceBuilder accordingly.