i edited my question , because set textfield maybe can't be simple, need references so this is my code, but still have issue :
this code for TableViewController :
import UIKit
protocol PaymentSelectionDelegate{
func userDidSelectPayment(title: NSString?)
}
class PopPaymentTableViewController: UITableViewController {
var delegate : PaymentSelectionDelegate!
override func viewDidLoad() {
super.viewDidLoad()
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath?) {
self.dismissViewControllerAnimated(true){
if self.delegate != nil{
if let ip = indexPath{
var payItem : PayMethod
payItem = self.myList[ip.row] as! PayMethod
var title = payItem.paymentName
self.delegate.userDidSelectPayment(title)
}
}
}
}
}
and for code TransactionViewController :
import UIKit
class TransactionViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, PaymentSelectionDelegate, UITextFieldDelegate, UIPopoverPresentationControllerDelegate{
#IBOutlet var paymentTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
func resign(){
paymentTextField.resignFirstResponder()
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
if (textField == paymentTextField){
resign()
performSegueWithIdentifier("seguePayment", sender: self)
}
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return .None
}
func userDidSelectPayment(title: NSString?) {
paymentTextField.text = title as! String
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "seguePayment"{
var VC = segue.destinationViewController as! UINavigationController
var controller = VC.popoverPresentationController
if controller != nil{
controller?.delegate = self
var paymentVC = PopPaymentTableViewController()
paymentVC.delegate = self
}
}
}
}
this issue is: variable delegate in TableViewController like seems always nil, so cant set value
NB : sorry i edited totally my question, because so many answer say cant be set textfield just like that
The context of where your TransactionViewController is, is not completely clear, but you are instantiating a new ViewController. If you want to refer to an existing ViewController, this is not the instance you are using here. If you want to create a new one and show it after your didSelectRowAtIndexPath, you have to make sure, that the TextField is instantiated in the init-Method of your ViewController. As you are not instantiating it from a Storyboard, it seems that your TransactionViewController is created programmatically. Probably you are setting the TextField only in viewDidLoad or something else after the init().
You are trying to set text to paymentTextField which is still no initialized.
For this you have to set text to paymentTextField in viewDidLoad method in TransactionViewController.
remove transVC.paymentTextField.text = title from didSelectRowAtIndexPath
Add it in TransactionViewController's viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
self.paymentTextField.text = self.payName
}
It is not correct way to do that. You have not initialised text field and trying to set it's text. First initialise text field:
transVC.paymentTextField = UITextField()
Then try to do something.
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I know this question has been asked so many many times. But here the scenario is changed. I am using MMParallaxView and that looks pretty awesome.
But Now at this point, I want to communicate between these two View controllers. Let me tell you that this github code helps us to make a view like ios Map App. I mean you can have Map (as 1st view controller) and you can have a list of places view controller on top of 1st View Controller. Just same like Map app.
Now I want to click on the UITableView cell and want to navigate on map. For this I know how to catch delegated method. And I am successfully getting of taps on the UItableView cell. But How to send that clicked item data to 1stView Controller so that It can show or mark selected area on Map.
I know this can also be done. But how?
To modify the example app for MMParallaxView...
In ChildBottomViewController.swift
Add this at the top (outside of the class):
protocol CellTapDelegate {
func didSelectRow(indexPath: IndexPath)
}
Add this line at the beginning of the class:
var cellTapDelegate: CellTapDelegate?
Change didSelectRowAt function to:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
DispatchQueue.main.async { [weak self] in
self?.cellTapDelegate?.didSelectRow(indexPath: indexPath)
}
}
In MapViewController.swift
Change the class declaration to:
class MapViewController: UIViewController, CellTapDelegate {
and add this function:
#objc func didSelectRow(indexPath: IndexPath) {
print("Delegate got didSelectRow for: \(indexPath)")
}
Then, in SecondViewController.swift
Add this at the end of viewDidLoad():
var mapVC: MapViewController?
var bottomVC: ChildBottomViewController?
for vc in self.childViewControllers {
if vc is MapViewController {
mapVC = vc as? MapViewController
} else if vc is ChildBottomViewController {
bottomVC = vc as? ChildBottomViewController
}
}
// if we found valid child view controllers, set the delegate
if mapVC != nil && bottomVC != nil {
bottomVC?.cellTapDelegate = mapVC
}
Now, selecting a row from the "slide up from bottom" table view will send the selected indexPath to its delegate - the map view controller.
protocol CellTapDelegate {
func didTapOnItem(obj: MyObject)
}
class MyViewControllerContainingTheTableView : UIViewcontroller {
weak var delegate: CellTapDelegate?
func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath) {
let item = arrayOfObjects[indexpath.row]
self.delegate.didTapOnItem(item)
}
}
//View Controller where you you will have the listner
class FirstViewController : UIViewController {
func setupParalax() {
// you have to modify this according to your need
let vc = MyViewControllerContainingTheTableView()
vc.delegate = self
}
}
extension FirstViewController: CellTapDelegate {
func didTapOnItem(obj: MyObject) {
// you have your required object here
}
}
This should be possible by implementing your own delegate, but my knowledge of MMParrallaxView is that there is only 1 view controller which displays two views meaning the view controller should be able to pass things between the two views as it is. In this case the view controller would implement the table view delegate methods and add the table view to the bottom view. Then add the map to the top view. This should allow you to catch the table view cell selection and update the map in the top view accordingly.
Example based on the structure I believe you are using:
public class FirstViewController {
public let parallaxView = MMParallaxView()
private var secondViewController: SecondViewController
private var thirdViewController: ThirdViewController
override open func viewDidLoad() {
secondViewController = SecondViewController()
thirdViewController = ThirdViewController()
super.viewDidLoad()
self.view.addSubview(parallaxView)
thirdViewController.delegate = secondViewController
parallaxView.parallaxTopView = secondViewController.view
self.addChildViewController(secondViewController)
secondViewController.didMove(toParentViewController: self)
parallaxView.parallaxBottomView = thirdViewController.view
self.addChildViewController(thirdViewController)
thirdViewController.didMove(toParentViewController: self)
}
}
public class SecondViewController: ThirdViewControllerDelegate {
public func updateMap() {
// Update map
}
}
public class ThirdViewController: UITableViewDelegate {
weak var delegate ThirdViewControllerDelegate?
private var table: UITableView = UITableView()
open override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
delegate?.updateMap()
}
}
protocol ThirdViewControllerDelegate: class {
public func updateMap() {}
}
if the view controllers are instantiated through the Storyboard then you can try this:
public class MasterViewController {
var secondViewController: SecondViewController?
override func viewDidLoad() {
super.viewDidLoad()
self.performSegue(withIdentifier: "MMParallaxTop", sender: nil)
self.performSegue(withIdentifier: "MMParallaxBottom", sender: nil)
}
override open func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let secondVC = segue.destination as? SecondViewController {
secondViewController = secondVC
}
if let thirdVC = segue.destination as? ThirdViewController, let second = secondViewController {
thirdVC.delegate = second
}
}
}
i need an help, see this class
import UIKit
protocol TypesTableViewControllerDelegate: class {
func typesController(controller: TypesTableViewController, didSelectTypes types: [String])
}
class TypesTableViewController: UITableViewController {
let possibleTypesDictionary = ["bakery":"Bakery", "bar":"Bar", "cafe":"Cafe", "grocery_or_supermarket":"Supermarket", "restaurant":"Restaurant"]
var selectedTypes: [String]!
weak var delegate: TypesTableViewControllerDelegate!
var sortedKeys: [String] {
return possibleTypesDictionary.keys.sort()
}
// MARK: - Actions
#IBAction func donePressed(sender: AnyObject) {
delegate?.typesController(self, didSelectTypes: selectedTypes)
}
// MARK: - Table view data source
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return possibleTypesDictionary.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("TypeCell", forIndexPath: indexPath)
let key = sortedKeys[indexPath.row]
let type = possibleTypesDictionary[key]!
cell.textLabel?.text = type
cell.imageView?.image = UIImage(named: key)
cell.accessoryType = (selectedTypes!).contains(key) ? .Checkmark : .None
return cell
}
// MARK: - Table view delegate
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
let key = sortedKeys[indexPath.row]
if (selectedTypes!).contains(key) {
selectedTypes = selectedTypes.filter({$0 != key})
} else {
selectedTypes.append(key)
}
tableView.reloadData()
}
}
here the user can tap a cell of the tableView so that his prefer types are used on the next viewController for a search, now i need to build a class that do the same thing but there is no a tableview rather only 6 buttons in a view that the user can tap (so a viewController with only 6 different buttons to tap). The problem is that i don't know how to pass to the next viewController what buttons have been pressed and what are not, how can i build this class?
here is the function in the other class that need to know what buttons have been pressed
func fetchNearbyPlaces(coordinate: CLLocationCoordinate2D) {
mapView.clear()
dataProvider.fetchPlacesNearCoordinate(coordinate, radius:searchRadius, types: searchedTypes) { places in
for place: GooglePlace in places {
let marker = PlaceMarker(place: place)
marker.map = self.mapView
where is "types: serchedTypes"
What you wanna do is called delegation here is how you do it:
Make a protocol like this one:
protocol TransferProtocol : class
{
func transferData(types:[String])
}
Make the view controller with the buttons conform to that protocol, I like to do it by adding extensions to my classes like so:
extension ButtonsViewController:TransferProtocol{
func transferData(types:[String]){
//Do whatever you want here
}
}
Declare a variable in your Table View Controller class with the protocol you created as its type, this is called a delegate
weak var transferDelegate:TransferProtocol?
Before you segue to the Buttons View Controller you want to set that view controller as the delegate you just created like so:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as? ButtonsViewController
transferDelegate = vc
vc?.transferData(types: selected)
}
If done correctly you should be able to work with the array you built in the Table View Controller(TypesTableViewController)
I am trying to create a protocol delegate between two UICollectionViewController. with the code I have I don't get any errors or warnings however, I cannot get the delegate to work. What am I missing?
Second Collection View
public protocol LettersCollectionViewDelegate: class {
func DidSelectLetter(collectioView: UICollectionView,letter: Character, resultString:String)
}
class LettersCollectionView: UICollectionViewController {
// DELEGATE
weak var delegate: LettersCollectionViewDelegate?
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let cell : UICollectionViewCell = collectionView.cellForItemAtIndexPath(indexPath)! as! LetterCellView
delegate?.DidSelectLetter(collectionView, letter: "T", resultString:"TestString")
}
}
First Collection View
class AnswerCollectionView: UICollectionViewController {
let lettersView = LettersCollectionView()
override func viewDidLoad() {
super.viewDidLoad()
self.lettersView.delegate = self
}
}
extension AnswerCollectionView: LettersCollectionViewDelegate {
func DidSelectLetter(collectioView: UICollectionView, letter: Character, resultString: String) {
print(letter)
}
}
UPDATE
You need your delegate to be the instance of the AnswerCollectionView that is embedded in your root view controller. Similarly, you need to set the delegate on the LettersCollectionView instance that is in the root view. let lettersView = LettersCollectionView() creates a new instance.
You can get the required references in prepareForSegue in your root view controller. You need to give the two embded segues in your storyboard identifiers, so you can identify them.
class ViewController: UIViewController {
var lettersView: LettersCollectionView?
var answersView: AnswersCollectionView?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "lettersSegue" {
let lettersView = segue.destinationViewController as? LettersCollectionView
} else if segue.identifier = "answersSegue" {
let answersView = segue.destinationViewController as? AnswersCollectionView
}
self.lettersView?.delegate = self.answersView
}
I have two viewcontrollers, one is rootviewcontroller, the other one is selectorviewcontroller. In rootvc, there is a textfield and button, when the button is clicked, it takes us to the selectorvc where we can choose and if necessary add a new item (area) and then choose the item, after we choose it, it takes us back to the rootvc, and display the selected item in the textfield. I understand that if we don't use data persistence measures, the data added in won't persist after we recommence the app. Although I can add in new item to the selectorvc, but the newly added data just gone even after we unwind the segue back to rootvc and re-enter the selectorvc. I am not sure where I did wrong, as the data storing array is mutable. It is great if you could pointing me to the right direction. Thanks a lot.
A simple array is defined to store the data,
import UIKit
class AreaClass {
var areaName: String
init? (areaName: String) {
self.areaName = areaName
if areaName.isEmpty {
return nil
}
}
}
This is the unwind segue in the rootvc,
#IBAction func unwindWithSelectedArea(segue:UIStoryboardSegue) {
if let SelectorViewController = segue.sourceViewController as? SelectorViewController,
selectedArea = SelectorViewController.selectedArea
{
AreaSelectedTextField.text = selectedArea
}
}
This is the declaration and addnewitem in the selectorvc,
var selectedArea: String?
var selectedAreaIndex: Int?
var areas = [AreaClass]()
var newarea = AreaClass?()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
areaNewnSelectedTF.delegate = self
saveButton.enabled = false
loadSample()
// Do any additional setup after loading the view.
}
func loadSample (){
let area1 = AreaClass(areaName: "TopHill")!
let area2 = AreaClass(areaName: "Foothill")!
let area3 = AreaClass(areaName: "Summit")!
let area4 = AreaClass(areaName: "Riverside")!
areas += [area1, area2, area3, area4]
}
#IBAction func addNewArea(sender: UIBarButtonItem) {
var dupli = false
if saveButton == sender {
let areaname = areaNewnSelectedTF.text ?? ""
newarea = AreaClass(areaName: areaname)
for var index = 0; index < areas.count; ++index {
if areaname == areas[index].areaName {
dupli = true
// Mark: alert for duplicate inputs
let alert = UIAlertController(title: "Duplicate", message: "Can't have same items", preferredStyle:.Alert)
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
presentViewController(alert, animated: true, completion: nil)
}
}
if dupli == false {
let newIndexPath = NSIndexPath(forRow: areas.count, inSection: 0)
areas.append(newarea!)
tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)
}
}
}
This is the PrepareforSegue in selectorvc
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "saveSelectionSegue" {
if let cell = sender as? UITableViewCell {
let indexPath = tableView.indexPathForCell(cell)
if let index = indexPath?.row {
selectedArea = areas[index].areaName
}
}
}
}
You need to use a delegate to pass data from a childViewController to your rootViewController.
Below there are ViewController(root) and SelectTableViewController(selector), they implement a simple example of what you look for. I used a struct to make it simple.
At first, you need to create a protocol using the role of your delegate => ViewControllerDelegate with a simple function with an argument "AreaStruct".
This protocol needs to be implemented by the "root". When you pressed the button, you performSegueWithIdentifier that will call prepareForSegue. In it, you pass yourself(ViewControllerDelegate) to the destinationViewController.
In your destinationViewController, the user select one row. By selecting, it triggers didSelectRowAtIndex. In it, you get the selectedArea (the concern AreaStruct) and with your delegate, you call choseArea(...). After that you pop the ViewController to go back to the root.
When you call choseArea, it will put the areaName into the label to display what you selected as shown by the implementation of choseArea(areaStruct : AreaStruct) in your RootViewController.(In this function, you can do whatever you want with your areaStruct)
------ EDIT ------
In your code, your "selectorviewcontroller" create the data => loadSample. So no matter what happened when you go to "selectorviewcontroller", you will always loadSample even if you added new "areas" before. I updated my example code based on your example.
ViewController -> press button -> SelectTableViewController -> add area -> select area -> back to ViewController
To summarise,
in ViewController, I setupListData when viewDidLoad is called
I pressed the button to go to SelectTableViewController
3 I passed myself(delegate) and dataList to SelectTableViewController in prepareForSegue
SelectTableViewController is loaded and display my list based on dataList !
I pressed addNewArea, it append a new area to "areas" that is only local to SelectTableViewController !
My list is refreshed and displays my new area
I select an area
I call the delegate by passing 2 arguments : What I selected and my updated areas(List); I pop SelectViewController => Popping SelectViewController, it disappear and all data are lost
When I called the delegate, I updated my label based on what I selected and also I updated dataList in ViewController with areas (from SelectTableViewController).
When I click on my button, see point 2.1. => You should understand
If you want to persist data, you could use different techniques such as singleton, core data, NSUserDefaults, etc.
ViewController.swift
import UIKit
import Foundation
struct AreaStruct {
var areaName : String
}
protocol ViewControllerDelegate : class{
func choseArea(areaStruct : AreaStruct, areas : [AreaStruct])
}
class ViewController: UIViewController, ViewControllerDelegate { //1. Implement the delegate here.
#IBOutlet weak var infoLabel: UILabel!
#IBOutlet weak var btnToVC: UIButton!
var dataList : [AreaStruct] = [AreaStruct]()
override func viewDidLoad() {
super.viewDidLoad()
self.setupListData()
}
func setupListData(){
for i in 0...5{
dataList.append(AreaStruct.init(areaName: "Coucou \(i)"))
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "SelectTableViewController" {
let destinationViewController = segue.destinationViewController as! SelectTableViewController
destinationViewController.delegate = self //2. Here you passed itself to the destinationViewController so it can know how to call you !
destinationViewController.areas = dataList //
}
}
func choseArea(areaStruct : AreaStruct, areas : [AreaStruct]) {
self.infoLabel.text = areaStruct.areaName
dataList = areas
}
#IBAction func pushToSelectTableViewController(sender: AnyObject) {
//0. When pressed, you want to go to SelectTableViewController
self.performSegueWithIdentifier("SelectTableViewController", sender: nil)
}
}
SelectTableViewController.swift
import UIKit
class SelectTableViewController: UITableViewController {
var areas : [AreaStruct] = [AreaStruct]()
weak var delegate : ViewControllerDelegate? // <-- Delegate to send a mess. to ViewController
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func addNewArea(sender: UIBarButtonItem) {
let hello_world = "Hello World" //For the example !
areas.append(AreaStruct.init(areaName: hello_world))
self.tableView.reloadData()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return areas.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath)
cell.textLabel?.text = areas[indexPath.row].areaName
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedArea = areas[indexPath.row]
self.delegate?.choseArea(selectedArea, areas: areas) //3. When you select, you pass the data to ViewController via the delegate
self.navigationController?.popViewControllerAnimated(true)//4. You dismiss itself
}
}
I am trying to pass data from a tableview to a previous screen, which is my Sign Up screen. Specifically what I am sending is the name of a school.
What is happening is that when I return to the previous screen through the segue, the label I am changing (schoolTextLabel) is showing "None Selected", when I want it to display the school name. Through some error checking, I have found that when I have it println(theSchoolName), it is correctly choosing the name I want displayed, It is just not passing the value to the other screen.
Please help!
I have searched endlessly on the internet for the answer and cannot for the life of me find the answer.
Thank you!
TableViewController
var namesArray = [String]()
var locationsArray = [String]()
var theSchoolName = "None Selected"
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "selectedSchool" {
var completeSignUpVC = segue.destinationViewController as! SignUpViewController
completeSignUpVC.school = self.theSchoolName
}
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.theSchoolName = self.namesArray[indexPath.row]
self.performSegueWithIdentifier("selectedSchool", sender: self.namesArray[indexPath.row])
println(theSchoolName)
}
SignUpViewController
class SignUpViewController: UIViewController, UITextFieldDelegate {
#IBOutlet var schoolTextLabel: UILabel!
var school = "No School Selected"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.schoolTextLabel.text = self.school
usernameSignupText.delegate = self
passwordSignupText.delegate = self
}
}
I found the answer. I needed to find the selected row index, and then call that number in the namesArray. I'm not exactly sure why my previous code didn't work, but this now works.
var selectedRowIndex = self.schoolTable.indexPathForSelectedRow()
var completeSignUpVC = segue.destinationViewController as! SignUpViewController
completeSignUpVC.school = namesArray[selectedRowIndex!.row]