簡體   English   中英

Swift 5 - 讓 tableview 等到來自 api 調用的數據返回(使用多個 tableviews)

[英]Swift 5 - make tableview wait until data from api call comes back (using multiple tableviews)

問題:加載視圖時出現致命錯誤。 我知道問題是因為在嘗試加載表視圖時沒有可用數據。 但是,因為我在一個 UI 中使用了多個 TableView,所以我必須在 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

    }



   }


我的用戶界面視圖:

在此處輸入圖像描述

如果我不強制它,我得到的錯誤:

在此處輸入圖像描述

如果數據源數組為空,我假設您想顯示一個空的“占位符”單元格。 您需要在cellForRow function 中明確檢查該條件。

至於協調從多個 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")
    }
}

當您的數據返回時,您可以將 tableView.dataSource 和 tableView.delegate 設置為 self

您的代碼存在多個問題。
1)您在表格視圖注冊它的單元格之前調用獲取數據。 因此,如果您的 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) 對多個 UITableView 使用一個 dataSOurce class 不是一個好主意,因為 dataSource 成為神 object。 更好的方法是使用一個包含三個子 UITableViewController 的 ContainerViewController,並在從 API 加載數據時將數據傳遞給子節點。

在一個屏幕上擁有多個視圖控制器是非常好的。 所以我建議你創建三個視圖控制器,每個表視圖一個。 每個表視圖都有自己的數據源。 然后使用自定義容器視圖 controller,如下所述: https://developer.apple.com/documentation/uikit/view_controllers/creating_a_custom_container_view_controller

嘗試以這種方式重新加載 TableView 數據,

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

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

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


    }

    func reloadTableViewsIfNeeded (_ conunt: Int) {
        if count == 3{

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

            }
        }
    }

通過使用它,您將獲得一個事件以在獲取所有數據后重新加載 tableviews。

讓我知道這是否有幫助。

享受和分享...

你總是可以返回一個默認的非樣式 UITableViewCell。 也停止強制展開。 您應該始終使用 if let 或 guard let 來保護。

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

        var cell:UITableViewCell?


        ...

        return UITableViewCell()
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM