如何确保一行代码在另一行代码完成后执行?
How to make sure a line of code is executed after another is completed?
我有一个名为 PostViewController 的视图控件,它有一个 posts 的 UITableView。我还有一个名为 PostCell 的 class,它定义了 UITableViewCell。我在 PostCell 中创建了一个名为 likeButtonClicked 的按钮函数,以支持类似于 twitter 的 post。
@IBAction func likesButtonClicked(_ sender: Any) { NotificationCenter.default.post(name: NSNotification.Name(rawValue: "likeButtonClicked"), object: nil, userInfo: ["cell":self, "likesButton":likesButton!]) }
这是将单元格indexPath和按钮名称传递给PostViewController。当 post 受到青睐时,我需要 indexPath 将点赞数增加 1,并需要按钮名称将其图像更改为粉红色。
然后我在PostViewController的viewDidLoad里订阅了通知
NotificationCenter.default.addObserver(self, selector: #selector(postLiked), name: NSNotification.Name(rawValue: "likeButtonClicked"), object: nil)
然后我在同一个地方写了这个函数viewController
@objc func postLiked(notification: Notification){
if let cell = notification.userInfo?["cell"] as? UITableViewCell{
let likesButton = notification.userInfo?["likesButton"] as? SpringButton
if let indexPath = postsTableView.indexPath(for: cell){
let post = posts[indexPath.row]
postId = post.id
PostAPI.getPostById(postId: postId) { post in
//Check if the same post were already favoured.
if !self.liked || self.oldPostId != self.postId{
self.newLikes = post.likes + 1
self.liked = true
self.oldPostId = self.postId
}else{
self.newLikes = self.newLikes - 1
self.liked = false
}
PostAPI.favourPost(postId: self.postId, likes: self.newLikes) {
PostAPI.getPostById(postId: self.postId) { postResponse in
let post = postResponse
self.posts[indexPath.row] = post
let cellNumber = IndexPath(row: indexPath.row, section: indexPath.section)
self.reloadRowData(cellNumber: cellNumber){
if !self.liked{
likesButton?.tintColor = .systemPink
}else{
likesButton?.tintColor = .darkGray
}
}
}
}
}
}
}
}
func reloadRowData(cellNumber: IndexPath, completion: @escaping () -> ()) {
self.postsTableView.reloadRows(at: [cellNumber], with: .none)
completion()
}
请告诉我为什么 postLiked 函数的最后 4 行在 reloadRowData 函数之前执行,这导致按钮的颜色变为粉红色,然后 returns 立即变为灰色,而它应该保持不变粉色.
任何帮助将不胜感激。
谢谢。
A table 视图的 .reloadRows(at:...)
(和 .reloadData()
)函数是 async 个进程。
因此您的 reloadRowData()
函数返回 在 之前 table 视图实际上重新加载行。
这是一种相当不寻常的方法 - 无论是使用 NotificationCenter
让您的单元格与控制器通信,还是通过保持对按钮的引用来尝试更改按钮的色调。
色调确实应该根据您的数据源在 cellForRowAt
中设置。
编辑
我对 table 视图数据重新加载 异步 的描述具有误导性。
我想表达的意思是……
如果我更改我的数据,调用.reloadData()
,然后再次更改我的数据:
// set myData values
myData = ["You", "Say", "Hello"]
// call .reloadData here
tableView.reloadData()
// change myData values
myData = ["I", "Say", "Goodbye"]
table 视图将不会 显示“You Say Hello”,即使我们在这些是 myData 的值时调用 .reloadData()
。
这里有一个完整、简单的示例来演示:
class TestTableViewController: UITableViewController {
var myData: [String] = ["Some", "Song", "Lyrics"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "c")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "c", for: indexPath)
c.textLabel?.text = myData[indexPath.row]
return c
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// call my func when a row is selected
self.myReload()
}
func myReload() {
// set myData values
myData = ["You", "Say", "Hello"]
// call .reloadData here
tableView.reloadData()
// change myData values
myData = ["I", "Say", "Goodbye"]
// table view doesn't reload itself we exit this func
// so we'll get
// I
// Say
// Goodbye
}
}
因此,在 Rob Napier 在他的回答中描述的其他问题中,您的原始代码试图在 before table 重新加载了它的数据。
我预计具体问题是您致电 reloadRows
。作为 docs note:
Reloading a row causes the table view to ask its data source for a new cell for that row. The table animates that new cell in as it animates the old row out. Call this method if you want to alert the user that the value of a cell is changing. If, however, notifying the user is not important—that is, you just want to change the value that a cell is displaying—you can get the cell for a particular row and set its new value.
所以这很可能创建了一个全新的单元格,然后后面的代码修改了从 table 中删除的旧单元格。 (所以你看到了变化,然后单元格被删除并替换。)
我首先要摆脱整个 reloadRowData
调用。
不过,总的来说,这段代码很脆弱,我会重新设计它。单元格应根据数据自行设置色调。你通常不应该进入一个单元格并操纵它的子视图。这会在回收单元格时给您带来问题(例如,当它滚动到屏幕外时)。所有配置都应该在cellForRow(at:)
中完成,cell应该观察它的Post并在有变化时更新自己。
数据应该存在于模型中。视图应该观察模型并做出反应。模型不应进入视图并操纵任何东西。
另一方面:您的 reloadRowData
看起来不同步,但事实并非如此。没有理由需要完成处理程序。它可以调用 return
.
我有一个名为 PostViewController 的视图控件,它有一个 posts 的 UITableView。我还有一个名为 PostCell 的 class,它定义了 UITableViewCell。我在 PostCell 中创建了一个名为 likeButtonClicked 的按钮函数,以支持类似于 twitter 的 post。
@IBAction func likesButtonClicked(_ sender: Any) { NotificationCenter.default.post(name: NSNotification.Name(rawValue: "likeButtonClicked"), object: nil, userInfo: ["cell":self, "likesButton":likesButton!]) }
这是将单元格indexPath和按钮名称传递给PostViewController。当 post 受到青睐时,我需要 indexPath 将点赞数增加 1,并需要按钮名称将其图像更改为粉红色。
然后我在PostViewController的viewDidLoad里订阅了通知
NotificationCenter.default.addObserver(self, selector: #selector(postLiked), name: NSNotification.Name(rawValue: "likeButtonClicked"), object: nil)
然后我在同一个地方写了这个函数viewController
@objc func postLiked(notification: Notification){
if let cell = notification.userInfo?["cell"] as? UITableViewCell{
let likesButton = notification.userInfo?["likesButton"] as? SpringButton
if let indexPath = postsTableView.indexPath(for: cell){
let post = posts[indexPath.row]
postId = post.id
PostAPI.getPostById(postId: postId) { post in
//Check if the same post were already favoured.
if !self.liked || self.oldPostId != self.postId{
self.newLikes = post.likes + 1
self.liked = true
self.oldPostId = self.postId
}else{
self.newLikes = self.newLikes - 1
self.liked = false
}
PostAPI.favourPost(postId: self.postId, likes: self.newLikes) {
PostAPI.getPostById(postId: self.postId) { postResponse in
let post = postResponse
self.posts[indexPath.row] = post
let cellNumber = IndexPath(row: indexPath.row, section: indexPath.section)
self.reloadRowData(cellNumber: cellNumber){
if !self.liked{
likesButton?.tintColor = .systemPink
}else{
likesButton?.tintColor = .darkGray
}
}
}
}
}
}
}
}
func reloadRowData(cellNumber: IndexPath, completion: @escaping () -> ()) {
self.postsTableView.reloadRows(at: [cellNumber], with: .none)
completion()
}
请告诉我为什么 postLiked 函数的最后 4 行在 reloadRowData 函数之前执行,这导致按钮的颜色变为粉红色,然后 returns 立即变为灰色,而它应该保持不变粉色.
任何帮助将不胜感激。 谢谢。
A table 视图的 .reloadRows(at:...)
(和 .reloadData()
)函数是 async 个进程。
因此您的 reloadRowData()
函数返回 在 之前 table 视图实际上重新加载行。
这是一种相当不寻常的方法 - 无论是使用 NotificationCenter
让您的单元格与控制器通信,还是通过保持对按钮的引用来尝试更改按钮的色调。
色调确实应该根据您的数据源在 cellForRowAt
中设置。
编辑
我对 table 视图数据重新加载 异步 的描述具有误导性。
我想表达的意思是……
如果我更改我的数据,调用.reloadData()
,然后再次更改我的数据:
// set myData values
myData = ["You", "Say", "Hello"]
// call .reloadData here
tableView.reloadData()
// change myData values
myData = ["I", "Say", "Goodbye"]
table 视图将不会 显示“You Say Hello”,即使我们在这些是 myData 的值时调用 .reloadData()
。
这里有一个完整、简单的示例来演示:
class TestTableViewController: UITableViewController {
var myData: [String] = ["Some", "Song", "Lyrics"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "c")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "c", for: indexPath)
c.textLabel?.text = myData[indexPath.row]
return c
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// call my func when a row is selected
self.myReload()
}
func myReload() {
// set myData values
myData = ["You", "Say", "Hello"]
// call .reloadData here
tableView.reloadData()
// change myData values
myData = ["I", "Say", "Goodbye"]
// table view doesn't reload itself we exit this func
// so we'll get
// I
// Say
// Goodbye
}
}
因此,在 Rob Napier 在他的回答中描述的其他问题中,您的原始代码试图在 before table 重新加载了它的数据。
我预计具体问题是您致电 reloadRows
。作为 docs note:
Reloading a row causes the table view to ask its data source for a new cell for that row. The table animates that new cell in as it animates the old row out. Call this method if you want to alert the user that the value of a cell is changing. If, however, notifying the user is not important—that is, you just want to change the value that a cell is displaying—you can get the cell for a particular row and set its new value.
所以这很可能创建了一个全新的单元格,然后后面的代码修改了从 table 中删除的旧单元格。 (所以你看到了变化,然后单元格被删除并替换。)
我首先要摆脱整个 reloadRowData
调用。
不过,总的来说,这段代码很脆弱,我会重新设计它。单元格应根据数据自行设置色调。你通常不应该进入一个单元格并操纵它的子视图。这会在回收单元格时给您带来问题(例如,当它滚动到屏幕外时)。所有配置都应该在cellForRow(at:)
中完成,cell应该观察它的Post并在有变化时更新自己。
数据应该存在于模型中。视图应该观察模型并做出反应。模型不应进入视图并操纵任何东西。
另一方面:您的 reloadRowData
看起来不同步,但事实并非如此。没有理由需要完成处理程序。它可以调用 return
.