getting the wrong index when I search something on the searcher - ios

This is an example to explain a problem that I’m having.
I have 3 elements in an array. the index of the array matches each sentences. Like
The 0 index matches “hello” (0 index)
The 1 index matches “nice” (1 index)
The 2 index matches “how are you?” (2 index)
I’ve put UISearchBar in my tableView. When I type “h” in the searchBar, “hello” and “how are you” show up perfectly, but the thing is, when I touch “how are you” which is 2 index on the tableview list, it shows “nice” on the next viewController (I have two viewControllers and the variable called myIndex use the index on the next viewController ) because the variable gets 1 index not 2 index. How should I solve this?
The full code is as follows :
import UIKit
class tableviewtest: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var searchBar: UISearchBar!
func tableView(_ tableView: UITableView,
moveRowAt sourceIndexPath: IndexPath,
to destinationIndexPath: IndexPath) {
}
var searchArr = [String]()
var searching = false
var copiedArray:[String] = eng // eng is the original array that has the full elements from another view controller.
let cellIdentifier = "xxx"
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:UITableViewCell = self.tableview.dequeueReusableCell(withIdentifier: cellIdentifier, for:indexPath)
if searching {
cell.textLabel?.text = searchArr[indexPath.row]
}
else {
cell.textLabel?.text = copiedArray[indexPath.row]
}
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching {
return searchArr.count
}
else {
return copiedArray.count
}
}
func tableView(_ tableview: UITableView, didSelectRowAt indexPath: IndexPath) {
myIndex = indexPath.row
performSegue(withIdentifier: "segue1", sender: self)
}
#IBOutlet weak var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableview.delegate = self
tableview.dataSource = self
self.tableview.register(UITableViewCell.self,forCellReuseIdentifier: cellIdentifier)
}
}
extension tableviewtest: UISearchBarDelegate {
func searchBar (_ searchBar:UISearchBar, textDidChange searchText: String) {
searchArr = copiedArray.filter({$0.prefix(searchText.count) == searchText})
searching = true
tableview.reloadData()
}
}

At the moment you get the string from the array you have to consider the searching state
let selectedItem = searching ? searchArr[myIndex] : filteredArr[myIndex]
and you have to reset searching if the search string is empty
func searchBar (_ searchBar:UISearchBar, textDidChange searchText: String) {
if searchText.isEmpty {
searching = false
searchArr.removeAll()
} else {
searchArr = filteredArr.filter({$0.prefix(searchText.count) == searchText})
searching = true
}
tableview.reloadData()
}
You could even pass the string as sender parameter in performSegue and hand it over to the next view controller in prepare(for segue
func tableView(_ tableview: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedItem = searching ? searchArr[indexPath.row] : filteredArr[indexPath.row]
performSegue(withIdentifier: "segue1", sender: selectedItem)
}
Side note:
Reconsider your naming of the variables: Why is the data source array once called filteredArr and then numberOfRows, and why is the non-filtered array called filteredArr anyway?

Related

Trouble passing the data from multiple selected tableview cells initially populated from Realm into another VC tableview. Swift

I have been at this for a few days now, what I am trying to do is to be able to select 5 cells (previous populated by a realm database). Any help on this would be really appreciated.
Thanks Mark
The following is my current code as is relates to the Segue to pass the data. I think I have edited the code down to the key elements. Let me know if you need any other information.
Edits Made - based on the suggestion made below. I took a step back and approached the problem differently, and developed a solution. I have updated the code below to reflect my working solution. Hope this helps someone else in the future. Cheers Mark
Primary VC
import UIKit
import RealmSwift
class ExercisesSelectionTimed: UITableViewController{
var realm = try! Realm()
var exercises : Results<ExercisesModel>?
#IBOutlet weak var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
//populates the Realm Data with the default list
exercises = realm.objects(ExercisesModel.self)
tableView.allowsMultipleSelection = true
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return exercises?.count ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ExercisesAddedTableViewCell", for: indexPath) as! ExercisesAddedTableViewCell
// get the specific exercise in the array
let exercise = exercises?[indexPath.row]
cell.exercisenameLabel.text = exercise?.name
cell.equipmentnameLabel.text = exercise?.equipment
return cell
}
private func registerTableViewCells() {
let textFieldCell = UINib(nibName: "ExercisesAddedTableViewCell",bundle: nil)
self.tableview.register(textFieldCell,forCellReuseIdentifier: "ExercisesAddedTableViewCell")
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch (timerSelected) {
case "FGB":
//User needs to select 5 exercises to populate the FGB timer on the next VC
if let selectedRows = tableView.indexPathsForSelectedRows {
//lets the user select 5 cells
if selectedRows.count == 5 {
performSegue(withIdentifier: K.FGB, sender: self)
}
}
default : print ("error")
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch (timerSelected) {
case "FGB":
if let selectedRows = tableView.indexPathsForSelectedRows {
let selected = selectedRows.map{exercises?[$0.row]}
let destinationVC = segue.destination as! FBGTimerVCTable
destinationVC.selectedExercise = selected
}
default : print ("error")
}
}
}//last bracket in Class
The target for the data
import UIKit
import RealmSwift
class FBGTimerVCTable: HeartRateMonitorBrain{
var realm = try! Realm()
var selectedExercise = [ExercisesModel?]()
#IBOutlet weak var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
}//last bracket in Class
extension FBGTimerVCTable: UITableViewDataSource{
//setup tableview
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ExercisesAddedTableViewCell", for: indexPath) as! ExercisesAddedTableViewCell
//get the specific 5 exercises selected in the ExercisesSelectionTimed VC
cell.exercisenameLabel.text = selectedExercise[indexPath.row]?.name
cell.equipmentnameLabel.text = selectedExercise[indexPath.row]?.equipment
return cell
}
private func registerTableViewCells() {
let textFieldCell = UINib(nibName: "ExercisesAddedTableViewCell",bundle: nil)
self.tableview.register(textFieldCell,forCellReuseIdentifier: "ExercisesAddedTableViewCell")
}
}//last bracket in Class Extension
Realm
import RealmSwift
class ExercisesModel : Object {
#objc dynamic var name : String = ""
#objc dynamic var equipment : String = ""
}

