How to update the data from the searchResultsController (UISearchController) - ios

So I am using a searchResultsController, which takes an array of Strings, and shows them in a tableview (It's an autocomplete list). When the user presses the 'Search' button on the keyboard, and the entered String is not yet in my Tableview, I want to add it, and update the tableview accordingly.
The issue is that once I added a String to the array, and make a new search, the array isn't updated with the new value!
Here is my code:
In my ViewDidLoad() on the Overview.swift class
class Overview: UIViewController,UISearchControllerDelegate,UISearchBarDelegate,UICollectionViewDelegate,UICollectionViewDataSource {
var mySearchController : UISearchController!
var mySearchBar : UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
let src = SearchResultsController(data: convertObjectsToArray())
// instantiate a search controller and keep it alive
mySearchController = UISearchController(searchResultsController: src)
mySearchController.searchResultsUpdater = src
mySearchBar = mySearchController.searchBar
//set delegates
mySearchBar.delegate = self
mySearchController.delegate = self
}
This is the data function, used for the UISearchController
func convertObjectsToArray() -> [String] {
//open realm and map al the objects
let realm = try! Realm()
let getAutoCompleteItems = realm.objects(AutoComplete).map({$0})
...
return convertArrayStrings // returns [String] with all words
}
So when the user pressed the search button on the keyboard, I save that word to my database.
Now I need to put the updated version of convertObjectsToArray() in my searchResultsController, but I haven't found out how to do this. All help is welcome
And last, but not least, my SearchResultsController class, which is used in the viewDidLoad of my Overview.swift class.
class SearchResultsController : UITableViewController {
var originalData : [String]
var filteredData = [String]()
init(data:[String]) {
self.originalData = data
super.init(nibName: nil, bundle: nil)
}
required init(coder: NSCoder) {
fatalError("NSCoding not supported")
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.filteredData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel!.text = self.filteredData[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
clickedInfo = filteredData[indexPath.row]
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
}
For the filtering of my words in the tableview (when user types something, only matching Strings are shown), I use the following extension.
extension SearchResultsController : UISearchResultsUpdating {
func updateSearchResultsForSearchController(searchController: UISearchController) {
let sb = searchController.searchBar
let target = sb.text!
self.filteredData = self.originalData.filter {
s in
let options = NSStringCompareOptions.CaseInsensitiveSearch
let found = s.rangeOfString(target, options: options)
return (found != nil)
}
self.tableView.reloadData()
}

You can use the search controller's update function for that I think:
func updateSearchResultsForSearchController(searchController: UISearchController) {
convertObjectsToArray()
self.tableView.reloadData()
}

Related

Searching TableView can't select row

While searching a tableView, every time I try to select a row it just takes me back to the unsearched tableView. What am I missing? the segue works perfectly when not filtering through the table. The ability to select a row just disapears while the searchBar is activated.
import UIKit
import Foundation
class BenchmarkWODViewController: UITableViewController, UISearchResultsUpdating {
var WodList = [WOD]()
var FilteredWodList = [WOD]()
var Keyword = ""
var searchController : UISearchController?
var index = Int()
#IBAction func backButton(sender: AnyObject) {
self.navigationController?.popViewControllerAnimated(true)
}
override func viewDidLoad() {
super.viewDidLoad()
for wodData in BenchmarkWODs.library {
let wod = WOD(dictionary: wodData)
WodList.append(wod)
}
// Search Bar
self.searchController = UISearchController(searchResultsController: nil)
self.searchController?.searchBar.autocapitalizationType = .None
self.tableView.tableHeaderView = self.searchController?.searchBar
self.searchController?.searchResultsUpdater = self
self.Keyword = ""
definesPresentationContext = true
self.filterByName()
}
func filterByName(){
self.FilteredWodList = self.WodList.filter({ (wod: WOD) -> Bool in
if self.Keyword.characters.count == 0 {
return true
}
if (wod.name?.lowercaseString.rangeOfString(self.Keyword.lowercaseString)) != nil {
return true
}
return false
})
self.tableView.reloadData()
}
// Search Bar Function
func updateSearchResultsForSearchController(searchController: UISearchController) {
Keyword = searchController.searchBar.text!
self.filterByName()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.FilteredWodList.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("BenchmarkCell", forIndexPath: indexPath) as UITableViewCell
let wod = self.FilteredWodList[indexPath.row]
if let wodName = wod.name {
cell.textLabel?.text = wodName
}
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.filterByName()
self.performSegueWithIdentifier("showBenchmarkDetail", sender: nil)
}
}
Figured it out after playing around. Apparently adding the below code corrects the problem.
searchController?.dimsBackgroundDuringPresentation = false
swift 'dimsBackgroundDuringPresentation' was deprecated in iOS 12.0 Use the obscuresBackgroundDuringPresentation property instead.
searchController?.obscuresBackgroundDuringPresentation = false
searchController.obscureBAckgroundDuringPresentation = false is deprecated in IOS 12.0, so for me it was issue with other gesture detector added to the tableview , so make sure you dont have any other gesture detector and touchesview method that distort the normal working flow of tablview's delegate method( didSelectAtRow ), hope it will work,

TableView not loading cells

My table view isn't loading the custom cells that are put into an array when I do a certain function. I know the the array contains the cells, but am at a loss as to why the cells aren't showing. It did work before but I made a few changes elsewhere in the code and now it does not work. Below is the code where I believe the error might possibly be:
#IBOutlet weak var incomeExpenseTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
incomeExpenseTableView.reloadData()
incomeExpenseTableView.delegate = self
incomeExpenseTableView.dataSource = self
}
override func viewDidAppear(animated: Bool) {
incomeExpenseTableView.reloadData()
}
#IBAction func addButtonPressed(sender: AnyObject) {
let addMessage = SCLAlertView()
addMessage.addButton("Income") {
self.performSegueWithIdentifier("incomeSegue", sender: self)
}
addMessage.addButton("Expense") {
self.performSegueWithIdentifier("expenseSegue", sender: self)
}
addMessage.showInfo("New Entry", subTitle: "Select either an income or expense to add")
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let post = DATA_KEY._loadedPosts[indexPath.row]
if let cell = tableView.dequeueReusableCellWithIdentifier("IncomeExpenseCell") as? IncomeExpenseCell {
cell.configureCell(post)
return cell
} else {
let cell = IncomeExpenseCell()
cell.configureCell(post)
return cell
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return DataService.instance._loadedPosts.count
}
func onPostsLoaded(notif: AnyObject) {
incomeExpenseTableView.reloadData()
}
FYI DATA_KEY = DataService.instance
Thanks
if you have a tableviewcell's identifier named "IncomeExpenseCell", the "if let" will always be successful, why using else, you can use as! instead of as?, you don't need 'else' here, and IncomeExpenseCell() maybe will not satisfy your 'cell init'
if let cell = tableView.dequeueReusableCellWithIdentifier("IncomeExpenseCell") as? IncomeExpenseCell {
cell.configureCell(post)
return cell
}
let cell = tableView.dequeueReusableCellWithIdentifier("IncomeExpenseCell") as! IncomeExpenseCell {
cell.configureCell(post)
return cell
}

UISearchController is appearing into nextView with swift

I have implemented a simple UISearchController into my UITableViewController programatically. And below is my complete code:
import UIKit
class TableViewController: UITableViewController, UISearchResultsUpdating {
let appleProducts = ["Mac","iPhone","Apple Watch","iPad"]
var filteredAppleProducts = [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()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if (self.resultSearchController.active){
return self.filteredAppleProducts.count
}else{
return self.appleProducts.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell?
if (self.resultSearchController.active)
{
cell!.textLabel?.text = self.filteredAppleProducts[indexPath.row]
return cell!
}
else
{
cell!.textLabel?.text = self.appleProducts[indexPath.row]
return cell!
}
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let vc = self.storyboard!.instantiateViewControllerWithIdentifier("Detail") as! DetailViewController
self.navigationController!.pushViewController(vc, animated: true)
}
func updateSearchResultsForSearchController(searchController: UISearchController){
self.filteredAppleProducts.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (self.appleProducts as NSArray).filteredArrayUsingPredicate(searchPredicate)
self.filteredAppleProducts = array as! [String]
self.tableView.reloadData()
}
}
My resultSearchController is working completely fine but when I click on any cell then UISearchController is appearing into my next view too as shown into below image:
But my Second view is empty.
how I can remove this resultSearchController when I switch to another view so it will not appear into next view?
Project Sample for more Info.
Just add one line in viewDidLoad
self.definesPresentationContext = true
Doc
A Boolean value that indicates whether this view controller's view is covered when the view controller or one of its descendants presents a view controller.
GIF

How to add a cell to my Table View dynamically using a button

I am trying to add a cell to my table view with a button. Everything I have read and watched suggests that what I have written should work, but it doesn't. Any suggestions?
import UIKit
class RootViewController: UITableViewController, UITableViewDataSource, UITableViewDelegate {
private var cellPointSize: CGFloat!
private var albumsList: AlbumList!
private var albums:[Album]!
private let albumCell = "Album"
#IBOutlet var myTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let preferredTableViewFont = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
cellPointSize = preferredTableViewFont.pointSize
albumsList = AlbumList.sharedAlbumList
albums = albumsList.albums
self.myTableView.dataSource = self
self.myTableView.delegate = self
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
}
// MARK: - Table view data source
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return albums.count
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Albums"
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = myTableView.dequeueReusableCellWithIdentifier(albumCell, forIndexPath: indexPath) as! UITableViewCell
//cell.textLabel?.font = fontForDisplay(atIndexPath: indexPath)
cell.textLabel?.text = albums[indexPath.row].name
cell.detailTextLabel?.text = albums[indexPath.row].artist
return cell
}
#IBAction func addNewAlbumAction(sender: UIBarButtonItem) {
var newAlbum = Album(nameIn: "New Title", yearIn: "New Year", artistIn: "New Artist", labelIn: "New Label")
albumsList.addAlbum(newAlbum)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.myTableView.reloadData()
})
}
func saveData(albumObject: Album) {
var archiveArray = NSMutableArray(capacity: albums.count)
for a in albums {
var albumEncodedObject = NSKeyedArchiver.archivedDataWithRootObject(a)
archiveArray.addObject(albumEncodedObject)
}
var userData = NSUserDefaults()
userData.setObject(archiveArray, forKey: "albums")
userData.synchronize()
}
My albums array is adding the data correctly. I can see the albums in the debugger. The delegate methods are never being called after the first time when the app loads. Any ideas?
in tableView:numberOfRowsInSection:, it returns albums.count
but when the button is pressed, you add the new album to albumsList
The problem is, albums will not get update.
So I think you should return albumsList.albums.count instead.
and in tableView:cellForRowAtIndexPath:, you modify the cell correspond to albumsList.albums[indexPath.row]

