I have a tableView currently that is being populated with content from a firebase database. I am able to print out the specific information that is to be populated in the cells, and the cells physically are being populated indicated by the ability to scroll the appropriate length of the populated data. I am not sure as to why it would be accessing the information and populating the cells, but not inserting the data into the cells?
TableView:
import UIKit
import FirebaseAuth
import FirebaseStorage
class Articles: UITableViewController {
var vcType:String = "Home"
//var valueTopass = [[String:String]]()
var rooms = [Room]()
var articleCell = ArticlesCell()
#IBOutlet weak var menuButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
if self.vcType == "Home"
{
self.rooms += ArticlesManager.sharedClient.rooms
}
else
{
if let obj = ArticlesManager.sharedClient.catRooms[self.vcType.lowercased()] //as? [Room]
{
self.rooms += obj
}
}
self.tableView.reloadData()
ArticlesManager.sharedClient.blockValueChangeInRoomArray = {
newRoom in
if self.vcType == "Home"
{
self.rooms.append(newRoom)
self.rooms.sort(by: {
if $0.created_Date == nil
{
return false
}
if $1.created_Date == nil
{
return true
}
return $0.created_Date.compare($1.created_Date) == ComparisonResult.orderedDescending
})
}
else
{
if self.vcType.lowercased() == newRoom.category
{
self.rooms.append(newRoom)
self.rooms.sort(by: {
if $0.created_Date == nil
{
return false
}
if $1.created_Date == nil
{
return true
}
return $0.created_Date.compare($1.created_Date) == ComparisonResult.orderedDescending
})
self.tableView.reloadData()
}
}
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rooms.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath as NSIndexPath).row == 0 {
let cell2 = tableView.dequeueReusableCell(withIdentifier: "featured", for: indexPath) as! featuredCell
let room = rooms[(indexPath as NSIndexPath).row]
cell2.configureCell(room)
self.tableView.reloadData()
return cell2
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ArticlesCell
let room = rooms[(indexPath as NSIndexPath).row]
cell.configureCell(room)
self.tableView.reloadData()
return cell
}
}
}
ArticleManager:
import UIKit
class ArticlesManager: NSObject {
static let sharedClient = ArticlesManager()
var dateFormater:DateFormatter{
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd HH:mm:ss a z"
return df
}
var roomsitems = ["Home", "News", "Features", "Opinion", "Sports", "Entertainment", "Editor"]
var rooms = [Room]()
var catRooms = [String:[Room]]()
var blockValueChangeInRoomArray:((Room) -> ())!
func fetchData()
{
Data.dataService.fetchData {
(room) in
self.rooms.append(room)
self.rooms.sort(by: {
if $0.created_Date == nil
{
return false
}
if $1.created_Date == nil
{
return true
}
return $0.created_Date.compare($1.created_Date) == ComparisonResult.orderedDescending
})
if let obj = self.catRooms[room.category] //as? [Room]
{
var objRooms = obj
objRooms.append(room)
objRooms.sort(by: {
if $0.created_Date == nil
{
return false
}
if $1.created_Date == nil
{
return true
}
return $0.created_Date.compare($1.created_Date) == ComparisonResult.orderedDescending })
self.catRooms[room.category] = objRooms
}
else
{
self.catRooms[room.category] = [room]
}
if self.blockValueChangeInRoomArray != nil
{
self.blockValueChangeInRoomArray(room)
}
}
}
}
DataService:
import UIKit
class ArticlesManager: NSObject {
static let sharedClient = ArticlesManager()
var dateFormater:DateFormatter{
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd HH:mm:ss a z"
return df
}
var roomsitems = ["Home", "News", "Features", "Opinion", "Sports", "Entertainment", "Editor"]
var rooms = [Room]()
var catRooms = [String:[Room]]()
var blockValueChangeInRoomArray:((Room) -> ())!
func fetchData()
{
Data.dataService.fetchData {
(room) in
self.rooms.append(room)
self.rooms.sort(by: {
if $0.created_Date == nil
{
return false
}
if $1.created_Date == nil
{
return true
}
return $0.created_Date.compare($1.created_Date) == ComparisonResult.orderedDescending
})
if let obj = self.catRooms[room.category] //as? [Room]
{
var objRooms = obj
objRooms.append(room)
objRooms.sort(by: {
if $0.created_Date == nil
{
return false
}
if $1.created_Date == nil
{
return true
}
return $0.created_Date.compare($1.created_Date) == ComparisonResult.orderedDescending })
self.catRooms[room.category] = objRooms
}
else
{
self.catRooms[room.category] = [room]
}
if self.blockValueChangeInRoomArray != nil
{
self.blockValueChangeInRoomArray(room)
}
}
}
}
Remove this line of code from cellForRowAt indexPath method:
self.tableView.reloadData()
This is reloading Table continuously without giving it chance to configureCell and display data.
I had been able to figure out the particular issue that I was having. As I am using firebase, the rules as to who could "read" was, as default, set to authenticated users. In order to change this, you must go into the firebase console and either change it to public or authenticate users before loading the table view. Either way, I hope this was able to help you!
Related
I have a problem with passing the data
To make it clear, I will explain the idea of what I worked on, and what is the problem.
The idea is in the first view controller the user will enter the title and description and then chooses from the options of the pop-up button, the options are (exchange, borrow, donation, sell). The data entered will be saved in the option chosen by the user. then the data will be displayed in the second view controller in the table view. If the user chooses the exchange option and enters the data, his data will be displayed in the table view in the exchange (index 0) and this works for me the data is displayed in the table view in the correct form as I want.
The problem I am experiencing is when I pass the data to the other view controller.
When the user clicks on any cell, it will pass the same data regardless of the difference in the index. If the user chooses the borrow (index 1) and clicks any cell, it'll display the exchange (index 0) data. No matter what indexes you choose and the cell you click on it will pass the same data!!!!!
first view controller
here I'm entering the data
it's shown in the table view in the right index of the segment no problem with that
after I click it pass the right data
look here if I change the index and click to any cell it will pass the same data!!
look here if I change the index and click to any cell it will pass the same data!!
Here's my code for the first vc
import UIKit
import FirebaseFirestore
class ViewController4: UIViewController {
#IBOutlet weak var mssglabel: UILabel!
#IBOutlet weak var selectservice: UIButton!
#IBOutlet weak var titleTextField: UITextField!
#IBOutlet weak var descriptionTextField: UITextView!
#IBOutlet weak var custombtun: UIButton!
let db = Firestore.firestore()
var chooseOption = ""
override func viewDidLoad() {
super.viewDidLoad()
setpopupbutn()
selectservice.layer.cornerRadius = 25
descriptionTextField.layer.cornerRadius = 25
custombtun.layer.cornerRadius = 25
}
#IBAction func containbutn(_ sender: Any) {
let vc = (storyboard?.instantiateViewController(withIdentifier: "vc3"))!
navigationController?.pushViewController(vc, animated: true)
spcificOption()
}
func saveDataDonation() {
if let description = descriptionTextField.text,
let tittle = titleTextField.text{
// Save Data to Database
db.collection("userDonationDatabase")
.addDocument(data: [
"description" : description,
"BookTitle": tittle ]) {
(error) in
if let err = error {
print(err.localizedDescription)
}else {
print("تم حفظ البيانات بنجاح")
print(description)
print(tittle)
}
} // end of closure
}
}
func saveDataSale() {
if let description = descriptionTextField.text,
let tittle = titleTextField.text{
// Save Data to Database
db.collection("userSaleDatabase")
.addDocument(data: [
"description" : description,
"BookTitle": tittle ]) {
(error) in
if let err = error {
print(err.localizedDescription)
}else {
print("تم حفظ البيانات بنجاح")
print(description)
print(tittle) }
}
}
}
func saveDataExchange() {
if let description = descriptionTextField.text,
let tittle = titleTextField.text {
// Save Data to Database
db.collection("userExchangeDatabase")
.addDocument(data: [
"description" : description,
"BookTitle": tittle ]) {
(error) in
if let err = error {
print(err.localizedDescription)
}else {
print("تم حفظ البيانات بنجاح")
print(description)
print(tittle) }
}
}
}
func saveDataBorrow() {
if let description = descriptionTextField.text,
let tittle = titleTextField.text {
// Save Data to Database
db.collection("userBorrowDatabase")
.addDocument(data: [
"description" : description,
"BookTitle": tittle]) {
(error) in
if let err = error {
print(err.localizedDescription)
}else {
print("تم حفظ البيانات بنجاح")
print(description)
print(tittle) }
}
}
}
func setpopupbutn () {
let option = {( ACTION : UIAction ) in
self.chooseOption = ACTION.title
print("حفظ الداتا في ",self.chooseOption)}
selectservice.menu = UIMenu (children : [
UIAction (title : "تبرع" , state: .on , handler: option),
UIAction (title : "بيع" , handler: option),
UIAction (title : "تبادل" , handler: option),
UIAction (title : "إستعارة" , handler: option),
])
saveDataDonation()
selectservice.showsMenuAsPrimaryAction = true
selectservice.changesSelectionAsPrimaryAction = true
}
func spcificOption() {
if chooseOption == ("تبرع") {
saveDataDonation()
} else if chooseOption == ("بيع") {
saveDataSale()
} else if chooseOption == ("تبادل") {
saveDataExchange()
} else if chooseOption == ("إستعارة") {
saveDataBorrow()
}
}
}
and this is the second vc (Table view)
import UIKit
import FirebaseFirestore
import Firebase
class ViewController3: UIViewController, UITableViewDataSource, UITableViewDelegate {
let db = Firestore.firestore()
var exchange : [exchange] = []
var borrow : [borrow] = []
var donation : [donation] = []
var sale : [sale] = []
#IBOutlet weak var segmentOutlet: UISegmentedControl!
#IBOutlet weak var userDataTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
userDataTableView.dataSource = self
userDataTableView.delegate = self
getDataDonation()
getDataSale()
getDataExchange()
getDataBorrow()
userDataTableView.reloadData()
}
#IBAction func serviceSeg(_ sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0 {
getDataExchange()
}
else if sender.selectedSegmentIndex == 1 {
getDataBorrow()
}
else if sender.selectedSegmentIndex == 2 {
getDataDonation()
}
else if sender.selectedSegmentIndex == 3 {
getDataSale()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if segmentOutlet.selectedSegmentIndex == 0 {
return exchange.count
} else if segmentOutlet.selectedSegmentIndex == 1 {
return borrow.count
}else if segmentOutlet.selectedSegmentIndex == 2 {
return donation.count
} else if segmentOutlet.selectedSegmentIndex == 3 {
return sale.count
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
if segmentOutlet.selectedSegmentIndex == 0 {
cell.textLabel?.text = exchange [indexPath.row].passTitle
} else if segmentOutlet.selectedSegmentIndex == 1 {
cell.textLabel?.text = borrow [indexPath.row].passTitle
} else if segmentOutlet.selectedSegmentIndex == 2 {
cell.textLabel?.text = donation [indexPath.row].passTitle
} else if segmentOutlet.selectedSegmentIndex == 3 {
cell.textLabel?.text = sale [indexPath.row].passTitle
}
return cell
}
func getDataDonation(){
donation.removeAll()
db.collection("userDonationDatabase")
.getDocuments { querySnapshot, error in
if let err = error { print(err.localizedDescription)}
else {
for document in querySnapshot!.documents {
let data = document.data()
print( data["BookTitle"] as! String )
self.donation.append(finalProject.donation(passTitle3:data["BookTitle"] as! String , passDes3: data["description"] as! String))
}
DispatchQueue.main.async {
self.userDataTableView.reloadData()
}
}
}
}
func getDataSale(){
sale.removeAll()
db.collection("userSaleDatabase")
.getDocuments { querySnapshot, error in
if let err = error { print(err.localizedDescription)}
else {
for document in querySnapshot!.documents {
let data = document.data()
print( data["BookTitle"] as! String )
self.sale.append(finalProject.sale(passTitle4:data["BookTitle"] as! String , passDes4: data["description"] as! String))
}
DispatchQueue.main.async {
self.userDataTableView.reloadData()
}
}
}
}
func getDataExchange(){
exchange.removeAll()
db.collection("userExchangeDatabase")
.getDocuments { querySnapshot, error in
if let err = error { print(err.localizedDescription)}
else {
for document in querySnapshot!.documents {
let data = document.data()
print( data["BookTitle"] as! String )
self.exchange.append(finalProject.exchange(passTitle1:data["BookTitle"] as! String , passDes1: data["description"] as! String))
}
DispatchQueue.main.async {
self.userDataTableView.reloadData()
}
}
}
}
func getDataBorrow(){
borrow.removeAll()
db.collection("userBorrowDatabase")
.getDocuments { querySnapshot, error in
if let err = error { print(err.localizedDescription)}
else {
for document in querySnapshot!.documents {
let data = document.data()
print( data["BookTitle"] as! String )
self.borrow.append(finalProject.borrow(passTitle2:data["BookTitle"] as! String , passDes2: data["description"] as! String))
}
DispatchQueue.main.async {
self.userDataTableView.reloadData()
}
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let vc = storyboard?.instantiateViewController(withIdentifier:"vc10") as? ViewController10 {
vc.recivedE = exchange[indexPath.row]
self.navigationController?.pushViewController(vc, animated: true)
}
}
}
Note... I tried to do this but it didn't work
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let vc = storyboard?.instantiateViewController(withIdentifier:"vc10") as? ViewController10 {
vc.recivedE = exchange[indexPath.row]
vc.recivedB = borrow[indexPath.row]
vc.recivedD = donation[indexPath.row]
vc.recivedS = sale[indexPath.row]
self.navigationController?.pushViewController(vc, animated: true)
}
}
This is the struct,
I created a struct for each index
public struct exchange {
var passTitle : String
var passDes : String
init (passTitle1:String, passDes1:String) {
self.passTitle = passTitle1
self.passDes = passDes1
}
}
public struct borrow {
var passTitle : String
var passDes : String
init (passTitle2:String, passDes2:String) {
self.passTitle = passTitle2
self.passDes = passDes2
}
}
public struct donation {
var passTitle : String
var passDes : String
init (passTitle3:String, passDes3:String) {
self.passTitle = passTitle3
self.passDes = passDes3
}
}
public struct sale {
var passTitle : String
var passDes : String
init (passTitle4:String, passDes4:String) {
self.passTitle = passTitle4
self.passDes = passDes4
}
}
this is the last vc
import UIKit
import FirebaseStorage
import Firebase
import FirebaseFirestore
import SDWebImage
class ViewController10: UIViewController {
#IBOutlet weak var userBookTitle: UILabel!
#IBOutlet weak var userBookDescription: UILabel!
var recivedE:exchange?
var recivedB:borrow?
var recivedD:donation?
var recivedS:sale?
override func viewDidLoad() {
super.viewDidLoad()
userBookTitle.text = recivedE?.passTitle
userBookDescription.text = recivedE?.passDes
}
}
Note... I tried to do this but it didn't work
override func viewDidLoad() {
super.viewDidLoad()
if let et = recivedE?.passTitle ,
let ed = recivedE?.passDes{
userBookTitle.text = et
userBookDescription.text = ed
}
else if let bt = recivedB?.passTitle ,
let bd = recivedB?.passDes {
userBookTitle.text = bt
userBookDescription.text = bd
}
else if let dt = recivedD?.passTitle ,
let dd = recivedD?.passDes {
userBookTitle.text = dt
userBookDescription.text = dd
}
else if let st = recivedS?.passTitle ,
let sd = recivedS?.passDes {
userBookTitle.text = st
userBookDescription.text = sd
}
}
and this also not working
override func viewDidLoad() {
super.viewDidLoad()
userBookTitle.text = recivedE?.passTitle
userBookDescription.text = recivedE?.passDes
userBookTitle.text = recivedB?.passTitle
userBookDescription.text = recivedB?.passDes
userBookTitle.text = recivedD?.passTitle
userBookDescription.text = recivedD?.passDes
userBookTitle.text = recivedS?.passTitle
userBookDescription.text = recivedS?.passDes
}
help me, please
In both of your numberOfRowsInSection and cellForRowAt functions, you are checking the selected segment index to determine which data to use:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if segmentOutlet.selectedSegmentIndex == 0 {
return exchange.count
} else if segmentOutlet.selectedSegmentIndex == 1 {
return borrow.count
}else if segmentOutlet.selectedSegmentIndex == 2 {
return donation.count
} else if segmentOutlet.selectedSegmentIndex == 3 {
return sale.count
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
if segmentOutlet.selectedSegmentIndex == 0 {
cell.textLabel?.text = exchange [indexPath.row].passTitle
} else if segmentOutlet.selectedSegmentIndex == 1 {
cell.textLabel?.text = borrow [indexPath.row].passTitle
} else if segmentOutlet.selectedSegmentIndex == 2 {
cell.textLabel?.text = donation [indexPath.row].passTitle
} else if segmentOutlet.selectedSegmentIndex == 3 {
cell.textLabel?.text = sale [indexPath.row].passTitle
}
return cell
}
However, in didSelectRowAt, you only use the exchange data:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let vc = storyboard?.instantiateViewController(withIdentifier:"vc10") as? ViewController10 {
vc.recivedE = exchange[indexPath.row]
self.navigationController?.pushViewController(vc, animated: true)
}
}
If you implement the same if / else if structure in didSelectRowAt you should get the desired results.
Greetings I have the disadvantage that the cells of the tableview are duplicated in a random way, at certain times the event occurs, not always
i think i need to clean or reload data but im not sure, could you help please.
final class MovementsDatasource: NSObject, PaginatedDatasource {
typealias Values = MovementsSectionModel
private let repository: TransactionsRepositoryProtocol
private let disposeBag = DisposeBag()
private let fetching = ActivityIndicator()
private let id: String, cvl: String
private let rowsObserver = PublishSubject<[Values]>()
var values = [Values]() { didSet { self.rowsObserver.onNext(self.values) } }
var page = 0
var hasNextPage = false
init(repository: TransactionsRepositoryProtocol, id: String, cvl: String) {
self.id = id
self.cvl = cvl
self.repository = repository
}
func getActivityIndicator() -> ActivityIndicator { self.fetching }
func getValuesObserver() -> Observable<[Values]> { self.rowsObserver }
func getNextPage() {
guard self.hasNextPage else { return }
getMovements(for: self.id, cvl: self.cvl, page: self.page)
}
func getFirstPage() {
self.page = 0
self.hasNextPage = false
self.values = []
getMovements(for: self.id, cvl: self.cvl, page: self.page)
}
}
extension MovementsDatasource {
private func getMovements(for productID: String, cvl: String, page: Int) {
self.repository
.movements(userCVL: cvl, ibanAccountId: productID, page: page)
.trackActivity(self.fetching)
.subscribe(onNext: { [weak self] response in
guard let _self = self else { return }
_self.hasNextPage = response.hasNextPage
_self.page = _self.hasNextPage ? (_self.page + 1) : _self.page
_self.publish(response)
})
.disposed(by: self.disposeBag)
}
private func publish(_ response: MovementsResponse) {
guard let rawMovement = response.values else { return }
let newRows = self.transform(rawMovement)
var rows = self.values
if !rows.isEmpty { rows += newRows }
else { rows = newRows }
self.values = self.merge(rows)
}
internal func transform(_ movements: [Movement]) -> [MovementsSectionModel] {
movements
.map { $0.movementDate.displayTimestamp() }
.unique()
.map { title -> MovementsSectionModel in
let sorted = movements.filter { $0.movementDate.displayTimestamp() == title }
return MovementsSectionModel(header: title, items: sorted)
}
}
internal func merge(_ sections: [MovementsSectionModel]) -> [MovementsSectionModel] {
sections
.map { $0.header }
.unique()
.map { title -> MovementsSectionModel in
let merged = sections.reduce(into: [MovementCellViewModel]()) {
accumulator, section in
if section.header == title { accumulator += section.items }
}
return MovementsSectionModel(header: title, items: merged)
}
}
}
extension MovementsDatasource {
func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
self.values[section].items.count
}
func numberOfSections(in _: UITableView) -> Int { self.values.count }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard !self.values.isEmpty, let cell = tableView.dequeueReusableCell(withIdentifier: MovementCell.reuseID, for: indexPath) as? MovementCell
else {
return UITableViewCell(frame: .zero)
}
let section = indexPath.section
let index = indexPath.item
if !self.values.isEmpty {
cell.viewModel = self.values[section].items[index]
}
return cell
}
}
How can I update/add/delete an item on a table view with sections and its source?
Im having issues when trying to update a cell. Sometimes it happens on delete and add too.
It seems that a problem on sections is triggering it.
1- Data is loaded from a JSON response from a server.
2 - This data is sorted alphabetically and sections based on the first letter from the name is created adding each client to its indexed letter.
Im adding to print-screens:
Before:
After:
I renamed 'Bowl' to 'Bowl 2' and it 'created' a new entry, keeping the old and new value. If I refresh (pull), it gets fixed.
Also, sometimes, it removes 'Abc MacDon' and after refreshing, it gets fixed.
class ClientsViewController: UITableViewController {
var sortedFirstLetters: [String] = []
var sections: [[Client]] = [[]]
var tableArray = [Client]()
var client: Client?
var wasDeleted: Bool?
var refresher: UIRefreshControl!
#IBOutlet weak var noClientsLabel: UILabel!
#IBOutlet var noClientsView: UIView!
#IBAction func unwindToClients(sender: UIStoryboardSegue) {
if let sourceViewController = sender.source as? ClientViewController,
let client = sourceViewController.client,
let wasDeleted = sourceViewController.wasDeleted {
if(wasDeleted) {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
print("Delteted")
tableArray.remove(at: selectedIndexPath.row)
// DispatchQueue.main.async {
// // Deleting the row in the tableView
// if self.tableView.numberOfRows(inSection: selectedIndexPath.section) > 1 {
// self.tableView.deleteRows(at: [selectedIndexPath], with: UITableViewRowAnimation.bottom)
// } else {
// let indexSet = NSMutableIndexSet()
// indexSet.add(selectedIndexPath.section)
// self.tableView.deleteSections(indexSet as IndexSet, with: UITableViewRowAnimation.bottom)
// }
//
// }
}
}
else {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update an existing client.
tableArray[selectedIndexPath.row] = client
//tableView.reloadRows(at: [selectedIndexPath], with: .automatic)
print("update")
print(tableArray)
}
else {
// Add a client.
tableArray.append(client)
print("add")
}
}
self.prepareData()
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let secondScene = segue.destination as! ClientViewController
if segue.identifier == "ShowDetail", let indexPath = self.tableView.indexPathForSelectedRow {
let currentPhoto = sections[indexPath.section][indexPath.row]
secondScene.client = currentPhoto
}
else if segue.identifier == "AddItem" {
print("add")
}
else {
fatalError("The selected cell is not being displayed by the table")
}
}
#objc func handleRefresh(_ refreshControl: UIRefreshControl) {
getClients()
}
}
extension ClientsViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.refreshControl?.addTarget(self, action: #selector(ClientsViewController.handleRefresh(_:)), for: UIControlEvents.valueChanged)
tableView.backgroundView = nil
noClientsLabel.text = ""
getClients() //for only the 1st time ==> when view is created ==> ok ish
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if(self.tableArray.count > 0) {
return sortedFirstLetters[section]
}
else {
return ""
}
}
override func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return sortedFirstLetters
}
override func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = sections[indexPath.section][indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "ClientCell", for: indexPath)
cell.textLabel?.text = item.name
cell.detailTextLabel?.text = item.city + " - " + item.province
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].count
}
func getClients() {
print("called server")
self.refreshControl?.beginRefreshing()
self.tableView.setContentOffset(CGPoint(x:0, y:-100), animated: true)
makeRequest(endpoint: "api/clients/all",
parameters: [:],
completionHandler: { (container : ApiContainer<Client>?, error : Error?) in
if let error = error {
print("error calling POST on /getClients")
print(error)
return
}
self.tableArray = (container?.result)!
self.prepareData()
DispatchQueue.main.async {
if(self.tableArray.isEmpty)
{
self.noClientsLabel.text = "bNo Clients"
self.tableView.backgroundView?.isHidden = false
self.noClientsLabel.text = ""
print("all")
}
else{
print("nothing")
}
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
}
} )
}
//sorts and makes the index
func prepareData() {
let firstLetters = self.tableArray.map { $0.nameFirstLetter }
let uniqueFirstLetters = Array(Set(firstLetters))
self.sortedFirstLetters = uniqueFirstLetters.sorted()
self.sections = self.sortedFirstLetters.map { firstLetter in
return self.tableArray
.filter { $0.nameFirstLetter == firstLetter }
.sorted { $0.name < $1.name }
}
}
}
Struct
struct Client: Codable {
var client_id: Int!
let name: String!
let postal_code: String!
let province: String!
let city: String!
let address: String!
init(name: String, client_id: Int! = nil, postal_code: String, province: String, city: String, address: String) {
self.client_id = client_id
self.name = name
self.postal_code = postal_code
self.province = province
self.city = city
self.address = address
}
var nameFirstLetter: String {
return String(self.name[self.name.startIndex]).uppercased()
}
}
code after some interaction with Hardik
import UIKit
import Foundation
class ClientsViewController: UITableViewController {
var sortedFirstLetters: [String] = []
var sections: [[Client]] = [[]]
// var tableArray = [Client]()
var tableArray : [Client] = [Client]()
var client: Client?
var wasDeleted: Bool?
var refresher: UIRefreshControl!
#IBOutlet weak var noClientsLabel: UILabel!
#IBOutlet var noClientsView: UIView!
#IBAction func unwindToClients(sender: UIStoryboardSegue) {
if let sourceViewController = sender.source as? ClientViewController,
let client = sourceViewController.client,
let wasDeleted = sourceViewController.wasDeleted {
if(wasDeleted) {
if let index = self.tableArray.index(where: { (item) -> Bool in
item.client_id == client.client_id
}) {
self.tableArray.remove(at: index)
print("Delteted")
// Find the client in the tableArray by the client id and remove it from the tableArray
// I am writing an example code here, this is not tested so just get the logic from here.
//self.tableArray.remove(at: selectedIndexPath.row)
}
}
else {
if self.tableArray.contains(where: { (item) -> Bool in
item.client_id == client.client_id
}) {
//Find the item in the tableArray by the client id and update it there too
// I am writing an example code here, this is not tested so just get the logic from here.
//if let index = self.tableArray.index(where: { (item) -> Bool in
// item.id == client.id
//}) {
self.tableArray[index] = client
//self.tableArray.replace(client, at: index)
//}
print("update")
print(tableArray)
}
else {
// Add a client.
tableArray.append(client)
print("add")
}
}
// Now update the sections Array and it will have all the correct values
self.prepareData()
DispatchQueue.main.async {
self.tableView.reloadData()
}
// if(wasDeleted) {
// if let selectedIndexPath = tableView.indexPathForSelectedRow {
// print("Delteted")
// sections[selectedIndexPath.section].remove(at: selectedIndexPath.row)
// }
//
// }
// else {
// if let selectedIndexPath = tableView.indexPathForSelectedRow {
// // Update an existing client.
// sections[selectedIndexPath.section][selectedIndexPath.row] = client
// //tableView.reloadRows(at: [selectedIndexPath], with: .automatic)
// print("update")
// print(tableArray)
// }
// else {
// // Add a client.
// tableArray.append(client)
// print("add")
// self.prepareData()
// }
// }
//
// DispatchQueue.main.async {
//
// self.tableView.reloadData()
// }
// if(wasDeleted) {
// if let selectedIndexPath = tableView.indexPathForSelectedRow {
// print("Delteted")
// tableArray.remove(at: selectedIndexPath.row)
//// DispatchQueue.main.async {
//// // Deleting the row in the tableView
//// if self.tableView.numberOfRows(inSection: selectedIndexPath.section) > 1 {
//// self.tableView.deleteRows(at: [selectedIndexPath], with: UITableViewRowAnimation.bottom)
//// } else {
//// let indexSet = NSMutableIndexSet()
//// indexSet.add(selectedIndexPath.section)
//// self.tableView.deleteSections(indexSet as IndexSet, with: UITableViewRowAnimation.bottom)
//// }
////
//// }
// }
//
// }
// else {
// if let selectedIndexPath = tableView.indexPathForSelectedRow {
// // Update an existing client.
// tableArray[selectedIndexPath.row] = client
// //tableView.reloadRows(at: [selectedIndexPath], with: .automatic)
// print("update")
// print(tableArray)
// }
// else {
// // Add a client.
// tableArray.append(client)
// print("add")
// }
// }
// self.prepareData()
// DispatchQueue.main.async {
//
// self.tableView.reloadData()
// }
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let secondScene = segue.destination as! ClientViewController
if segue.identifier == "ShowDetail", let indexPath = self.tableView.indexPathForSelectedRow {
let currentPhoto = sections[indexPath.section][indexPath.row]
secondScene.client = currentPhoto
}
else if segue.identifier == "AddItem" {
print("add")
}
else {
fatalError("The selected cell is not being displayed by the table")
}
}
#objc func handleRefresh(_ refreshControl: UIRefreshControl) {
getClients()
}
}
extension ClientsViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.refreshControl?.addTarget(self, action: #selector(ClientsViewController.handleRefresh(_:)), for: UIControlEvents.valueChanged)
tableView.backgroundView = nil
noClientsLabel.text = ""
getClients() //for only the 1st time ==> when view is created ==> ok ish
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if(self.tableArray.count > 0) {
return sortedFirstLetters[section]
}
else {
return ""
}
}
override func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return sortedFirstLetters
}
override func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = sections[indexPath.section][indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "ClientCell", for: indexPath)
cell.textLabel?.text = item.name
cell.detailTextLabel?.text = item.city + " - " + item.province
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].count
}
func getClients() {
print("called server")
self.refreshControl?.beginRefreshing()
self.tableView.setContentOffset(CGPoint(x:0, y:-100), animated: true)
makeRequest(endpoint: "api/clients/all",
parameters: [:],
completionHandler: { (container : ApiContainer<Client>?, error : Error?) in
if let error = error {
print("error calling POST on /getClients")
print(error)
return
}
self.tableArray = (container?.result)!
self.prepareData()
DispatchQueue.main.async {
if(self.tableArray.isEmpty)
{
self.noClientsLabel.text = "bNo Clients"
self.tableView.backgroundView?.isHidden = false
self.noClientsLabel.text = ""
print("all")
}
else{
print("nothing")
}
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
}
} )
}
//sorts and makes the index
func prepareData() {
let firstLetters = self.tableArray.map { $0.nameFirstLetter }
let uniqueFirstLetters = Array(Set(firstLetters))
self.sortedFirstLetters = uniqueFirstLetters.sorted()
self.sections = self.sortedFirstLetters.map { firstLetter in
return self.tableArray
.filter { $0.nameFirstLetter == firstLetter }
.sorted { $0.name < $1.name }
}
}
}
You are editing the wrong datasource. You should edit or update the sections array not tableArray.
Change your code in unwindToClients here :
if(wasDeleted) {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
print("Delteted")
tableArray.remove(at: selectedIndexPath.row)
}
}
else {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update an existing client.
tableArray[selectedIndexPath.row] = client
//tableView.reloadRows(at: [selectedIndexPath], with: .automatic)
print("update")
print(tableArray)
}
else {
// Add a client.
tableArray.append(client)
print("add")
}
}
self.prepareData()
DispatchQueue.main.async {
self.tableView.reloadData()
}
with this :
if(wasDeleted) {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
print("Delteted")
sections[selectedIndexPath.section].remove(at: selectedIndexPath.row)
}
}
else {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update an existing client.
sections[selectedIndexPath.section][selectedIndexPath.row] = client
//tableView.reloadRows(at: [selectedIndexPath], with: .automatic)
print("update")
print(tableArray)
}
else {
// Add a client.
tableArray.append(client)
print("add")
self.prepareData()
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
To fix the incorrect section headers, try doing something like this:
if(wasDeleted) {
if let index = self.tableArray.index(where: { (item) -> Bool in
item.id == client.id
}) {
print("Delteted")
// Find the client in the tableArray by the client id and remove it from the tableArray
// I am writing an example code here, this is not tested so just get the logic from here.
//self.tableArray.remove(at: index)
}
}
else {
if self.tableArray.contains(client) {
//Find the item in the tableArray by the client id and update it there too
// I am writing an example code here, this is not tested so just get the logic from here.
//if let index = self.tableArray.index(where: { (item) -> Bool in
// item.id == client.id
//}) {
// self.tableArray.replace(client, at: index)
//}
print("update")
print(tableArray)
}
else {
// Add a client.
tableArray.append(client)
print("add")
}
}
// Now update the sections Array and it will have all the correct values
self.prepareData()
DispatchQueue.main.async {
self.tableView.reloadData()
}
I've autogenerated an application from the SAP Cloud SDK Assistant, and I'd like to use a FUIValuePickerFormCell in the detail view of the application, but can't seem to do so.
I've used the sample codes given by the SAP Fiori mentor app, and added a Table View Cell of class "FUIValuePickerFormCell" in main.storyboard, but nothing happens when the cell is tapped, the pickerview doesn't come up, nor is the cell editable. Does anyone knows why is this so? Below are the codes that I used
Cell that i want to change in ProductsTypeDetailTableDelegate
case 6:
let cell = tableView.dequeueReusableCell(withIdentifier: FUIValuePickerFormCell.reuseIdentifier, for: indexPath) as! FUIValuePickerFormCell
valuePickerCell = cell
cell.isEditable = true
cell.keyName = "Appointment Status"
cell.valueOptions = ["1", "2", "3"]
cell.value = 1 //index of first value
cell.onChangeHandler = { newValue in
if let option = self.valuePickerCell?.valueOptions[newValue]{
print("Selected value option \(option)")
}
}
return cell
DetailViewController:
import SAPFoundation
import SAPOData
import SAPFiori
import SAPCommon
class DetailViewController: FUIFormTableViewController, Notifier, LoadingIndicator {
private let appDelegate = UIApplication.shared.delegate as! AppDelegate
private var tableDelegate: DetailTableDelegate!
var tableUpdater: TableUpdaterDelegate?
var loadingIndicator: FUILoadingIndicatorView?
private let logger = Logger.shared(named: "DetailViewControllerLogger")
var services: ServicesDataAccess {
return appDelegate.services
}
// The Entity which will be edited on the Detail View
var selectedEntity: EntityValue!
var entityArray: [EntityValue]!
var entityArray2: [EntityValue]!
var entityArray3: [EntityValue]!
var prodimages: [EntityValue]!
var collectionType: CollectionType = .none {
didSet {
if let delegate = self.generatedTableDelegate() {
self.tableDelegate = delegate
if self.selectedEntity != nil {
self.tableDelegate.entity = self.selectedEntity
}
if self.entityArray != nil {
self.tableDelegate.arrayEntity = self.entityArray
}
if self.entityArray2 != nil {
self.tableDelegate.arrayEntity2 = self.entityArray2
}
if self.entityArray3 != nil {
self.tableDelegate.arrayEntity3 = self.entityArray3
}
if self.prodimages != nil{
self.tableDelegate.prodimages = self.prodimages
}
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.allowsSelection = false
self.tableView.dataSource = tableDelegate
self.tableView.delegate = tableDelegate
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 44
}
#IBAction func updateEntity(_ sender: AnyObject) {
self.showIndicator()
self.view.endEditing(true)
self.logger.info("Updating entity in backend.")
self.services.service.updateEntity(self.tableDelegate.entity) { error in
self.hideIndicator()
if let error = error {
self.logger.error("Update entry failed.", error: error)
self.displayAlert(title: NSLocalizedString("keyErrorEntityUpdateTitle", value: "Update entry failed", comment: "XTIT: Title of alert message about entity update failure."),
message: NSLocalizedString("keyErrorEntityUpdateBody", value: error.localizedDescription, comment: "XMSG: Body of alert message about entity update failure."))
return
}
self.logger.info("Update entry finished successfully.")
FUIToastMessage.show(message: NSLocalizedString("keyUpdateEntityFinishedTitle", value: "Updated", comment: "XTIT: Title of alert message about successful entity update."))
self.tableUpdater?.updateTable()
}
}
func createEntity() {
self.showIndicator()
self.view.endEditing(true)
self.logger.info("Creating entity in backend.")
self.services.service.createEntity(self.tableDelegate.entity) { error in
self.hideIndicator()
if let error = error {
self.logger.error("Create entry failed.", error: error)
self.displayAlert(title: NSLocalizedString("keyErrorEntityCreationTitle", value: "Create entry failed", comment: "XTIT: Title of alert message about entity creation error."),
message: NSLocalizedString("keyErrorEntityCreationBody", value: error.localizedDescription, comment: "XMSG: Body of alert message about entity creation error."))
return
}
self.logger.info("Create entry finished successfully.")
DispatchQueue.main.async {
self.dismiss(animated: true) {
FUIToastMessage.show(message: NSLocalizedString("keyEntityCreationBody", value: "Created", comment: "XMSG: Title of alert message about successful entity creation."))
self.tableUpdater?.updateTable()
}
}
}
}
func cancel() -> Void {
DispatchQueue.main.async {
self.dismiss(animated: true, completion: nil)
}
}
// Test code
private func updateTable(completionHandler: #escaping() -> Void) {
self.tableDelegate?.requestEntities { error in
defer {
completionHandler()
}
if let error = error {
self.displayAlert(title: NSLocalizedString("keyErrorLoadingData", value: "Loading data failed!", comment: "XTIT: Title of loading data error pop up."),
message: error.localizedDescription)
self.logger.error("Could not update table.", error: error)
return
}
DispatchQueue.main.async {
self.tableView.reloadData()
self.logger.info("Table updated successfully!")
}
}
}
private func configureView() {
if self.collectionType != .none {
self.title = collectionType.rawValue
if let tableDelegate = self.generatedTableDelegate() {
self.tableDelegate = tableDelegate
if let tableView = self.tableView {
tableView.delegate = tableDelegate
tableView.dataSource = tableDelegate
self.updateTable()
}
}
}
}
func updateTable() {
self.showIndicator()
DispatchQueue.global().async {
self.updateTable() {
self.hideIndicator()
}
}
}
// test code ends
}
DetailTableDelegate:
import SAPOData
import SAPFiori
import SAPFoundation
protocol DetailTableDelegate: UITableViewDelegate, UITableViewDataSource {
var entity: EntityValue { get set }
var arrayEntity: [EntityValue] { get set }
var arrayEntity2: [EntityValue] { get set }
var arrayEntity3: [EntityValue] { get set }
var prodimages: [EntityValue] { get set}
}
extension DetailTableDelegate {
}
extension DetailViewController {
func generatedTableDelegate() -> DetailTableDelegate? {
switch self.collectionType {
case .customers:
return CUSTOMERSTypeDetailTableDelegate(dataAccess: self.services, rightBarButton: self.navigationItem.rightBarButtonItem!)
case .suppliers:
return SUPPLIERSTypeDetailTableDelegate(dataAccess: self.services, rightBarButton: self.navigationItem.rightBarButtonItem!)
case .orders:
return ORDERSTypeDetailTableDelegate(dataAccess: self.services, rightBarButton: self.navigationItem.rightBarButtonItem!, count: Int())
case .products:
return PRODUCTSTypeDetailTableDelegate(dataAccess: self.services, rightBarButton: self.navigationItem.rightBarButtonItem!)
default:
return nil
}
}
}
ProductsTypeDetailTableDelegate:
import Foundation
import UIKit
import SAPOData
import SAPCommon
import SAPFiori
class PRODUCTSTypeDetailTableDelegate: NSObject, DetailTableDelegate {
private let dataAccess: ServicesDataAccess
private var _entity: PRODUCTSType?
// test code
private var _arrayEntity: [PRODUCTSType] = [PRODUCTSType]()
private var _arrayEntity2: [PRODUCTSType] = [PRODUCTSType]()
private var _arrayEntity3: [PRODUCTSType] = [PRODUCTSType]()
private var _prodimages: [PRODIMGType] = [PRODIMGType]()
var valuePickerCell: FUIValuePickerFormCell?
var prodimages: [EntityValue] {
get {
return _prodimages
}
set {
self._prodimages = newValue as! [PRODIMGType]
}
}
var arrayEntity: [EntityValue] {
get {
return _arrayEntity
}
set {
self._arrayEntity = newValue as! [PRODUCTSType]
}
}
var arrayEntity2: [EntityValue] {
get {
return _arrayEntity2
}
set {
self._arrayEntity2 = newValue as! [PRODUCTSType]
}
}
var arrayEntity3: [EntityValue] {
get {
return _arrayEntity3
}
set {
self._arrayEntity3 = newValue as! [PRODUCTSType]
}
}
// test code ends
var entity: EntityValue {
get {
if _entity == nil {
_entity = createEntityWithDefaultValues()
}
return _entity!
}
set {
_entity = newValue as? PRODUCTSType
}
}
var rightBarButton: UIBarButtonItem
private var validity = Array(repeating: true, count: 8)
init(dataAccess: ServicesDataAccess, rightBarButton: UIBarButtonItem) {
self.dataAccess = dataAccess
self.rightBarButton = rightBarButton
self.rightBarButton.isEnabled = false
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let currentEntity = self.entity as? PRODUCTSType else {
return cellForDefault(tableView: tableView, indexPath: indexPath)
}
switch indexPath.row {
case 0:
var value = ""
let name = "Product ID"
if currentEntity.hasDataValue(for: PRODUCTSType.prodid) {
value = "\(currentEntity.prodid)"
}
return cellForProperty(tableView: tableView, indexPath: indexPath, property: PRODUCTSType.prodid, value: value, name: name, changeHandler: { (newValue: String) -> Bool in
if let validValue = TypeValidator.validInteger(from: newValue) {
currentEntity.prodid = validValue
self.validity[0] = true
} else {
self.validity[0] = false
}
self.barButtonShouldBeEnabled()
return self.validity[0]
})
case 1:
var value = ""
let name = "Name"
if currentEntity.hasDataValue(for: PRODUCTSType.prodname) {
if let prodname = currentEntity.prodname {
value = "\(prodname)"
}
}
return cellForProperty(tableView: tableView, indexPath: indexPath, property: PRODUCTSType.prodname, value: value, name: name, changeHandler: { (newValue: String) -> Bool in
// The property is optional, so nil value can be accepted
if newValue.isEmpty {
currentEntity.prodname = nil
self.validity[1] = false
} else {
if let validValue = TypeValidator.validString(from: newValue, for: PRODUCTSType.prodname) {
currentEntity.prodname = validValue
self.validity[1] = true
} else {
self.validity[1] = false
}
}
self.barButtonShouldBeEnabled()
return self.validity[1]
})
case 2:
var value = ""
let name = "Description"
if currentEntity.hasDataValue(for: PRODUCTSType.proddesc) {
if let proddesc = currentEntity.proddesc {
value = "\(proddesc)"
}
}
return cellForProperty(tableView: tableView, indexPath: indexPath, property: PRODUCTSType.proddesc, value: value, name: name, changeHandler: { (newValue: String) -> Bool in
// The property is optional, so nil value can be accepted
if newValue.isEmpty {
currentEntity.proddesc = nil
self.validity[2] = false
} else {
if let validValue = TypeValidator.validString(from: newValue, for: PRODUCTSType.proddesc) {
currentEntity.proddesc = validValue
self.validity[2] = true
} else {
self.validity[2] = false
}
}
self.barButtonShouldBeEnabled()
return self.validity[2]
})
case 3:
var value = ""
let name = "Current Stock"
if currentEntity.hasDataValue(for: PRODUCTSType.currstock) {
if let currstock = currentEntity.currstock {
value = "\(currstock)"
}
}
return cellForProperty(tableView: tableView, indexPath: indexPath, property: PRODUCTSType.currstock, value: value, name: name, changeHandler: { (newValue: String) -> Bool in
// The property is optional, so nil value can be accepted
if newValue.isEmpty {
currentEntity.currstock = 0
self.validity[3] = true
} else {
if let validValue = TypeValidator.validInteger(from: newValue) {
currentEntity.currstock = validValue
self.validity[3] = true
} else {
self.validity[3] = false
}
}
self.barButtonShouldBeEnabled()
return self.validity[3]
})
case 4:
var value = ""
let name = "Minimum Stock"
if currentEntity.hasDataValue(for: PRODUCTSType.minstock) {
if let minstock = currentEntity.minstock {
value = "\(minstock)"
}
}
return cellForProperty(tableView: tableView, indexPath: indexPath, property: PRODUCTSType.minstock, value: value, name: name, changeHandler: { (newValue: String) -> Bool in
// The property is optional, so nil value can be accepted
if newValue.isEmpty {
currentEntity.minstock = 0
self.validity[4] = true
} else {
if let validValue = TypeValidator.validInteger(from: newValue) {
currentEntity.minstock = validValue
self.validity[4] = true
} else {
self.validity[4] = false
}
}
self.barButtonShouldBeEnabled()
return self.validity[4]
})
case 5:
var value = ""
let name = "Price"
if currentEntity.hasDataValue(for: PRODUCTSType.price) {
if let price = currentEntity.price {
value = "\(price)"
}
}
return cellForProperty(tableView: tableView, indexPath: indexPath, property: PRODUCTSType.price, value: value, name: name, changeHandler: { (newValue: String) -> Bool in
// The property is optional, so nil value can be accepted
if newValue.isEmpty {
currentEntity.price = nil
self.validity[5] = false
} else {
if let validValue = TypeValidator.validBigDecimal(from: newValue) {
currentEntity.price = validValue
self.validity[5] = true
} else {
self.validity[5] = false
}
}
self.barButtonShouldBeEnabled()
return self.validity[5]
})
case 6:
// var value = ""
// let name = "Category"
// if currentEntity.hasDataValue(for: PRODUCTSType.cat) {
// if let cat = currentEntity.cat {
// value = "\(cat)"
// }
// }
// return cellForProperty(tableView: tableView, indexPath: indexPath, property: PRODUCTSType.cat, value: value, name: name, changeHandler: { (newValue: String) -> Bool in
// // The property is optional, so nil value can be accepted
// if newValue.isEmpty {
// currentEntity.cat = nil
// self.validity[6] = false
// } else {
// if let validValue = TypeValidator.validString(from: newValue, for: PRODUCTSType.cat) {
// currentEntity.cat = validValue
// self.validity[6] = true
// } else {
// self.validity[6] = false
// }
// }
// self.barButtonShouldBeEnabled()
// return self.validity[6]
// })
let cell = tableView.dequeueReusableCell(withIdentifier: FUIValuePickerFormCell.reuseIdentifier, for: indexPath) as! FUIValuePickerFormCell
valuePickerCell = cell
cell.isEditable = true
cell.keyName = "Appointment Status"
cell.valueOptions = ["1", "2", "3"]
cell.value = 1 //index of first value
cell.onChangeHandler = { newValue in
if let option = self.valuePickerCell?.valueOptions[newValue]{
print("Selected value option \(option)")
}
}
return cell
case 7:
var value = ""
let name = "Supplier ID"
if currentEntity.hasDataValue(for: PRODUCTSType.suppid) {
if let suppid = currentEntity.suppid {
value = "\(suppid)"
}
}
return cellForProperty(tableView: tableView, indexPath: indexPath, property: PRODUCTSType.suppid, value: value, name: name, changeHandler: { (newValue: String) -> Bool in
// The property is optional, so nil value can be accepted
if newValue.isEmpty {
currentEntity.suppid = nil
self.validity[7] = true
} else {
if let validValue = TypeValidator.validInteger(from: newValue) {
currentEntity.suppid = validValue
self.validity[7] = true
} else {
self.validity[7] = false
}
}
self.barButtonShouldBeEnabled()
return self.validity[7]
})
default:
return cellForDefault(tableView: tableView, indexPath: indexPath)
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 8
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func createEntityWithDefaultValues() -> PRODUCTSType {
let newEntity = PRODUCTSType()
newEntity.prodid = self._arrayEntity.count + 1
return newEntity
}
// Check if all text fields are valid
private func barButtonShouldBeEnabled() {
let anyFieldInvalid = self.validity.first { (field) -> Bool in
return field == false
}
self.rightBarButton.isEnabled = anyFieldInvalid == nil
}
func cellForProperty(tableView: UITableView, indexPath: IndexPath, property: Property, value: String, name: String, changeHandler: #escaping((String) -> Bool)) -> UITableViewCell {
let cell: UITableViewCell!
if property.dataType.isBasic {
// The property is a key or we are creating new entity
if (!property.isKey || self.entity.isNew) {
// .. that CAN be edited
cell = self.cellWithEditableContent(tableView: tableView, indexPath: indexPath, property: property, with: value, with: name, changeHandler: changeHandler)
} else {
// .. that CANNOT be edited
cell = self.cellWithNonEditableContent(tableView: tableView, indexPath: indexPath, for: property.name, with: value, with: name)
}
} else {
// A complex property
cell = self.cellWithNonEditableContent(tableView: tableView, indexPath: indexPath, for: property.name, with: "...", with: name)
}
return cell
}
func cellForDefault(tableView: UITableView, indexPath: IndexPath) -> FUISimplePropertyFormCell {
let cell = tableView.dequeueReusableCell(withIdentifier: FUISimplePropertyFormCell.reuseIdentifier, for: indexPath) as! FUISimplePropertyFormCell
cell.textLabel!.text = ""
cell.textLabel!.numberOfLines = 0
cell.textLabel!.lineBreakMode = NSLineBreakMode.byWordWrapping
cell.keyName = "default"
return cell
}
private func cellWithEditableContent(tableView: UITableView, indexPath: IndexPath, property: Property, with value: String, with name: String, changeHandler: #escaping((String) -> Bool)) -> FUISimplePropertyFormCell {
let cell = tableView.dequeueReusableCell(withIdentifier: FUISimplePropertyFormCell.reuseIdentifier, for: indexPath) as! FUISimplePropertyFormCell
cell.isEditable = true
cell.keyName = name
cell.value = value
if !property.isOptional {
cell.valueTextField!.placeholder = NSLocalizedString("keyRequiredPlaceholder", value: "Required", comment: "XSEL: Placeholder text for required but currently empty textfield.")
}
cell.onChangeHandler = { (newValue) -> Void in
if !changeHandler(newValue) {
cell.valueTextField.textColor = UIColor.red
} else {
cell.valueTextField.textColor = UIColor.gray
}
}
return cell
}
private func cellWithNonEditableContent(tableView: UITableView, indexPath: IndexPath, for key: String, with value: String, with name: String) -> FUISimplePropertyFormCell {
let cell = tableView.dequeueReusableCell(withIdentifier: FUISimplePropertyFormCell.reuseIdentifier, for: indexPath) as! FUISimplePropertyFormCell
cell.keyName = name
cell.value = value
return cell
}
private func selectKeyboardFor(_ type: DataType) -> UIKeyboardType {
switch type.code {
case DataType.byte, DataType.short, DataType.integer, DataType.int:
return .decimalPad
case DataType.decimal, DataType.double, DataType.localDateTime, DataType.globalDateTime:
return .numbersAndPunctuation
default:
return .`default`
}
}
func defaultValueFor(_ property: Property) -> Double {
if let defaultValue = property.defaultValue {
return Double(defaultValue.toString())!
} else {
return Double()
}
}
func defaultValueFor(_ property: Property) -> BigDecimal {
if let defaultValue = property.defaultValue {
return (defaultValue as! DecimalValue).value
} else {
return BigDecimal.fromDouble(Double())
}
}
func defaultValueFor(_ property: Property) -> Int {
if let defaultValue = property.defaultValue {
return Int(defaultValue.toString())!
} else {
return Int()
}
}
func defaultValueFor(_ property: Property) -> BigInteger {
if let defaultValue = property.defaultValue {
return BigInteger(defaultValue.toString())
} else {
return BigInteger.fromInt(Int())
}
}
func defaultValueFor(_ property: Property) -> Int64 {
if let defaultValue = property.defaultValue {
return Int64(defaultValue.toString())!
} else {
return Int64()
}
}
func defaultValueFor(_ property: Property) -> Float {
if let defaultValue = property.defaultValue {
return Float(defaultValue.toString())!
} else {
return Float()
}
}
func defaultValueFor(_ property: Property) -> LocalDateTime {
if let defaultValue = property.defaultValue {
return LocalDateTime.parse(defaultValue.toString())!
} else {
return LocalDateTime.now()
}
}
func defaultValueFor(_ property: Property) -> GlobalDateTime {
if let defaultValue = property.defaultValue {
return GlobalDateTime.parse(defaultValue.toString())!
} else {
return GlobalDateTime.now()
}
}
func defaultValueFor(_ property: Property) -> GuidValue {
if let defaultValue = property.defaultValue {
return GuidValue.parse(defaultValue.toString())!
} else {
return GuidValue.random()
}
}
func defaultValueFor(_ property: Property) -> String {
if let defaultValue = property.defaultValue {
return defaultValue.toString()
} else {
return ""
}
}
func defaultValueFor(_ property: Property) -> Bool {
if let defaultValue = property.defaultValue {
return defaultValue.toString().toBool()!
} else {
return Bool()
}
}
}
From your description, I assume you are using a UITableViewController and not the FUIFormTableViewController. The events of “FUI*FormCells” are handled only by the FUIFormTableViewController, not by a UITableViewController.
I have a table view with two different prototype cells.
The second prototype cell has a custom Label, but when I execute the project it's showing nothing. The text is there (I know because it's printing) but it's not appearing.
TableView Storyboard
Simulator: Image cell ok, but text cell is empty
-> TableViewController:
import UIKit
import Twitter
class TweetMentionsTableViewController: UITableViewController
{
lazy var images: [MediaItem] = []
lazy var userMentions: [Mention]? = []
lazy var hashtags: [Mention]? = []
lazy var urls: [Mention]? = []
var tweet: Twitter.Tweet?
private enum MentionTypes {
case Images
case Hashtags
case Users
case URLs
}
private var mentionsCollection: Dictionary<MentionTypes, Bool> = [
MentionTypes.Images: false,
MentionTypes.Hashtags: false,
MentionTypes.Users: false,
MentionTypes.URLs: false
]
private var sectionTypes: Dictionary<MentionTypes, Int?> = [
MentionTypes.Images: nil,
MentionTypes.Hashtags: nil,
MentionTypes.Users: nil,
MentionTypes.URLs: nil
]
override func numberOfSections(in tableView: UITableView) -> Int {
var count = 0
if !(tweet?.media.isEmpty)! {
mentionsCollection[MentionTypes.Images] = true
count += 1
}
if !(tweet?.hashtags.isEmpty)! {
mentionsCollection[MentionTypes.Hashtags] = true
count += 1
}
if !(tweet?.userMentions.isEmpty)! {
mentionsCollection[MentionTypes.Users] = true
count += 1
}
if !(tweet?.urls.isEmpty)! {
mentionsCollection[MentionTypes.URLs] = true
count += 1
}
return count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if mentionsCollection[MentionTypes.Images] == true {
sectionTypes[MentionTypes.Images] = section
mentionsCollection[MentionTypes.Images] = false
return (tweet?.media.count)!
} else if mentionsCollection[MentionTypes.Hashtags] == true {
sectionTypes[MentionTypes.Hashtags] = section
mentionsCollection[MentionTypes.Hashtags] = false
return (tweet?.hashtags.count)!
} else if mentionsCollection[MentionTypes.Users] == true {
sectionTypes[MentionTypes.Users] = section
mentionsCollection[MentionTypes.Users] = false
return (tweet?.userMentions.count)!
} else if mentionsCollection[MentionTypes.URLs] == true {
sectionTypes[MentionTypes.URLs] = section
mentionsCollection[MentionTypes.URLs] = false
return (tweet?.urls.count)!
}
return 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//let data = temp[indexPath.row][indexPath.section]
let cell = tableView.dequeueReusableCell(withIdentifier: "TweetMention", for: indexPath)
// Configure the cell...
if let sec = sectionTypes[MentionTypes.Images], indexPath.section == sec {
let image = tweet?.media[indexPath.row].url
if let tweetMentionCell = cell as? TweetMentionsTableViewCell {
tweetMentionCell.imageURL = image!
return cell
}
}
let cell2 = tableView.dequeueReusableCell(withIdentifier: "TweetMention2Cell", for: indexPath)
if let sec = sectionTypes[MentionTypes.Hashtags], indexPath.section == sec {
let hashtag = tweet?.hashtags[indexPath.row]
if let tweetMentionCell = cell2 as? TweetMentions2TableViewCell {
tweetMentionCell.hashtag = (hashtag?.keyword)!
}
} else if let sec = sectionTypes[MentionTypes.Users], indexPath.section == sec {
let userMention = tweet?.userMentions[indexPath.row]
if let tweetMentionCell = cell2 as? TweetMentions2TableViewCell {
tweetMentionCell.userMention = (userMention?.keyword)!
}
} else if let sec = sectionTypes[MentionTypes.URLs], indexPath.section == sec {
let url = tweet?.urls[indexPath.row]
if let tweetMentionCell = cell2 as? TweetMentions2TableViewCell {
tweetMentionCell.url = (url?.keyword)!
}
}
return cell2
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if let sec = sectionTypes[MentionTypes.Images], section == sec {
return "Images"
} else if let sec = sectionTypes[MentionTypes.Hashtags], section == sec {
return "Hashtags"
} else if let sec = sectionTypes[MentionTypes.Users], section == sec {
return "Users"
} else if let sec = sectionTypes[MentionTypes.URLs], section == sec {
return "URLs"
}
return nil
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let cell = sender as? TweetMentionsTableViewCell {
if segue.identifier == "Show Image" {
if let imageVC = segue.destination as? ImageViewController {
imageVC.imageURL = cell.imageURL?.absoluteURL
//imageVC.imageRatio = tweet?.media[0].aspectRatio
}
}
}
if let cell = sender as? TweetMentions2TableViewCell {
if segue.identifier == "Show Text" {
if let mentionsSTTVC = segue.destination as? MentionsSearchTweetTableViewController {
if let text = cell.mentLabel {
mentionsSTTVC.mentionToSearch = text.text!
}
}
}
}
}
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if let cell = sender as? TweetMentions2TableViewCell {
if (cell.url.contains("http")) {
UIApplication.shared.open(NSURL(string: cell.url)! as URL)
return false
}
}
return true
}
}
-> TableViewCell:
import UIKit
class TweetMentions2TableViewCell: UITableViewCell
{
#IBOutlet weak var mentLabel: UILabel!
var userMention = "" {
didSet {
updateUI(mentionType: 2)
}
}
var hashtag = "" {
didSet {
updateUI(mentionType: 1)
}
}
var url = "" {
didSet {
updateUI(mentionType: 3)
}
}
func updateUI(mentionType: Int) {
if mentionType == 1 {
mentLabel.text = hashtag
} else if mentionType == 2 {
mentLabel.text = userMention
} else if mentionType == 3 {
mentLabel.text = url
}
print(mentLabel.text!)
}
}