I'm trying to build a simple app in which the user would input a date from a date picker(a birthday or any event), then the code would subtract today's date from the date the user gave the app, to get the difference in days and then display a countdown of the number of days left in a table view, along with the name of the event. I have 2 View Controllers, the main TableViewController and then the AddViewController. I've figured out how to get the difference in days, my table view is set up and all the code is running quite well. I just have a problem in displaying the number of days left in the table view as they are integers. I would love to get some help from more experienced developers as I'm relatively new to iOS programming. So without further due, here's my code:
#IBAction func addButtonTapped(sender: AnyObject) {
var chosen: NSDate = self.datePicker.date
var today: NSDate = NSDate()
let calendar = NSCalendar.currentCalendar()
let components = calendar.components(.DayCalendarUnit, fromDate: today, toDate: chosen, options: nil)
let secondsInADay = ((60 * 60) * 24)
let daysLeft = (components.hashValue / secondsInADay)
var userDefaults: NSUserDefaults = NSUserDefaults()
var itemList: NSMutableArray? = userDefaults.objectForKey("itemList") as? NSMutableArray
var dataSet: NSMutableDictionary = NSMutableDictionary()
dataset.setObject(eventTextField.text, forKey: "itemEventName")
dataSet.setObject(datePicker.date, forKey: "itemEventDate")
dataSet.setObject(daysLeft, forKey: "itemDaysLeft")
if ((itemList) != nil) {
var newMutableList: NSMutableArray = NSMutableArray()
for dict: AnyObject in itemList! {
newMutableList.addObject(dict as NSDictionary)
}
userDefaults.removeObjectForKey("itemList")
newMutableList.addObject(dataSet)
userDefaults.setObject(newMutableList, forKey: "itemList")
} else {
userDefaults.removeObjectForKey("itemList")
itemList = NSMutableArray()
itemList!.addObject(dataSet)
userDefaults.setObject(itemList, forKey: "itemList")
}
userDefaults.synchronize()
self.navigationController?.popToRootViewControllerAnimated(true)
}
So above is the code in my addViewController, users input a name from a text field, and a date from a date picker. Below is a part of the code in my tableViewController. Basically I want the Int daysLeft to be displayed in the cell.
var toDoItems: NSMutableArray = NSMutableArray()
override func viewDidAppear(animated: Bool) {
var userDefaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
var itemListFromUserDefaults: NSMutableArray? = userDefaults.objectForKey("itemList") as? NSMutableArray
if ((itemListFromUserDefaults) != nil) {
toDoItems = itemListFromUserDefaults!
}
self.tableView.reloadData()
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
var toDoItem: NSDictionary = toDoItems.objectAtIndex(indexPath.row) as NSDictionary
cell.textLabel?.text = toDoItem.objectForKey("itemDaysLeft") as? String
return cell
}
Put a break point on this line: var toDoItem: NSDictionary = toDoItems.objectAtIndex(indexPath.row) as NSDictionary to see what toDoItemContains, just to verify that it is actually what you expect it to be. But I think the below code may help.
Instead of:
cell.textLabel?.text = toDoItem.objectForKey("itemDaysLeft") as? String
try:
cell.textLabel?.text = NSString(format:"%#", toDoItem.objectForKey("itemDaysLeft")) as String
Related
I made a struct dictionary to get the user title and URL, and then I store them on the phone but when I come to retrieve the data in cellForRow method the cell label is empty, what should appear is the title.(tableView starts off empty until user starts to populate it with the AddArticle action)
So my question is if I'm doing it right because the cell label just turns out nil?
Struct Dictionary:
struct AddMagazine {
let rssUrl: String
let title: String
init(dict: [String : String]){
title = dict["title"] ?? ""
rssUrl = dict["rssUrl"] ?? ""
}
}
var userMagazineTitle = [AddMagazine]()
Getting values from textField:
#IBAction func AddArticle(_ sender: Any) {
animateIn()
tableView.isScrollEnabled = false
}
func addArticleTitle() {
let UserMagazines = AddMagazine.init(dict: ["title": RssTitle.text!, "rssUrl": RssText.text!])
let storedRssUrl = UserMagazines.rssUrl
self.dataString = storedRssUrl
//setting
defaults.set(dataString, forKey: "storedArray")
userMagazineTitle.append(UserMagazines)
tableView.reloadData()
}
Trying to retrieve title here:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyFeedTableViewCell
let headlineName = defaults.object(forKey: "storedArray") as? AddMagazine
cell.myHeadline.text = headlineName?.title
cell.indentationLevel = 3
return cell
}
You’re storing a String object in defaults for “storedArray” but then you typecast it to an AddMagazine when you read it from defaults. Change what you store or read it as a string.
I agree with #Joakim Danielson. You are storing storedRssUrl which is a string into userdefaults and while retrieving you are type casting as AddMagazine hence it will be nil.
self.dataString = storedRssUrl
//setting
defaults.set(dataString, forKey: "storedArray") --> Here you are storing string
let headlineName = defaults.object(forKey: "storedArray") as? AddMagazine --> Here you are fetching as AddMagazine.
//It should be like this
let headlineName = defaults.object(forKey: "storedArray") as? String
So I'm saving a dictionary to my firebase. The dictionary is located in a custom class I made called FoodItem. Here's the lines where I save the dictionary:
let favRef = self.ref.childByAppendingPath("urlhidden")
favRef.setValue(foodItem.toAnyObject())
foodItem is a FoodItem object, here's the part of my FoodItem class that implements .toAnyObject()
var key: String
var ref: Firebase?
var name: String
var description: String
var minCal: Int
var maxCal: Int
var containsNuts: Bool
var vegetarian: Bool
var price: Double //**NOTE THAT PRICE IS A DOUBLE**
func toAnyObject()->[String:AnyObject]{
return ["name":self.name,
"price":self.price,
"description":self.description,
"minCal":self.minCal,
"maxCal":self.maxCal,
"containsNuts":self.containsNuts,
"vegetarian":self.vegetarian]
}
So those bits of code combined saves my data to my firebase. Works as it should. But when I retrieve the data, my price variable is a string? I'll explain...
Here's where I retrieve the data:
func getCurrentOrder(){
let uid = ref.authData.uid
ref = Firebase(url: "urlhidden")
ref.observeEventType(.Value, withBlock: { snapshot in
var newItems = [FoodItem]()
for item in snapshot.children {
let foodItem = FoodItem(snapshot: item as! FDataSnapshot)
newItems.append(foodItem)
}
self.order = newItems
self.tableView.reloadData()
})
}
That FoodItem constructor you see with the snapshot takes the data from my firebase and assigns my FoodItem variables to those values in my firebase. Here's that code:
init(snapshot: FDataSnapshot) {
key = snapshot.key
name = snapshot.value["name"] as! String
price = snapshot.value["price"] as! Double //**NOTE STILL A DOUBLE**
description = snapshot.value["description"] as! String
minCal = snapshot.value["minCal"] as! Int
maxCal = snapshot.value["maxCal"] as! Int
containsNuts = snapshot.value["containsNuts"] as! Bool
vegetarian = snapshot.value["vegetarian"] as! Bool
ref = snapshot.ref
}
However when I run my code, I see that it prints the double as a String
As you can see, there are clearly quotes around the Double variable "price".
And I get this error:
Could not cast value of type '__NSCFString' (0xff0ee0) to 'NSNumber' (0x13ff81c).
With this line of code highlighted in red:
price = snapshot.value["price"] as! Double
Which is located inside the FoodItem constructor with the snapshot as I posted above.
Any Ideas???
*EDIT: It does the same thing for my int variables. Same error message
It never actually saved it as a String. I believe I had that value in there as some old code in which I did save it as a String. I deleted my entries in the database and tried it again and it works just fine. Thank you to those who attempted to answer my question.
2017 example
It may save someone some typing, here's some completely typical code to observe a simple number value. Let's say it's a fraction called SomeFraction.
As always, Firebase rocks :)
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
dev_listenForLiveFraction()
}
var r1: DatabaseReference? = nil
func dev_listenForLiveFraction() {
let p1 = "something/someFractionForExample"
r1 = Database.database().reference(withPath: p1)
r1!.observe(.value) { (snapshot) in
let sf: NSNumber = snapshot.value as? NSNumber ?? 0.666
self.example_handleSomeFraction(sf)
}
}
func example_handleSomeFraction(_ sf: NSNumber) {
print("the fraction is now \(sf)")
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
clearObservations()
}
private func clearObservations() {
if r1 != nil {
r1?.removeAllObservers()
r1 = nil
}
}
hello I have implemented an auto complete search on my app. I have cities stored in my mysql database and in app when user types any character or word, the app fetches result from the database and shows it. The problem which I am having is there are more then 1000 cities stored in database and when user lets say type one character my app keyboard got stuck a little and it takes a lot of memory while it fetches the result and shows it. Is there any better way to implement this kind a functionality. Please Please look at my code and let me know what changes should I done in my code
func updateSearchResultsForSearchController(searchController: UISearchController) {
filterTableData.removeAll(keepCapacity: false)
let searchWord = searchController.searchBar.text!
getCityNamesFromServer(searchWord)
let searchPredict = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
for var i = 0; i < self.dict.count; i++ {
let cityname = (((self.dict["\(i)"] as?NSDictionary)!["City"] as?NSDictionary)!["name"] as?NSString)! as String
newTableData.append(cityname)
}
let array = (newTableData as NSArray).filteredArrayUsingPredicate(searchPredict)
print("array is\(array)")
filterTableData = array as! [String]
self.tableView.reloadData()
}
Full Code:
class CityTableViewController: UITableViewController, UISearchResultsUpdating {
var dict = NSDictionary()
var filterTableData = [String]()
var resultSearchController = UISearchController()
var newTableData = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
self.tableView.tableHeaderView = controller.searchBar
return controller
})()
self.tableView.reloadData()
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(self.resultSearchController.active){
return self.filterTableData.count
}else {
return dict.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CountryTableViewCell
if(self.resultSearchController.active){
cell.cityNameLabel.text = filterTableData[indexPath.row]
return cell
}else{
cell.cityNameLabel.text = (((self.dict["\(indexPath.row)"] as?NSDictionary)!["City"] as?NSDictionary)!["name"] as?NSString)! as String
return cell
}
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
filterTableData.removeAll(keepCapacity: false)
let searchWord = searchController.searchBar.text!
getCityNamesFromServer(searchWord)
let searchPredict = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
print("searchPredict is \(searchController.searchBar.text!)")
for var i = 0; i < self.dict.count; i++ {
let cityname = (((self.dict["\(i)"] as?NSDictionary)!["City"] as?NSDictionary)!["name"] as?NSString)! as String
newTableData.append(cityname)
}
let array = (newTableData as NSArray).filteredArrayUsingPredicate(searchPredict)
print("array is\(array)")
filterTableData = array as! [String]
self.tableView.reloadData()
}
func getCityNamesFromServer(searchWord:String){
let url:String = "http://localhost/"
let params = ["city":searchWord]
ServerRequest.postToServer(url, params: params) { result, error in
if let result = result {
print(result)
self.dict = result
}
}
}
}
The best way to limit the amount of data is returned by adding a LIMIT clause to your query. Example SQL query:
SELECT name FROM cities WHERE name like '%something%';
Change this to:
SELECT name FROM cities WHERE name like '%something%' LIMIT 10;
From the user point of view it does not make sense to return 1000 rows, you would not be able to display it anyways, therefore you have to come up with a number that fits on your screen. After any additional key press you can repeat the query with the updated search string.
For a few days, I've been trying to make this piece of code work. I'm trying to build a simple to do list app using core data, integrating the days left until a certain task. I've put the code in my viewWillAppear function. Normally every time the view appears the daysLeft should be updated, but for some reason my display of daysLeft in my table view remains the same all the time, even after a few days. I've been trying to figure out what might be going wrong, did I put the code in the right function or should it go in viewDidLoad or something like that. Could it be a problem with the simulator and it works on a real iPhone? Maybe I haven't replace the object right, maybe my for loop doesn't work well or it could be something else. I'm completely new to programming as a whole, so this is beyond my understanding. Any help would be much appreciated! Cheers :)
var myList: Array<AnyObject> = []
override func viewWillAppear(animated: Bool) {
let appDeleg: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let contexts: NSManagedObjectContext = appDeleg.managedObjectContext!
let freq = NSFetchRequest(entityName: "ToDoList")
let sortDescriptor = NSSortDescriptor(key: "itemDaysLeft", ascending: false)
freq.sortDescriptors = [sortDescriptor]
myList = contexts.executeFetchRequest(freq, error: nil)!
for var index = 0; index < myList.count; index++ {
var data: NSManagedObject = myList[index] as NSManagedObject
var chosenDate: NSDate = data.valueForKey("userChosenDate") as NSDate
var todayDate: NSDate = NSDate()
let calendar = NSCalendar.currentCalendar()
let components = calendar.components(.DayCalendarUnit, fromDate: todayDate, toDate: chosenDate, options: nil)
let secondsInADay = ((60 * 60) * 24)
let newDaysLeft: Int = ((components.hashValue / secondsInADay) + 1)
var newDaysLeftStr: String = "\(newDaysLeft)"
myList[index].setValue(NSNumber(short: newDaysLeft), forKey: "itemDaysLeft")
myList[index].setValue(NSString(UTF8String: newDaysLeftStr), forKey: "daysLeftStr")
// added suggested line by Mike
contexts.save(nil)
}
tableView.reloadData()
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let CellID: NSString = "myCell"
var cell: CustomCell = tableView.dequeueReusableCellWithIdentifier(CellID) as CustomCell
if let ip = indexPath as NSIndexPath! {
var data: NSManagedObject = myList[ip.row] as NSManagedObject
cell.textLabel?.text = data.valueForKeyPath("itemName") as? String
cell.detailTextLabel?.text = data.valueForKeyPath("daysLeftStr") as? String
}
return cell
}
I have a tableView with messages.
Messages are store into parse.com.
I download asynchronously, put the message in a struct message array
import UIKit
var messMgr : messageObjet = messageObjet()
var date : NSDate = NSDate()
struct message {
var commentText = ""
var commentDate = ""
}
class messageObjet: NSObject {
var messageData = [message]()
func addMessage(comment : String, date : String) {
//messageData.append(message(commentText: comment, commentDate: date))
var mess = message(commentText: comment, commentDate: date)
messageData.insert(mess, atIndex: 0)
}
}
and populate my tableView
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:CommentTableViewCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as CommentTableViewCell
cell.tag = indexPath.row
// Configure the cell...
// check if it is in the cache
println("the messageCache in cellForRow in commentTableViewController is \(self.commentCache.objectForKey(indexPath.row))")
if let messageCached: AnyObject = self.commentCache.objectForKey(indexPath.row){
cell.messageLabel.text = messageCached as? String
}
if let dateCached: AnyObject = self.dateCache.objectForKey(indexPath.row){
cell.dateLabel.text = dateCached as? String
}
else if messMgr.messageData.count != 0 {
if cell.tag == indexPath.row {
cell.messageLabel.text = messMgr.messageData[indexPath.row].commentText
self.commentCache.setObject(cell.messageLabel.text!, forKey: indexPath.row)
cell.dateLabel.text = messMgr.messageData[indexPath.row].commentDate
self.dateCache.setObject(cell.dateLabel.text!, forKey: indexPath.row)
}
}
return cell
}
I have modal viewController to add a new message.
In order to display immediately the message after dismissing the modal VC
i did this
#IBAction func sendComment(sender: AnyObject) {
let uuid = userDefaults.stringForKey("ApplicationUniqueIdentifier")
var comment = PFObject(className:"Comment")
comment.setObject(textToSend.text, forKey: "CommentText")
comment.setObject(post, forKey: "Post")
comment.setObject(uuid, forKey: "from")
comment.saveEventually()
let date = NSDate()
newMessage.commentText = textToSend.text as String
newMessage.commentDate = date.relativeTimeToString() as String
messMgr.messageData.insert(newMessage, atIndex: 0)
// messMgr.addMessage(textToSend.text as String, date: date.relativeTimeToString() as String)
NSNotificationCenter.defaultCenter().postNotificationName("reloadMessageTableView", object: nil)
println(messMgr.messageData)
self.dismissViewControllerAnimated(true, completion: nil)
}
The problem is when i come back to my tableView the message added to the index 0 is always displayed as the previous message and when i print my message array the index 0 message is the good one..
Any idea ?
Ok the problem was that i store the message in a NSCache with the indexPath as a key...
Removed this and everyThings work good.