我的智慧到此为止。我已经尝试了所有我能想到的,并且无法使它正常工作。

我觉得我在这里想要做的事情应该很简单-但是Swift使它变得异常荒唐,我感到沮丧。

最终,我要做的就是从Web脚本中获取一条数据(它将返回一个float),然后使用该float为UIView设置动画。

因此-为了简单起见-我们假设网络脚本返回“ 50.0”。然后,我想获取该数字,并为UIView设置动画,使其高度从0px变为50px。

我读到您不能从异步任务获取数据,并且您基本上必须运行任何要在闭包内部使用该数据运行的任务。但是,当我尝试在闭包中运行动画时,Xcode拍了拍手,并告诉我我正在尝试在其他线程中修改AutoLayout属性。那我该怎么办?

以下是我的代码,尽我所能:

首先,我的ViewController中有一个具有紫色背景的UIView。我正在向此UIView中添加一个绿色的子视图,该子视图将基于Web脚本的返回值垂直增长,从而有效地制作了一个“仪表”(参见图)。

ios - 需要帮助基于URLSession(Swift3)的数据运行动画-LMLPHP

//This variable is passed in from the Segue & will eventually be passed to the web script, so it knows which ID to pull the data from.
var deviceID: String = ""


//This is a programmatically created UIView that will be added as a subview to the UIView that has been dragged to the View Controller. Based on the image, this will eventually be the "green" rectangle that you see.
let oilReading = UIView()


//This is the outlet for the UIView that I dragged into the ViewController. Based on the image, this is the purple background.
@IBOutlet weak var tankRep: UIView!


override func viewDidLoad() {
    super.viewDidLoad()
}


override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)


    //This block styles the purple block to give it rounded corners & a shadow.
    tankRep.layer.cornerRadius = 8
    tankRep.clipsToBounds = true
    tankRep.layer.masksToBounds = false;
    tankRep.layer.shadowOffset = CGSize(width: 5.0, height: 5.0);
    tankRep.layer.shadowRadius = 8;
    tankRep.layer.shadowOpacity = 0.25;



    //These lines set some default styles for the green block (color & position)
    oilReading.frame = CGRect(x: tankRep.bounds.origin.x, y: CGFloat(tankRep.bounds.height), width: CGFloat(100), height: CGFloat(0))
    oilReading.backgroundColor = UIColor(red: 56/255, green: 221/255, blue: 166/255, alpha: 1.0)


    //These lines create a mask for the green block so the corners are rounded.
    let maskPath = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 100, height: Int(oilMeterHeight)), byRoundingCorners: [.bottomLeft, .bottomRight], cornerRadii: CGSize(width: 8.0, height: 8.0))
    let maskLayer = CAShapeLayer()
    maskLayer.path = maskPath.cgPath
    self.oilReading.layer.mask = maskLayer

    //Add the green block as a subview to the purple block
    tankRep.addSubview(oilReading)
    tankRep.sendSubview(toBack: oilReading)

    //================================================================
    // THIS NEEDS TO RUN BASED ON THE VALUE OF THE WEB SCRIPT
    // Where I am referencing "oilMeterHeight" is what would ultimately come from the web script
    //================================================================

    UIView.transition(with: oilReading, duration: 1.0, animations: {self.oilReading.frame = CGRect(x: self.tankRep.bounds.origin.x, y: CGFloat(self.tankRep.bounds.height - CGFloat(oilMeterHeight)), width: 100, height: CGFloat(oilMeterHeight))}, completion: nil)



    //This calls the function that runs the webscript
    getMeterHeight(deviceID: deviceID)
}


private func getMeterHeight(deviceID: String) {

    guard let URL = URL(string: "http://www.mywebsite.com/getLatestReading.php") else {return}
    let request = NSMutableURLRequest(url: URL)

    request.httpMethod = "POST"

    request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")

    let bodyObject: [String: String] = [
        "deviceID": deviceID
    ]

    request.httpBody = try! JSONSerialization.data(withJSONObject: bodyObject, options: [])
    var responseString: NSString = ""

    let task = URLSession.shared.dataTask(with: request as URLRequest) {
        data, response, error in

        if (error == nil) {
            responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)!

            //This is the value that I need to get out of this unescapable black hole
            let oilMeterHeight = 175 * responseString.doubleValue;

            //Alternately, if I cannot get the previously set value out of this method, then I would like to run the following code - which in its current state does not work...
            //UIView.transition(with: self.oilReading, duration: 1.0, animations: {self.oilReading.frame = CGRect(x: self.tankRep.bounds.origin.x, y: CGFloat(self.tankRep.bounds.height - CGFloat(oilMeterHeight)), width: 100, height: CGFloat(oilMeterHeight))}, completion: nil)
        } else {
            // Failure
            print("URL Session Task Failed: %@", error!.localizedDescription);
        }
    }

    task.resume()
}

最佳答案

在iOS中,您无法在后台线程上更新UI。要更新UI,请在主线程中编写代码,如下所示:

    DispatchQueue.main.async{
      //update your UI here
    }


在您的情况下,为了使视图具有响应Web服务的响应值,请在获取结果后立即编写此代码块。如果api调用一切正常,则getMeterHeight方法应如下所示:

private func getMeterHeight(deviceID: String) {

    guard let URL = URL(string: "http://www.mywebsite.com/getLatestReading.php") else {return}
    let request = NSMutableURLRequest(url: URL)

    request.httpMethod = "POST"

    request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")

    let bodyObject: [String: String] = [
    "deviceID": deviceID
    ]

    request.httpBody = try! JSONSerialization.data(withJSONObject: bodyObject, options: [])
    var responseString: NSString = ""

    let task = URLSession.shared.dataTask(with: request as URLRequest) {
    data, response, error in

     if (error == nil) {
        responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)!

        //This is the value that I need to get out of this unescapable black hole
        let oilMeterHeight = 175 * responseString.doubleValue;

         DispatchQueue.main.async{
          //update your UI here
           UIView.transition(with: self.oilReading, duration: 1.0, animations: {self.oilReading.frame = CGRect(x: self.tankRep.bounds.origin.x, y: CGFloat(self.tankRep.bounds.height - CGFloat(oilMeterHeight)), width: 100, height: CGFloat(oilMeterHeight))}, completion: nil)

        }
     } else {
        // Failure
        print("URL Session Task Failed: %@", error!.localizedDescription);
    }
}

task.resume()
    }

08-04 07:44