简体   繁体   English

Swift 5 - 让 tableview 等到来自 api 调用的数据返回(使用多个 tableviews)

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

Issue: Fatal error when View is loading.问题:加载视图时出现致命错误。 I know the problem is because there is no data available when to table view is trying to load.我知道问题是因为在尝试加载表视图时没有可用数据。 But, because I am using multiple TableViews in one UI I have to force the cell return in CellForRowAt.但是,因为我在一个 UI 中使用了多个 TableView,所以我必须在 CellForRowAt 中强制返回单元格。

Is there a better way of setting up different data for each TableView?有没有更好的方法为每个 TableView 设置不同的数据?

THANKS FOR THE HELP!谢谢您的帮助!


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:我的用户界面视图:

在此处输入图像描述

The error I get if I don't force it:如果我不强制它,我得到的错误:

在此处输入图像描述

I assume that you want to display an empty "placeholder" cell if the data source array is empty.如果数据源数组为空,我假设您想显示一个空的“占位符”单元格。 You need to check explicitly for that condition in your cellForRow function.您需要在cellForRow function 中明确检查该条件。

As far as co-ordinating the fetch from multiple API endpoints, you can use a DispatchGroup - Some commented code indicates you may have tried this.至于协调从多个 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")
    }
}

you can set tableView.dataSource & tableView.delegate to self when your data is back当您的数据返回时,您可以将 tableView.dataSource 和 tableView.delegate 设置为 self

There is multiple problems with your code.您的代码存在多个问题。
1) You call get data before your table view had registered it cells. 1)您在表格视图注册它的单元格之前调用获取数据。 So if your API would load data immediately table view will be call dataSource methods因此,如果您的 API 会立即加载数据,则表视图将调用 dataSource 方法

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

but since the但由于

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

called in the end of viewDidLoad, your would get crash when you dequeue your cells in cellForAtIndexPath method.在 viewDidLoad 结束时调用,当您在 cellForAtIndexPath 方法中将单元格出列时,您会崩溃。

The solution is to move getData call to the end of viewDidLoad method.解决方案是将 getData 调用移至 viewDidLoad 方法的末尾。

2) If you want to display all tables data at one time (when API is complete loading getFramesData, getTaskTypeData and GETUserData) you would need to synchronise this callbacks. 2) 如果要一次显示所有表数据(当 API 完成 getFramesData、getTaskTypeData 和 GETUserData 加载时),则需要同步此回调。 You could do this with DispatchGroup.您可以使用 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) It's not good idea to use one dataSOurce class for mutltiple UITableView, because of dataSource become god object. 3) 对多个 UITableView 使用一个 dataSOurce class 不是一个好主意,因为 dataSource 成为神 object。 The better approach is to use one ContainerViewController that contains three child UITableViewController, and pass data to the childs when data has loaded from the API.更好的方法是使用一个包含三个子 UITableViewController 的 ContainerViewController,并在从 API 加载数据时将数据传递给子节点。

It is perfectly fine to have multiple view controllers in one screen.在一个屏幕上拥有多个视图控制器是非常好的。 So I suggest that you create three view controllers, one for each table view.所以我建议你创建三个视图控制器,每个表视图一个。 And each table view has its own datasource.每个表视图都有自己的数据源。 Then use a custom container view controller as described here: https://developer.apple.com/documentation/uikit/view_controllers/creating_a_custom_container_view_controller然后使用自定义容器视图 controller,如下所述: https://developer.apple.com/documentation/uikit/view_controllers/creating_a_custom_container_view_controller

Try to reload TableView data this way,尝试以这种方式重新加载 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")

            }
        }
    }

By using this, you will get an event to reload the tableviews after getting all the data.通过使用它,您将获得一个事件以在获取所有数据后重新加载 tableviews。

Let me know if this helps.让我知道这是否有帮助。

Enjoy and Share...享受和分享...

You can always return a default non styled UITableViewCell.你总是可以返回一个默认的非样式 UITableViewCell。 Also stop force unwrapping.也停止强制展开。 You should always guard with if let or guard let.您应该始终使用 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.

相关问题 Swift:如何对服务进行 API 调用但不等待响应 - Swift: How can I make an API call to a service but not wait for response 在 Swift 中使用 TableViews 和 CollectionViews 并管理多个数据源 - Working with TableViews and CollectionViews and managing multiple data sources in Swift 如何在Swift中的Json文件中显示多个TableView? - How can I display multiple TableViews from a Json File in Swift? Swift - 用多维数组中的多个部分填充 TableViews - Swift - Populating TableViews with multiple sections from multidimensional array 快速使用segue将数据从一个表视图传递到另一个表视图 - Passing data from one tableview to another tableview using segue in swift 当我使用2个表视图时,如何将数据(托管对象)从一个表视图移动到另一表视图? - How can I move data(managedobject) from one tableview to another tableview when I used 2 tableviews? 当数据来自远程API时,在哪个函数中加载tableView数据 - In which function to load tableView data when the data comes from remote API 在我点击另一个标签并返回之前,tableView不会更新-swift - tableView is not updating until i tap another tab and get back - swift 快速浏览多个子视图并返回原始TableView - Swift multiple subviews and getting back to original TableView Swift - 如何创建数组列表以从 API 数据填充 TableView - Swift - How to create Array list to fill the TableView from API data
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM