Swift 3/iOS 检索远程 JSON 数据后 UIView 未更新
Swift 3/iOS UIView not updating after retrieving remote JSON data
我有一个包含用户列表的 UITableView
。当您点击一行时,用户的 uid 将传递到 UIViewController
详细信息视图。 URLRequest
用于检索用户的 JSON
数据(用户名、头像等)。但是,详细视图不一致地更新信息。有时会显示用户名、头像等,但有时什么也不显示,或者只显示用户名或只显示头像等
在 fetchUser()
方法中,我有一个 print("Username: \(self.user.username)")
显示 100% 的时间正在检索正确的数据,但它不会 100% 的时间显示在视图中.
如有任何帮助,我们将不胜感激。
谢谢!
class ProfileViewController: UIViewController {
@IBOutlet weak var avatarImageView: UIImageView!
@IBOutlet weak var usernameLabel: UILabel!
@IBOutlet weak var networthLabel: UILabel!
var user: User!
var uid: Int?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
fetchUser()
}
func reloadView() {
self.usernameLabel.text = user.username
self.networthLabel.text = "$" + NumberFormatter.localizedString(from: Int((user.networth)!)! as NSNumber, number: NumberFormatter.Style.decimal)
self.avatarImageView.downloadImage(from: user.avatar!)
circularImage(photoImageView: self.avatarImageView)
}
func fetchUser() {
// Post user data to server
let myUrl = NSURL(string: "http://localhost/test/profile")
let urlRequest = NSMutableURLRequest(url: myUrl! as URL);
urlRequest.httpMethod = "POST"
let postString = "uid=\(uid!)"
urlRequest.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: urlRequest as URLRequest) { (data, response, error) in
if (error != nil) {
print("error=\(String(describing: error))")
return
} // end if
self.user = User()
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String : AnyObject]
if let parseJSON = json?["data"] as? [[String : AnyObject]] {
for userFromJson in parseJSON {
let userData = User()
if let uid = userFromJson["uid"] as? String,
let username = userFromJson["username"] as? String,
let networth = userFromJson["networth"] as? String,
let avatar = userFromJson["avatar"] as? String {
userData.uid = Int(uid)
userData.username = username
userData.networth = networth
userData.avatar = avatar
self.usernameLabel.text = username
self.networthLabel.text = networth
self.avatarImageView.downloadImage(from: avatar)
circularImage(photoImageView: self.avatarImageView)
} // end if
self.user = userData
} // end for
} // end if
DispatchQueue.main.async {
print("Username: \(self.user.username)")
self.reloadView()
}
} catch let error {
print(error)
}
}
task.resume()
}
两条建议:
- 严格来说,所有对
UIView
对象的访问都应该在主线程上。您正在分派到主线程以调用 reloadView
,但是当您在标签 上设置 "username" 和 "net worth" 值时,也应该这样做
- 您确定标签是空白的吗?它可能是一个自动布局问题吗? (尝试将标签的背景颜色设置为黄色,以检查它们的大小是否正确。有时,如果存在冲突约束,自动布局可以将视图压缩为空)
首先,像这样调用 viewWillAppear
中的获取用户:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
fetchUser()
}
然后,像我一样更改这里的代码,不要使用你的 reloadView
功能,而是在 [= 结束时更新主线程上的 UI 元素14=] 函数。我也改变了它所以你没有更新 UI 两次,因为你在 fetchUser
中的 if let uid = ...
语句底部有 4 行更新了 UI 元素' t 在主线程中,这就是为什么在我的版本中我删除了那 4 行代码。如果这对你有用,请告诉我。
let task = URLSession.shared.dataTask(with: urlRequest as URLRequest) { (data, response, error) in
if (error != nil) {
print("error=\(String(describing: error))")
return
} // end if
self.user = User()
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String : AnyObject]
if let parseJSON = json?["data"] as? [[String : AnyObject]] {
for userFromJson in parseJSON {
let userData = User()
if let uid = userFromJson["uid"] as? String,
let username = userFromJson["username"] as? String,
let networth = userFromJson["networth"] as? String,
let avatar = userFromJson["avatar"] as? String {
userData.uid = Int(uid)
userData.username = username
userData.networth = networth
userData.avatar = avatar
} // end if
self.user = userData
} // end for
} // end if
DispatchQueue.main.async {
self.usernameLabel.text = user.username
self.networthLabel.text = "$" + NumberFormatter.localizedString(from: Int((user.networth)!)! as NSNumber, number: NumberFormatter.Style.decimal)
self.avatarImageView.downloadImage(from: user.avatar!)
circularImage(photoImageView: self.avatarImageView)
}
} catch let error {
print(error)
}
}
task.resume()
我有一个包含用户列表的 UITableView
。当您点击一行时,用户的 uid 将传递到 UIViewController
详细信息视图。 URLRequest
用于检索用户的 JSON
数据(用户名、头像等)。但是,详细视图不一致地更新信息。有时会显示用户名、头像等,但有时什么也不显示,或者只显示用户名或只显示头像等
在 fetchUser()
方法中,我有一个 print("Username: \(self.user.username)")
显示 100% 的时间正在检索正确的数据,但它不会 100% 的时间显示在视图中.
如有任何帮助,我们将不胜感激。
谢谢!
class ProfileViewController: UIViewController {
@IBOutlet weak var avatarImageView: UIImageView!
@IBOutlet weak var usernameLabel: UILabel!
@IBOutlet weak var networthLabel: UILabel!
var user: User!
var uid: Int?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
fetchUser()
}
func reloadView() {
self.usernameLabel.text = user.username
self.networthLabel.text = "$" + NumberFormatter.localizedString(from: Int((user.networth)!)! as NSNumber, number: NumberFormatter.Style.decimal)
self.avatarImageView.downloadImage(from: user.avatar!)
circularImage(photoImageView: self.avatarImageView)
}
func fetchUser() {
// Post user data to server
let myUrl = NSURL(string: "http://localhost/test/profile")
let urlRequest = NSMutableURLRequest(url: myUrl! as URL);
urlRequest.httpMethod = "POST"
let postString = "uid=\(uid!)"
urlRequest.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: urlRequest as URLRequest) { (data, response, error) in
if (error != nil) {
print("error=\(String(describing: error))")
return
} // end if
self.user = User()
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String : AnyObject]
if let parseJSON = json?["data"] as? [[String : AnyObject]] {
for userFromJson in parseJSON {
let userData = User()
if let uid = userFromJson["uid"] as? String,
let username = userFromJson["username"] as? String,
let networth = userFromJson["networth"] as? String,
let avatar = userFromJson["avatar"] as? String {
userData.uid = Int(uid)
userData.username = username
userData.networth = networth
userData.avatar = avatar
self.usernameLabel.text = username
self.networthLabel.text = networth
self.avatarImageView.downloadImage(from: avatar)
circularImage(photoImageView: self.avatarImageView)
} // end if
self.user = userData
} // end for
} // end if
DispatchQueue.main.async {
print("Username: \(self.user.username)")
self.reloadView()
}
} catch let error {
print(error)
}
}
task.resume()
}
两条建议:
- 严格来说,所有对
UIView
对象的访问都应该在主线程上。您正在分派到主线程以调用reloadView
,但是当您在标签 上设置 "username" 和 "net worth" 值时,也应该这样做
- 您确定标签是空白的吗?它可能是一个自动布局问题吗? (尝试将标签的背景颜色设置为黄色,以检查它们的大小是否正确。有时,如果存在冲突约束,自动布局可以将视图压缩为空)
首先,像这样调用 viewWillAppear
中的获取用户:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
fetchUser()
}
然后,像我一样更改这里的代码,不要使用你的 reloadView
功能,而是在 [= 结束时更新主线程上的 UI 元素14=] 函数。我也改变了它所以你没有更新 UI 两次,因为你在 fetchUser
中的 if let uid = ...
语句底部有 4 行更新了 UI 元素' t 在主线程中,这就是为什么在我的版本中我删除了那 4 行代码。如果这对你有用,请告诉我。
let task = URLSession.shared.dataTask(with: urlRequest as URLRequest) { (data, response, error) in
if (error != nil) {
print("error=\(String(describing: error))")
return
} // end if
self.user = User()
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String : AnyObject]
if let parseJSON = json?["data"] as? [[String : AnyObject]] {
for userFromJson in parseJSON {
let userData = User()
if let uid = userFromJson["uid"] as? String,
let username = userFromJson["username"] as? String,
let networth = userFromJson["networth"] as? String,
let avatar = userFromJson["avatar"] as? String {
userData.uid = Int(uid)
userData.username = username
userData.networth = networth
userData.avatar = avatar
} // end if
self.user = userData
} // end for
} // end if
DispatchQueue.main.async {
self.usernameLabel.text = user.username
self.networthLabel.text = "$" + NumberFormatter.localizedString(from: Int((user.networth)!)! as NSNumber, number: NumberFormatter.Style.decimal)
self.avatarImageView.downloadImage(from: user.avatar!)
circularImage(photoImageView: self.avatarImageView)
}
} catch let error {
print(error)
}
}
task.resume()