0

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.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Addzy
  • 664
  • 1
  • 6
  • 16
  • You normally do this using a delegate protocol. There are many posts about that. – rdelmar Apr 25 '15 at 19:05
  • possible duplicate of [Passing Data between View Controllers](http://stackoverflow.com/questions/5210535/passing-data-between-view-controllers) – jtbandes Apr 25 '15 at 19:20
  • Another thing to mention: The code where you add your `UITextField` should really be in `-viewDidLoad`, and then you should set the `UITextField`'s frame in `-viewWillLayoutSubviews`. Also it seems to me you're using a UITableViewController for what is quite a simple UI, rather than using cells with red backgrounds why not simply try adding UIView's to a simple `UIViewController`? :) – simonthumper May 01 '15 at 20:11

2 Answers2

5

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!

simonthumper
  • 1,834
  • 2
  • 22
  • 44
  • sorry if all the self's get confusing, Objective-C habits! :P – simonthumper Apr 25 '15 at 19:24
  • Thank you, I'll get implementing that now and try and get it working, I really appreciate the help. Were you suppose to paste in some code about how the view controller housing the text field should look or have i misread your answer? – Addzy Apr 25 '15 at 19:33
  • 1
    This bit is silly: `if let delegate = self.delegate {self.delegate?.searchViewControllerDidSelectResult(self, result: data)}` – Duncan C Apr 25 '15 at 19:34
  • 1
    If you use optional binding (if let) then you should use the now required variable: `if let delegate = delegate {delegate.searchViewControllerDidSelectResult(self, result: data)}` – Duncan C Apr 25 '15 at 19:36
  • Yeah just realised that, once again force of habit from Obj-C using self! Haven't written much swift in a while! – simonthumper Apr 25 '15 at 19:37
  • @simonthumper When I try to make the SetAlertTableControllerto conform to the delegate like so: `class SetAlertTableController: UITableViewController, UITableViewDelegate, ViewControllerDelegate {` I'm getting an error stating "Type 'SetAlertTableController' does not conform to protocol 'ViewControllerDelegate'" Any ideas? – Addzy Apr 25 '15 at 19:53
  • You should just need to add the method in the last block of code in my answer to `SetAlertTableController`! If a class is complaining that it doesn't conform to a protocol, it just means you haven't implemented one or more of the methods defined in the protocol within your class :) – simonthumper Apr 25 '15 at 19:58
  • @simonthumper Ahhh yes, rookie mistake, cheers. I still can't get my head around it though, don't think my brains working at all today, it's like it's in meltdown. I'm getting an error on `self.result = result` stating it doesn't have a member named result. My brains given up completely – Addzy Apr 25 '15 at 20:07
  • If you want to use self.result you need to need to declare it as a var in your `SetAlertTableController` class outside of any methods :) – simonthumper May 01 '15 at 20:07
  • I think i've set it up right but the `func searchViewControllerDidSelectResult(searchVC:ViewController, result: String) {` isn't triggering, I've placed print lns but it doesn't seem to be firing, any ideas? – Addzy May 01 '15 at 22:04
  • Have you used break points before? Try putting them where you expect code to run and see if you hit them. I'd recommend starting by adding one in `tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)` and then place them in order of how you'd expect your code to run. If one that you expect to get hit doesn't then you should get more of an idea what your problem is. You can use the left panel of the debug area to see all your object allocations when you're at a breakpoint and continue execution of your code :) https://www.youtube.com/watch?v=7NUQ7LutQlM – simonthumper May 01 '15 at 22:12
  • This is also a really nice article on breakpoints http://jeffreysambells.com/2014/01/14/using-breakpoints-in-xcode – simonthumper May 01 '15 at 22:15
0

It is hard to break oneself from the used conventions (including myself).

Rather than writing

if let delegate = self.delegate {
           delegate.searchViewControllerDidSelectResult(self, result: data)
}

It would be better to go with the following:

          delegate?.searchViewControllerDidSelectResult(self, result: data)

As it is not permitting me to comment, I am just providing an update (Swift is all about simplicity and elegance). It took couple of weeks for me to write in the said way.

Lukesivi
  • 2,206
  • 4
  • 25
  • 43