Search and Tableview - ios

I need little help.
Peace everyone, hope that you are doing great.
I made an app with tableview, I wanted to add a searchbar to my tableview, so I found a tutorial how to add searchbar to tableview, but the in the tutorial it was just an list with item but not items that when you type those items they move you to Viewcontroller with textview. Different from tutorial, my app is so, when you type one of those items they move you to Viewcontroller with textview. But better you can see how my tablview looks like:
[![enter image description here][1]][1]
It looks nice, it also looks nice when I use search-bar :
[![enter image description here][2]][2]
But problem is, when you use search, it gives me different data from different variable, as in image:
[![enter image description here][3]][3]
Hope that I was clear, and hope someone will explain me how to fix this issue.
Peace.
Here is my tablview code:
import UIKit
class TableViewController: UITableViewController, UISearchResultsUpdating {
var FirstTableArray = [String]()
var passThisArray = [String]()
var filteredVerbs = [String]()
var resultSearchController = UISearchController()
override func viewDidLoad() {
super.viewDidLoad()
self.resultSearchController = UISearchController(searchResultsController: nil)
self.resultSearchController.searchResultsUpdater = self
self.resultSearchController.dimsBackgroundDuringPresentation = false
self.resultSearchController.searchBar.sizeToFit()
self.tableView.tableHeaderView = self.resultSearchController.searchBar
self.tableView.reloadData()
// This array will display on your tableviewcell.
FirstTableArray = [åadlyde, åanbefale, åansette, åarbeide, åavslå, åavstå, åbade, åbanke, åbarbere, åbe, åbearbeide, åbedra, åbedøve, åbegynne, åbehandle, åbeite, åbeskrive, åbestemme…]
//You can pass element of this array
passThisArray = ["å adlyde", "å anbefale", "å ansette", "å arbeide", "å avslå", "å avstå", "å bade", "å banke", "å barbere", "å be", "å bearbeide", "å bedra", "å bedøve", "å begynne", "å behandle"…]
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(passThisArray.count > 3){
if (self.resultSearchController.active)
{
return self.filteredVerbs.count;
}
else
{
return self.passThisArray.count
}
}
return FirstTableArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let Cell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
Cell.textLabel?.text = passThisArray[indexPath.row]
if (self.resultSearchController.active)
{
Cell.textLabel?.text = self.filteredVerbs[indexPath.row]
return Cell
}
else
{
Cell.textLabel?.text = self.passThisArray[indexPath.row]
return Cell
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "detailView") {
let vc = segue.destinationViewController as! ViewwController
//Get the Index of selected Cell
var indexPath : NSIndexPath = self.tableView.indexPathForSelectedRow!
//assign string to next view controller instance from selected cell.
vc.FirstString = FirstTableArray[indexPath.row]
if (self.resultSearchController.active) {
vc.FirstString = FirstTableArray[filteredVerbs.count]
}
}
}
func updateSearchResultsForSearchController(searchController: UISearchController)
{
self.filteredVerbs.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (self.passThisArray as NSArray).filteredArrayUsingPredicate(searchPredicate)
self.filteredVerbs = array as! [String]
self.tableView.reloadData()
}
}

