Swift 5 - заставляет табличное представление ждать, пока данные от вызова API возвращаются (используя несколько табличных представлений) - PullRequest
1 голос
/ 25 октября 2019

Проблема: фатальная ошибка при загрузке просмотра. Я знаю, что проблема в том, что нет доступных данных, когда в таблицу пытается загрузить. Но, поскольку я использую несколько TableViews в одном пользовательском интерфейсе, я должен принудительно вернуть ячейку в CellForRowAt.

Есть ли лучший способ настроить разные данные для каждого TableView?

СПАСИБО ЗА ПОМОЩЬ!


import UIKit

class NewCustomTaskVC: UIViewController {


    @IBAction func CancelPressed (_ sender: Any) {
        dismiss(animated: true, completion: nil)
    }
    @IBOutlet weak var taskTypeSelectionBtn: UIButton!
    @IBOutlet weak var FrameSelectionBtn: UIButton!
    @IBOutlet weak var AssignSelectionBtn: UIButton!


    @IBAction func SelecttaskTypePressed(_ sender: Any) {
        if tableViewTaskType.isHidden {
            self.tableViewTaskType.isHidden = false
            self.tableViewTaskType.rowHeight = 43.5
        } else {
            self.tableViewTaskType.isHidden = true
        }
    }


    @IBAction func SelectFramePressed(_ sender: Any) {
        if tableViewFrame.isHidden {
            self.tableViewFrame.isHidden = false
        } else {
            self.tableViewFrame.isHidden = true
        }
    }

    @IBAction func SelectAssignToPressed(_ sender: Any) {
        if tableViewAssignTo.isHidden {
            self.tableViewAssignTo.isHidden = false
        } else {
            self.tableViewAssignTo.isHidden = true
        }
    }





    @IBOutlet weak var tableViewTaskType: UITableView!
    @IBOutlet weak var tableViewFrame: UITableView!
    @IBOutlet weak var tableViewAssignTo: UITableView!



    var cellID = ""
    var array = ["String", "Test", "Next","Test 2", "Test 3"]

    override func viewDidLoad() {
        super.viewDidLoad()

        getData()

        tableViewTaskType.isHidden = true
        tableViewFrame.isHidden = true
        tableViewAssignTo.isHidden = true

        tableViewTaskType.delegate = self
        tableViewFrame.delegate = self
        tableViewAssignTo.delegate = self

        tableViewTaskType.dataSource = self
        tableViewFrame.dataSource = self
        tableViewAssignTo.dataSource = self

        self.tableViewTaskType.register(UITableViewCell.self, forCellReuseIdentifier: "cell1")
        self.tableViewFrame.register(UITableViewCell.self, forCellReuseIdentifier: "cell2")
        self.tableViewAssignTo.register(UITableViewCell.self, forCellReuseIdentifier: "cell3")

    }



    func getData () {
        //dispatchGroup.enter()
        var count = 0
        APICallBack.getFramesData(completion: { success in
            if success == true {
                print("frames success")
             count += 1
            } })

        APICallBack.getTaskTypeData { success in
            if success == true {
                print("task success")
             count += 1
            }
        }

        APICallBack.GETUserData(completion:  { success in
        if success == true {
            print("user success")
            count += 1
        } })

        if count == 3{

            DispatchQueue.main.async {
                self.tableViewTaskType.reloadData()
                self.tableViewAssignTo.reloadData()
                self.tableViewFrame.reloadData()
                print("ALL COMPLETE")

            }
        }
    }


}





   extension NewCustomTaskVC : UITableViewDelegate, UITableViewDataSource {
    func numberOfSections(in tableView: UITableView) -> Int {
       return 1

    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        var count = 1
               switch tableView {
               case tableViewTaskType:
                   count = TaskTypeData.typeModel.count

               case tableViewFrame:
                   count = FramesData.framesModel.count

               case tableViewAssignTo:
                   count = CustomerData.customerModel.count

               default:
                   print("none")
                   return count
               }
        //return 5
        return count
    }

**PROBLEM IS HERE 
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        var cell:UITableViewCell?

        if tableView == self.tableViewTaskType{
            cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath)
            cell!.textLabel!.text = TaskTypeData.typeModel[indexPath.row].TaskTypeName
           // cell!.textLabel?.text = array[indexPath.row]
        }

        if tableView == tableViewFrame{
             cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath)
            cell!.textLabel!.text = FramesData.framesModel[indexPath.row].FrameName
           // cell!.textLabel?.text = array[indexPath.row]
        }
        if tableView ==  self.tableViewAssignTo {
             cell = tableView.dequeueReusableCell(withIdentifier: "cell3", for: indexPath)
            cell!.textLabel!.text = UserData.userModel[indexPath.row].UserFirst
          //  cell.textLabel?.text = array[indexPath.row]
        }
//       let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath)
//        cell.textLabel?.text = array[indexPath.row]


        return cell!
    }
** TO HERE!



    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let item = array[indexPath.row]
        print(item)
        tableViewTaskType.isHidden = true

    }



   }


My UI View:

enter image description here

Ошибка, которую я получаю, если не форсирую ее:

enter image description here

Ответы [ 4 ]

0 голосов
/ 28 октября 2019

Прекрасно иметь несколько контроллеров представления на одном экране. Поэтому я предлагаю вам создать три контроллера представления, по одному для каждого представления таблицы. И каждое табличное представление имеет свой собственный источник данных. Затем используйте пользовательский контроллер представления контейнера, как описано здесь: https://developer.apple.com/documentation/uikit/view_controllers/creating_a_custom_container_view_controller

