Swift/Xcode why does only first row have subtitle? - swift

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!
}
}

Related

Table View is now shown in simülatör swift/xcode

hello i am new to learning swift. I want to save data in database and show it in tableview. But even though I do everything, the tableview does not appear in the simulator. white screen appears. Any idea about the reason? I looked at the previous questions but couldn't find an answer.
import UIKit
import CoreData
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet weak var tableView: UITableView!
//tanımlamalar
var nameArray = [String]()
var idArray = [UUID]()
override func viewDidLoad() {
super.viewDidLoad()
//tableview gösterme
tableView.delegate = self
tableView.dataSource = self
//add butonu ekleme
navigationController?.navigationBar.topItem?.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.add, target: self, action: #selector(addButtonClicked))
}
//verileri getirme
func getData() {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
//getirme
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName : "Paintings")
fetchRequest.returnsObjectsAsFaults = false
do{
let results = try context.fetch(fetchRequest)
for result in results as! [NSManagedObject] {
if let name = result.value(forKey: "name") as? String{
self.nameArray.append(name)
}
if let id = result.value(forKey: "id") as? UUID{
self.idArray.append(id)
}
//yeni veri eklenirse tableviewı reload et
self.tableView.reloadData()
}
}catch{
print("error")
}
}
//add butonuna tıklanırsa ne olacak
#objc func addButtonClicked() {
performSegue(withIdentifier: "DetailsVC", sender: nil)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return nameArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
var content = cell.defaultContentConfiguration()
content.text = nameArray[indexPath.row]
cell.contentConfiguration = content
return cell
}
}

MapKit local search results populate the table

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!

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.

How to use multi labels in UITableView?

I get data from JSON (First name, last name and email) but I'm only able to show first name in UITableView. I tried my best but I couldn't make it work. Following is my code.
import UIKit
struct User: Codable {
let firstName: String
let lastName: String
let email: String
enum CodingKeys: String, CodingKey {
case firstName = "first_name"
case lastName = "last_name"
case email = "email"
}
}
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableview: UITableView!
private var dataSource = [User]() {
didSet {
self.tableview.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableview.register(UITableViewCell.self, forCellReuseIdentifier: "groupCell")
self.tableview.dataSource = self
self.tableview.delegate = self
let url = URL(string: "https://x.com/x.php")
URLSession.shared.dataTask(with: url!, completionHandler: { [weak self] (data, response, error) in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "An error occurred")
return
}
DispatchQueue.main.async {
self?.dataSource = try! JSONDecoder().decode([User].self, from: data)
}
}).resume()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
tableview.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "groupCell", for: indexPath)
let user = self.dataSource[indexPath.row]
cell.textLabel?.text = user.firstName
// cell.textLabel?.text = user.lastName If I write this line then it only shows last name
return cell
}
}
You can use UITableViewCell.CellStyle.subtitle, like so:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: UITableViewCell! = tableView.dequeueReusableCell(withIdentifier: "groupCell")
if cell == nil {
cell = UITableViewCell(style: .subtitle, reuseIdentifier: "groupCell")
}
let user = self.dataSource[indexPath.row]
cell.textLabel?.text = user.firstName + " " + user.lastName
cell.detailTextLabel?.text = user.email
return cell
}
You do not need to register cell, so DELETE following line:
tableview.register(UITableViewCell.self, forCellReuseIdentifier: "groupCell")

Why is JSON data from local path shows incorrectly in the UITableView?

