Addzy Addzy - 22 days ago 8
iOS Question

Pass data from one view to another in swift programatically

I've searched the site and the only threads I've found around this subject remain unanswered so I hoped I would have more luck.

I am relatively sure what I am trying to do is fairly easy however I can't seem to find the answer.

I have a view controller with a Uitext field which when the text field is clicked it presents a Search controller modally.

Here is the code for the text field which is SetAlertTableController.swift:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell

if indexPath.section == 0 {

cell.textLabel!.text = "Set a Station Alert"
cell.backgroundView = UIImageView(image: UIImage(named: "red-full"))
cell.textLabel?.backgroundColor = UIColor.whiteColor().colorWithAlphaComponent(0.0)
cell.imageView!.image = UIImage(named: "metro-no-pad")
cell.textLabel?.textColor = UIColor.whiteColor()

var searchField = UITextField(frame: CGRectMake(60.0, 25.0, 250.0, 30.0))
searchField.backgroundColor = UIColor.whiteColor()
searchField.borderStyle = UITextBorderStyle.Line
searchField.borderStyle = .RoundedRect
searchField.placeholder = "Search Stations"
searchField.textColor = UIColor.lightGrayColor()
searchField.delegate = self
self.view.addSubview(searchField)



} else if indexPath.section == 1 {

cell.backgroundView = UIImageView(image: UIImage(named: "red-full"))
cell.userInteractionEnabled = false

var slider=UISlider(frame:CGRectMake(10, 120, 300, 10));
slider.minimumValue = 0;
slider.maximumValue = 5;
slider.continuous = false;
slider.value = 0;
slider.addTarget(self, action: "sliderValueDidChange:", forControlEvents: .ValueChanged);
self.view.addSubview(slider);


} else if indexPath.section == 2 {

cell.textLabel!.text = "Set Alert"
cell.backgroundView = UIImageView(image: UIImage(named: "red-full"))
cell.textLabel?.backgroundColor = UIColor.whiteColor().colorWithAlphaComponent(0.0)
cell.imageView!.image = UIImage(named: "confirm")
cell.textLabel?.textColor = UIColor.whiteColor()
cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator

}

return cell

}


When you search it filters the results in to table view cells. When the correct results cell is pressed I need to dismiss the modal view and show the data from the selected cell in the textfield.

Here is the code which for the search controller (ViewController.swift) as it stands:

import UIKit
import Foundation

class ViewController: UIViewController {
let kCellIdentifier = "Cell"

var searchController: UISearchDisplayController!
var tableView: UITableView!
var tableData: [String]? {
didSet {
self.tableView.reloadData()
}
}
var tableDataFiltered = [String]()

override func viewDidLoad() {
super.viewDidLoad()

tableView = UITableView(frame: CGRectZero, style: .Plain)
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: kCellIdentifier)
tableView.delegate = self
tableView.dataSource = self
self.view.addSubview(tableView)

let searchBar = UISearchBar()
searchBar.sizeToFit()
searchBar.becomeFirstResponder()
//tableView.tableHeaderView = searchBar
self.navigationItem.titleView = searchBar;

searchController = UISearchDisplayController(searchBar: searchBar, contentsController: self)
searchController.searchResultsDataSource = self
searchController.searchResultsDelegate = self
searchController.searchResultsTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: kCellIdentifier)


tableData = ["Bournemouth", "Branksome", "Parkstone"]

let cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.Plain, target: self, action: "dismissView")
self.navigationItem.rightBarButtonItem = cancelButton

}

func dismissView() {

dismissViewControllerAnimated(true, completion: nil)

}

func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {




println(searchBar.text)

}

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.frame = self.view.bounds
}
}

extension ViewController: UITableViewDataSource {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView != self.tableView {
return self.tableDataFiltered.count
}

if let count = tableData?.count {
return count
}
return 0
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(kCellIdentifier, forIndexPath: indexPath) as! UITableViewCell

var data: String
if tableView != self.tableView {
data = tableDataFiltered[indexPath.row]
} else {
data = tableData![indexPath.row]
}

cell.textLabel?.text = data
return cell
}
}


extension ViewController: UITableViewDelegate {
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if let data = tableData?[indexPath.row] {

dismissViewControllerAnimated(true, completion: nil)
println(indexPath.row)



}
}
}


Everything works as it should at this stage, even dismissing the modal view when clicked, I just can't work out a way to pass the result back to the previous controller's text field. Any help at all is greatly appreciated.

What is the best or more practical way to do this without storyboards?

People that are claiming the question to be a duplicate of previous posts and on the verge of this post being removed, all of other posts are either in Objective-C not Swift or they rely heavily on storyboard and segue use.

Unfortunately you can't use segues without a storyboard.

Answer

There's multiple ways to do this, the one which I would recommend (As you have experience with delegates having used UITableViewController) would be to create a swift protocol which acts as the delegate on the UISearchController which would look something like this:

import UIKit

protocol ViewControllerDelegate {

    func searchViewControllerDidSelectResult(searchVC:ViewController,result: String)
}

class ViewController: UIViewController {

var delegate: ViewControllerDelegate?

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

    if let data = tableData?[indexPath.row] {

        if let delegate = self.delegate {

            delegate.searchViewControllerDidSelectResult(self, result: data)
        }
//            dismissViewControllerAnimated(true, completion: nil) // Personally I would dismiss the search view controller within the delegate method of the view controller with the text field
        println(indexPath.row)

    }
}
}

The view controller with the text field would then look something like this. Your SetAlertTableViewController then needs to conform to ViewControllerDelegate like so:

class SetAlertTableViewController: UITableViewController, ViewControllerDelegate

And also implement the method declared in the protocol to set the text in the text field:

func searchViewControllerDidSelectResult(searchVC:ViewController, result: String) {

    // Keep track of the result
    self.result = result
    self.tableView.reloadData()
    self.dismissViewControllerAnimated(true, completion: nil)
}

You'll also need to make sure that when you show the ViewController for your search results you set the delegate on it so it will know what it's delegate is! If you need any more help, let me know!