I am struggling to get my head around Firebase Real-time Database and to have it playing nicely with Table View in my restaurant ordering app!
Before reading my code (please don't laugh), what I am hoping to get my TableView is able to do are
1) sort the requests by category (using value retrieved from the key "RequestItemCategory" which has Int value of 1, 2, 3)
2) add the new request to the tableview AND sort the requests again according to the category
3) remove completed food request from the tableview when the customer or employee tick "complete order" on another device which will trigger a change to the "RequestCompleted" key of the requested child on Firebase to "true")
The only things that I can achieve after hours of mucking around are
when app starts, the app loads the table and displays all the requests in the right order according to their category but even the requests that are tagged completed on the Firebase are still displayed.
new orders are added to the bottom of the list but not sort into a category.
What I tried
1) Tag .queryEqual(toValue: "false", childKey: "RequestCompleted") to try to filter out completed requests returns an empty table (not nil information)
2) I can use a button to clear the requestArray then calls retrieveRequest() again. This will refresh the table and all requests will be in the right order but I like to have it done automatically if possible. Also, this method will create another observer so I ended multiple identical orders added to the list...
3) using .value instead of .childAdded - returns nil and the app crashes - I think I need to use for loops but not sure how to do it.
Please help thanks!
Codes are:
Class object
class Request {
var name : String = ""
var requestItem : String = ""
var seat : String = ""
var requestCategory : String = ""
}
Cell.swift
import UIKit
class CustomCellTableViewCell: UITableViewCell {
#IBOutlet var requestText : UITextField!
#IBOutlet var nameText: UITextField!
#IBOutlet var seatNumberText: UITextField!
#IBOutlet var timerText: UITextField!
#IBOutlet var cellBackground : UIView!
#IBOutlet var cellCategoryView : UIView!
#IBOutlet var cellCategoryLabel : UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
ViewController.swift
import UIKit
import Firebase
import SVProgressHUD
class MainPageViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
#IBOutlet weak var requestTableView: UITableView!
var requestArray : [Request] = [Request]()
override func viewDidLoad() {
super.viewDidLoad()
requestTableView.delegate = self
requestTableView.dataSource = self
requestTableView.register(UINib(nibName: "CustomCellTableViewCell", bundle: nil), forCellReuseIdentifier: "customRequestCell")
configureTableView()
retrieveRequest()
// Do any additional setup after loading the view.
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customRequestCell", for: indexPath) as! CustomCellTableViewCell
cell.nameText.text = requestArray[indexPath.row].name
cell.seatNumberText.text = requestArray[indexPath.row].seat
cell.requestText.text = requestArray[indexPath.row].requestItem
cell.cellCategoryLabel.text = requestArray [indexPath.row].requestCategory
if cell.cellCategoryLabel.text == "1" {
cell.cellBackground.backgroundColor = UIColor.init(red: 142/255, green: 0/255, blue: 0/255, alpha: 0.65)
} else if cell.cellCategoryLabel.text == "2" {
cell.cellBackground.backgroundColor = UIColor.init(red: 234/255, green: 179/255, blue: 0/255, alpha: 0.75)
} else {
cell.cellBackground.backgroundColor = UIColor.init(red: 0/255, green: 113/255, blue: 119/255, alpha: 0.65)
}
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return requestArray.count
}
func configureTableView() {
requestTableView.rowHeight = UITableView.automaticDimension
requestTableView.estimatedRowHeight = 73
}
func retrieveRequest() {
let ref = Database.database().reference().child(“Restaurant”).child("Requests").queryOrdered(byChild: "RequestItemCategory")**.queryEqual(toValue: "false", childKey: "RequestCompleted")** adding .queryEqual doesn't work...
ref.observe(.childAdded) { (snapshot) in
let snapshotValue = snapshot.value as? [String : AnyObject] ?? [:]
let customerName = snapshotValue["Lastname"]
let customerSeatnumber = snapshotValue[“Seat”]
let customerRequestedItem = snapshotValue["RequestItem"]
let customerRequestedItemCategory = snapshotValue ["RequestItemCategory"]
let request = Request()
request.name = customerName as! String
request.seat = customerSeatnumber as! String
request.requestItem = customerRequestedItem as! String
request.requestCategory = "\(customerRequestedItemCategory!)"
self.requestArray.append(request)
self.configureTableView()
self.requestTableView.reloadData()
}
}
}
ps. the requestLabel.text is used to change the color of a particular UIView according to the category (RequestItemCategory)
Related
Most of the answers I found for transitioning between view controllers had to do with the normal pushViewController. I am using coordinators for my navigation so was wondering how I can add transition between my UITableView controller and the next transition controller:
Below is the code I am using to navigate between view controllers:
The coordinator:
class ItemsCoordinator: ItemsBaseCoordinator {
func passData(i: Int) {
let vc = Items2ViewController()
vc.selectedIndex = i
//resetToRoot(animated: false)
navigationRootViewController?.pushViewController(vc, animated: false)
}
First View Controller:
class ItemsViewController: UITableViewController, ItemsBaseCoordinated{
var coordinator: ItemsBaseCoordinator?
init(coordinator: ItemsBaseCoordinator) {
super.init(nibName: nil, bundle: nil)
self.coordinator = coordinator
title = "Items"
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(ItemCell.self, forCellReuseIdentifier: "MyCell")
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
coordinator?.passData(i: indexPath.row)
}
}
Second view controller:
class Items2ViewController: UIViewController, ItemsBaseCoordinated {
var coordinator: ItemsBaseCoordinator?
var itemList: [ItemInfo] = [ItemInfo(itemName: "HP Ink Cartridge", itemPrice: 50, itemStatus: "Published", itemImage: UIImage(named: "printer.jpeg")!), ItemInfo(itemName: "iPad",itemPrice: 600, itemStatus: "In Progress", itemImage: UIImage(named: "ipad.jpeg")!),ItemInfo(itemName: "Hoodie",itemPrice: 45, itemStatus: "Unpublished", itemImage: UIImage(named: "hoodie.jpeg")!)]
var items: ItemInfo!
var selectedIndex = 0
lazy var descriptionLabel: UILabel = {
let dl = UILabel()
let a = itemList[selectedIndex].itemPrice
let string = NSMutableAttributedString(string: "Item Price: $\(a)")
string.setColorForText("Item Price:", with: #colorLiteral(red: 0.411764705882353, green: 0.411764705882353, blue: 0.411764705882353, alpha: 1))
dl.attributedText = string
dl.translatesAutoresizingMaskIntoConstraints = false
dl.font = UIFont.systemFont(ofSize: 18)
return dl
}()
I am passing data between the view controllers using a protocol function, how do I add a transition delay between the screens?
I just had to set animation to true in the pushviewcontroller
I am having some issues with the RxDataSources cell reload animations for RxSwift. I have a simple table setup like so:
import UIKit
import RxDataSources
import RxCocoa
import RxSwift
import Fakery
class ViewController1: UIViewController {
#IBOutlet weak var tableView: UITableView!
let bag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
}
private func setupTableView() {
tableView.register(UINib(nibName: "TestTableViewCell", bundle: nil), forCellReuseIdentifier: "cell")
let dataSource = RxTableViewSectionedAnimatedDataSource<SectionOfTestData>(
animationConfiguration: AnimationConfiguration(insertAnimation: .none, reloadAnimation: .none, deleteAnimation: .none),
configureCell: { dataSource, tableView, indexPath, element in
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TestTableViewCell
cell.testData = element
return cell
})
someData
.bind(to: tableView.rx.items(dataSource: dataSource))
.disposed(by: bag)
}
let someData = BehaviorRelay<[SectionOfTestData]>(value: [SectionOfTestData(items: [
TestData(color: .red, name: "Henry"),
TestData(color: .blue, name: "Josh")
])])
#IBAction func didTapUpdateButton(_ sender: Any) {
let colors: [UIColor] = [.blue, .purple, .orange, .red, .brown]
let items = someData.value.first!.items
// Add random data when button is tapped
someData.accept([SectionOfTestData(items: items + [TestData(color: colors.randomElement()!, name: Faker().name.firstName())])])
}
}
The models:
struct TestData {
let color: UIColor
let name: String
}
extension TestData: IdentifiableType, Equatable {
typealias Identity = Int
var identity: Identity {
return Int.random(in: 0..<20000)
}
}
struct SectionOfTestData {
var items: [Item]
var identity: Int {
return 0
}
}
extension SectionOfTestData: AnimatableSectionModelType {
typealias Identity = Int
typealias Item = TestData
// Implement default init
init(original: SectionOfTestData, items: [Item]) {
self = original
self.items = items
}
}
class TestTableViewCell: UITableViewCell {
#IBOutlet weak var colorView: UIView!
#IBOutlet weak var nameLabel: UILabel!
var testData: TestData! {
didSet {
colorView.backgroundColor = testData.color
nameLabel.text = testData.name
}
}
}
When the button is tapped the BehaviorRelay is updated and the table seems to refresh however the "animations" are always the same. In the supplied code I have actually set all animation types to .none yet it is still performing an animation. If I try to change the animation type to another type such as .bottom again the animation is the same. What am I doing wrong here?
Is this a reload animation or insert animation? I have no idea if the table reloads or inserts when the data is updated, I can't find any information in the documents. Any pointers on this would be greatly appreciated!
Your problem is:
var identity: Identity {
return Int.random(in: 0..<20000)
}
RxDataSources uses the identity value in order to compute the changeset.
You have implemented it in a way that essentially returns a new value each time (unless you get a collision) so from the framework point of view, you are always removing all the items and adding new items. You can check this by implementing
decideViewTransition: { _, _, changeset in
print(changeset)
return .animated
}
To complete the accepted answer :
make a uuid and pass it to identity:
let id = UUID().uuidString
var identity: String {
return id
}
then the animations work perfectly.
I need help I have these ColorSlider App that change the color of Square with 3 sliders RGB .I like to create a Tableview where i can store presets of color and retrieve from preset name e.g. Ocean Blue,
Emerald Green, Red Ferrary etc. I create The Tableview with the push of the button but I am stuck with the rest of implementation. Please I need help.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var redSlider: UISlider!
#IBOutlet weak var greenSlider: UISlider!
#IBOutlet weak var blueSlider: UISlider!
#IBOutlet weak var displaylbl: UILabel!
#IBOutlet weak var displayView: UIView!
var color:Color!
override func viewDidLoad() {
super.viewDidLoad()
color = Color(red: redSlider.value, green: greenSlider.value, blue: blueSlider.value)
displaylbl.text = color.getString()
displayView.backgroundColor = color.getColor()
}
#IBAction func sliderChanged(_ sender: UISlider) {
if (sender.tag == 1){
color.setRed(red: sender.value)
displaylbl.text = color.getString()
displayView.backgroundColor = color.getColor()
}
else if (sender.tag == 2) {
color.setGreen(green: sender.value)
displaylbl.text = color.getString()
displayView.backgroundColor = color.getColor()
}
else if (sender.tag == 3) {
color.setBlue(blue: sender.value)
displaylbl.text = color.getString()
displayView.backgroundColor = color.getColor()
}
}
}
import UIKit
class Color {
private var red:CGFloat
private var green:CGFloat
private var blue:CGFloat
init(red:Float, green:Float, blue:Float) {
self.red = CGFloat(red)
self.green = CGFloat(green)
self.blue = CGFloat(blue)
}
func setRed(red:Float) {
self.red = CGFloat(red)
}
func setGreen(green:Float) {
self.green = CGFloat(green)
}
func setBlue(blue:Float) {
self.blue = CGFloat(blue)
}
func getColor() -> UIColor {
let color = UIColor(red: red/255, green: green/255, blue: blue/255, alpha: 1.0)
return color
}
func getString() -> String {
let string = "Red: \(Int(round(red)))\nGreen: \(Int(round(green)))\nBlue: \(Int(round(blue)))"
return string
}
}
First you need to get the RGB color from slider and show in box, if you have done this part then now you need to do is whenever you are showing color in box you just make a dictionary with keys like :
let dict = [ colorName : "whatever you want",
colorCode : "Which you get from slider"
]
Add it in array too like:
arrayColor.append(dic)
Populate your tableview from arrayColor array, and in didselectatindexpath you need to get colorCode atIndexPath.row and set in box.
Hope this will help you. For any other query comment below.
I'm having a Sign up View controller in storyboard and it keeps showing me the same error in console:
this class is not key value coding-compliant for the key displayNameTextField.
I'm aware that this means that there's a connection that no longer exists and it causing it to crash. The problem is that I don't see any issues whatsoever. This is the View controller in Storyboard along with all the connections:
This is the code:
class SignUpVC: UIViewController
{
#IBOutlet weak var displayNameTextField: UITextField!
#IBOutlet weak var usernameTextField: UITextField!
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var registerButton: UIButton!
override func viewDidLoad()
{
super.viewDidLoad()
setupTextFields()
}
func setupTextFields()
{
displayNameTextField.tintColor = UIColor.white
displayNameTextField.textColor = UIColor.white
displayNameTextField.backgroundColor = UIColor(red: 138/255, green: 82/255, blue: 108/255, alpha: 1.0)
displayNameTextField.attributedPlaceholder = NSAttributedString(string : displayNameTextField.placeholder!,
attributes: [NSAttributedStringKey.foregroundColor : UIColor(white: 1.0, alpha: 0.6)])
handleTextFields()
}
func handleTextFields()
{
displayNameTextField.addTarget(self, action: #selector(self.textFieldDidChange), for: .editingChanged)
}
#objc func textFieldDidChange() {
guard let username = usernameTextField.text, !username.isEmpty,
let displayName = displayNameTextField.text, !displayName.isEmpty,
let email = emailTextField.text, !email.isEmpty,
let password = passwordTextField.text, !password.isEmpty
else
{
return
}
registerButton.isEnabled = true
}
}
What am I doing wrong? It's one of those cases where this should be painfully obvious but I just don't see it.
This error is usually an indication that you haven’t set the subclass of the view controller correctly.
Check in the attributes inspector in interface builder for that view controller.
I'm changing background colour of a view in tableViewCell but it doesn't change at first load and i need to scroll to see changes. This is my cell class:
#IBOutlet weak var status_back: UIView!
#IBOutlet weak var status_label: UILabel!
#IBOutlet weak var node_label: UILabel!
#IBOutlet weak var time_label: UILabel!
#IBOutlet weak var name_label: UILabel!
#IBOutlet weak var res_date: UILabel!
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func layoutSubviews() {
status_back.layer.cornerRadius = status_back.frame.height/2
}
and the code that changes color of status_back in ViewController:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "MealCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! ReservationCell
let reservation = filteredreservations[(indexPath as NSIndexPath).row]
cell.name_label.text = "نام: "+reservation.client_name
cell.name_label.font = UIFont(name: "WeblogmaYekan", size: 17)
cell.time_label.text="زمان: "+reservation.ft_of_time+"-"+reservation.ft_to_time
cell.time_label.font = UIFont(name: "B Yekan", size: 17)
cell.res_date.text="تاریخ: \(reservation.date)"
cell.res_date.font = UIFont(name: "B Yekan", size: 17)
var status = ""
if reservation.type {
cell.node_label.text="ex1 \(reservation.node_title)"
} else {
cell.node_label.text="ex2"
}
switch reservation.res_status {
case "CanceledByAdmin":
cell.status_back.backgroundColor=Utils.UIColorFromRGB(rgbValue: 0xFFFC635D)
status = "ex"
case "Canceled":
cell.status_back.backgroundColor=Utils.UIColorFromRGB(rgbValue: 0xFFEE903D)
status = "ex"
case "Deprecated":
cell.status_back.backgroundColor=Utils.UIColorFromRGB(rgbValue: 0xFF757575)
status = "ex"
default:
cell.status_back.backgroundColor=Utils.UIColorFromRGB(rgbValue: 0xFF3BA757)
status = "ex"
}
cell.status_label.text=status
cell.status_label.font = UIFont(name: "WeblogmaYekan", size: 17)
return cell
}
You must not use break in this situation, as break means it'll exit from the switch.
From Swift 3 docs:
In contrast with switch statements in C and Objective-C, switch
statements in Swift do not fall through the bottom of each case and
into the next one by default. Instead, the entire switch statement
finishes its execution as soon as the first matching switch case is
completed, without requiring an explicit break statement. This makes
the switch statement safer and easier to use than the one in C and
avoids executing more than one switch case by mistake.
The switch control flow ends right after the certain case is met in Swift.
switch reservation.res_status {
case "CanceledByAdmin":
color = Utils.UIColorFromRGB(rgbValue: 0xFFFC635D)
status = "example4"
case "Canceled":
color = Utils.UIColorFromRGB(rgbValue: 0xFFEE903D)
status = "example3"
case "Deprecated":
color = Utils.UIColorFromRGB(rgbValue: 0xFF757575)
status = "example2"
default:
color = Utils.UIColorFromRGB(rgbValue: 0xFF3BA757)
status = "example"
}
cell.status_label.text=status
cell.status_back.backgroundColor = color
cell.status_label.font = UIFont(name: "WeblogmaYekan", size: 17)
return cell
I've found the answer. when cell is created there is no data for status_back.frame.height so
override func layoutSubviews() {
status_back.layer.cornerRadius = status_back.frame.height/2
}
makes status_back invisible and the next time it recycled the code works