MapKit local search results populate the table - swift

I am trying to load the updated search results but it doesn't populate the table view.
I used this link https://www.thorntech.com/how-to-search-for-location-using-apples-mapkit/ which belongs to the previous versions but it still works very well except showing the local search results. Please help
class LocationSearchTable : UITableViewController, UISearchResultsUpdating {
var matchingItems:[MKMapItem] = []
var mapView: MKMapView? = nil
}
extension LocationSearchTable {
func updateSearchResults(for searchController: UISearchController) {
guard let MapView = mapView,
let searchBarText = searchController.searchBar.text else { return }
let request = MKLocalSearch.Request()
request.naturalLanguageQuery = searchBarText
request.region = MapView.region
let search = MKLocalSearch(request: request)
search.start { response, _ in
guard let response = response else {
print("No response")
return
}
self.matchingItems = response.mapItems
self.tableView.reloadData()
}
}
}
extension LocationSearchTable {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return matchingItems.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
let selectedItem = matchingItems[indexPath.row].placemark
cell.textLabel?.text = selectedItem.name
cell.detailTextLabel?.text = ""
return cell
}
}

//use IndexPath rather than NSIndexPath and you need to use
//override
override func tableView(_ tableView: UITableView,
cellForRowAtIndexPath
indexPath: IndexPath) -> UITableViewCell {
let cell =tableView.dequeueReusableCell(withIdentifier:"cell")!
let selectedItem = matchingItems[indexPath.row].placemark
cell.textLabel?.text = selectedItem.name
cell.detailTextLabel?.text = ""
return cell
}
Hope it is not too late to answer you!

Related

Hide cell from UITableView

I'm trying to hide cells from a UITableView. My codes are below.
When I open the app I see empty rows in my TableViewas you can see here
How can I hide or remove(not delete) empty cells from UITableView?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FeedTableViewCell
let row = self.items[indexPath.row]
cell.lblTitle.text = row.title
cell.isHidden = !checkCurrentUser(email: row.email)
return cell
}
I added filtered array but then I take different error like this. My new codes are below. How can I solve this problem?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FeedTableViewCell
let row = self.items[indexPath.row]
self.items = self.items.filter{checkCurrentUser(email: $0.email)}
cell.lblTitle.text = row.title
//cell.isHidden = !checkCurrentUser(email: row.email)
return cell
}
Whole codes are below
import UIKit
import Firebase
class OyuncularVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
var items = [ItemModel]()
#IBOutlet weak var tblView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tblView.tableFooterView = UITableViewHeaderFooterView()
retrieveItems()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FeedTableViewCell
let row = self.items[indexPath.row]
self.items = self.items.filter{checkCurrentUser(email: $0.email)} //bunu ekledim siliceksem bunu silicem aga
cell.lblTitle.text = row.title
//cell.isHidden = !checkCurrentUser(email: row.email)
return cell
}
/* Retriev Items */
func retrieveItems() {
DataService.dataService.ITEM_REF.observe(.value, with: { (snapshot: DataSnapshot?) in
if let snapshots = snapshot?.children.allObjects as? [DataSnapshot] {
self.items.removeAll()
print(snapshots.count)
for snap in snapshots {
if let postDic = snap.value as? Dictionary<String, AnyObject> {
let itemModel = ItemModel(key: snap.key, dictionary: postDic)
print(itemModel)
self.items.insert(itemModel, at: 0)
}
}
self.tblView.reloadData()
}
})
}
func checkCurrentUser(email: String) -> Bool {
let currentUser = Auth.auth().currentUser
return email == currentUser?.email
}
}
}
If you want to display only the emails of the current user what don't you filter the items in the database (applying a predicate) which is the most efficient way.
Or filter the items in the for snap in snapshots loop.
However if you want to keep the entire data set declare a second array
var items = [ItemModel]()
var filteredItems = [ItemModel]()
and replace
for snap in snapshots {
if let postDic = snap.value as? Dictionary<String, AnyObject> {
let itemModel = ItemModel(key: snap.key, dictionary: postDic)
print(itemModel)
self.items.insert(itemModel, at: 0)
}
}
with the following it performs the check in the loop
let currentUser = Auth.auth().currentUser
self.filteredItems.removeAll()
for snap in snapshots {
if let postDic = snap.value as? Dictionary<String, AnyObject> {
let itemModel = ItemModel(key: snap.key, dictionary: postDic)
print(itemModel)
self.items.insert(itemModel, at: 0)
if itemModel.email == currentUser?.email {
self.filteredItems.insert(itemModel, at: 0)
}
}
}
And replace also the two data source methods with
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FeedTableViewCell
let row = self.filteredItems[indexPath.row]
cell.lblTitle.text = row.title
return cell
}
And delete the method checkCurrentUser