So it seems that your problem lies in this method, in the selected line :
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "detailView") {
let vc = segue.destinationViewController as! ViewwController
//Get the Index of selected Cell
var indexPath : NSIndexPath = self.tableView.indexPathForSelectedRow!
//assign string to next view controller instance from selected cell.
vc.FirstString = FirstTableArray[indexPath.row]
if (self.resultSearchController.active) {
vc.FirstString = FirstTableArray[filteredVerbs.count] // <<---
}
}
}
If the search is active, you should rather take your FirstString this way self.filteredVerbs[indexPath.row].
With knowledge from comments :
It seems there is no easy way to map between values stored in these two arrays.
Also, this is not an ideal way to store this data. It is not flexible and mixes data with presentation, which should generally be avoided. I'd suggest creating a struct to store the data, something like this :
class Flexion : NSObject {
var infinitiv:String = ""
var presens:String = ""
var preteritum:String = ""
var perfektum:String = ""
var english:String = ""
init(infinitiv:String, presens:String, preteritum:String, perfektum:String, english:String) {
self.infinitiv = infinitiv
self.presens = presens
self.preteritum = preteritum
self.perfektum = perfektum
self.english = english
}
}
The main benefit in your case would be to have only one array with objects from which your table view is sourced. Thw whole thing would also get more readable and become easier for further upkeep.
So your code from the original question would become something like this :
class TableViewController: UITableViewController, UISearchResultsUpdating {
var FirstTableArray = [Flexion]()
var filteredVerbs = [Flexion]()
var resultSearchController = UISearchController()
override func viewDidLoad() {
super.viewDidLoad()
self.resultSearchController = UISearchController(searchResultsController: nil)
self.resultSearchController.searchResultsUpdater = self
self.resultSearchController.dimsBackgroundDuringPresentation = false
self.resultSearchController.searchBar.sizeToFit()
self.tableView.tableHeaderView = self.resultSearchController.searchBar
//create your objects like this :
var åadlyde = Flexion(infinitiv: "å adlyde", presens: "adlyder", preteritum: "adlød", perfektum: "har adlydt", english: "obey")
//create the rest of them
FirstTableArray = [åadlyde, åanbefale, åansette, åarbeide, åavslå, åavstå, åbade, åbanke, åbarbere, åbe, åbearbeide, åbedra, åbedøve, åbegynne, åbehandle, åbeite, åbeskrive, åbestemme…]
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (self.resultSearchController.active) {
return self.filteredVerbs.count
}
return FirstTableArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let Cell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
if (self.resultSearchController.active) {
let verb = self.filteredVerbs[indexPath.row] as Flexion
Cell.textLabel?.text = verb.infinitiv
} else {
let verb = self.FirstTableArray[indexPath.row] as Flexion
Cell.textLabel?.text = verb.infinitiv
}
return Cell
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "detailView") {
let vc = segue.destinationViewController as! UIViewController
var indexPath : NSIndexPath = self.tableView.indexPathForSelectedRow!
if (self.resultSearchController.active) {
vc.verb = filteredVerbs[indexPath.row]
} else {
vc.verb = FirstTableArray[indexPath.row]
}
}
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.filteredVerbs.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF.infinitiv CONTAINS[c] %#", searchController.searchBar.text!)
let array = (self.FirstTableArray as NSArray).filteredArrayUsingPredicate(searchPredicate)
self.filteredVerbs = array as! [Flexion]
self.tableView.reloadData()
}
}
You'd also need to make neccessary changes in "detailView" view controller - adding a verb variable of Flexion type and changing the way you present the data to user.
This way, there is no need for this "mapping" between arrays.
NOTE: note the change in the NSPredicate - it will now check only the infinitiv version of the word.

Related

prepareForSegue sending the wrong (delayed) string extracted from array to second view controller

