![](/img/trans.png)
[英]Swift: How can I make an API call to a service but not wait for response
[英]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.