如何停止 UI 线程直到网络线程完成?
How to Stop UI thread until Network Thread Completes?
我正在将 JSON
中的一些数据显示到我的 AccordionView
中。问题是我的视图线程在我的网络线程之前完成执行。所以我无法将从数组中获取的数据填充到 AccordionView 中。
所以显示Array index out of range
异常。我也试过使用reloadData方法,但是没用。
我研究了这个问题并发现了 GCD。我不知道如何使用 GCD 来解决这个问题。
这是 AccordionView 图书馆。
import UIKit
import Alamofire
import SwiftyJSON
var CourseIdinController : String!
var CourseDescriptionC: String!
var ChapNameC : [String] = []
var titleLabel : UILabel?
class CoursePageController: UIViewController {
@IBOutlet weak var courseDesc: UITextView!
var CourseName : String!
var ChapName : [String] = []
var LessonName : [String] = []
var myAccordionView : MKAccordionView?
override func viewDidLoad() {
super.viewDidLoad()
self.title = CourseName
self.courseDesc.text = CourseDescriptionC
self.courseDesc.setContentOffset(CGPointZero, animated: true)
view.bounds.size.height = 450.0
myAccordionView = MKAccordionView(frame: CGRectMake(0, 111, CGRectGetWidth(view.bounds), CGRectGetHeight(view.bounds)));
println(ChapNameC.count)
myAccordionView!.delegate = self;
myAccordionView!.dataSource = self;
view.addSubview(myAccordionView!);
getData()
getlData()
}
func getData(){
Alamofire.request(.GET, "http://www.wgve.com/index.php/capp/get_chapter_by_course_id/\(CourseIdinController)")
.responseJSON { (_, _, data, _) in
let json = JSON(data!)
let catCount = json.count
for index in 0...catCount-1 {
let cname = json[index]["CHAPTER_NAME"].string
self.ChapName.append(cname!)
println(self.ChapName[index])
}
self.myAccordionView!.tableView?.reloadData()
}}
func getlData(){
Alamofire.request(.GET, "http://www.wgve.com/index.php/capp/get_lesson_by_course_id/\(CourseIdinController)")
.responseJSON { (_, _, data, _) in
let json = JSON(data!)
let catCount = json.count
for index in 0...catCount-1 {
let cname = json[index]["LESSON_NAME"].string
self.LessonName.append(cname!)
}
self.myAccordionView!.tableView?.reloadData()
}}
func accordionView(accordionView: MKAccordionView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell : UITableViewCell? = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: nil)
//cell?.imageView = UIImageView(image: UIImage(named: "lightGrayBarWithBluestripe"))
// Background view
var bgView : UIView? = UIView(frame: CGRectMake(0, 0, CGRectGetWidth(accordionView.bounds), 50))
var bgImageView : UIImageView! = UIImageView(image: UIImage(named: "lightGrayBarWithBluestripe"))
bgImageView.frame = (bgView?.bounds)!
bgImageView.contentMode = UIViewContentMode.ScaleToFill
bgView?.addSubview(bgImageView)
cell?.backgroundView = bgView
// You can assign cell.selectedBackgroundView also for selected mode
cell?.textLabel?.text = LessonName[0]
return cell!
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
// MARK: - Implemention of MKAccordionViewDatasource method
extension CoursePageController : MKAccordionViewDatasource {
func numberOfSectionsInAccordionView(accordionView: MKAccordionView) -> Int {
println(ChapName.count)
return ChapName.count
}
func accordionView(accordionView: MKAccordionView, numberOfRowsInSection section: Int) -> Int {
println(LessonName.count)
return LessonName.count
}
}
// MARK: - Implemention of MKAccordionViewDatasource method
extension CoursePageController : MKAccordionViewDatasource {
func numberOfSectionsInAccordionView(accordionView: MKAccordionView) -> Int {
return ChapName.count
}
func accordionView(accordionView: MKAccordionView, numberOfRowsInSection section: Int) -> Int {
return LessonName.count
}
}
// MARK: - Implemention of MKAccordionViewDelegate method
extension CoursePageController : MKAccordionViewDelegate {
func accordionView(accordionView: MKAccordionView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 50
}
func accordionView(accordionView: MKAccordionView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
func accordionView(accordionView: MKAccordionView, viewForHeaderInSection section: Int, isSectionOpen sectionOpen: Bool) -> UIView? {
var view : UIView! = UIView(frame: CGRectMake(0, 0, CGRectGetWidth(accordionView.bounds), 50))
// Background Image
var bgImageView : UIImageView = UIImageView(frame: view.bounds)
bgImageView.image = UIImage(named: ( sectionOpen ? "grayBarSelected" : "grayBar"))!
view.addSubview(bgImageView)
// Arrow Image
var arrowImageView : UIImageView = UIImageView(frame: CGRectMake(15, 15, 20, 20))
arrowImageView.image = UIImage(named: ( sectionOpen ? "close" : "open"))!
view.addSubview(arrowImageView)
// Title Label
titleLabel = UILabel(frame: CGRectMake(50, 0, CGRectGetWidth(view.bounds) - 120, CGRectGetHeight(view.bounds)))
if (ChapName.count != 0) {
let count = ChapName.count
for index in 0...count-1 {
titleLabel!.text = self.ChapName[index]
}
}
titleLabel!.textColor = UIColor.whiteColor()
view.addSubview(titleLabel!)
return view
}
}
注:
它向我显示错误,因为 ChapName 和课程名称数组为空。如果我对值进行硬编码。它在我的控制台中打印了两个数组。
可能是accordionView不支持reload方法?
或者我哪里出错了?
更新
- numberOfSectionsInAccordionView 在第 233 行抛出这样的错误
fatal error: Can't form Range with end < start
参考这个 link
2.The titleLabel returns 仅数组中的最后一项。我不知道为什么?
关于上面的代码为什么不起作用:
numberOfRowsInSection
return 是一个硬编码值,与 JSON 响应无关。
关于停止 UI 线程:
我们有点高兴你不能那样做。 GCD
帮助您有效地共享处理器时间,这与占用 UI 线程人质正好相反。如果您的网络线程以某种方式挂起,想象一下后果。 UI 线程会发生什么情况?别这样。
解决方案:您使 UI 线程足够聪明以处理数据缺失问题。即不要 return 硬编码部分或单元格的计数。当您收到数据时,更新您的记录(模型),向UI发送刷新消息(例如UITableView
中的reloadData
)。
在尝试重新加载之前,您应该确保所有 json 数据已成功存储到数组 "accordionDataArray" 中。
注意:确保数据源方法 numberOfRowsInSection 和 numberOfSectionsInAccordionView 依赖 accordionDataArray 内容(或您拥有的任何数组)来确定手风琴的部分和行数。
P.S. 不管怎样 为什么要循环重新加载手风琴的内容?
改为这样做:
for index in 0...catCount-1 {
let cname = json[index]["CHAPTER_NAME"].string
self.ChapName.append(cname!)
println(self.ChapName[index])
}
self.myAccordionView!.tableView?.reloadData()
更新:
为了让 ChapName 和 lessonName 数组在 Empty 时被要求提供一些值这一事实,您需要在它们出现时像这样检查:
if (ChapName.count != 0) {
titleLabel!.text = self.ChapName[0]
}
if (LessonName.count != 0){
// do something
}
确保更新代码并进行测试。在方法中:
func accordionView(accordionView: MKAccordionView, viewForHeaderInSection section: Int, isSectionOpen sectionOpen: Bool) -> UIView? {
[...]
if (ChapName.count != 0) {
titleLabel!.text = self.ChapName[0]
}
[...]
}
UPDATE 2:您使用的图书馆似乎不允许节数为0。如果您不能制作,我建议您联系图书馆的所有者确保节数至少为 1.
关于 titleLabel 始终是数组最后一项的其他问题,您需要更改此设置:
if (ChapName.count != 0) {
let count = ChapName.count
for index in 0...count-1 {
titleLabel!.text = self.ChapName[index]
}
}
进入这个:
titleLabel!.text = self.ChapName[section]
我正在将 JSON
中的一些数据显示到我的 AccordionView
中。问题是我的视图线程在我的网络线程之前完成执行。所以我无法将从数组中获取的数据填充到 AccordionView 中。
所以显示Array index out of range
异常。我也试过使用reloadData方法,但是没用。
我研究了这个问题并发现了 GCD。我不知道如何使用 GCD 来解决这个问题。
这是 AccordionView 图书馆。
import UIKit
import Alamofire
import SwiftyJSON
var CourseIdinController : String!
var CourseDescriptionC: String!
var ChapNameC : [String] = []
var titleLabel : UILabel?
class CoursePageController: UIViewController {
@IBOutlet weak var courseDesc: UITextView!
var CourseName : String!
var ChapName : [String] = []
var LessonName : [String] = []
var myAccordionView : MKAccordionView?
override func viewDidLoad() {
super.viewDidLoad()
self.title = CourseName
self.courseDesc.text = CourseDescriptionC
self.courseDesc.setContentOffset(CGPointZero, animated: true)
view.bounds.size.height = 450.0
myAccordionView = MKAccordionView(frame: CGRectMake(0, 111, CGRectGetWidth(view.bounds), CGRectGetHeight(view.bounds)));
println(ChapNameC.count)
myAccordionView!.delegate = self;
myAccordionView!.dataSource = self;
view.addSubview(myAccordionView!);
getData()
getlData()
}
func getData(){
Alamofire.request(.GET, "http://www.wgve.com/index.php/capp/get_chapter_by_course_id/\(CourseIdinController)")
.responseJSON { (_, _, data, _) in
let json = JSON(data!)
let catCount = json.count
for index in 0...catCount-1 {
let cname = json[index]["CHAPTER_NAME"].string
self.ChapName.append(cname!)
println(self.ChapName[index])
}
self.myAccordionView!.tableView?.reloadData()
}}
func getlData(){
Alamofire.request(.GET, "http://www.wgve.com/index.php/capp/get_lesson_by_course_id/\(CourseIdinController)")
.responseJSON { (_, _, data, _) in
let json = JSON(data!)
let catCount = json.count
for index in 0...catCount-1 {
let cname = json[index]["LESSON_NAME"].string
self.LessonName.append(cname!)
}
self.myAccordionView!.tableView?.reloadData()
}}
func accordionView(accordionView: MKAccordionView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell : UITableViewCell? = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: nil)
//cell?.imageView = UIImageView(image: UIImage(named: "lightGrayBarWithBluestripe"))
// Background view
var bgView : UIView? = UIView(frame: CGRectMake(0, 0, CGRectGetWidth(accordionView.bounds), 50))
var bgImageView : UIImageView! = UIImageView(image: UIImage(named: "lightGrayBarWithBluestripe"))
bgImageView.frame = (bgView?.bounds)!
bgImageView.contentMode = UIViewContentMode.ScaleToFill
bgView?.addSubview(bgImageView)
cell?.backgroundView = bgView
// You can assign cell.selectedBackgroundView also for selected mode
cell?.textLabel?.text = LessonName[0]
return cell!
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
// MARK: - Implemention of MKAccordionViewDatasource method
extension CoursePageController : MKAccordionViewDatasource {
func numberOfSectionsInAccordionView(accordionView: MKAccordionView) -> Int {
println(ChapName.count)
return ChapName.count
}
func accordionView(accordionView: MKAccordionView, numberOfRowsInSection section: Int) -> Int {
println(LessonName.count)
return LessonName.count
}
}
// MARK: - Implemention of MKAccordionViewDatasource method
extension CoursePageController : MKAccordionViewDatasource {
func numberOfSectionsInAccordionView(accordionView: MKAccordionView) -> Int {
return ChapName.count
}
func accordionView(accordionView: MKAccordionView, numberOfRowsInSection section: Int) -> Int {
return LessonName.count
}
}
// MARK: - Implemention of MKAccordionViewDelegate method
extension CoursePageController : MKAccordionViewDelegate {
func accordionView(accordionView: MKAccordionView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 50
}
func accordionView(accordionView: MKAccordionView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
func accordionView(accordionView: MKAccordionView, viewForHeaderInSection section: Int, isSectionOpen sectionOpen: Bool) -> UIView? {
var view : UIView! = UIView(frame: CGRectMake(0, 0, CGRectGetWidth(accordionView.bounds), 50))
// Background Image
var bgImageView : UIImageView = UIImageView(frame: view.bounds)
bgImageView.image = UIImage(named: ( sectionOpen ? "grayBarSelected" : "grayBar"))!
view.addSubview(bgImageView)
// Arrow Image
var arrowImageView : UIImageView = UIImageView(frame: CGRectMake(15, 15, 20, 20))
arrowImageView.image = UIImage(named: ( sectionOpen ? "close" : "open"))!
view.addSubview(arrowImageView)
// Title Label
titleLabel = UILabel(frame: CGRectMake(50, 0, CGRectGetWidth(view.bounds) - 120, CGRectGetHeight(view.bounds)))
if (ChapName.count != 0) {
let count = ChapName.count
for index in 0...count-1 {
titleLabel!.text = self.ChapName[index]
}
}
titleLabel!.textColor = UIColor.whiteColor()
view.addSubview(titleLabel!)
return view
}
}
注:
它向我显示错误,因为 ChapName 和课程名称数组为空。如果我对值进行硬编码。它在我的控制台中打印了两个数组。
可能是accordionView不支持reload方法? 或者我哪里出错了?
更新
- numberOfSectionsInAccordionView 在第 233 行抛出这样的错误
fatal error: Can't form Range with end < start
参考这个 link
2.The titleLabel returns 仅数组中的最后一项。我不知道为什么?
关于上面的代码为什么不起作用:
numberOfRowsInSection
return 是一个硬编码值,与 JSON 响应无关。
关于停止 UI 线程:
我们有点高兴你不能那样做。 GCD
帮助您有效地共享处理器时间,这与占用 UI 线程人质正好相反。如果您的网络线程以某种方式挂起,想象一下后果。 UI 线程会发生什么情况?别这样。
解决方案:您使 UI 线程足够聪明以处理数据缺失问题。即不要 return 硬编码部分或单元格的计数。当您收到数据时,更新您的记录(模型),向UI发送刷新消息(例如UITableView
中的reloadData
)。
在尝试重新加载之前,您应该确保所有 json 数据已成功存储到数组 "accordionDataArray" 中。
注意:确保数据源方法 numberOfRowsInSection 和 numberOfSectionsInAccordionView 依赖 accordionDataArray 内容(或您拥有的任何数组)来确定手风琴的部分和行数。
P.S. 不管怎样 为什么要循环重新加载手风琴的内容? 改为这样做:
for index in 0...catCount-1 {
let cname = json[index]["CHAPTER_NAME"].string
self.ChapName.append(cname!)
println(self.ChapName[index])
}
self.myAccordionView!.tableView?.reloadData()
更新: 为了让 ChapName 和 lessonName 数组在 Empty 时被要求提供一些值这一事实,您需要在它们出现时像这样检查:
if (ChapName.count != 0) {
titleLabel!.text = self.ChapName[0]
}
if (LessonName.count != 0){
// do something
}
确保更新代码并进行测试。在方法中:
func accordionView(accordionView: MKAccordionView, viewForHeaderInSection section: Int, isSectionOpen sectionOpen: Bool) -> UIView? {
[...]
if (ChapName.count != 0) {
titleLabel!.text = self.ChapName[0]
}
[...]
}
UPDATE 2:您使用的图书馆似乎不允许节数为0。如果您不能制作,我建议您联系图书馆的所有者确保节数至少为 1.
关于 titleLabel 始终是数组最后一项的其他问题,您需要更改此设置:
if (ChapName.count != 0) {
let count = ChapName.count
for index in 0...count-1 {
titleLabel!.text = self.ChapName[index]
}
}
进入这个:
titleLabel!.text = self.ChapName[section]