0 голосов
/ 26 октября 2019

вы можете установить tableView.dataSource & tableView.delegate на себя, когда ваши данные вернутся

0 голосов
/ 27 октября 2019

Существует несколько проблем с вашим кодом.
1) Вы вызываете get data до того, как ваше табличное представление зарегистрировало ячейки. Поэтому, если ваш API будет загружать данные немедленно, табличное представление будет вызывать методы dataSource

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int  
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell  

, но поскольку

self.tableViewTaskType.register(UITableViewCell.self, forCellReuseIdentifier: "cell1")
self.tableViewFrame.register(UITableViewCell.self, forCellReuseIdentifier: "cell2")
self.tableViewAssignTo.register(UITableViewCell.self, forCellReuseIdentifier: "cell3")

вызывается в конце viewDidLoad, вы получите сбой при удалении из очередиваши ячейки в методе cellForAtIndexPath.

Решение состоит в том, чтобы переместить вызов getData в конец метода viewDidLoad.

2) Если вы хотите отобразить данные всех таблиц за один раз (когда API будет завершен)загружая getFramesData, getTaskTypeData и GETUserData) вам нужно будет синхронизировать эти обратные вызовы. Вы можете сделать это с DispatchGroup.

    func getData () {
        let apiDispatchGroup = DispatchGroup()

        APICallBack.getFramesData { success in
            apiDispatchGroup.leave()
        }
        apiDispatchGroup.enter()

        APICallBack.getTaskTypeData { success in
            apiDispatchGroup.leave()
        }
        apiDispatchGroup.enter()

        APICallBack.GETUserData { success in
            apiDispatchGroup.leave()
        }
        apiDispatchGroup.enter()

        apiDispatchGroup.notify(queue: DispatchQueue.main) {
            self.tableViewTaskType.reloadData()
            self.tableViewAssignTo.reloadData()
            self.tableViewFrame.reloadData()
        }
    }

3) Не очень хорошая идея использовать один класс dataSOurce для множественного UITableView, потому что dataSource становится объектом бога. Лучше всего использовать один ContainerViewController, который содержит три дочерних UITableViewController, и передавать данные дочерним элементам, когда данные загружаются из API.

0 голосов
/ 26 октября 2019

Я предполагаю, что вы хотите отобразить пустую ячейку "заполнитель", если массив источника данных пуст. Вам необходимо явно проверить это условие в функции cellForRow.

Что касается координации выборки из нескольких конечных точек API, вы можете использовать DispatchGroup - в некотором закомментированном коде указано, что вы, возможно, пыталисьэто.

override func viewDidLoad() {
    super.viewDidLoad()

    tableViewTaskType.isHidden = true
    tableViewFrame.isHidden = true
    tableViewAssignTo.isHidden = true

    tableViewTaskType.delegate = self
    tableViewFrame.delegate = self
    tableViewAssignTo.delegate = self

    tableViewTaskType.dataSource = self
    tableViewFrame.dataSource = self
    tableViewAssignTo.dataSource = self

    self.tableViewTaskType.register(UITableViewCell.self, forCellReuseIdentifier: "cell1")
    self.tableViewFrame.register(UITableViewCell.self, forCellReuseIdentifier: "cell2")
    self.tableViewAssignTo.register(UITableViewCell.self, forCellReuseIdentifier: "cell3")

    getData()
}

func getData () {
    let dispatchGroup = DispatchGroup()

    dispatchGroup.enter()
    APICallBack.getFramesData(completion: { success in
        if success == true {
            print("frames success")
        } 
        dispatchGroup.leave()
    })

    APICallBack.getTaskTypeData { success in
        if success == true {
            print("task success")
        }
        dispatchGroup.leave()
    }

    APICallBack.GETUserData(completion:  { success in
        if success == true {
            print("user success")
        } 
        dispatchGroup.leave()
    })

    dispatchGroup.notify {
            self.tableViewTaskType.reloadData()
            self.tableViewAssignTo.reloadData()
            self.tableViewFrame.reloadData()
            print("ALL COMPLETE")
    }
}


extension NewCustomTaskVC : UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
   return 1
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    switch tableView {
        case tableViewTaskType:
            return max(1,TaskTypeData.typeModel.count)

        case tableViewFrame:
            return max(1,FramesData.framesModel.count)

        case tableViewAssignTo:
            return max(1,CustomerData.customerModel.count)

        default:
            fatalError("Unexpected table view")
    } 
}


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

    switch tableView {
        case self.tableViewTaskType:
            let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath)
            if !TaskTypeData.typeModel.isEmpty {
                cell.textLabel!.text = TaskTypeData.typeModel[indexPath.row].TaskTypeName
            }
            return cell

        case tableViewFrame:
            let cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath)
            if !FramesData.framesModel.isEmpty {
                cell!.textLabel!.text = FramesData.framesModel[indexPath.row].FrameName
            }
            return cell

        case self.tableViewAssignTo:     
           let cell = tableView.dequeueReusableCell(withIdentifier: "cell3", for: indexPath)
           if !UserData.userModel.isEmpty {
               cell!.textLabel!.text = UserData.userModel[indexPath.row].UserFirst
           }
           return cell

        default:
           fatalError("Unexpected Tableview")
    }
}
...