When I tapped segment Odd or even I am getting true and false value in Tableview instead of odd value or even value

When I press odd it shows true or false. How can Get values of array I'm trying to print odd value when i tap odd on segment or even values when i tap even , How can get this. I'm new in ios. Please help
class ViewController: UIViewController, UITableViewDelegate,UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var segmentOutlet: UISegmentedControl!
let numberArray = Array(1...100)
var segmentIndex = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
tableView.delegate = self
tableView.dataSource = self
}
#IBAction func segmentControl(_ sender: UISegmentedControl) {
tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : TableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableViewCell
cell.numberLbl.text = String(numberArray[indexPath.row])
if segmentOutlet.selectedSegmentIndex == 1 {
cell.numberLbl.text = String(numberArray[indexPath.row] % 2 == 0)
}
if segmentOutlet.selectedSegmentIndex == 2 {
cell.numberLbl.text = String(numberArray[indexPath.row] % 2 == 1)
}
return cell
}
}
my Simulator image:
Your numberArray[indexPath.row] % 2 == 0 has a ==, which evaluates to true/false. It doesn't really make sense. Also, there's no need to check if the current indexPath.row is odd or even... you're trying to display a list of even or odd numbers, so what does indexPath.row have to do with that?
Instead, try making a second array, filteredArray. This will be where the table view directly gets its data from.
let numberArray = Array(1...100)
lazy var filteredArray: [Int] = { numberArray }() /// return `numberArray` by default
You can then update filteredArray based on the selectedSegmentIndex.
#IBAction func segmentControl(_ sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case 1: /// even numbers
filteredArray = numberArray.filter { $0 % 2 == 0}
case 2: /// odd numbers
filteredArray = numberArray.filter { $0 % 2 == 1}
default: /// all numbers
filteredArray = numberArray
}
tableView.reloadData()
}
Finally, just read from filteredArray in your data source methods.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredArray.count /// use `filteredArray`
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: TableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableViewCell
cell.numberLbl.text = String(filteredArray[indexPath.row]) /// also use `filteredArray`
return cell
}

UISearchBar is not searching when entering text