In VC1, I am trying to extract a string from an array, and send it to VC2. I do this in my prepareForSegue method. However, when I run it, there is a lag:
I tap row1(apples) in VC1 it goes to VC2 and shows apples in VC2
Tap back to VC 1
Tap row2(bananas), string passes as apples to
VC2
Tap back to VC1
I tap row3(oranges) in VC1 it goes to VC2
and shows bananas in VC2
etc etc.
Below is the code I am using. If anyone could help show me where I am going wrong, it would be much appreciated. Thank you..
class HomeSearchViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchResultsUpdating, UISearchBarDelegate {
var filteredArray = [String]()
var shouldShowSearchResults = false
var searchController: UISearchController!
var databaseRef = FIRDatabase.database().reference()
var loggedInUser = AnyObject?()
var loggedInUserData = AnyObject?()
var productSearchArray = [AnyObject?]()
var productBrandArray = [String]()
var itemNameArray = [String]()
var picUrlArray = [String]()
var skuArray = [String]()
var nextScreenRow = Int()
var nextSceenRowNonSearch = Int()
var toPass = String()
var toPassSearch = String()
var initialSearchList = ["Apple", "Samsung", "Windows", "Google"]
#IBOutlet weak var tblSearchResults: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
loadListOfCountries()
configureSearchController()
tblSearchResults.delegate = self
tblSearchResults.dataSource = self
}
// MARK: Table Cells Stuff
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if shouldShowSearchResults {
return filteredArray.count
}
else {
return initialSearchList.count
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("homeSearchCell", forIndexPath: indexPath) as! HomeSearchTableViewCell
//note to self: need to pass on more than one variable for search
if shouldShowSearchResults {
cell.configure(filteredArray[indexPath.row], itemNameLabel: "text'", skuLabel: "TEXT")
//cell.textLabel?.text = filteredArray[indexPath.row]
}
else if shouldShowSearchResults == false{
cell.configureNISearch(initialSearchList[indexPath.row])
}
return cell
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 60.0
}
//to find out row and pass variable.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
//println("You selected cell #\(indexPath.row)!")spDetailSegue
if shouldShowSearchResults {
nextScreenRow = indexPath.row
// get to the next screen
self.performSegueWithIdentifier("spDetailSegue", sender: self)
}
else if shouldShowSearchResults == false{
nextSceenRowNonSearch = indexPath.row
// get to the next screen
self.performSegueWithIdentifier("searchSegue", sender: self)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "spDetailSegue") {
//Checking identifier is crucial as there might be multiple
// segues attached to same view
let detailVC = segue.destinationViewController as? pDetailViewController;
detailVC!.toPassSku = skuArray[nextScreenRow]
// print("vSku", )
}
if (segue.identifier == "searchSegue"){
//print("selected indexpath = ", selectedIndex)
let detailVC = segue.destinationViewController as? SearchDetailViewController;
detailVC!.toPassSearch = initialSearchList[nextSceenRowNonSearch]
}
}
//Search Bar stuff
func configureSearchController() {
// Initialize and perform a minimum configuration to the search controller.
searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search here..."
searchController.searchBar.delegate = self
searchController.searchBar.sizeToFit()
//searchController.searchBar.scopeButtonTitles = ["Products", "Users"]
searchController.searchBar.barTintColor = UIColor.whiteColor()
searchController.searchBar.tintColor = UIColor.redColor()
// Place the search bar view to the tableview headerview.
tblSearchResults.tableHeaderView = searchController.searchBar
}
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
shouldShowSearchResults = true
tblSearchResults.reloadData()
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
shouldShowSearchResults = false
tblSearchResults.reloadData()
}
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
if !shouldShowSearchResults {
shouldShowSearchResults = true
tblSearchResults.reloadData()
}
searchController.searchBar.resignFirstResponder()
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
let searchString = searchController.searchBar.text
// Filter the data array and get only those countries that match the search text.
filteredArray = itemNameArray.filter({ (item_name) -> Bool in
let countryText: NSString = item_name
return (countryText.rangeOfString(searchString!, options: NSStringCompareOptions.CaseInsensitiveSearch).location) != NSNotFound
})
// Reload the tableview.
tblSearchResults.reloadData()
}
}
once try by removing below line
tableView.deselectRowAtIndexPath(indexPath, animated: true)
from your didSelectRowAtIndexPath.
as #Lu_ suggested,
are you sure that didSelectRowAtIndexPath is called before prepareForSegue? you did not put your segue action from cell to controller?
This solved the issue. Thanks!

Detect Taps in Different areas of a tableViewCell

