在显示视图控制器之前修改视图控制器属性

时间:2016-11-04 00:13:30

标签: ios swift layout autolayout viewcontroller

我即将完成我的第一个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?我已经尝试过但它不起作用。而这个函数应该在哪里调用?

再次感谢您的帮助:)

2 个答案:

答案 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()
    }

}