Good day! I'm building an app that needs to construct a sentence from the data from Core Data. I have this:
var players = Array<AnyObject> = []
#IBOutlet weak var sentenceLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context: NSManagedObjectContext = appDelegate.managedObjectContext!
let freq = NSFetchRequest(entityName: "Orders")
players = context.executeFetchRequest(freq, error: nil)!
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return players.count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("checkCell") as! SummaryCustomCell!
if cell == nil {
cell = SummaryCustomCell(style: UITableViewCellStyle.Default, reuseIdentifier: "checkCell")
}
var data: NSManagedObject = players[indexPath.row] as! NSManagedObject
cell.playerNameLabel.text = data.valueForKey("playerName") as? String
cell.teamLabel.text = data.valueForKey("team") as? String
cell.yearsLabel.text = data.valueForKey("yearsOfPlaying") as? String
for var i = 0; i < players.count;i++ {
var dataLooped: NSManagedObject = myOrders[i] as! NSManagedObject
var playerName = dataLooped.valueForKey("playerName") as? String
var team = dataLooped.valueForKey("team") as? String
var years = dataLooped.valueForKey("yearsOfPlaying") as? String
var constructedSentence: NSString = NSString(format: "%# was playing for %# for %# years.", playerName, team, years)
sentenceLabel.text = constructedSentence as! String
}
}
return cell
}
Then it gives only the last row in the database. It must loop the sentence in a single string. For example. "Kobe Bryant was playing for LA Lakers for 5 years; Lebron James was playing for Cavs for 3 years." and so on..
How can I implement this? Thank you very much!
Related
I have a tableview loading data from firebase database. When I open my app the data does not populate. when I create a new post I can see the tableview cells modifying like changed were made but the post doesn't populate. I can't figure it out.
import UIKit
import FirebaseAuth
import FirebaseDatabase
class EventsViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var eventsRef: DatabaseReference?
var eventsDatabaseHandle:DatabaseHandle?
var eventsTitles = [String]()
var eventTimestamps = [String]()
var eventsLocations = [String]()
var eventsImages = [UIImage]()
#IBOutlet weak var addEventsButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
adminAuth()
eventsRef = Database.database().reference()
tableView.reloadData()
tableView.transform = CGAffineTransform(rotationAngle: -CGFloat.pi)
tableView.delegate = self
tableView.dataSource = self
eventsDatabaseHandle = eventsRef?.child("Church Events").observe(.childAdded, with: { (snaphot) in
let eventPost = snaphot.value as! [String: Any]
self.eventTimestamps.append(eventPost["eventdate"] as! String)
self.eventsTitles.append(eventPost["eventtitle"] as! String)
self.eventsLocations.append(eventPost["eventlocation"] as! String)
let task = URLSession.shared.dataTask(with: URL(string: eventPost["ImageUrl"] as! String)!) {(data, response, error) in
if let image: UIImage = UIImage(data: data!) {
self.eventsImages.append(image)
}
}
self.tableView.reloadData()
task.resume()
})
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return eventsImages.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "events") as! EventsTableViewCell
let image = eventsImages[indexPath.row]
cell.flyerImages.image! = image
cell.eventTitle.text! = eventsTitles[indexPath.row]
cell.eventDate.text! = eventTimestamps[indexPath.row]
cell.eventLocation.text! = eventsLocations[indexPath.row]
cell.transform = CGAffineTransform(rotationAngle: CGFloat.pi)
tableView.reloadData()
return cell
}
func adminAuth() {
if (Auth.auth().currentUser!.displayName != "Neil Leon") {
self.addEventsButton.tintColor = UIColor.clear
self.addEventsButton.isEnabled = false
}
else{
self.addEventsButton.isEnabled = true
}
}
}
image of empty tableview
]
So the code below is not tested as I don't have firebase setup currently.
However, observing childAdded... the documentation says it will pass all of the current records in the database at first and will then just post new additions. So all you need to do is loop through them, setup your tableView data source and reload the table.
Rather than use multiple arrays for values I've created an array of ChurchEvent objects instead.
struct ChurchEvents {
let title: String
let location: String?
let date: Date?
let imageUrlString: String?
init(dict: [String: Any]) {
self.title = dict["title"] as String
self.location = dict["location"] as? String
// etc
}
}
var events = [ChurchEvents]()
eventsDatabaseHandle = eventsRef?.child("Church Events").observe(.childAdded, with: { snapshot in
let list = snapshot.value as? [[String : AnyObject]]
let newEvents = list.map { ChurchEvent(dict: $0) }
events.append(newEvents)
tableView.reloadData()
}
Other improvements you could make:
class EventsTableViewCell: UICollectionViewCell {
func configure(with event: ChurchEvent {
eventDate.text = event.date
eventTitle.text = event.title
eventLocation.text = event.location
// etc
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "events") as! EventsTableViewCell
let event = events[indexPath.row]
cell.configure(with: event)
return cell
}
I currently have NSArray that gets its data from a mySQL database.
I need to filter this data based on a hard-coded string "Customer1"
The following is what I have so far:
import UIKit
class showCustomerDetails: UIViewController, UITableViewDataSource, UITableViewDelegate, FeedDetailProtocol {
var feedItems: NSArray = NSArray()
var selectedStock : DetailModel = DetailModel()
#IBOutlet weak var stockResultsFeed: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.stockResultsFeed.delegate = self
self.stockResultsFeed.dataSource = self
let detailModel = FeedDetail()
detailModel.delegate = self
detailModel.downloadItems()
}
func itemsDownloaded(items: NSArray) {
feedItems = items
self.stockResultsFeed.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of feed items
return feedItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Retrieve cell
let cellIdentifier: String = "customerDetails"
let myCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
myCell.textLabel?.textAlignment = .center
// Get the stock to be shown
let item: DetailModel = feedItems[indexPath.row] as! DetailModel
// Configure our cell title made up of name and price
let customerDetails = [item.code, item.manufacturer, item.model].compactMap { $0 }.joined(separator: " — ")
print(customerDetails)
// Get references to labels of cell
myCell.textLabel!.text = customerDetails
return myCell
}
}
The following is what I was thinking of doing, but I am not sure how to properly apply it:
let searchString = "Customer1"
let predicate = NSPredicate(format: "SELF contains %#", searchString)
let searchDataSource = feedItems.filter { predicate.evaluateWithObject($0) }
And then:
let item: DetailModel = searchDataSource[indexPath.row] as! DetailModel
NSArray data is coming from:
import Foundation
protocol FeedDetailProtocol: class {
func itemsDownloaded(items: NSArray)
}
class FeedDetail: NSObject, URLSessionDataDelegate {
weak var delegate: FeedDetailProtocol!
let urlPath = "https://www.example.com/test1/test1.php"
func downloadItems() {
let url: URL = URL(string: urlPath)!
let defaultSession = Foundation.URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: url) { (data, response, error) in
if error != nil {
print("Error")
}else {
print("details downloaded")
self.parseJSON(data!)
}
}
task.resume()
}
func parseJSON(_ data:Data) {
var jsonResult = NSArray()
do{
jsonResult = try JSONSerialization.jsonObject(with: data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
} catch let error as NSError {
print(error)
}
var jsonElement = NSDictionary()
let stocks = NSMutableArray()
for i in 0 ..< jsonResult.count
{
jsonElement = jsonResult[i] as! NSDictionary
let stock = DetailModel()
//the following insures none of the JsonElement values are nil through optional binding
if let code = jsonElement[“code”] as? String,
let customer = jsonElement["customer"] as? String,
let manufacturer = jsonElement["manufacturer"] as? String,
let model = jsonElement["model"] as? String
{
print(code)
print(manufacturer)
print(model)
print(customer)
stock.code = code
stock.manufacturer = manufacturer
stock.model = model
stock.customer = customer
}
stocks.add(stock)
}
DispatchQueue.main.async(execute: { () -> Void in
self.delegate.itemsDownloaded(items: stocks)
})
}
}
This is Swift. Use Array, not NSArray, and just call Array's filter method. NSArray belongs to Cocoa and Objective-C; you should use native Swift types and Swift methods as much as possible.
If you insist on filtering an NSArray using a Cocoa Objective-C method, and you insist on using NSPredicate, the simplest approach is to form your predicate with init(block:).
Here's a simple illustration:
let arr = ["Manny", "Moe", "Jack"] as NSArray
let p = NSPredicate { element, _ in
return (element as? String)?.contains("a") ?? false
}
let arr2 = arr.filtered(using: p)
print(arr2) // [Manny, Jack]
But (just to drive home the point) it's so much simpler in native Swift:
let arr = ["Manny", "Moe", "Jack"]
let arr2 = arr.filter {$0.contains("a")}
I've already seen: Swift UITableView reloadData in a closure but it still does not work. That's why I'm creating a new thread for this.
I'm trying to insert Firestore data into a custom tableview. But when I print the numbers it returns (In the console):
"MyDogwalk.listTxt"
And no data is showing up on the tableview.
I guess all of this is relevant. (I also have 2 classes, with init etc)
class HistoryViewController: UIViewController {
//Tableview
#IBOutlet weak var tableView: UITableView!
let db = Firestore.firestore()
var list: [listTxt] = []
override func viewDidLoad()
{
super.viewDidLoad()
list = createArray()
tableView.delegate = self
tableView.dataSource = self
}
func createArray() -> [listTxt]
{
var tempTxt: [listTxt] = []
//Authentication
let authentication = Auth.auth().currentUser?.uid
//Choosing collection
db.collection("rastad").document(authentication!).collection("promenad").getDocuments()
{ (QuerySnapshot, err) in
if err != nil
{
print("Error getting documents: \(String(describing: err))");
}
else
{
//For-loop
for _ in QuerySnapshot!.documents
{
self.list.removeAll()
let document = QuerySnapshot!.documents.first
let data = document!.data()
data.forEach { (item) in
let data1 = data["Dog"] as? String
let data2 = data["Person"] as? String
let data3 = data["What"] as? String
let data4 = data["Date"] as? String
let data5 = data["Time"] as? String
let txt = listTxt(dog: data1!, person: data2!, action: data3!, time: data4!, date: data5!)
print(txt)
tempTxt.append(txt)
}
}
self.tableView.reloadData()
}
}
//return tempTxt
return list
}
}
extension HistoryViewController: UITableViewDelegate, UITableViewDataSource
{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return list.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let listPath = list[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "ListCell") as! HistoryCell
cell.setCell(list: listPath)
return cell
}
}
And.. Why is this downvoted? I got an answer that was actually working for this case, and the question is detailed enough for people to understand, isn't it?
createArray() method runs async code, and fills tempTxt. But you are returning tempTxt before async code has been run. So instead returning from createArray method and setting its value to self.list, just do it in the method itself:
self.list = tempTxt
self.tableView.reloadData()
You are iterating over documents but always using data of documents.first. Try this:
self.list.removeAll()
for document in QuerySnapshot!.documents {
let data = document!.data()
data.forEach { (item) in
let data1 = data["Dog"] as? String
let data2 = data["Person"] as? String
let data3 = data["What"] as? String
let data4 = data["Date"] as? String
let data5 = data["Time"] as? String
self.list.append(listTxt(dog: data1!, person: data2!, action: data3!, time: data4!, date: data5!))
}
}
self.tableView.reloadData()
Change self.tableView.reloadData() to
self.list = tempTxt
DispatchQueue.main.async {
self.tableView.reloadData()
}
And skip returning array from that func
The problem in the given code is, that the Firebase Data is not loaded into the Arrays before they get returned. Since I am new to swift I am really satisfied with my data model set up to load multiple sections into the tableview and don't want to change everything. The only problem is as I said, that the Firebase data collectors need some kind of a completion handler, but I don't really know how to apply them and I have no idea how to change the following code as well...
This is the class which defines an object:
class Court {
var AnzahlToreKoerbe: String?
var Breitengrad: String?
var Groesse: String?
var Hochgeladen: String?
var Laengengrad: String?
var Stadt: String?
var Stadtteil: String?
var Strasse: String?
var Untergrund: String?
var Upload_Zeitpunkt: Int?
var Platzart: String?
init(AnzahlToreKoerbeString: String, BreitengradString: String, GroesseString: String, HochgeladenString: String, LaengengradString: String, StadtString: String, StadtteilString: String, StrasseString: String, UntergrundString: String, UploadTime: Int, PlatzartString: String) {
AnzahlToreKoerbe = AnzahlToreKoerbeString
Breitengrad = BreitengradString
Groesse = GroesseString
Hochgeladen = HochgeladenString
Laengengrad = LaengengradString
Stadt = StadtString
Stadtteil = StadtteilString
Strasse = StrasseString
Untergrund = UntergrundString
Upload_Zeitpunkt = UploadTime
Platzart = PlatzartString
}
}
This is the class which collects the objects and load them into multiple arrays, which are then called with the getCOurts function in the tableViewController:
class Platzart
{
var Courts: [Court]
var name: String
init(name: String, Courttypes: [Court]) {
Courts = Courttypes
self.name = name
}
class func getCourts() -> [Platzart]
{
return [self.AllSoccer(), self.AllBasketball(), self.Test(), self.Test2()]
}
This is an example private class function which loads the data:
private class func AllSoccer() -> Platzart {
var allSoccer = [Court]()
let databaseref = FIRDatabase.database().reference()
databaseref.child("Court").child("Fußball").observe(.childAdded, with: { (snapshot) in
if let Courtdictionary = snapshot.value as? [String : Any] {
let city = Courtdictionary["Stadt"] as? String
let district = Courtdictionary["Stadtteil"] as? String
let street = Courtdictionary["Strasse"] as? String
let surface = Courtdictionary["Untergrund"] as? String
let latitude = Courtdictionary["Breitengrad"] as? String
let longitude = Courtdictionary["Laengengrad"] as? String
let size = Courtdictionary["Groesse"] as? String
let Userupload = Courtdictionary["Hochgeladen"] as? String
let timestamp = Courtdictionary["Upload_Zeitpunkt"] as? Int
let numberofHoops = Courtdictionary["AnzahlToreKoerbe"] as? String
let courttype = Courtdictionary["Platzart"] as? String
allSoccer.append(Court(AnzahlToreKoerbeString: numberofHoops!, BreitengradString: latitude!, GroesseString: size!, HochgeladenString: Userupload!, LaengengradString: longitude!, StadtString: city!, StadtteilString: district!, StrasseString: street!, UntergrundString: surface!, UploadTime: timestamp!, PlatzartString: courttype!))
print(allSoccer)
}
})
return Platzart(name: "Fußballplatz", Courttypes: allSoccer)
}
The data is then loaded into the tableview:
class CourtlistViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
lazy var Courtarrays: [Platzart] = {
return Platzart.getCourts()
}()
#IBOutlet weak var CourtlisttableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
CourtlisttableView.delegate = self
CourtlisttableView.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
CourtlisttableView.reloadData()
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let Courttype = Courtarrays[section]
return Courttype.name
}
func numberOfSections(in tableView: UITableView) -> Int {
return Courtarrays.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Courtarrays[section].Courts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = CourtlisttableView.dequeueReusableCell(withIdentifier: "Court Cell", for: indexPath) as! CourtCell
let Platzarray = Courtarrays[indexPath.section]
let Sportplatz = Platzarray.Courts[indexPath.row]
cell.updateUI(Sportplatz)
return cell
}
}
This procedure works well with data which I append to an array manually. For the Firebase Sections (AllSoccer() and AllBasketball()) only the headers are loaded and displayed.
I have UITableViewController with files from document folder. I name the cells by artist name. I have three artist and four songs. UITableViewCell shows two cells with the same artist. How can I fix it?
This code export data from document folder
var mp3Files: Array<String!>!
func exportData() {
var generalURL: [AnyObject]?
var arrayFiles: Array<NSURL!>!
var directory = fileManager.URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask)
var urlFromDirectory = directory.first as! NSURL
var file = fileManager.contentsOfDirectoryAtURL(urlFromDirectory, includingPropertiesForKeys: nil, options: NSDirectoryEnumerationOptions.SkipsHiddenFiles, error: nil)!
println("file \(file)")
mp3Files = file.map(){ $0.lastPathComponent }.filter(){ $0.pathExtension == "mp3" }
println("mp3 files \(mp3Files)")
}
and code fill the UITableViewCell
var cellStrings: String!
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
var dataForCell = mp3Files[indexPath.row]
var generalURL: NSURL!
var documentFolder = fileManager.URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask)
if var urlFromFolder: NSURL = documentFolder.first as? NSURL {
generalURL = urlFromFolder.URLByAppendingPathComponent(dataForCell)
println("general \(generalURL)")
}
var player = AVPlayerItem(URL: generalURL)
var metaData = player.asset.commonMetadata as! [AVMetadataItem]
for item in metaData {
if item.commonKey == "artist" {
nameArtist = item.stringValue
}
}
cell.textLabel?.text = nameArtist
//
cellStrings = cell.textLabel?.text
println("cell strings \(cellStrings)")
// Configure the cell...
return cell
}
var superArray = [String]()
var filterArray = [String]()
func filter() {
var proString: String!
for proItem in mp3Files {
var proFolder = fileManager.URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask)
var americaURL: NSURL!
if var proURL: NSURL = proFolder.first as? NSURL {
americaURL = proURL.URLByAppendingPathComponent(proItem)
}
var proPlayerItem = AVPlayerItem(URL: americaURL)
var proData = proPlayerItem.asset.commonMetadata as! [AVMetadataItem]
for proFiles in proData {
if proFiles.commonKey == "artist" {
superArray.append(proFiles.stringValue)
}
}
}
filterArray = Array(Set(superArray))
filterArray.sort(){ $0 < $1 }
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1 ?? 0
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return filterArray.count ?? 0
}
var name: String!
var nameArtist: String!
//
var cellStrings: String!
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
nameArtist = filterArray[indexPath.row]
cell.textLabel?.text = nameArtist
return cell
}