this one has me pretty well stumped so hoping someone can help. I have a tableview that has a custom cell with 3 labels and 2 buttons. If a user taps the label, i use didSelectRowAtIndexPath to set a variable from an array (i.e. variable = array[indexPath.row]) and then perform a segue, passing the info from the variable to a new VC with prepareForSegueWithIdentifier - this works fine and all is well in the world.
I'd like to implement the same functionality for the button within the same cell, however I'm having trouble detecting which row was tapped. I've tried an IBAction from the button where i code "func didSelectRowAtIndexPath" a second time, and it kind of works but the row is inaccurate (for example, it'll pass info for a cell a few below the one I actually tapped) -- I also tried a global variable that would get the row from the original didSelectRowAtIndexPath function but that had similar results.
What is the best practices for doing this? Is there another way to get the row i tapped from a second place within the tableView? Should i have used another label instead of a button?
Any help/ideas is greatly appreciated. Happy to post my code as well if need. Thank you!!
import UIKit
import Parse
var rowNumber:Int = 0
class FeedTableViewController: UITableViewController {
var postToWeb:String = ""
var usernames = [String]()
var linkArr = [String]()
var titleArray = [String]()
var users = [String: String]()
var createdAt = [String]()
var userList: [User] = []
var sortedArticles: [User] = []
var commentUrl:String = ""
var commentTitle: String = ""
var commentUser:String = ""
struct User {
var date:NSDate
var username:String
var url:String
var title:String
}
let cellSpacingHeight: CGFloat = 10
class commentButtonClass: UIButton {
var section:Int = 0
}
#IBAction func commentButtonPressed(sender: AnyObject) {
commentTitle = self.userList[rowNumber].title
commentUrl = userList[rowNumber].url
commentUser = (PFUser.currentUser()?.username)!
performSegueWithIdentifier("commentView", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
var query = PFUser.query()
query?.orderBySortDescriptor(NSSortDescriptor(key: "createdAt", ascending: false))
query?.findObjectsInBackgroundWithBlock({ (objects, error) in
if let users = objects {
self.usernames.removeAll(keepCapacity: true)
self.linkArr.removeAll(keepCapacity: true)
self.titleArray.removeAll(keepCapacity: true)
self.createdAt.removeAll(keepCapacity: true)
for object in users {
if let user = object as? PFUser {
self.users[user.objectId!] = user.username!
}
}
}
let getFollowedUsersQuery = PFQuery(className: "followers")
getFollowedUsersQuery.whereKey("follower", equalTo: PFUser.currentUser()!.objectId!)
getFollowedUsersQuery.orderBySortDescriptor(NSSortDescriptor(key: "createdAt", ascending: false))
getFollowedUsersQuery.findObjectsInBackgroundWithBlock { (objects, error) in
if let objects = objects {
for object in objects {
let followedUser = object["following"] as! String
let query = PFQuery(className: "posts")
query.whereKey("userId", equalTo: followedUser)
query.orderBySortDescriptor(NSSortDescriptor(key: "createdAt", ascending: false))
query.findObjectsInBackgroundWithBlock({ (objects, error) in
//create a struct with createdAt as struct name since will be unique majority of time if include time and date.
if let objects = objects {
for object in objects {
let dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = NSDateFormatterStyle.MediumStyle
dateFormatter.timeStyle = NSDateFormatterStyle.ShortStyle
let dateString = dateFormatter.stringFromDate(object.createdAt!)
self.userList.append(User(date: object.createdAt!, username:self.users[object["userId"] as! String]!, url: object["linkURL"] as! String, title: object["title"] as! String))
self.userList.sortInPlace{ $0.date.compare($1.date) == NSComparisonResult.OrderedDescending }
self.tableView.reloadData()
}
}
})
}
}
}
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Set the spacing between sections
override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return cellSpacingHeight
}
// Make the background color show through
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
headerView.backgroundColor = UIColor.clearColor()
return headerView
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return userList.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! cell
let dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = NSDateFormatterStyle.MediumStyle
dateFormatter.timeStyle = NSDateFormatterStyle.ShortStyle
let dateString = dateFormatter.stringFromDate(self.userList[indexPath.section].date)
myCell.titleLabel.text = self.userList[indexPath.section].title
myCell.userLabel.text = self.userList[indexPath.section].username
myCell.createdDateLabel.text = dateString
myCell.rowNumberLabel.tag = indexPath.section
// add border and color
myCell.backgroundColor = UIColor.whiteColor()
myCell.layer.borderColor = UIColor.clearColor().CGColor
myCell.layer.borderWidth = 2
myCell.layer.cornerRadius = 0
myCell.clipsToBounds = true
return myCell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let currentCell = tableView.cellForRowAtIndexPath(indexPath) as UITableViewCell!;
rowNumber = indexPath.section
postToWeb = userList[indexPath.section].url
print(postToWeb)
performSegueWithIdentifier("webView", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "webView") {
// initialize new view controller and cast it as your view controller
let vc = segue.destinationViewController as! WebViewController
// your new view controller should have property that will store passed value
vc.passedValue = postToWeb
}
if (segue.identifier == "commentView") {
let commentVc = segue.destinationViewController as! commentsViewController
commentVc.passedCommentTitle = commentTitle
commentVc.passedCommentUrl = commentUrl
commentVc.passedCommentUsername = commentUser
}
}
in the above, the segue to "webView" works perfectly and the data is passed. In the commentView - data is passed, but it's incorrect.
To get the UITableViewCell that was tapped (as an Action of the Button), you have different options:
1) Use Target-Action method, something like:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! cell
//...More Code
myCell.myBtn.addTarget(self, action: "myActionMethod:", forControlEvents: .TouchUpInside)
//... More Code
return myCell
}
And then define your action method (note, yourTbl is an instance of UITableView):
func myActionMethod(sender:UIButton){
//base on the hierarchy of the UI
//Suppose in this case:
//(UITableView)->(UITableViewCell)->(ContentView)->UIButton then:
if let tableViewCell = sender.superview?.superview as? UITableViewCell, indexPath = yourTbl.indexPathForCell(tableViewCell){
//Here you have the IndexPath
}
}
2) The same Idea, but basically your going to set the Tag property of the UIButton (to your row cell), and using Target-Action on the sender (parameter) you get the tag of the button.
Update Sample:
https://github.com/Abreu0101/SampleTargetActionMethod
What is the best practices for doing this?
You need you delegate pattern. See below example.
The CustomTableViewCell class:
protocol CustomTableViewCellDelegate {
func customTableViewCell(cell: CustomTableViewCell, didTextInLabelChanged text: String)
}
class CustomTableViewCell: UITableViewCell {
var delegate: CustomTableViewCellDelegate?
func handeLabelTextChanged(text: String) {
delegate?.customTableViewCell!(self, didTextInLabelChanged: text)
}
// ... some other code
}
The CustomViewController class:
class CustomViewController {
// some code
}
class CustomViewController: UITableViewDataSource {
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = // dequeue the right cell
cell.delegate = self
return cell
}
}
class CustomViewController: CustomTableViewCellDelegate {
func customTableViewCell(cell: CustomTableViewCell, didTextInLabelChanged text: String) {
// do tableView update with changed text
}
}
As you see all "changed" (or "tapped", or whatever) logic is going on in you cell. To notify controller about this changes you simply create a protocol with all methods what you need (in this case i notify my controller about text in label changed), then you need to create a var delegate: YourDelegate? and call it where you need. Next step you need implement "handle" function in your controller. It's very simple. You just need implement the customTableViewCell() function from CustomTableViewCellDelegate and also set the delegate variable to self for CustomTableViewCell.
UPDATE
When a user taps a button within the cell, i need the specific info from that cell, based on the indexPath.row
Very easy. Remember that in your controller you implement the CustomTableViewCellDelegate? So, to get specific info from cell you can do this:
class CustomViewController: CustomTableViewCellDelegate {
func customTableViewCell(cell: CustomTableViewCell, didTextInLabelChanged text: String) {
// here you get `someVariable` from your cell.
// where `someVariable` you set in the `cellForRowAtIndexPath` controller's
// method
let info = cell.someVariable
// or, if for some reason you not save in your cell some data,
// but you need the `indexPath.row` to get this data you
// can find this `indexPath` like this:
if let indexPath = tableView.indexPathForCell(cell) {
let number = youArrayOfNumbers[indexPath.row]
}
}
}