Is there a way that I can use my "data," "values," and "rowNumber" constants from my "func startObservingDB()" in my "tableView" functions?

I know the issue is regarding scope; I just dont know if theres an easy fix I can do without changing my code much. but open to anything
import UIKit
import Firebase
import FirebaseAuth
import FirebaseFirestore
class AdminViewController: UIViewController, UITableViewDelegate,
UITableViewDataSource {
#IBOutlet var custodianRunReportsTableView: UITableView!
var dbRef: DatabaseReference!
var data = [String]()
override func viewDidLoad() {
super.viewDidLoad()
startObservingDB()
custodianRunReportsTableView.delegate = self
custodianRunReportsTableView.dataSource = self
// Do any additional setup after loading the view.
}
// Gets users' names from Cloud Firestore Database
func startObservingDB() {
let db = Firestore.firestore()
let namesDocumentRef = db.collection("Users").document("Names")
namesDocumentRef.addSnapshotListener { DocumentSnapshot, error in
guard let document = DocumentSnapshot else {
print("Error fetching document: \(error!)")
return
}
guard let data = document.data() else {
print("Document data was empty.")
return
}
let values = data.values
let rowNumber = data.count
print("Current data: \(data)")
print("Current data has the values: \(values)")
print("Current data totals \(data.count) items.")
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped me!")
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rowNumber
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = custodianRunReportsTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = values[indexPath.row]
print("Names in cell: \(values)")
print("\(data)")
return cell
}
}
UPDATED CODE:
This is updated after an answer to the original post. The code no longer has the unresolved identified error; however, the table view does not display any cell text and is empty.
class AdminViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var custodianRunReportsTableView: UITableView!
var valuesArray:[String] = []
var data:[String] = []
var namesDocumentRef:DocumentReference!
override func viewDidLoad() {
super.viewDidLoad()
startObservingDB()
custodianRunReportsTableView.delegate = self
custodianRunReportsTableView.dataSource = self
}
// Gets users' names from Cloud Firestore Database
func startObservingDB() {
var namesDocumentRef:DocumentReference!
let db = Firestore.firestore()
namesDocumentRef = db.collection("Users").document("Names")
namesDocumentRef.addSnapshotListener { DocumentSnapshot, error in
if error != nil{
return
}
else {
guard let snapshot = DocumentSnapshot, snapshot.exists else {return}
guard let data = snapshot.data() else { return }
self.valuesArray = Array(data.values) as! Array<String>
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped me!")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return valuesArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = custodianRunReportsTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = valuesArray[indexPath.row]
return cell
}
}
The variables data,values and rowCount are inside closure so you can't just write values[indexPath.row] because you can't return from inside closure. Usually completionHandlers are used for this purpose, but in this scenario, you should put the values inside an array and then use inside the tableview. Let me show you how to do.
At the start of your viewController, declare a string array.
var valuesArray:[String] = []
Then, inside modify your startObservingDB() function
func startObservingDB() {
var docRef:DocumentReference!
let db = Firestore.firestore()
docRef = db.collection("Users").document("Names")
docRef.addSnapshotListener { (docSnapshot, error) in
if error != nil {
return
}
else {
guard let snapshot = docSnapshot, snapshot.exists else {return}
guard let data = snapshot.data() else { return }
self.valuesArray = Array(data.values) as! Array<String>
self.tableView.reloadData()
}
}
}
In viewDidLoad call this function
startObservingDB()
Then in tableView methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return ValuesArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = custodianRunReportsTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = valuesArray[indexPath.row]
return cell
}
In your class AdminViewController you can reference properties defined outside of methods with self (in your example you have dbRef that you can reference like self.dbRef in methods).
So I suggest you make properties for data, value and rowNumber and change them in startObservingDB method rather than declare them. This way you will be able to reference them in tableView methods.

Swift/Xcode why does only first row have subtitle?

Can't figure out why every row except the first row doesn't have the subtitle text. The cell's main textLabel is working for all rows.
Could I have hooked up the outlets wrong or changed a setting in Storyboard?
p.s. - I'm a beginner level programmer, sorry if my code is pretty unorganized and scattered.
import UIKit
import Firebase
class ViewSelectedShedVC: UIViewController, UITableViewDelegate,
UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
let db = Firestore.firestore()
var shedInfo = [[String:Any]]()
var styleType = String()
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "\(styleType) Sheds"
tableView.delegate = self
tableView.dataSource = self
db.collection("\(styleType.lowercased())_sheds").order(by: "stock_id", descending: false).getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting Documents: \(err)")
} else {
for document in querySnapshot!.documents {
let id = document.documentID
let size = document.get("size")
let style = document.get("style")
let rto_price = self.formatPrice(p: document.get("rto_price") as! Double)
let cash_price = self.formatPrice(p: document.get("cash_price") as! Double)
self.shedInfo.append(["id": id.uppercased(), "size": size!, "style": style!, "rto_price": rto_price, "cash_price": cash_price])
}
}
self.tableView.reloadData()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return shedInfo.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
cell.textLabel?.text = "\(String(describing: shedInfo[indexPath.row]["id"]!))"
cell.detailTextLabel?.text = "\(String(describing: shedInfo[indexPath.row]["size"]!)) / \(String(describing: shedInfo[indexPath.row]["style"]!))"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
func formatPrice(p: Double) -> String {
let price = p as NSNumber
let formatter = NumberFormatter()
formatter.numberStyle = .currency
let nprice = formatter.string(from: price)
return nprice!
}
}

