Saltar al contenido

Personalice el controlador visual de descripción general para mostrar clientes y productos

En el paso anterior, registró las celdas requeridas, configuró la Vista de tabla y aplicó los stumps del método para la fuente de datos y la delegación. Implementará estos métodos paso a paso para que la tabla muestre datos y responda a la interacción del usuario.

  1. Se supone que la vista de tabla consta de dos partes, una para los clientes y otra para los productos.

    Regreso 2 sa numberOfSections(in:):


    override func numberOfSections(in tableView: UITableView) -> Int { return 2 }
  2. Cada sección debe ser única, para ello puede indicarle a la tabla Ver qué encabezados de vista de tabla deben mostrar.

    Pon el tableView(_:viewForHeaderInSection:) como sigue:


    override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { // First dequeue the Header Footer View you registered in the viewDidLoad(:). let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: FUITableViewHeaderFooterView.reuseIdentifier) as! FUITableViewHeaderFooterView // Set it's style to title. header.style = .title header.separators = .bottom // For the first section give back a Header that is for the customers and the second is for the products switch section { case 0: header.titleLabel.text = "Customers" break case 1: header.titleLabel.text = "Products" break default: break } return header }
  3. El pie de página de las secciones se utilizará como separadores. Estos divisores no tienen un significado funcional, pero hacen que la interfaz de usuario sea más limpia.

    Pon el tableView(_:viewForFooterInSection:) como sigue:


    override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { if section == 1 { return UIView() } let divider = UITableViewHeaderFooterView() divider.backgroundColor = .preferredFioriColor(forStyle: .backgroundBase) return divider }
  4. Ahora encontrando los métodos de fuente de datos reales requeridos. El es tableView(_:numberOfRowsInSection:) es bastante simple de implementar:


    // If the data arrays are empty return 0, else return 5. override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { switch section { case 0: if customers.isEmpty { return 0 } case 1: if products.isEmpty { return 0 } default: return 0 } return 5 }
  5. Llegando a la parte emocionante, tomando el tableView(_:cellForRowAt:) método. Este método se denomina vista de tabla cada vez que necesita desinflar una celda.

    Aplique el siguiente código y lea los comentarios en línea con atención:


    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // Dequeue the FUIObjectTableViewCell and cast it accordingly. let cell = tableView.dequeueReusableCell(withIdentifier: FUIObjectTableViewCell.reuseIdentifier) as! FUIObjectTableViewCell // Set the accessory type of the cell to disclosure, this will indicate to the user that those cells are tappable. cell.accessoryType = .disclosureIndicator // Distinct the cell setup depending on the section. switch indexPath.section { case 0: // Get the currently needed customer and fill the cell's properties let customer = customers[indexPath.row] cell.headlineText = "(customer.firstName ?? "") (customer.lastName ?? "")" cell.subheadlineText = "(customer.city ?? ""), (customer.country ?? "")" cell.footnoteText = "# Sales Orders : (customer.salesOrders.count)" return cell case 1: // Get the currently needed product and fill the cell's properties let product = products[indexPath.row] cell.headlineText = product.name ?? "" cell.subheadlineText = product.categoryName ?? "" // If there is a product price set, format it with the help of a NumberFormatter if let price = product.price { let formatter = NumberFormatter() formatter.numberStyle = .currency let formattedPrice = formatter.string(for: price.intValue()) cell.footnoteText = formattedPrice ?? "" } return cell default: return UITableViewCell() } }
  6. Su controlador de vista de tabla ahora debería verse así:

//
//  OverviewTableViewController.swift
//  SalesAssistant
//
//  Created by Muessig, Kevin on 03.11.20.
//  Copyright © 2020 SAP. All rights reserved.
//

import UIKit
import SAPFiori
import SAPOData
import SAPOfflineOData
import SAPCommon
import SAPFoundation
import SAPFioriFlows

class OverviewTableViewController: UITableViewController, SAPFioriLoadingIndicator {

    // The Logger is already setup in the AppDelegate through the SAP iOS Assistant, that's why you can easily can get an instance here.
    private let logger = Logger.shared(named: "OverviewViewController")
    var loadingIndicator: FUILoadingIndicatorView?

    private var customers = [Customer]()
    private var products = [Product]()

    /// First retrieve the destinations your app can talk to from the AppParameters.
    let destinations = FileConfigurationProvider("AppParameters").provideConfiguration().configuration["Destinations"] as! NSDictionary

    var dataService: ESPMContainer<OfflineODataProvider>? {
        guard let odataController = OnboardingSessionManager.shared.onboardingSession?.odataControllers[destinations["com.sap.edm.sampleservice.v2"] as! String] as? Comsapedmsampleservicev2OfflineODataController, let dataService = odataController.espmContainer else {
            AlertHelper.displayAlert(with: NSLocalizedString("OData service is not reachable, please onboard again.", comment: ""), error: nil, viewController: self)
            return nil
        }
        return dataService
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        self.view.backgroundColor = .preferredFioriColor(forStyle: .backgroundBase)

        // Define the estimated row height for each row as well as setting the actual row height to define it's dimension itself.
        // This will cause the Table View to display a cell for at least 80 points.
        tableView.estimatedRowHeight = 80
        tableView.rowHeight = UITableView.automaticDimension

        // Register an FUIObjectTableViewCell and a FUITableViewHeaderFooterView. You can use the convenience reuse identifier defined in the cell classes to later dequeue the cells.
        tableView.register(FUIObjectTableViewCell.self, forCellReuseIdentifier: FUIObjectTableViewCell.reuseIdentifier)
        tableView.register(FUITableViewHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: FUITableViewHeaderFooterView.reuseIdentifier)

        loadInitialData()
    }

    private func loadInitialData() {
        // start showing the loading indicator
        self.showFioriLoadingIndicator()

        // Using a DispatchGroup will help you to get notified when all the needed data sets are loaded
        let group = DispatchGroup()

        // Fetch customers and products, pass in the DispatchGroup to handle entering and leaving of the group
        fetchCustomers(group)

        fetchProducts(group)

        // When all data tasks are completed, hide the loading indicator and reload the table view. This will cause a refresh of the UI, displaying the newly loaded data
        group.notify(queue: DispatchQueue.main) {
            self.hideFioriLoadingIndicator()
            self.tableView.reloadData()
        }
    }

    private func fetchCustomers(_ group: DispatchGroup) {
        // Enter the DispatchGroup
        group.enter()

        // Define a Data Query which is a class of the SAPOData framework. This query will tell the OData Service to also load the available Sales Orders for each Customer
        let query = DataQuery().expand(Customer.salesOrders)

        // Now call the data service and fetch the customers matching the above defined query. When during runtime the block gets entered you expect a result or an error. Also you want to hold a weak reference of self to not run into object reference issues during runtime.
        dataService?.fetchCustomers(matching: query) { [weak self] result, error in

            // If there is an error show an AlertDialog using the generated convenience class AlertHelper. Also log the error to the console and leave the /group.
            if let error = error {
                AlertHelper.displayAlert(with: "Failed to load list of customers!", error: error, viewController: self!)
                self?.logger.error("Failed to load list of customers!", error: error)
                group.leave()
                return
            }
            // sort the customer result set by the number of available sales orders by customer.
            self?.customers = result!.sorted(by: { $0.salesOrders.count > $1.salesOrders.count })

            group.leave()
        }
    }

    private func fetchProducts(_ group: DispatchGroup) {
        // Enter the DispatchGroup
        group.enter()

        // Define a Data Query only fetching the top 5 products.
        let query = DataQuery().top(5)

        dataService?.fetchProducts(matching: query) { [weak self] result, error in
            if let error = error {
                AlertHelper.displayAlert(with: "Failed to load list of products!", error: error, viewController: self!)
                self?.logger.error("Failed to load list of products!", error: error)
                group.leave()
                return
            }
            self?.products = result!
            group.leave()
        }
    }

    // MARK: - Table view data source
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 2
    }

    override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        // First dequeue the Header Footer View you registered in the viewDidLoad(:).
        let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: FUITableViewHeaderFooterView.reuseIdentifier) as! FUITableViewHeaderFooterView

        // Set it's style to title.
        header.style = .title
        header.separators = .bottom

        // For the first section give back a Header that is for the customers and the second is for the products
        switch section {
        case 0:
            header.titleLabel.text = "Customers"
            break
        case 1:
            header.titleLabel.text = "Products"
            break
        default:
            break
        }

        return header
    }

    override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
        if section == 1 { return UIView() }

        let divider = UITableViewHeaderFooterView()
        divider.backgroundColor = .preferredFioriColor(forStyle: .backgroundBase)

        return divider
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        switch section {
        case 0:
            if customers.isEmpty { return 0 }
        case 1:
            if products.isEmpty { return 0 }
        default:
            return 0
        }

        return 5
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        // Dequeue the FUIObjectTableViewCell and cast it accordingly.
        let cell = tableView.dequeueReusableCell(withIdentifier: FUIObjectTableViewCell.reuseIdentifier) as! FUIObjectTableViewCell

        // Set the accessory type of the cell to disclosure, this will indicate to the user that those cells are tappable.
        cell.accessoryType = .disclosureIndicator

        // Distinct the cell setup depending on the section.
        switch indexPath.section {
        case 0:

            // Get the currently needed customer and fill the cell's properties
            let customer = customers[indexPath.row]
            cell.headlineText = "(customer.firstName ?? "") (customer.lastName ?? "")"
            cell.subheadlineText = "(customer.city ?? ""), (customer.country ?? "")"
            cell.footnoteText = "# Sales Orders : (customer.salesOrders.count)"
            return cell
        case 1:

            // Get the currently needed product and fill the cell's properties
            let product = products[indexPath.row]
            cell.headlineText = product.name ?? ""
            cell.subheadlineText = product.categoryName ?? ""

            // If there is a product price set, format it with the help of a NumberFormatter
            if let price = product.price {
                let formatter = NumberFormatter()
                formatter.numberStyle = .currency
                let formattedPrice = formatter.string(for: price.intValue())

                cell.footnoteText = formattedPrice ?? ""
            }

            return cell
        default:
            return UITableViewCell()
        }
    }

    // MARK: - Table view delegate

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        //TODO: Implement
    }
}

Hecho

Inicie sesión para responder la pregunta