UISearchController for TableView

This is what my Table View looks like. Each cell Strings a text from a .txt
Search Filter works fine. But When I select one of the results e.g. "How" it prints the the Content of "Hello" instead.
I'm thinking this is probably because "Hello" is programmed as the first String so it will always be the result of the first cell when clicked. I am needing help with figuring out how the search results are linked to it's own content. if that makes any sense?
Here's the code below. The code reads from .txt file swell as search the text file. the results when clicked on do not match.
import UIKit
class title_ViewController: UITableViewController, UISearchResultsUpdating {
var SongTitle = [String]()
var SongLyrics = [LText]()
var filteredSongs = [String]()
var resultSearchController = UISearchController()
override func viewDidLoad() {
super.viewDidLoad()
self.resultSearchController = UISearchController(searchResultsController: nil)
self.resultSearchController.searchResultsUpdater = self
self.resultSearchController.dimsBackgroundDuringPresentation = false
self.resultSearchController.searchBar.sizeToFit()
self.tableView.tableHeaderView = self.resultSearchController.searchBar
self.tableView.reloadData()
if let path = NSBundle.mainBundle().pathForResource("DataBase", ofType: "txt"){
do {
let stringFromFile = try String(contentsOfFile:path, encoding: NSUTF8StringEncoding)
var Title: [String] = stringFromFile.componentsSeparatedByString("#")
Title.removeAtIndex(0)
//Seperates the Title from the Lyrics
var t1 = Array(Title[0].componentsSeparatedByString("^"))
t1.removeAtIndex(0)
SongTitle = Title
SongLyrics = [LText(LyricsText: t1)]
}catch{
print(error)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.resultSearchController.active{
return self.filteredSongs.count
}else{
return self.SongTitle.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let Cell: AnyObject = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
Cell.textLabel?!.text = SongTitle[indexPath.row]
if self.resultSearchController.active{
Cell.textLabel?!.text = self.filteredSongs[indexPath.row]
}else{
Cell.textLabel?!.text = self.SongTitle[indexPath.row]
}
return Cell as! UITableViewCell
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.filteredSongs.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c]%#", searchController.searchBar.text!)
let array = (self.SongTitle as NSArray).filteredArrayUsingPredicate(searchPredicate)
self.filteredSongs = array as! [String]
self.tableView.reloadData()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let indexPath: NSIndexPath = self.tableView.indexPathForSelectedRow!
let DestViewController = segue.destinationViewController as! ViewController
DestViewController.LyricString = SongTitle[indexPath.row]
}
}
any help is welcomed, thanks in advance.
The problem lies in your prepareForSegue. You need to amend it to test whether the searchController is active. If so, you should use filteredSongs to determine which song to pass to the destination VC, rather than SongTitle:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let indexPath: NSIndexPath = self.tableView.indexPathForSelectedRow!
let DestViewController = segue.destinationViewController as! ViewController
if self.resultSearchController.active {
DestViewController.LyricString = self.filteredSongs[indexPath.row]
}else{
DestViewController.LyricString = self.SongTitle[indexPath.row]
}
}
(As an aside, it is convention that variable names should start with a lowercase letter - songTitle not SongTitle, etc.)
You can perform segue manually in didSelectRowAtIndexPath by searching position of selected String from array, then you can get correct position -
Example -
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
let Cell: AnyObject = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let str: String= Cell.textLabel.text
// now find str in array and then get position
int pos = getPositionOfStringInArray(str)
// now perform segue for pos
//code...
}

