我即将完成我的第一个iOS应用程序,但我遇到了一个我不知道如何解决的问题。
该应用程序的初始ViewController包含一个MKMapView,它几乎没有注释。单击任何注释时,弹出窗口会显示一些信息,然后会出现一个信息按钮,指向新的ViewController,其中包含与所选注释相关的详细信息。
问题是第二个ViewController(DetailedViewController),几个标签(标题,描述),图像和几个链接,应该在显示ViewController本身之前加载。我正在从JSON中读取这些值,但这没有问题。
我正在阅读所有值并在ViewDidLoad()
中设置它们所以问题是,当第二个ViewController(DetailedViewController)在模拟器或物理iPhone中加载时,所有字段都有其默认值(故事板中设置的那个)并花费几秒钟来更新所需的值即使他们从JSON中读得如此之快(根据日志)。
此外,控制台中出现错误:
This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
Stack:(
0 CoreFoundation 0x000000018a0ca1d8 <redacted> + 148
1 libobjc.A.dylib 0x0000000188b0455c objc_exception_throw + 56
2 CoreFoundation 0x000000018a0ca108 <redacted> + 0
3 Foundation 0x000000018acb1ea4 <redacted> + 192
4 Foundation 0x000000018acb1be4 <redacted> + 76
5 Foundation 0x000000018aafd54c <redacted> + 112
6 Foundation 0x000000018acb0880 <redacted> + 112
7 UIKit 0x000000018ff2140c <redacted> + 1688
8 QuartzCore 0x000000018d3e1188 <redacted> + 148
9 UIKit 0x000000019056de90 <redacted> + 64
10 QuartzCore 0x000000018d3d5e64 <redacted> + 292
11 QuartzCore 0x000000018d3d5d24 <redacted> + 32
12 QuartzCore 0x000000018d3527ec <redacted> + 252
13 QuartzCore 0x000000018d379c58 <redacted> + 512
14 QuartzCore 0x000000018d37a124 <redacted> + 660
15 libsystem_pthread.dylib 0x000000018915efbc <redacted> + 572
16 libsystem_pthread.dylib 0x000000018915ece4 <redacted> + 200
17 libsystem_pthread.dylib 0x000000018915e378 pthread_mutex_lock + 0
18 libsystem_pthread.dylib 0x000000018915dda4 start_wqthread + 4
)
我已经阅读了类似的问题,他们正在谈论类似的事情:
dispatch_async(dispatch_get_main_queue(){
// code here
})
但我不确切知道如何使用它:(
有人能帮助我吗?
非常感谢您提前:))
编辑:
我将添加详细视图控制器的代码,以更准确地解释发生的情况:
class BarDetailsViewController: UIViewController {
@IBOutlet var BarDetailsView: UIView!
@IBOutlet weak var BarNameLabel: UILabel!
@IBOutlet weak var BarDescriptionLabel: UILabel!
@IBOutlet weak var BarImage: UIImageView!
var barPhoneNumber: AnyObject! = "anyBarPhoneNumber"
var barWebsite: AnyObject! = "anyBarWebsite"
var barName: AnyObject! = "anyBarName"
var pinCoordinates : CLLocationCoordinate2D!
var pinId : String!
override func viewDidLoad() {
super.viewDidLoad()
let jsonURL: String = "http://www.craftbr.me/services/getbiz/index.php?barid=" + self.pinId
let requestURL: NSURL = NSURL(string: jsonURL)!
let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(urlRequest) {
(data, response, error) -> Void in
let httpResponse = response as! NSHTTPURLResponse
let statusCode = httpResponse.statusCode
if (statusCode == 200) {
do{
let json: AnyObject! = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments)
let barName: AnyObject! = json["name"]
self.BarNameLabel.text = String(barName)
self.barName = String(barName)
self.BarDescriptionLabel.text = (json["description"] as! String)
self.barPhoneNumber = json["telf"]
self.barWebsite = json["website"] as! String
let barImgUrl: AnyObject! = json["imgurl"]
let barImgUrlString: String! = String(barImgUrl)
if let imageUrl = NSURL(string: barImgUrlString),
let data = NSData(contentsOfURL: imageUrl)
{
self.BarImage.image = UIImage(data: data)
}
}catch {
print("Error with Json: \(error)")
}
}
}
task.resume()
}
}
所以在这里,在ViewDidLoad()内部,我使用从json获取的信息修改在Storyboard中创建的标签内容。
事情是从json获取信息是如此之快,但是,修改标签是缓慢的事情并抛出错误。
所以@fragilecat,我应该在哪里添加这个功能?我如何申报DispatchQueue?我已经尝试过但它不起作用。而这个函数应该在哪里调用?
再次感谢您的帮助:)
答案 0 :(得分:0)
关于UI的所有更新操作必须在主线程中完成,例如在视图控制器中修改视图的背景,您可以执行以下操作:
DispatchQueue.main.async {
self.view.backgroundColor = UIColor.red
}
答案 1 :(得分:0)
Apple使用队列来表示线程,正如@Adam在评论中所说,您应该查看Concurrency Programming Guide以更好地了解您应用的行为。
然而,解释您的应用程序正在发生的崩溃可能是有用的,因为它是iOS中非常常见的模式。
您正在向某些网络服务发出请求。除非明确地说这个reuqest将在后台队列上发生。如果不是这种情况,那么在向Web服务发出请求时,您的应用程序UI将会冻结。这里需要记住的是,所有ui工作必须在主队列上完成! @Leon Guo向您展示了如何从不同的队列访问主队列。
那么这对您的Web服务请求有何影响?好吧,当响应返回并解析它并且您准备将数据分配给ui元素(如标签和文本字段)时,您希望在主队列上执行此操作,如下所示:
// comment: example method on DetailedViewController class
func prepareUI(data: MyDataType){
// Swift 3
DispatchQueue.main.async {
titleLabel.text = data.titleString
descriptionLabel.text = data.descriptionText
}
}
或者其他方面,您将从主队列以外的队列中访问DetailedViewController中的ui元素,从而导致崩溃。因此,要立即解决您的问题,请在DispatchQueue.main.async
代码块中对UI元素进行分配。这应该让你重新开始运行。
从长远来看,您需要了解如何在iOS中实现并发性。 The App Programming Guide for iOS是一个很好的起点!
修改强>
以下是使用dispatch_async(dispatch_get_main_queue())
功能的代码。我会说这只是为了让你起来......
import UIKit
class BarDetailsViewController: UIViewController {
@IBOutlet var barDetailsView: UIView!
@IBOutlet weak var barNameLabel: UILabel!
@IBOutlet weak var barDescriptionLabel: UILabel!
@IBOutlet weak var BarImage: UIImageView!
var barPhoneNumber = "anyBarPhoneNumber"
var barWebsite = "anyBarWebsite"
var barName = "anyBarName"
var pinCoordinates : CLLocationCoordinate2D!
var pinId : String!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// For example purposes only
networkCall()
}
func networkCall() {
let jsonURL: String = "http://www.craftbr.me/services/getbiz/index.php?barid=" + self.pinId
let requestURL: NSURL = NSURL(string: jsonURL)!
let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(urlRequest) {
(data, response, error) -> Void in
let httpResponse = response as! NSHTTPURLResponse
let statusCode = httpResponse.statusCode
if (statusCode == 200) {
do{
let json: AnyObject = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments)
// Here we get back on the main queue since we are altering UI elements
dispatch_async(dispatch_get_main_queue()) {
let barName: AnyObject! = json["name"]
self.barNameLabel.text = String(barName)
self.barName = String(barName)
if let text = json["description"] as? String {
self.barDescriptionLabel.text = text
}
if phoneNumber = json["telf"] as? String {
self.barPhoneNumber = phoneNumber
}
if webSite = json["website"] as? String {
self.barWebsite = barWebsite
}
let barImgUrl: AnyObject = json["imgurl"]
let barImgUrlString: String = String(barImgUrl)
if let imageUrl = NSURL(string: barImgUrlString), let data = NSData(contentsOfURL: imageUrl) {
self.BarImage.image = UIImage(data: data)
}
}
}catch {
print("Error with Json: \(error)")
}
}
}
task.resume()
}
}