I want to get firestore data in dequeueReusableCell

I rewrote all the text and now I got the code I wanted to realize.
It can not be displayed on the tableCell, and the layout also collapses. I am sorry that the code and the body I wrote are not explained enough.
guard let userID = Auth.auth (). currentUser? .uid I want to always acquire userID with else {return}.
// guard let docSnapshot = querySnapshot, document.exists else {return}
Since an error occurs, it is commented out.
Within viewidLoad of UIViewController
var profDict: [ProfDic] = [] is in the UIViewController.
profUIView is being added to UIViewController.
func getFirebaseData() {
db = Firestore.firestore()
guard let userID = Auth.auth().currentUser?.uid else {return}
let ref = db.collection("users").document(userID)
ref.getDocument{ (document, error) in
if let document = document {
// guard let docSnapshot = querySnapshot, document.exists else {return}
if let prof = ProfDic(dictionary: document.data()!) {
self.profDict.append(prof)
print("Document data \(document.data())")
}
}else{
print("Document does not exist")
}
self.profUIView.tableView1.reloadData()
}
}
tableView1 has been added to ProfUIView.
class ProfUIView: UIView, UITableViewDelegate, UITableViewDataSource {
//omission...
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .blue
addSubview(tableView1)
tableView1.anchor(top: //omission...
sections = [
Section(type: .prof_Sec, items: [.prof]),
Section(type: .link_Sec, items: [.link]),
Section(type: .hoge_Sec, items: [.hoge0])
]
tableView1.register(TableCell0.self, forCellReuseIdentifier: TableCellId0)
tableView1.register(TableCell3.self, forCellReuseIdentifier: TableCellId3)
tableView1.register(TableCell5.self, forCellReuseIdentifier: TableCellId5)
tableView1.delegate = self
tableView1.dataSource = self
}
var tableView1:UITableView = {
let table = UITableView()
table.backgroundColor = .gray
return table
}()
//omission
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (baseVC?.profDict.count)!//sections[section].items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch sections[indexPath.section].items[indexPath.row] {
case .prof:
let cell0 = tableView.dequeueReusableCell(withIdentifier: TableCellId0, for: indexPath) as? TableCell0
cell0?.nameLabel.text = baseVC?.profDict[indexPath.row].userName
return cell0!
}
//omission...
}
}
Additional notes
import Foundation
import FirebaseFirestore
struct ProfDic {
var userName :String
var dictionary:[String:Any] {
return
["userName" : userName
]
}
}
extension ProfDic {
init?(dictionary:[String:Any]) {
guard let userName = dictionary["userName"] as? String
else {return nil}
self.init(userName: userName as String)
}
}
enter image description here
First create an empty array of ProfDic elements:
var profDict: [ProfDic] = []
Then create a function to load your Firebase Data:
func getFirebaseData() {
db = Firestore.firestore()
let userRef = db.collection("users").getDocuments() {
[weak self] (querySnapshot, error) in
for document in querySnapshot!.documents {
guard let docSnapshot = docSnapshot, docSnapshot.exists else {return}
if let prof = ProfDic(dictionary: docSnapshot.data()!) {
profDict.append(prof)
}
}
tableView.reloadData()
}
}
Call this function in viewDidLoad or viewDidAppear.
Then in tableView cellForRowAt you access your data like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch sections[indexPath.section].items[indexPath.row] {
case .prof:
let cell = tableView.dequeueReusableCell(withIdentifier: TableCellId, for: indexPath) as? TableCell
cell?.nameLabel.text = profDict[indexPath.row].userName
return cell!
}
}
EDIT:
Also in numberOfRowsInSection:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return profDict.count
}

