我已经从SAP Cloud SDK Assistant自动生成了一个应用程序,我想在应用程序的详细视图中使用FUIValuePickerFormCell,但似乎无法这样做。
我使用了SAP Fiori指导者应用程序提供的示例代码,并在main.storyboard中添加了类“FUIValuePickerFormCell”的表格视图单元格,但是当单元格被点击时没有任何反应,挑选者视图没有出现,细胞也不可编辑。有谁知道为什么会这样?以下是我使用的代码
我想在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()
}
}
}
答案 0 :(得分:0)
根据您的描述,我假设您使用的是UITableViewController,而不是FUIFormTableViewController。 “FUI * FormCells”的事件仅由FUIFormTableViewController处理,而不是由UITableViewController处理。