I want to parse JSON data from a local file that is available in the project and then populate these data to UITableView.
My Requirements
parse the json data from local path not from URL
Populate the json data to UITableView
Facing problems
Unable to display the parsed data, ( bracket is displaying in the table view.
I am able to print the data in console using dump() but unable to print data in tableView
Updated view controller for passing data to another controller.
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lookArrayModel.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cells = myTableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let displayData = lookArrayModel[indexPath.row]
cells.textLabel?.text = String(describing: displayData.Lookname!)
cells.detailTextLabel?.text = String(describing: displayData.Lookdetails!)
// print(displayData.shadeModel)
return cells
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You selected cell #\(indexPath.row)!")
// Get Cell Label
let indexPath = myTableView.indexPathForSelectedRow;
let currentCell = myTableView.cellForRow(at: indexPath!) as UITableViewCell!;
lookNameValue = currentCell?.textLabel?.text
lookDetailValue = currentCell?.detailTextLabel?.text
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//let lookShade = LookModelData()
if (segue.identifier == "segueToLook") {
let destController:DetailsViewController = segue.destination as! DetailsViewController
//Set the selecte row index value
destController.LabelText = String(describing: lookNameValue)
destController.DetailText = String(describing: lookDetailValue)
// destController.arrayData = lookShade.shadeModel as! NSMutableArray
}
}
}
Destination view controller. Swift
class DetailsViewController:UIViewController,UITableViewDataSource,UITableViewDelegate {
var lookArrayModel = [LookModelData]()
var arrayData: NSMutableArray = []
#IBOutlet weak var secondView: UITableView!
var LabelText = String()
var DetailText = String()
var shadeText = String()
#IBOutlet weak var LookLabel: UILabel!
#IBOutlet weak var LookName: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
print(arrayData)
LookName?.text = LabelText
LookLabel?.text = DetailText
secondView.dataSource = self
secondView.delegate = self
secondView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayData.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cells = secondView.dequeueReusableCell(withIdentifier: "secondCell", for: indexPath)
let displayData = arrayData
// cells.textLabel?.text = String(describing: (displayData as AnyObject))
// print(arrayData)
return cells
}
}
Please check my code :
Changed lookArrayModel type NSMutableArray to [LookModelData]. Like those I did some changes. Please check.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var lookArrayModel = [LookModelData]()
#IBOutlet weak var myTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
guard let Path = Bundle.main.path(forResource: "ColorShade", ofType: "json") else { return }
let url = URL(fileURLWithPath: Path)
do {
let data = try Data(contentsOf: url)
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
myTableView.dataSource = self
myTableView.delegate = self
//Calling the function for adding look
createLooks(dictionary: json as! NSArray)
myTableView.reloadData()
} catch {
print(error)
}
}
func createLooks(dictionary:NSArray) {
for item in dictionary {
let item1 = item as! NSDictionary
let lookModal = LookModelData()
lookModal.Lookname = item1.value(forKey: "Lookname") as? String
lookModal.LookId = item1.value(forKey: "LookId") as? String
lookModal.Lookdetails = item1.value(forKey: "Lookdetails") as? String
lookModal.shadeModel = createshade(shades: item1.value(forKey: "shades") as! NSArray)
lookArrayModel.append(lookModal)
}
}
func createshade(shades: NSArray) -> [ShadeDescription] {
var arrayShade = [ShadeDescription]()
for item in shades
{
let item1 = item as! NSDictionary
let shadeModal = ShadeDescription()
shadeModal.comboID = item1.value(forKey: "comboID") as? String
shadeModal.shadeName = item1.value(forKey: "shadeName") as? String
shadeModal.ShadeType = item1.value(forKey: "ShadeType") as? String
shadeModal.ShadeCode = item1.value(forKey: "shadeCode") as? String
arrayShade.append(shadeModal)
}
return arrayShade
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lookArrayModel.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cells = myTableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let displayData = lookArrayModel[indexPath.row]
// You will get like this
// print(displayData.LookId!)
// print(displayData.Lookname!)
// print(displayData.Lookdetails!)
// print(displayData.shadeModel!)
// This is the way to get shade model data
if let shadeModels = displayData.shadeModel {
for var shadeModel in shadeModels {
print(shadeModel.comboID)
print(shadeModel.ShadeType)
print(shadeModel.shadeName)
print(shadeModel.ShadeCode)
}
}
cells.textLabel?.text = String(describing: displayData.Lookname!)
return cells
}
}
class LookModelData
{
var Lookname:String?
var LookId:String?
var Lookdetails:String?
//Shades Array
var shadeModel : [ShadeDescription]?
}
class ShadeDescription {
var ShadeType:String?
var shadeName:String?
var comboID:String?
var ShadeCode:String?
}