How to filter object array in updateSearchResultsForSearchController when using UISearchController?

My search controller class is as following:
class FeastSearchTableViewController: UITableViewController, UISearchResultsUpdating {
var feastEntries = [FeastEntry]()
var filteredFeasts = [FeastEntry]()
/* let tableData = ["One","Two","Three","Twenty-One"]
var filteredTableData = [String]()*/
var resultSearchController = UISearchController()
override func viewDidLoad() {
super.viewDidLoad()
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
self.tableView.tableHeaderView = controller.searchBar
return controller
})()
self.feastEntries = [FeastEntry(category:"Chocolate", name:"chocolate Bar"),
FeastEntry(category:"Chocolate", name:"chocolate Chip"),
FeastEntry(category:"Chocolate", name:"dark chocolate"),
FeastEntry(category:"Hard", name:"lollipop"),
FeastEntry(category:"Hard", name:"candy cane"),
FeastEntry(category:"Hard", name:"jaw breaker"),
FeastEntry(category:"Other", name:"caramel"),
FeastEntry(category:"Other", name:"sour chew"),
FeastEntry(category:"Other", name:"gummi bear")]
// Reload the table
self.tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// 2
if (self.resultSearchController.active) {
return self.filteredFeasts.count
}
else {
return self.feastEntries.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let feasts = self.feastEntries[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier("TimelineCellPhoto") as! TimelineCell
cell.typeImageView.image = UIImage(named: "timeline-photo")
cell.profileImageView.image = UIImage(named: "profile-pic-2")
cell.nameLabel.text = feasts.name
cell.photoImageView?.image = UIImage(named: "dish")
cell.dateLabel.text = "2 mins ago"
cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("feastDetail", sender: tableView)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "feastDetail" {
let candyDetailViewController = segue.destinationViewController as! UIViewController
}
}
func updateSearchResultsForSearchController(searchController: UISearchController)
{
filteredFeasts.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "category CONTAINS[c] %#", searchController.searchBar.text)
//let array = (feastEntries as NSMutableArray).filteredArrayUsingPredicate(searchPredicate)
/* let array = (feastEntries as NSArray).filteredArrayUsingPredicate(searchPredicate)
filteredFeasts = array as! [String]
*/
self.tableView.reloadData()
}
}
struct FeastEntry {
let category : String
let name : String
}
I have two requirements. First one is as how can I filter feastEntries array on the basis of category inside updateSearchResultsForSearchController.
Second is as how can I implement scope bar filter for this scenario.
Check filter method in swift...using this you can filter any collection type,and you could write something like this:
let filteredEntry = self.feastEntries.filter { $0.0 == “Chocolate”//filter condition }
ScopeBar Implementation:
Create a segmented control with chocolate,hard and other segment ...
Create an IBaction for that segmented control depending on selected segment apply filter on feastEntries array as shown above.

Segue from Custom Data Cell in Swift

I am new to Swift and IOS, I used to have a normal table view and everything worked. I have now implemented a Custom Table view cell and was wondering how to implement my PrepareForSegue method with my UITableView. I want to be able to send the selected Table Cell index to the segue for the next controller to access a certain array position. Right now the sender Object is a CustomCell: UITableViewCell object. can I access the table index from that object or some other way?
//
// ViewController.swift
// OBU Bus Tracker
//
// Created by AJ Norton on 4/20/15.
// Copyright (c) 2015 AJ Norton. All rights reserved.
//
import UIKit
class ViewController: UIViewController, UITableViewDelegate {
#IBOutlet var table: UITableView!
var locations = [String]()
var overall = Dictionary<String, AnyObject>()
override func viewDidLoad() {
super.viewDidLoad()
// check if the user is running the app for the first time
// if let firstTime = NSUserDefaults.standardUserDefaults().objectForKey("firstTime")
// {
//
// println("second time")
// }
// else
// {
// println("worked")
// NSUserDefaults.standardUserDefaults().setBool(true, forKey: "firstTime")
// }
if let path = NSBundle.mainBundle().pathForResource("busSchedule", ofType: "plist")
{
if let dict = NSDictionary(contentsOfFile: path) as? Dictionary<String, AnyObject>
{
locations = dict.keys.array
overall = dict
}
}
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return locations.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell: CustomRouteViewCell = table.dequeueReusableCellWithIdentifier("Cell") as! CustomRouteViewCell
cell.locationTitle.text = "\(locations[indexPath.row])"
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
println("I was clicked")
performSegueWithIdentifier("routeToTime", sender: indexPath.row)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//TODO
println("\(sender?.integerValue)")
if segue.identifier == "routeToTime"
{
var ttvc = segue.destinationViewController as! TimeViewController
var s = sender as! CustomRouteViewCell
println("\(s.in)")
var place = s.indentationLevel as! Int
var dicts = overall[locations[place]] as! Dictionary<String,AnyObject>
var arr = dicts["Weekday"] as! [Int]
ttvc.days = dicts
ttvc.times = arr.reverse()
}
}
}
performSegueWithIdentifier("routeToTime", sender: indexPath.row)
you call above method. so in this method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
the local argument sender is indexPath.row, is NOT a instance of CustomRouteViewCell.
So you can write code:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "routeToTime"
{
var ttvc = segue.destinationViewController as! TimeViewController
var place = sender as! Int
var dicts = overall[locations[place]] as! Dictionary<String,AnyObject>
var arr = dicts["Weekday"] as! [Int]
ttvc.days = dicts
ttvc.times = arr.reverse()
}
}
It is best to write code in the MVC patten, so don't store your data in a tableview cell.
Whateve you want do that, you must set a cell's property(For example: rowIndex: Int) that indicate the indexRow of the data in this method 'func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)'. Your code looks like:
if segue.identifier == "routeToTime"
{
var ttvc = segue.destinationViewController as! TimeViewController
var cell = sender as! CustomRouteViewCell
var place = cell.indexRow
var dicts = overall[locations[place]] as! Dictionary<String,AnyObject>
var arr = dicts["Weekday"] as! [Int]
ttvc.days = dicts
ttvc.times = arr.reverse()
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell: CustomRouteViewCell = table.dequeueReusableCellWithIdentifier("Cell") as! CustomRouteViewCell
cell.locationTitle.text = "\(locations[indexPath.row])"
cell.indexRow = ... //Set the index of cell's data.
return cell
}
By the way, the indentationLevel property is:
var indentationLevel: Int // adjust content indent. default is 0
How about storing the cell index as a NSUserDefault and retrieving it from the other side?
That way you can use a generic segue created through the storyboard, implement the table cells on selected function.

Resources