UITableView don't reload data

I got an array that populates a tableview, it works fine when I run the app.
I created a popover with a PickerView to choose one option to sort the TableView data.
I get the user choise in the popover, pass it to the main ViewController, sorted the data and called tableview.reloadData() but nothing happens.
I printed the array after the sort and the array is sorted but I can't saw the changes.
But if I go to other ViewController and came back the data is changed.
Why the changes are not showing when I call the tableview.reloadData().
Here's the code:
var dataModel = DataModel()
var ordenacao = String()
override func viewWillAppear(_ animated: Bool) {
dataModel.loadData()
tableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
ordenadados(ordem: ordenacao)
tableView.reloadData()
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell! = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier")
cell.textLabel?.text = dataModel.notas[indexPath.row].titulo
cell.detailTextLabel?.text = dataModel.notas[indexPath.row].datafinal
print("Ordena Tableview")
for nota in dataModel.notas {
print (nota.titulo ?? "")
}
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataModel.notas.count
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
dataModel.notas.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath as IndexPath], with: UITableViewRowAnimation.fade)
dataModel.saveData()
}
EDIT:
func ordenadados(ordem: String){
dataModel.loadData()
if(ordenacao == "Titulo Asc."){
print("Titulo Asc")
dataModel.notas.sort { $0.titulo! < $1.titulo! }
}else if(ordenacao == "Titulo Desc."){
print("Titulo Desc.")
dataModel.notas.sort { $0.titulo! > $1.titulo! }
}
dataModel.saveData()
for nota in dataModel.notas {
print (nota.titulo ?? "")
}
dataModel.loadData()
tableView.reloadData()
}
In the output the array was sorted but in the TableView nothing changed.
Save and Load Data methods:
//save data
func saveData() {
let data = NSMutableData()
let archiver = NSKeyedArchiver(forWritingWith: data)
archiver.encode(notas, forKey: "teste")
archiver.finishEncoding()
data.write(toFile: dataFilePath(), atomically: true)
}
//read data
func loadData() {
let path = self.dataFilePath()
let defaultManager = FileManager()
if defaultManager.fileExists(atPath: path) {
let url = URL(fileURLWithPath: path)
let data = try! Data(contentsOf: url)
let unarchiver = NSKeyedUnarchiver(forReadingWith: data)
notas = unarchiver.decodeObject(forKey: "teste") as! Array
unarchiver.finishDecoding()
}
}