I have a Table View that is working fine. However, when I try to implement a UISearchBar and display filtered data, nothing gets filtered. This is my View Controller:
import UIKit
class MealPlanViewController: UIViewController, UISearchBarDelegate {
private var model = MealPlanModel()
private var mealPlan = [MealPlan]()
var filteredData: [MealPlan]!
#IBOutlet weak var topBarStackView: UIStackView!
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
model.delegate = self
searchBar.delegate = self
filteredData = mealPlan
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filteredData = []
if searchText == "" {
filteredData = mealPlan
}
else {
for item in mealPlan {
if ((item.title?.lowercased().contains(searchText.lowercased())) != nil) {
filteredData.append(item)
}
}
}
self.tableView.reloadData()
}
}
extension MealPlanViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MealPlanCell", for: indexPath) as! MealPlanCell
let filteredMealPlaninTable = filteredData[indexPath.row]
cell.displayMealPlan(filteredMealPlaninTable)
return cell
}
}
extension MealPlanViewController: MealPlanProtocol {
func mealPlansRetrieved(mealPlans: [MealPlan]) {
self.filteredData = mealPlans
tableView.reloadData()
}
}
A couple of notes:
When I run a print(self.filteredData) in my `func mealPlansRetrieved', the console returns all of my data as if it wasn't filtered, but
As soon as I start typing in the search bar, the table view doesn't return any cells, which seems contradictory to the above
For reference, this is the code before filtering that did work:
extension MealPlanViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return mealPlan.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MealPlanCell", for: indexPath) as! MealPlanCell
let mealPlanInTable = mealPlan[indexPath.row]
cell.displayMealPlan(mealPlanInTable)
return cell
}
}
extension MealPlanViewController: MealPlanProtocol {
func mealPlansRetrieved(mealPlans: [MealPlan]) {
self.mealPlan = mealPlans
tableView.reloadData()
}
}
Any help/guidance is much appreciated!
Contains returns boolean so this will never fail: item.title?.lowercased().contains(searchText.lowercased())) != nil
To make check work you can simply remove "!= nil".
Im not sure from where you are calling mealPlansRetrieved, but you might want to keep the line "self.mealPlan = mealPlans" instead of "self.filteredData = mealPlans".

Finding the [indexpath.row] value in a TableView

Essentially I am attempting to change a variable when a specific row is selected however, the code is still printing -1. Here is all my code relating. I am trying to be able to click a certain tableview cell and then be able to print out that text. Would the searchBar effect my values? I first code the tableview and then the searchbar and then I implement a submit button which prints the values of my variables.
class ViewController: UIViewController, UITableViewDataSource, UISearchBarDelegate, UITableViewDelegate {
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView!
let Data = ["dog","cat","goat"]
var filteredData: [String]!
var num = -1
var animal: String = ""
override func viewDidLoad() {
super.viewDidLoad()
if tableView != nil {
self.tableView.dataSource = self
}
filteredData = Data
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as UITableViewCell
cell.textLabel?.text = filteredData[indexPath.row]
print(indexPath.row)
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredData.count
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
num = 0
}
if indexPath.row == 1 {
num = 1
}
if indexPath.row == 2 {
num = 2
}
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filteredData = searchText.isEmpty ? Data : Data.filter { (item: String) -> Bool in
// If dataItem matches the searchText, return true to include it
return item.range(of: searchText, options: .caseInsensitive, range: nil, locale: nil) != nil
}
tableView.reloadData()
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
self.searchBar.showsCancelButton = true
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchBar.showsCancelButton = false
searchBar.text = ""
searchBar.resignFirstResponder()
}
#IBAction func Submit(_ sender: Any) {
print(num)
print(filteredData.count)
if num == 0 {
animal = "dog"
}
if num == 1 {
animal = "cat"
}
if num == 2 {
animal = "goat"
}
print(animal)
}
}
There are a few issues that are not allowing you to achieve what you want:
The == operator is checking if two variables are equal to each other, not assigning one variable to another, and it will return a boolean value, either true or false. In the body of your if statements, change == to = to assign a value to the variable num.
Change your code to:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
num = 0
}
if indexPath.row == 1 {
num = 1
}
if indexPath.row == 2 {
num = 2
}
}
After seeing your updated code, it looks like you only set the dataSource of the tableView and not the delegate. You need to add the line to viewDidLoad:
tableView.delegate = self
Also, instead of having multiple if statements to check the indexPath, you could replace that entire body of code with one line:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
num = indexPath.row
}

Using searchController to filter tableView, but the tableView isn't updating

I have a UITableViewController that is displaying the titles of Tags I created. When I first navigate to the UITableViewController, it displays the Array of Tags just fine, but when I use the UISearchController to filter through Tags, the Array I created to store the filtered results updates and holds the correct data, but the TableView doesn't change. here are the two functions that are most likely causing the problem, but just in case, I will have the entire class (not long) down below.
numberOfRowsInSection:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(searchController.searchBar.text != "") {
return filteredTags.count
}
return Tags.count
}
cellForRowAt:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tagcell", for: indexPath) as! TagCell
var text = ""
if (searchController.searchBar.text != ""){
text = filteredTags[indexPath.row].title
} else {
text = Tags[indexPath.row].title
}
cell.cellLabel.text = text
return cell
}
Whole Class:
class TagCell: UITableViewCell{
#IBOutlet weak var cellLabel: UILabel!
}
class TagTableVC: UITableViewController{
//Table Content
var Tags: [Tag] = [globTS.animals, globTS.civilrights, globTS.guncontrol, globTS.gunrights, globTS.LGBTQ, globTS.prochoice, globTS.prolife]
var filteredTags = [Tag]()
//Searchbar Initialization
let searchController = UISearchController(searchResultsController: nil)
//Required Functions
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
tableView.tableHeaderView = searchController.searchBar
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(searchController.searchBar.text != "") {
return filteredTags.count
}
return Tags.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tagcell", for: indexPath) as! TagCell
var text = ""
if (searchController.searchBar.text != ""){
text = filteredTags[indexPath.row].title
} else {
text = Tags[indexPath.row].title
}
cell.cellLabel.text = text
return cell
}
//Filters Tags array into Filtered array based on search query
func filterContentForSearchText(searchText: String, scope: String = "All"){
filteredTags = Tags.filter{ $0.title.lowercased().contains(searchText.lowercased())}
}
}
extension TagTableVC: UISearchResultsUpdating {
//calls the filter function everytime the searchbar is activated
func updateSearchResults(for searchController: UISearchController) {
filterContentForSearchText(searchText: searchController.searchBar.text!)
}
}
After reevaluating the filteredTags, you should call reloadData on your tableview
func filterContentForSearchText(searchText: String, scope: String = "All"){
filteredTags = Tags.filter{ $0.title.lowercased().contains(searchText.lowercased())}
self.tableView.reloadData()
}

Resources