我的智慧到此为止。我已经尝试了所有我能想到的,并且无法使它正常工作。
我觉得我在这里想要做的事情应该很简单-但是Swift使它变得异常荒唐,我感到沮丧。
最终,我要做的就是从Web脚本中获取一条数据(它将返回一个float),然后使用该float为UIView设置动画。
因此-为了简单起见-我们假设网络脚本返回“ 50.0”。然后,我想获取该数字,并为UIView设置动画,使其高度从0px变为50px。
我读到您不能从异步任务获取数据,并且您基本上必须运行任何要在闭包内部使用该数据运行的任务。但是,当我尝试在闭包中运行动画时,Xcode拍了拍手,并告诉我我正在尝试在其他线程中修改AutoLayout属性。那我该怎么办?
以下是我的代码,尽我所能:
首先,我的ViewController中有一个具有紫色背景的UIView。我正在向此UIView中添加一个绿色的子视图,该子视图将基于Web脚本的返回值垂直增长,从而有效地制作了一个“仪表”(参见图)。
//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()
}