headerview from previous view takes up space in UISearchController search results

I have a simple tableView, DailyAttendanceController, with a searchbar. Searchbar was added in its viewDidLoad. View TableView here.
import UIKit
class OldCellsTableViewController: UITableViewController {
let data = ["A", "B", "C"]
var searchController:UISearchController!
let resultsController = SearchResultsUpdater()
override func viewDidLoad() {
super.viewDidLoad()
resultsController.data = data
searchController = UISearchController(searchResultsController: resultsController)
let searchBar = self.searchController.searchBar
searchBar.scopeButtonTitles = ["All", "Short", "Long"]
searchBar.placeholder = "Search for a student"
searchBar.sizeToFit()
self.tableView.tableHeaderView = searchBar
self.searchController.searchResultsUpdater = resultsController
}
// MARK: - Table view data source
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
let info = data[indexPath.row]
cell.textLabel?.text = info
return cell
}
}
I then created another class, SearchResultsController, to handle the search. Searching works fine, but space is allocated for DailyAttendanceController's header. View SearchResultsController here
How do I remove this?
Code for my SearchResultsController:
import UIKit
import CoreData
class SearchResultsController: UITableViewController, UISearchResultsUpdating {
var data:[String] = []
var filteredData:[String] = []
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredData.count
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
filteredData.removeAll(keepCapacity: true)
let searchString = searchController.searchBar.text
for info in data {
if (info.uppercaseString.rangeOfString(searchString.uppercaseString) != nil) {
filteredData.append(info)
}
}
tableView.reloadData()
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = filteredData[indexPath.row]
return cell
}
}

Resources