picture
How can i resize this table view so that I can place 2 buttons in the top of the screen without overlapping with the table view? On my screen I only have the button. there is no table view container.
My code:
class FriendListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var friendArray: [Friends] = [];
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
override func viewDidLoad() {
super.viewDidLoad()
tableView = UITableView(frame: UIScreen.mainScreen().bounds, style: UITableViewStyle.Plain)
tableView.delegate = self
tableView.dataSource = self
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
self.view.addSubview(self.tableView)
let entityDescription = NSEntityDescription.entityForName("Friends", inManagedObjectContext:
managedObjectContext)
let request = NSFetchRequest()
request.entity = entityDescription
var friendObjs = [Friends]()
do {
friendObjs = try managedObjectContext.executeFetchRequest(request) as! [Friends]
}
catch {
// show something
}
for friend in friendObjs {
friendArray.append(friend)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return friendArray.count;
}
// assign the values in your array variable to a cell
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell:UITableViewCell=UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "cell")
cell.textLabel!.text = friendArray[indexPath.row].firstName;
return cell;
}
// Register when user taps a cell via alert message
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
let alert = UIAlertController(title: "Selected Item", message: "clicked", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Okay", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
One option is to add a header view which contains your buttons. This would require setting the headerView property of the tableView or using tableView delegate methods.
Another option is to add a view and add it to the top of your frame. You would then need to adjust the frame of the table view upon initialize to add it below that custom view.
Something I noticed as well in order for your data to appear is to reload the table view after your fetch completes.
Did you tried putting a tableview header, You can create a view and set the frames matching your need and add 2 buttons. set the tableview's tableviewviewheader property with the view object you just created.
let headerView = UIView(frame: CGRectMake(0, 0, tableView.frame.size.width, 50))
Add your buttons as subview to headerView and then
self.tableView.tableHeaderView = headerView
Related
Swift
As you can see in this swift code I am trying to create a table view for my logout controller. However, I am facing problems with the "log out" button showing as a cell in my view controller. I don't know what the problem is because it builds without any errors and I have gone and checked several times but can't find the problem.
import UIKit
struct SettingsCellModel {
let title: String
let handler: (() -> Void)
}
final class SettingsViewController: UIViewController {
private var tableView: UITableView {
let tableView = UITableView(frame: .zero, style: .grouped)
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
return tableView
}
private var data = [[SettingsCellModel]]()
override func viewDidLoad() {
super.viewDidLoad()
configureModels()
view.backgroundColor = .systemBackground
view.addSubview(tableView)
tableView.delegate = self
tableView.dataSource = self
// Do any additional setup after loading the view.
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.frame = view.bounds
}
private func configureModels() {
let section = [SettingsCellModel(title: "Log Out") { [weak self] in
self?.didTapLogOutButton()
}
]
data.append(section)
}
private func didTapLogOutButton() {
let actionSheet = UIAlertController(title: "Log Out", message: "Are you sure you want to log out", preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
actionSheet.addAction(UIAlertAction(title: "Log Out", style: .destructive, handler: { _ in
logOut (completion: { success in
DispatchQueue.main.async {
if success {
//present log in
let loginVC = LoginViewController()
loginVC.modalPresentationStyle = .fullScreen
self.present(loginVC, animated: true) {
self.navigationController?.popToRootViewController(animated: false)
self.tabBarController?.selectedIndex = 0
}
}
else {
//error occurred
fatalError("Could not log out user")
}
}
})
}))
actionSheet.popoverPresentationController?.sourceView = tableView
actionSheet.popoverPresentationController?.sourceRect = tableView.bounds
present(actionSheet, animated: true)
}
}
extension SettingsViewController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = data[indexPath.section][indexPath.row].title
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
data[indexPath.section][indexPath.row].handler()
}
}
Issue is you are using Computed property private var tableView: UITableView { that means every time you access tableView in your code its closure is executed (or its value is evaluated), and because you instantiate a new instance of tableView in its closure you receive different instances of tableView in all the 3 statements
view.addSubview(tableView)
tableView.delegate = self
tableView.dataSource = self
hence tableView instance you added as subView is different from the one that has delegate and data source set as self.
What you need
Option 1:
private var tableView: UITableView = {
let tableView = UITableView(frame: .zero, style: .grouped)
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
return tableView
}()
Option 2: (Preferred)
private lazy var tableView: UITableView = {
let tableView = UITableView(frame: .zero, style: .grouped)
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
return tableView
}()
Hope it helps
I am working on an app which has TableViewCells in three Scenes. I would like to populate by using an AlertBox to take the "item tapped" and pass that data to a TableViewCell in another Scene..
Here is the first Scene I have the alertBox in
class SupplementScene: UIViewController, UITableViewDataSource,
UITableViewDelegate
{
#IBOutlet weak var lblSupplement: UILabel!
#IBOutlet weak var sbSupplement: UISearchBar!
#IBOutlet weak var tvSupplement: UITableView!
let stackScene = StackScene()
var suppArray:[Supplement] = [Supplement]()
var stackArray:[Stack] = [Stack]()
let cellReuseIdentifier = "cell1"
var count:Int = 0
// create the managed object context
// it is used for CoreData. Must be created in the ViewController
let managedObjectContext = (UIApplication.shared.delegate
as! AppDelegate).persistentContainer.viewContext
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view.
applyDesign()
tvSupplement.dataSource = self
tvSupplement.delegate = self
suppArray = CoreDataHandler.getAllSupplementObjects(managedObjectContext: managedObjectContext)
}
// functions from the protocols
// number of rows in table view
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return suppArray.count
}
// create a cell for each table view row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
// create a new cell if needed or reuse an old one
// had to change cell to cell1 since identified was used before
let cell:SupplementTableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell1") as! SupplementTableViewCell!
// set the text from the array into the cells labels
cell.lblName.text = suppArray[indexPath.row].name
cell.backgroundColor = UIColor.darkGray
cell.lblName.textColor = UIColor.white
return cell
}
// method to run when table view cell is tapped
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
// print("You tapped cell number \(indexPath.row).")
// get the tableViewCell
let cell = tableView.cellForRow(at: indexPath) as! SupplementTableViewCell
// print the text property in the label in the tableViewCell
print("TableView tapped = \(cell.lblName.text!)")
updateStack(name: cell.lblName.text!)
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// update function
func updateStack(name:String)
{
// get the objectby name
_ = CoreDataHandler.getSupplementByName(managedObjectContext: managedObjectContext, name: name)!
// create an Alert with a textFields for all ContactBusiness fields
let alertController = UIAlertController(title: "Udpate \(name)",
message: "",
preferredStyle: UIAlertControllerStyle.alert)
// create a default action for the Alert
let defaultAction = UIAlertAction(
title: "Ok",
style: UIAlertActionStyle.default,
handler: {(alertAction: UIAlertAction!) in
// get the input from the alert controller
// put the user updated fields back in the contactBusiness object
// save the managedObject
CoreDataHandler.addStackObject(managedObjectContext: self.managedObjectContext)
// get all Contacts from CoreData
self.stackArray = CoreDataHandler.getAllStackObjects(managedObjectContext: self.managedObjectContext)
// reload the data into the TableView
self.tvSupplement.reloadData()
self.stackScene.tvStack.reloadData()
})
let cancelAction = UIAlertAction(
title: "Cancel",
style: UIAlertActionStyle.cancel,
handler:nil)
// add the action to the Alert
alertController.addAction(defaultAction)
alertController.addAction(cancelAction)
// display the Alert
present(alertController, animated: true, completion: nil)
}
}
and now the class in which I want to retrieve the data from.
class StackScene: UIViewController, UITableViewDelegate,
UITableViewDataSource
{
#IBOutlet weak var lblStack: UILabel!
#IBOutlet weak var sbStack: UISearchBar!
#IBOutlet weak var tvStack: UITableView!
let cellReuseIdentifier = "cell3"
var stackArray:[Stack] = [Stack]()
// var suppArray:[Supplement] = [Supplement]()
var count : Int = 0
// create the managed object context
// it is used for CoreData. Must be created in the ViewController
let managedObjectContext = (UIApplication.shared.delegate
as! AppDelegate).persistentContainer.viewContext
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view.
tvStack.delegate = self
tvStack.dataSource = self
stackArray = CoreDataHandler.getAllStackObjects(managedObjectContext: managedObjectContext)
// doing this to apply the design changes to the app
applyDesign()
}
// functions from the protocols
// number of rows in table view
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return stackArray.count
}
// create a cell for each table view row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
// create a new cell if needed or reuse an old one
// had to change cell to cell1 since identified was used before
let cell:StackTableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell3") as! StackTableViewCell!
// set the text from the array into the cells labels
cell.lblName.text = stackArray[indexPath.row].name
cell.backgroundColor = UIColor.darkGray
cell.lblName.textColor = UIColor.white
return cell
}
#IBAction func longPressDelete(_ sender: UILongPressGestureRecognizer)
{`enter code here`
// get the location of the long press in the table
// location returns a CGPoint
let p = sender.location(in: tvStack)
// get indexPath at location of the long press
let indexPath = tvStack.indexPathForRow(at: p)
// if nil, selected the table, not a row
// if not nil, check for long touch began.
// we are not interested in the long touch ended
if indexPath == nil
{
print("Long press on table view, not row.")
}
else if (sender.state == UIGestureRecognizerState.began)
{
// get the tableViewCell
let cell = tvStack.cellForRow(at: indexPath!) as! StackTableViewCell
print("TableView LongPress = \(cell.lblName.text!), Pos: \(indexPath!.row)")
// call the deleteContact function
deleteContact(name: cell.lblName.text!, position: indexPath!.row)
// do something for long press here
}
else if(sender.state == UIGestureRecognizerState.ended)
{
print("Long press ended")
}
}
// function to display alert and confirm deletion
func deleteContact(name:String, position:Int)
{
// create the AlertController
let alertController = UIAlertController(title: "Delete",
message: name + ": Confirm Delete",
preferredStyle: UIAlertControllerStyle.alert)
// create a default action button
let deleteAction = UIAlertAction(title: "Delete",
style: UIAlertActionStyle.default,
handler: {(alertAction: UIAlertAction!) in
// remove from array
self.stackArray.remove(at: position)
// reload the tableView
self.tvStack.reloadData()
// remove from CoreData
CoreDataHandler.deleteStackByName(managedObjectContext: self.managedObjectContext, name: name)
})
// Alerts can only have one cancel action
// It is bolded and always comes last
let cancelAction = UIAlertAction(title: "Cancel",
style: UIAlertActionStyle.cancel,
handler: nil)
// Add the actions to the Alert
alertController.addAction(deleteAction)
alertController.addAction(cancelAction)
// present or display the Alert
present(alertController, animated: true, completion: nil)
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I know I am not using a segue to perform this type of transaction. But I have searched these threads and google for any info and am struggling on what is the best solution to this. The alertBox pops and has the tapped item in the textField, but I cannot get the data from that alertBox textField to populate into the TableViewCell of the "stack" class.
Let me know if there is any other info I can provide. I am still learning Swift.
My code:
//Adding Destination VC as subview in my current view's view container
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "DestinationViewControllerID")
containerView.addSubview(viewController.view)
viewController.didMove(toParentViewController: self)
//Now implementing Table view in the destination vc
class DestinationViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var tableView = UITableView()
var tableData = ["Beach", "Clubs", "Chill", "Dance"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let screenBounds = UIScreen.main.bounds
let rect = CGRect(x: 0, y: 0, width: screenBounds.width, height: screenBounds.height)
tableView = UITableView(frame: rect, style: UITableViewStyle.plain)
tableView.dataSource = self
tableView.delegate = self
tableView.backgroundColor = UIColor.blue
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "my")
view.addSubview(tableView)
tableView.reloadData()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "my", for: indexPath)
cell.textLabel?.text = "This is row \(tableData[indexPath.row])"
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4//tableData.count
}
}
If I'm Setting the destination viewController as my initial vc from storyboard then everything is working fine.But if adding as subview in container view the cellForRowAt method is not being called but numberOfSection and numberOfRowsInSection is being called.
Is there anyone having any solution ?
Add self.addChildViewController(viewController) before subviewing
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "DestinationViewControllerID")
self.addChildViewController(viewController)
containerView.addSubview(viewController.view)
viewController.didMove(toParentViewController: self)
Make sure that constraints are set on UITableView. If so, try to to set height of UITableViewCell manually.
Firstly, you need to have a tableview in your storyboard. After creating tableView you may subview it programmatically by:
self.view.addSubview(tableView)
or add tableView in your storyboard and create an IBOutlet like this:
#IBOutlet weak var tableView: UITableView!
Secondly, you haven't initialized your cell, you have only dequeued a previously created cell(which, in your case has never got created). Your code should be something like this:
var cell = tableView.dequeueReusableCell(withIdentifier: "my", for: indexPath) as UITableViewCell?
if cell == nil {
cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "my")
}
Check your table's top-bottom-left-right constant. If your table's height is 0 then cellForRow method may not be call. If you not set your table's constant properly then then it's height may become 0.
Recheck are registered reusableidentifier
tableView.dequeueReusableCell(withIdentifier: "my", for: indexPath)
I'm working with Swift 2.0 and Xcode 7.2.
I want to learn how to make an app without a storyboard (UI with pure programming code). To start off, I am trying to make a simple app, with three labels, inside a custom UITableView cell which will be updated dynamically through the internet.
Here is what I have achieved so far:
Created a new Swift project and deleted the main.storyboard from the project
Added a view controller as the rootViewController in AppDelegate
Included code to create a UITableView inside this view
Here are the other tasks I want to accomplish (all programmatically, without using the attribute inspector):
Insert a UINavigationController into the ViewController
Add a custom cell with three labels
Update the table view with data
If possible, I would want to have the ability to have everything working in landscape mode as well.
Can anyone tell me how to do this?
AppDelegate.swift
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.mainScreen().bounds)
window!.backgroundColor = UIColor.whiteColor()
window!.rootViewController = ViewController()
window!.makeKeyAndVisible()
return true
}
ViewController.swift
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableView = UITableView(frame: self.view.bounds, style: UITableViewStyle.Plain)
tableView.dataSource = self
tableView.delegate = self
tableView.backgroundColor = UIColor.whiteColor()
tableView.frame = CGRectMake(0 , 0, self.view.bounds.width, self.view.bounds.height)//Optional for table size
self.view.addSubview(tableView)
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "myIdentifier")
myCell.textLabel?.text = "\(indexPath.row)"
myCell.detailTextLabel?.text = "Subtitle"
return myCell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I have no idea how to create a custom cell programmatically to which I can add objects.
Help would be appreciated.
Thanks.
If you are not using storyboard, you can define your cell just above the class where your ViewController where your are including your tableView something like myCell which is your custom UITableViewCell as given below.
In this myCell, you can add as many objects as your want and set them up in the setUpCell() block.
The full code is as below, please make sure you call setUpCell() when you use your cell's in cellForRowAtIndexPath.
ViewController.swift
import #UIKit
class myCell: UITableViewCell {
// Define label, textField etc
var aMap: UILabel!
// Setup your objects
func setUpCell() {
aMap = UILabel(frame: CGRectMake(0, 0, 200, 50))
self.contentView.addSubview(aMap)
}
}
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var tableView = UITableView()
// for ex, lets say, your data array is defined in the variable below
var dataArray = [[String:AnyObject]]() //Array of your data to be displayed
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableView = UITableView(frame: self.view.bounds, style: UITableViewStyle.Plain)
tableView.dataSource = self
tableView.delegate = self
tableView.backgroundColor = UIColor.whiteColor()
// register your class with cell identifier
self.tableView.registerClass(myCell.self as AnyClass, forCellReuseIdentifier: "Cell")
self.view.addSubview(tableView)
dataArray = // Something loaded from internet
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return flightDataArr.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// let myCell = tableView.dequeueReusableCellWithIdentifier("myIdentifier", forIndexPath: indexPath)
var cell:myCell? = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as? myCell
if cell == nil {
cell = myCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
}
var data = dataArray[indexPath.row]
cell?.setUpCell()
cell!.aMap.text = String(dict["productName"])
return cell!
}
}
See if this works for you. I never used programming to create tableView, so this may not be the optimal way to create your tableView programmatically. I hope someone else may help you with a better answer if possible.
You can create a sub class of UITableViewCell say PackageListTableViewCell.
Declare number of labels in tabelViewCell custom class as per your requirements like below,
var label1 : UILabel?;
override init:reuseIdentifier: in custom cell with additional parameters as below.
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
//create labels as per your requirement
self.label1 = //initialise you label
//set frame, or constraint
//set text color, background color etc
//add created labels to cell as below
self.contentView.addSubView(self.label1);
}
your tableView:cellForRowAtIndexPath: will be look like,
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let lable1String = "lbl1"
let lable2String = "lbl2"
let lable3String = "lbl3"
var cell : PackageListTableViewCell! = tableView.dequeueReusableCellWithIdentifier("cellID") as?PackageListTableViewCell
if (cell == nil) {
cell = PackageListTableViewCell.init(style: UITableViewCellStyle.Default,
reuseIdentifier:"cellID");
}
cell.selectionStyle = UITableViewCellSelectionStyle.None;
//set text of your lables as below
cell.label1.text = lable1String;
return cell;
}
You have to register a custom tableviewcell class using method registerClass on tableview.
Use this modified Viewcontroller code:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableView = UITableView(frame: self.view.bounds, style: UITableViewStyle.Plain)
tableView.dataSource = self
tableView.delegate = self
tableView.backgroundColor = UIColor.whiteColor()
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "myIdentifier")
tableView.frame = CGRectMake(0 , 0, self.view.bounds.width, self.view.bounds.height)//Optional for table size
self.view.addSubview(tableView)
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCellWithIdentifier("myIdentifier", forIndexPath: indexPath)
myCell.textLabel?.text = "\(indexPath.row)"
myCell.detailTextLabel?.text = "Subtitle"
return myCell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
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.
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!
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.