I have a TabBar with various Tabs in my RestaurantApp, When I click the addToCart and goes to the CartViewContorller, the added item don't show I have to relaunch the App to see the item there. I have seen similar questions with various answer on this question here but non of the solutions seems to work in my case I don't really know whats wrong. Below is my code for the CartViewContorller I want to reload tableview anytime it is loaded. Thanks all for your help
import UIKit
import Alamofire
import os.log
class CartViewController: UITableViewController {
var cartData = [CartResponse.Cart]()
override func viewDidLoad() {
super.viewDidLoad()
cart()
tableView.delegate = self
tableView.dataSource = self
let nib = UINib(nibName: "viewCartCell", bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "cartCustomCell")
let footerView = UIView()
footerView.backgroundColor = UIColor.red
footerView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 60)
tableView.tableFooterView = footerView
}
override func viewDidAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tableView.reloadData()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cartData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell: CartTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "cartCustomCell", for: indexPath) as? CartTableViewCell else {
os_log("Dequeue cell isn't an instance of CustomTableCell", log: .default, type: .debug)
fatalError()
}
cell.recipeNameLbl?.text = cartData[indexPath.row].recipeName
cell.restaurantNameLbl?.text = cartData[indexPath.row].restaurantName
cell.addtionalNoteLbl?.text = cartData[indexPath.row].additionalNote
cell.quantityLbl?.text = cartData[indexPath.row].recipeQuantity
cell.totalLbl?.text = cartData[indexPath.row].recipePrice
cell.totalCostLbl?.text = cartData[indexPath.row].totalCost
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
guard editingStyle == .delete else {return}
//getting userId from defaults
let CartId = cartData[indexPath.row].cartId
let cartId = CartId
//creating parameters for the post request
let parameters: Parameters=[
"cartId":Int(cartId)
]
//Constant that holds the URL for web service
let URL_SELECT_FROM_CART = "http://localhost:8888/restaurant/deleteFromCart.php?"
Alamofire.request(URL_SELECT_FROM_CART, method: .post, parameters: parameters).responseJSON {
response in
//printing response
print(response)
}
cartData.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
}
//Fetching from Cart Method
func cart(){
//getting userId from defaults
let defaultValues = UserDefaults.standard
let userId = defaultValues.string(forKey: "userid")
//creating parameters for the post request
let parameters: Parameters=[
"userId":Int(userId!)!
]
//Constant that holds the URL for web service
let URL_SELECT_FROM_CART = "http://localhost:8888/restaurant/cart.php?"
Alamofire.request(URL_SELECT_FROM_CART, method: .post, parameters: parameters).responseJSON {
(response) in
let result = response.data
do{
let decoder = JSONDecoder()
let downloadedCart = try decoder.decode(CartResponse.self, from: result!)
self.cartData = downloadedCart.cartItem
DispatchQueue.main.async {
self.tableView.reloadData()
}
}catch {
print(error)
}
}.resume()
}
}
You can use :
import UserNotifications
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "loadCart"), object: nil)
see more in this answer
You have to call cart() this method in viewWillAppear instead of calling viewDidload
override func viewWillAppear(_ animated: Bool) {
self.cart()
}
Related
I am not able to display the JSON data in my table view. I don't know why. I tried to get the JSON data, but I am not able to display it on screen in a table format.
This is the model:
class PastTripsVC: UIViewController {
var past = [PastRide]()
#IBOutlet weak var mTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let nibCell = UINib(nibName: "PastTableView", bundle: nil)
mTable.register(nibCell, forCellReuseIdentifier: "cell")
apiCalling()
}
func apiCalling(){
if let url = URL(string: "https://pincood.com/pincood/public/api/user/trips") {
var request = URLRequest(url: url)
request.allHTTPHeaderFields = [
"Content-Type": "application/json",
"Session": "fb4e7f9b-0f31-4709-",
"AUthorization":"<some key>"
]
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard error == nil else { return }
guard let data = data else { return }
do{
let codabledata = try JSONDecoder().decode([PastRide].self, from: data)
print(codabledata)
DispatchQueue.main.async {
self.past = codabledata
self.mTable.reloadData()
}
} catch {
print(error)
}
}.resume()
}
}
In the extension we try:
extension PastTripsVC : UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return past.count
print(past.count)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! PastTableView
cell.usernm.text = past[indexPath.row].provider.firstName
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let details : PastDetailView = self.storyboard?.instantiateViewController(withIdentifier: "PastDetailView") as! PastDetailView
navigationController?.pushViewController(details, animated: true)
}
}
You need to set table view's delegate and datasource properties in viewDidLoad.
Update your viewDidLoad to look like this:
override func viewDidLoad() {
super.viewDidLoad()
let nibCell = UINib(nibName: "PastTableView", bundle: nil)
mTable.register(nibCell, forCellReuseIdentifier: "cell")
mTable.delegate = self
mTable.datasource = self
apiCalling()
}
I am working on a twitter clone and I connected my app to firebase to read and write the posts. However, when I delete a cell the animation starts and the deletion actually happens from my posts array. The problem is when I try to delete another post the post I already deleted re-appears for some reason.
Here are some screenshots of what is happening:
First I delete a post and everything is normal.
deleting a post
Then the post is deleted
deleted post
now I try to delete another post
deleting another post
Then the older post re-appears in the place of the last post deleted.
deleted post reappears
This continues until I get an index out of bounds exception when I attempt to delete all the cells.
As seen from the screenshots, I delete the cells from a delete button which is why I need a callback from the cell.
Here is my code for the cell and for the "Home Screen"
class PostTableViewCell: UITableViewCell, UITextViewDelegate{
var didDelete : ((UITableViewCell) -> Void)?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
func set(post: Post){
username.text = post.username
postText.text = post.text
timestamp.text = post.createdAt.calenderTimeSinceNow()
postId = post.id
setPopupMenu(post: post)
}
func setPopupMenu(post: Post){
let options = { (action: UIAction) in
print(action.title)
if(action.title == "Delete"){
// delete post
self.didDelete!(self)
}
}
menuButton.showsMenuAsPrimaryAction = true
}
}
and this is the Home Screen code :
class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
let cellNib = UINib(nibName: "PostTableViewCell", bundle: nil)
var posts = [Post]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(cellNib, forCellReuseIdentifier: "postCell")
tableView.delegate = self
tableView.dataSource = self
readPosts()
// Do any additional setup after loading the view.
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
tableView.reloadRows(at: [indexPath], with: .fade)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! PostTableViewCell
cell.set(post: posts[indexPath.row])
cell.didDelete = { aCell in
self.progressView.isHidden = false
let currentIndexPath = tableView.indexPath(for: aCell)!
let db = Firestore.firestore()
db.collection("Posts").document(Auth.auth().currentUser!.uid).collection("Posts").document(cell.postId).delete() { err in
self.progressView.isHidden = false
if let err = err {
print("Error removing document: \(err)")
} else {
self.posts.remove(at: indexPath.row)
tableView.deleteRows(at: [currentIndexPath], with: .automatic)
self.progressView.isHidden = true
}
}
}
return cell
}
func readPosts(){
let db = Firestore.firestore()
db.collection("Posts").addSnapshotListener { documentSnapshot, error in
guard let document = documentSnapshot else {
print("Error fetching document: \(error!)")
return
}
var tempPosts = [Post]()
for doc in documentSnapshot!.documents{
db.collection("Posts").document(doc.documentID).collection("Posts").order(by: "timestamp").addSnapshotListener { documentSnapshot, error in
for docu in documentSnapshot!.documents {
let daate = docu.data()["timestamp"] as! Timestamp
let mydbl = daate.dateValue().timeIntervalSince1970 * 1000
let post = Post(id: docu.documentID, username: "#\(docu.data()["username"]!)", userId: doc.documentID , text: docu.data()["text"] as! String, timestamp: mydbl , numLikes: 0, numReposts: 0)
let isDuplicate = tempPosts.contains(where: { $0.id == post.id })
if(!isDuplicate){
tempPosts.insert(post, at: 0)
}
}
self.posts.removeAll()
self.posts = tempPosts
self.tableView.reloadData()
}
}
self.progressView.isHidden = true
}
}
}
Note that when I comment out the firebase document deletion part everything works fine and the deleted document is not re produced
var list = [String]()
#IBOutlet weak var TableView: UITableView!
override func viewDidLoad() {
self.title = "Routines"
TableView.delegate = self
TableView.dataSource = self
super.viewDidLoad()
}
//refresh view when going back to this viewcontroller
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("Test Worked")
TableView.reloadData()
}
//generating rows
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return (list.count)
}
//returning text in UITableViewCell
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = UITableViewCell(style:
UITableViewCell.CellStyle.default, reuseIdentifier:
"prototype1")
print("printed")
cell.textLabel?.text = list[indexPath.row]
return cell
}
//deleting rows
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath)
{
if editingStyle == UITableViewCell.EditingStyle.delete{
deleteAllData("ToDo")
self.list.remove(at: indexPath.row)
TableView.reloadData()
}
}
#IBAction func didAdd() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(identifier: "addRoutinePage")as! addRoutinePage
self.navigationController?.pushViewController(vc, animated: true)
}
//function to get data from core data
func getData()
{
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "ToDo")
request.returnsObjectsAsFaults = false
do{
//fetching data from coredata
let result = try context.fetch(request)
for data in result as! [NSManagedObject]
{
//appending the list from the value in coredata (attribute) or entity
self.list.append(data.value(forKey: "title")as! String)
print("append success")
}
}catch {
print("failed")
}
}
What is wrong with my code? Everything seems to work except for the UITableViewCell, the print command I entered just to check if the function is executed didn't even work. I tried TableView.reloadData() but it still didn't work. Logically if the problem is with the public function or data source or delegate it won't even generate any rows, but rows are generated. I tried resizing the cell height size too but it still won't work. Please help!
There are a few errors with the code:
You need to reload once the data fetching from CoreData is complete.
func getData() {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "ToDo")
request.returnsObjectsAsFaults = false
do {
let result = try context.fetch(request)
for data in result as! [NSManagedObject] {
self.list.append(data.value(forKey: "title")as! String)
}
self.TableView.reloadData()
} catch {
print(error)
}
}
Also, don't forget to call the getData function.
override func viewDidLoad() {
super.viewDidLoad()
title = "Routines"
TableView.delegate = self
TableView.dataSource = self
getData()
}
I have a ViewController, where a list of data is being fetched from the RealtimeDatabase and being displayed in the UITableView. When a post is clicked, I want the data from the ViewController such as Title, Description, and Image to be carried forward into a DetailView i.e: PostController.Swift where I can further call more data from the RealtimeDatabase to be displayed in the PostController.Swift
I tried to use didSelectRowAt but was unable to fetch and display data in the PostController, I'm also currently trying to use prepare but getting stuck at the same place.
Here is the ViewController.Swift:
import UIKit
import Firebase
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var tableView:UITableView!
var posts = [Post]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableView = UITableView(frame: view.bounds, style: .plain)
view.addSubview(tableView)
let cellNib = UINib(nibName: "PostTableViewCell", bundle: nil)
tableView.register(cellNib, forCellReuseIdentifier: "postCell")
var layoutGuide:UILayoutGuide!
layoutGuide = view.safeAreaLayoutGuide
tableView.leadingAnchor.constraint(equalTo: layoutGuide.leadingAnchor).isActive = true
tableView.topAnchor.constraint(equalTo: layoutGuide.topAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: layoutGuide.trailingAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: layoutGuide.bottomAnchor).isActive = true
tableView.delegate = self
tableView.dataSource = self
tableView.tableFooterView = UIView()
tableView.reloadData()
observePosts()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func observePosts() {
let postsRef = Database.database().reference().child("Data")
postsRef.observe(.value, with: { snapshot in
var tempPosts = [Post]()
for child in snapshot.children{
if let childSnapshot = child as? DataSnapshot,
let dict = childSnapshot.value as? [String:Any],
let title = dict["title"] as? String,
let logoImage = dict["image"] as? String,
let url = URL(string:logoImage),
let description = dict["description"] as? String{
let userProfile = UserProfile(title: title, photoURL: url)
let post = Post(id: childSnapshot.key, title: userProfile, description: description, image: userProfile)
print(post)
tempPosts.append(post)
}
}
self.posts = tempPosts
self.tableView.reloadData()
})
}
func getImage(url: String, completion: #escaping (UIImage?) -> ()) {
URLSession.shared.dataTask(with: URL(string: url)!) { data, response, error in
if error == nil {
completion(UIImage(data: data!))
} else {
completion(nil)
}
}.resume()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! PostTableViewCell
cell.set(post: posts[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
tableView.estimatedRowHeight = 50
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "PostDetailSegue" {
if let indexPaths = self.tableView!.indexPathForSelectedRow{
}
}
}
}
You need to store a reference to the Post in your DetailViewController.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "PostDetailSegue" {
let detailViewController = segue.destinationViewController as! DetailViewController
let indexPath = tableView.indexPathForSelectedRow
let post = posts[indexPath.row]
detailViewController.setPost(post)
}
}
And in your DetailViewController:
func detailViewController.setPost(_ post: Post) {
// Store the post and fetch other data as necessary
self.post = post
}
try this one.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let postsInfo = posts[indexPath.row]
print(postsInfo)
performSegueWithIdentifier("PostDetailSegue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "PostDetailSegue" {
if let indexPaths = posts[indexPath.row]{
//add parmaters which passed to another viewcontroller
}
}
}
I have a create a xib as customTableViewCell and on my main storyboard, I have a ViewController as a popUp and have link the viewController to the popUp through a segue way. Now I am trying to open the popUp through didSelectRowAt. When I run the app and click on a row it gives error
<CustomTableCell.ViewController: 0x7feb4650ea00>) has no segue with identifier 'popUp'
Below is my code
import UIKit
import os.log
class ViewController: UITableViewController {
var data = [Response.Recipe]()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Restaurants"
downloadJson()
tableView.delegate = self
tableView.dataSource = self
//tableView.register(UITableViewCell.classForCoder(), forCellReuseIdentifier: "Cell")
let nib = UINib(nibName: "viewTblCell", bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "customCell")
if #available(iOS 10.0, *) {
tableView.refreshControl = refresher
} else {
tableView.addSubview(refresher)
}
// sampleData()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell: CustomTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as? CustomTableViewCell else {
os_log("Dequeue cell isn't an instance of CustomTableCell", log: .default, type: .debug)
fatalError()
}
cell.recipeNameLbl?.text = data[indexPath.row].recipeName
let image = data[indexPath.row].recipeImage
let ImageUrl = "http://localhost:8888/restaurant/recipeImages/"+image
if let imageURL = URL(string: ImageUrl) {
DispatchQueue.global().async {
let data = try? Data(contentsOf: imageURL)
if let data = data {
let image = UIImage(data: data)
DispatchQueue.main.async {
cell.recipeImg?.image = image
}
}
}
}
let restaurantOpen = data[indexPath.row].restaurantStatus
if restaurantOpen == "Closed" {
cell.restaurantStatusLbl?.text = restaurantOpen
cell.restaurantStatusLbl?.isHidden = false
}else{
cell.restaurantStatusLbl?.isHidden = true
}
cell.restaurantOperateDays?.text = data[indexPath.row].restaurantOpenDays
cell.restaurantOperateTime?.text = data[indexPath.row].restaurantOpenTime
cell.retaurantNameLbl?.text = data[indexPath.row].restaurantName
cell.recipeTimeLbl?.text = "Delivers in " + String(describing: data[indexPath.row].recipeTime) + " minutes"
cell.recipePriceLbl?.text = "GHS " + String(describing: data[indexPath.row].recipePrice)
//cell.delegate = self
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "popUp", sender: self)
print(data[indexPath.row].recipeName)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? AddToCartViewController {
destination.popUpDetails = data[(tableView.indexPathForSelectedRow?.row)!]
}
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 230
}
func downloadJson() {
let url = URL(string: "http://localhost:8888/restaurant/tableView.php")
guard let downloadURL = url else { return }
URLSession.shared.dataTask(with: downloadURL) { data, urlResponse, error in
guard let data = data, error == nil, urlResponse != nil else {
print("something is wrong")
return
}
print("downloaded")
do
{
let decoder = JSONDecoder()
let downloadedRecipes = try decoder.decode(Response.self, from: data)
self.data = downloadedRecipes.recipeTbl
print(downloadedRecipes.recipeTbl)
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print(error)
}
}.resume()
}
lazy var refresher: UIRefreshControl = {
let refreshControl = UIRefreshControl()
refreshControl.tintColor = UIColor.red
refreshControl.addTarget(self, action: #selector(requestData), for: .valueChanged)
return refreshControl
}()
#objc func requestData(){
let deadline = DispatchTime.now() + .milliseconds(800)
DispatchQueue.main.asyncAfter(deadline: deadline){
self.downloadJson()
self.refresher.endRefreshing()
}
}
}
I finally achieved what I wanted to do
I move the popUp to its own storyboard then gave it Storyboard ID "addToCart"
Then I open it in the didSelectRowat method
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let popup = UIStoryboard(name: "AddToCart", bundle: nil)
let addToCartPopup = popup.instantiateInitialViewController()! as! AddToCartViewController
addToCartPopup.popUpDetails = data[(tableView.indexPathForSelectedRow?.row)!]
self.present(addToCartPopup, animated: true)
}