Swift UITableViewCell 滚动时按钮状态更改
Posted
技术标签:
【中文标题】Swift UITableViewCell 滚动时按钮状态更改【英文标题】:Swift UITableViewCell Button State Change on Scroll 【发布时间】:2018-04-04 14:14:45 【问题描述】:所以我在UITableViewCell
中有一个按钮,我可以更改状态并更新数据库。但是,当我滚动并返回时,状态仍处于加载视图时的原始状态。
如何让滚动后状态保持变化?
我尝试在 prepareForReuse 中设置状态图像,但没有成功。
// MARK: - Outlets
@IBOutlet weak var locationImage: UIImageView!
@IBOutlet weak var displayName: UILabel!
@IBOutlet weak var countryLabel: UILabel!
@IBOutlet weak var beenHereLabel: SpringLabel!
@IBOutlet weak var needToGoLabel: SpringLabel!
@IBOutlet weak var wavetrotterButton: SpringButton!
@IBOutlet weak var checkmarkButton: SpringButton!
// MARK: - Variables
var db:Firestore!
let selection = UISelectionFeedbackGenerator()
let notification = UINotificationFeedbackGenerator()
var documentId:String!
// MARK: - Nib shown
override func awakeFromNib()
super.awakeFromNib()
// Initialization code
db = Firestore.firestore()
func customInit(displayName: String, id: String, country: String, image: UIImage)
self.displayName.text = displayName
self.documentId = id
self.countryLabel.text = country
self.locationImage.image = image
// MARK: - Actions
@IBAction func checkmarkButtonPressed(_ sender: UIButton)
notification.notificationOccurred(.success)
checkmarkButton.animation = "pop"
beenHereLabel.animation = "pop"
if checkmarkButton.isSelected == true
checkmarkButton.animate()
beenHereLabel.animate()
checkmarkButton.isSelected = false
// Delete location surfed
if let user = Auth.auth().currentUser
Firestore.firestore().collection("users").document(user.uid).collection("surfed").document("\(documentId!)").delete() err in
if let err = err
print("Error removing document: \(err)")
else
print("\(self.documentId!) successfully removed!")
else
checkmarkButton.animate()
beenHereLabel.animate()
checkmarkButton.isSelected = true
// Add location surfed
if let user = Auth.auth().currentUser
Firestore.firestore().collection("users").document(user.uid).collection("surfed").document("\(documentId!)").setData([
"name":displayName.text ?? "",
"country":countryLabel.text ?? ""
]) err in
if let err = err
print("Error writing document: \(err)")
else
print("\(self.documentId!) added to surfed locations")
@IBAction func wavetrotterButtonPressed(_ sender: UIButton)
notification.notificationOccurred(.success)
wavetrotterButton.animation = "pop"
needToGoLabel.animation = "pop"
if wavetrotterButton.isSelected == true
wavetrotterButton.animate()
needToGoLabel.animate()
wavetrotterButton.isSelected = false
// Delete location wantToSurf
if let user = Auth.auth().currentUser
Firestore.firestore().collection("users").document(user.uid).collection("wantToSurf").document("\(documentId!)").delete() err in
if let err = err
print("Error removing document: \(err)")
else
print("\(self.documentId!) successfully removed!")
else
wavetrotterButton.animate()
needToGoLabel.animate()
wavetrotterButton.isSelected = true
// Add location wantToSurf
if let user = Auth.auth().currentUser
Firestore.firestore().collection("users").document(user.uid).collection("wantToSurf").document("\(documentId!)").setData([
"name":displayName.text ?? "",
"country":countryLabel.text ?? ""
]) err in
if let err = err
print("Error writing document: \(err)")
else
print("\(self.documentId!) added to surfed locations")
LocationResultsTableViewController.swift
// MARK: - Variables
var listName: String?
var listId: String?
var db:Firestore!
let storage = Storage.storage().reference()
var locationArray = [Location]()
var userSurfedArray = [String]()
var userWantToSurfArray = [String]()
let pullToRefreshControl = UIRefreshControl()
var selectedDocumentId: String?
// MARK: - View Did Load
override func viewDidLoad()
super.viewDidLoad()
self.title = listName
db = Firestore.firestore()
SVProgressHUD.show()
getListLocations()
getUserSurfedArray()
getUserWantToSurfArray()
// Configure the cell to the nib file
let nib = UINib(nibName: "LocationCell", bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "locationCell")
self.refreshControl = pullToRefreshControl
pullToRefreshControl.addTarget(self, action: #selector(refreshTable), for: .valueChanged)
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: UIBarButtonItemStyle.plain, target: nil, action: nil)
// MARK: - View Will Appear
override func viewWillAppear(_ animated: Bool)
tableView.reloadData()
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int
// #warning Incomplete implementation, return the number of sections
return 1
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
// #warning Incomplete implementation, return the number of rows
return locationArray.count
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = tableView.dequeueReusableCell(withIdentifier: "locationCell", for: indexPath) as! LocationCell
// Configure the cell...
let location = locationArray[indexPath.row]
cell.documentId = location.documentId
// Set button states
if self.userSurfedArray.contains(cell.documentId!)
cell.checkmarkButton.isSelected = true
else
cell.checkmarkButton.isSelected = false
if self.userWantToSurfArray.contains(cell.documentId!)
cell.wavetrotterButton.isSelected = true
else
cell.wavetrotterButton.isSelected = false
let locationImageRef = storage.child("locationImages/"+(location.documentId)+".jpg")
// Download in memory with a maximum allowed size of 1MB (1 * 1024 * 1024 bytes)
locationImageRef.getData(maxSize: 1 * 1024 * 1024) data, error in
if let error = error
// Uh-oh, an error occurred! Display Default image
print("Error - unable to download image: \(error)")
else
// Data for "locationImages/(locationId).jpg" is returned
cell.customInit(displayName: location.name, id: location.documentId, country: location.country, image: UIImage(data: data!)!)
SVProgressHUD.dismiss()
return cell
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
selectedDocumentId = locationArray[indexPath.row].documentId
self.performSegue(withIdentifier: "goToLocationProfileSegue", sender: self)
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
return 260
// MARK: - Functions
func getListLocations()
if Auth.auth().currentUser != nil
db.collection("locations").whereField("lists."+listId!, isEqualTo: true).getDocuments() (querySnapshot, error) in
if let error = error
print("Error getting documents: \(error)")
else
print(querySnapshot?.documents.count ?? "0")
for document in querySnapshot!.documents
self.locationArray.append(Location(documentId: document.documentID, name: document["name"] as! String, country: document["country"] as! String))
DispatchQueue.main.async
self.tableView.reloadData()
func getUserSurfedArray()
if let user = Auth.auth().currentUser
db.collection("users").document(user.uid).collection("surfed").getDocuments() (querySnapshot, error) in
if let error = error
print("Error getting documents: \(error)")
else
for document in querySnapshot!.documents
self.userSurfedArray.append(document.documentID)
DispatchQueue.main.async
self.tableView.reloadData()
func getUserWantToSurfArray()
if let user = Auth.auth().currentUser
db.collection("users").document(user.uid).collection("wantToSurf").getDocuments() (querySnapshot, error) in
if let error = error
print("Error getting documents: \(error)")
else
for document in querySnapshot!.documents
self.userWantToSurfArray.append(document.documentID)
DispatchQueue.main.async
self.tableView.reloadData()
【问题讨论】:
您没有在单击按钮时更新userSurfedArray
和userWantToSurfArray
,这就是为什么当使用dequeueReusableCell(withIdentifier:)
重新加载表格单元格时,它会重置为以前的状态。单击按钮时更新这些数组。
不鼓励您将异步任务放在 视图(单元格)中。考虑到可以立即释放单元,因此数据库访问变得不可预测。将代码放在controller或model中。
【参考方案1】:
背后的原因是单元格重用,您必须将按钮的状态保存在该 indexPath ,并在cellForRowAt
中恢复它
【讨论】:
【参考方案2】:从您的代码中可以看出,您正在从下面几行加载按钮:
在cellForRowAt indexPath
// Set button states
if self.userSurfedArray.contains(cell.documentId!)
cell.checkmarkButton.isSelected = true
else
cell.checkmarkButton.isSelected = false
if self.userWantToSurfArray.contains(cell.documentId!)
cell.wavetrotterButton.isSelected = true
else
cell.wavetrotterButton.isSelected = false
事情正在滚动,当表格单元格隐藏时,它会出列,当它再次出现时,此单元格会重新加载dequeueReusableCell(withIdentifier:)
,并且您没有在单击按钮时更新userSurfedArray
和userWantToSurfArray
。
您需要在单击按钮时更新userSurfedArray
和userWantToSurfArray
。
这似乎不够,请将您的代码发布到您加载这些数组的位置,以便我可以帮助您解释如何更新这些。
更新:
在您更新的代码中,我可以看到您仅在 viewDidLoad()
中加载了 userSurfedArray
和 userWantToSurfArray
。
要更新
userSurfedArray
和userWantToSurfArray
:在 UITableViewCell 中创建一个委托并在LocationResultsTableViewController
中遵循此协议,如下所示:
在 UITableViewCell 中:(假设表格单元格的名称是LocTableViewCell
)
import UIKit
protocol UpdateUserArrayDelegate: class
func updateUserSurfedArray(documentId: String, isAdd: Bool)
func updateUserWantToSurfArray(documentId: String, isAdd: Bool)
class LocTableViewCell: UITableViewCell
weak var cellDelegate: UpdateUserArrayDelegate?
// MARK: - Outlets
@IBOutlet weak var locationImage: UIImageView!
@IBOutlet weak var displayName: UILabel!
@IBOutlet weak var countryLabel: UILabel!
@IBOutlet weak var beenHereLabel: SpringLabel!
@IBOutlet weak var needToGoLabel: SpringLabel!
@IBOutlet weak var wavetrotterButton: SpringButton!
@IBOutlet weak var checkmarkButton: SpringButton!
// MARK: - Variables
var db:Firestore!
let selection = UISelectionFeedbackGenerator()
let notification = UINotificationFeedbackGenerator()
var documentId:String!
// MARK: - Nib shown
override func awakeFromNib()
super.awakeFromNib()
// Initialization code
db = Firestore.firestore()
func customInit(displayName: String, id: String, country: String, image: UIImage)
self.displayName.text = displayName
self.documentId = id
self.countryLabel.text = country
self.locationImage.image = image
// MARK: - Actions
@IBAction func checkmarkButtonPressed(_ sender: UIButton)
notification.notificationOccurred(.success)
checkmarkButton.animation = "pop"
beenHereLabel.animation = "pop"
if checkmarkButton.isSelected == true
checkmarkButton.animate()
beenHereLabel.animate()
checkmarkButton.isSelected = false
// Delete location surfed
if let user = Auth.auth().currentUser
Firestore.firestore().collection("users").document(user.uid).collection("surfed").document("\(documentId!)").delete() err in
if let err = err
print("Error removing document: \(err)")
else
cellDelegate?.updateUserSurfedArray(documentId: self.documentId, isAdd: false)
print("\(self.documentId!) successfully removed!")
else
checkmarkButton.animate()
beenHereLabel.animate()
checkmarkButton.isSelected = true
// Add location surfed
if let user = Auth.auth().currentUser
Firestore.firestore().collection("users").document(user.uid).collection("surfed").document("\(documentId!)").setData([
"name":displayName.text ?? "",
"country":countryLabel.text ?? ""
]) err in
if let err = err
print("Error writing document: \(err)")
else
cellDelegate?.updateUserSurfedArray(documentId: self.documentId, isAdd: true)
print("\(self.documentId!) added to surfed locations")
@IBAction func wavetrotterButtonPressed(_ sender: UIButton)
notification.notificationOccurred(.success)
wavetrotterButton.animation = "pop"
needToGoLabel.animation = "pop"
if wavetrotterButton.isSelected == true
wavetrotterButton.animate()
needToGoLabel.animate()
wavetrotterButton.isSelected = false
// Delete location wantToSurf
if let user = Auth.auth().currentUser
Firestore.firestore().collection("users").document(user.uid).collection("wantToSurf").document("\(documentId!)").delete() err in
if let err = err
print("Error removing document: \(err)")
else
cellDelegate?.updateUserWantToSurfArray(documentId: self.documentId, isAdd: false)
print("\(self.documentId!) successfully removed!")
else
wavetrotterButton.animate()
needToGoLabel.animate()
wavetrotterButton.isSelected = true
// Add location wantToSurf
if let user = Auth.auth().currentUser
Firestore.firestore().collection("users").document(user.uid).collection("wantToSurf").document("\(documentId!)").setData([
"name":displayName.text ?? "",
"country":countryLabel.text ?? ""
]) err in
if let err = err
print("Error writing document: \(err)")
else
cellDelegate?.updateUserWantToSurfArray(documentId: self.documentId, isAdd: true)
print("\(self.documentId!) added to surfed locations")
在 LocationResultsTableViewController 中
使用协议方法添加扩展
extension LocationResultsTableViewController: UpdateUserArrayDelegate
func updateUserSurfedArray(documentId: String, isAdd: Bool)
if isAdd
self.userSurfedArray.append(documentId)
else
if self.userSurfedArray.contains(documentId)
self.userSurfedArray.remove(at: self.userSurfedArray.index(of: documentId)!)
func updateUserWantToSurfArray(documentId: String, isAdd: Bool)
if isAdd
self.userWantToSurfArray.append(documentId)
else
if self.userWantToSurfArray.contains(documentId)
self.userWantToSurfArray.remove(at: self.userWantToSurfArray.index(of: documentId)!)
将cellForRowAt indexPath
更新为:(添加 cell.cellDelegate = self)
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = tableView.dequeueReusableCell(withIdentifier: "locationCell", for: indexPath) as! LocationCell
// Configure the cell...
let location = locationArray[indexPath.row]
cell.documentId = location.documentId
// Conform table cell delegate here
cell.cellDelegate = self
// Set button states
if self.userSurfedArray.contains(cell.documentId!)
cell.checkmarkButton.isSelected = true
else
cell.checkmarkButton.isSelected = false
if self.userWantToSurfArray.contains(cell.documentId!)
cell.wavetrotterButton.isSelected = true
else
cell.wavetrotterButton.isSelected = false
let locationImageRef = storage.child("locationImages/"+(location.documentId)+".jpg")
// Download in memory with a maximum allowed size of 1MB (1 * 1024 * 1024 bytes)
locationImageRef.getData(maxSize: 1 * 1024 * 1024) data, error in
if let error = error
// Uh-oh, an error occurred! Display Default image
print("Error - unable to download image: \(error)")
else
// Data for "locationImages/(locationId).jpg" is returned
cell.customInit(displayName: location.name, id: location.documentId, country: location.country, image: UIImage(data: data!)!)
SVProgressHUD.dismiss()
return cell
更新
对于isAdd
的情况,我们不需要检查self.userSurfedArray.contains(documentId)
:
extension LocationResultsTableViewController: UpdateUserArrayDelegate
func updateUserSurfedArray(documentId: String, isAdd: Bool)
if isAdd
self.userSurfedArray.append(documentId)
else
if self.userSurfedArray.contains(documentId)
self.userSurfedArray.remove(at: self.userSurfedArray.index(of: documentId)!)
func updateUserWantToSurfArray(documentId: String, isAdd: Bool)
if isAdd
self.userWantToSurfArray.append(documentId)
else
if self.userWantToSurfArray.contains(documentId)
self.userWantToSurfArray.remove(at: self.userWantToSurfArray.index(of: documentId)!)
【讨论】:
感谢我更新了代码以包含 locationresultstablevc 以显示我从哪里获取数组 哇非常感谢您的详细帖子!我只是重新运行了基础,如果选择了一个按钮并取消选择它,它似乎可以工作。但是,如果一个按钮没有被选中,我选择了它,然后滚动按钮回到原来的未选中状态。 更新了extension LocationResultsTableViewController
。它现在可以正常工作了。
啊是的,就是这样 - 我也在寻找扩展名!再次感谢!
然而,有一个错误会不断弹出 - 每次应用启动第一个按钮选择时(无论是选择还是取消选择)仍然会受到影响。我试图编辑LocationResultsTableVC
中的代码,上面写着// set button states
,但它不起作用【参考方案3】:
您需要更新数据源并替换最新数据以保留状态,否则您的 tableviewcells 是可重复使用的。
【讨论】:
以上是关于Swift UITableViewCell 滚动时按钮状态更改的主要内容,如果未能解决你的问题,请参考以下文章
UITableViewCell中的Swift ScrollView不滚动
Swift 中 UITableViewCell 中的同步滚动 UICollectionViews