问题
Before anyone suggests to pull the Firebase data from within the PlayerController's viewWillAppear, I already know how to do that and if I did it that way I know how to pass the data to the ScoreController. In this situation I need to pull the data directly from within the cell and somehow pass the data back from there.
I have a tableView inside a PlayerController that displays the randomPower, name, and score of each player. Inside the tableView's cell I pull the name and score from Firebase using a function getScoreDataFromFirebase()
. The function is located directly inside the tableView's PlayerCell and once I get the values from Firebase I initialize the cell's name and score outlets right then and there.
Inside the tableView's cellForRowAtIndexPath I call cell.getScoreDataFromFirebase()
and everything works fine because both outlets display the correct values.
From that point on I have a ScoreController. When a tableView cell is chosen the score is sent to the ScoreController.
The problem is since I'm pulling the data directly from within the cell itself the only way I could pass the score (pulled from Firebase) to ScoreController was to 1st set a didSet
score property inside the cell.
Still inside the cell when I pull the score data from Firebase 2nd I initialize the score property with it
3rd inside the tableView's cellForAtIndexPath I use an if let
to pass the value from the cell's score property to the the tableData.
When I first try to send the indexPath of that tableData over to the ScoreController sometimes it's nil even though the cell's score property definitely has a value (I used to break points to check). If I select any of the very first few tableView cells that are visible they will have a nil value for the score property. However if I scroll further down through the cells and back up then those same cells will no longer have a nil score property.
What I found out was the if let
statement was running before the Firebase code was pulled so the score property was nil for first few cells that are on scene. The odd thing is everything works fine once I start scrolling.
How can I pass a value pulled directly from a cell to the tableView's didSelectRow?
PlayerModel:
class PlayerModel{
name:String?
score:String?
randomPower:String?
}
TableViewCell SubClass:
class PlayerCell:UITableViewCell{
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var scoreLabel: UILabel!
@IBOutlet weak var randomPowerLabel: UILabel!
internal var score: String?{
didSet{
print("**********\(score ?? "*********")")
}
}
override func prepareForReuse() {
super.prepareForReuse()
nameLabel.text = " "
scoreLabel.text = " "
}
func getScoreDataFromFirebase(){
let scoreRef = usersRef?.child("score")
scoreRef?.observe( .value, with: {
(snapshot) in
for child in snapshot.children{
let user = child as! DataSnapshot
for player in user.children{
let eachPlayer = player as! DataSnapshot
if let dict = eachPlayer.value as? [String:Any]{
let name = dict["name"] as? String ?? " "
let score = dict["score"] as? String ?? " "
self.nameLabel.text = name
self.scoreLabel.text = score
self.score = score
}
}
}
}
}
}
TableView:
class PlayerController: UITableViewDataSource, UITableViewDelegate{
@IBOutlet weak fileprivate var tableView: UITableView!
var players = [PlayerModel]() // this array is populated with data from a previous vc. The number of players in the array are the same exact number of players that's pulled from the getScoreDataFromFirebase() function
override func viewDidLoad() {
super.viewDidLoad()
tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return players.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PlayerCell", for: indexPath) as! PlayerCell
let cellData = players[indexPath.row]
cellData.randomPowerLabel.text = cellData.power
cell.getScoreDataFromFirebase()
if let score = cell.score{
cellData.score = score
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let indexPath = tableView.indexPathForSelectedRow else { return }
let scoreVC = storyboard?.instantiateViewController(withIdentifier: "ScoreController") as! ScoreController
scoreVC.score = players[indexPath.row].score
}
回答1:
You can achieve this using delegation :
Create a protocol
protocol UpdateValueDelegate: class {
func changeValue(score: String, row: Int)
}
Your UIViewController
should look like this :
PlayController : UITableViewDataSource, UITableViewDelegate, UpdateValueDelegate
{
var scoreDict:[String:String] = [:]
//
//
func changeValue(score: String, row: Int)
{
self.scoreDict["\(row)"] = score
}
}
In cellForRowAtIndexPath
set cell.delegate = self and cell.row = indexPath.row
Your UITableViewCell
should look like this :
class PlayerCell:UITableViewCell: UITableViewCell
{
weak var delegate: UpdateValueDelegate?
var row: Int?
//
//
}
Finally pass score from getScoreDataFromFirebase by calling delegate function:
func getScoreDataFromFirebase()
{
//
//
delegate. changeValue(score: localScore, row: self.row)
}
Now you have the value in your viewController from where it can be easily passed to didSelectRow
using the global dictionary ** scoreDict**.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
var score = self.scoreDict["\(indexPath.row)"]
// Use score
}
来源:https://stackoverflow.com/questions/48773528/swift-ios-how-can-i-pass-a-value-obtained-directly-from-a-within-tableviewcell