I don't need _index. I was just trying different things. I implemented your method I am getting an error at runtime at line 3. Unexpectedly found nil while unwrapping an Optional value.
Post
Replies
Boosts
Views
Activity
I can't figure it out. On line 34 of my original post, I don't have anything in addOject() parameters. What am I supposed to pass through its parameters? Thank you for your help.
This is the actual file from my app. Can you help me please? When I run it, line 71 is throwing an error that it found nil while unwrapping a nil value.import UIKit
var myRoster: [String]?
struct DataToPass {
var playerData: String = ""
}
@available(iOS 11.0, *)
class Top_30_Quarterbacks: UIViewController, UITableViewDelegate, UITableViewDataSource {
var objectsArray: [String] = ["1. Patrick Mahomes, KC", "2. Deshaun Watson, HOU", "3. Aaron Rodgers, GB", "4. Matt Ryan, ATL", "5. Baker Mayfield, CLE", "6. Carson Wentz, PHI", "7. Jared Goff, LAR", "8. Cam Newton, CAR", "9. Andrew Luck, IND", "10. Drew Brees, NO", "11. Ben Roethlisberger, PIT", "12. Dak Prescott, DAL", "13. Russell Wilson, SEA", "14. Tom Brady, NE", "15. Lamar Jackson, BAL", "16. Mitchell Trubisky, CHI", "17. Jameis Winston, TB", "18. Philip Rivers, LAC", "19. Kirk Cousins, MIN", "20. Derek Carr, OAK", "21. Sam Darnold, NYJ", "22. Josh Allen, BUF", "23. Matthew Stafford, DET", "24. Marcus Mariota, TEN", "25. Jimmy Garoppolo, SF", "26. Andy Dalton, CIN", "27. Eli Manning, NYG", "28. Nick Foles, JAC", "29. Joe Flacco, DEN", "30. Ryan Fitzpatrick, MIA"
]
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objectsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath as IndexPath)
cell.textLabel?.text = objectsArray[indexPath.row]
cell.textLabel?.adjustsFontSizeToFitWidth = true
cell.textLabel?.font = UIFont.systemFont(ofSize: 22)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
top30QuarterbacksTable.deselectRow(at: indexPath, animated: true)
}
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let player: String = objectsArray[indexPath.row]
let add = UIContextualAction(style: .normal, title: "Add") { (contextualAction, view, actionPerformed: @escaping (Bool) -> Void) in
let alert = UIAlertController(title: "Add Player", message: "Are you sure you want to add '\(player)' to your roster?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { (alertAction) in
actionPerformed(false)
}))
alert.addAction(UIAlertAction(title: "Yes", style: .destructive, handler: { (alertAction) in
self.addPlayer(index: indexPath.row)
}))
self.present(alert, animated: true)
}
add.backgroundColor = .systemGreen
let taken = UIContextualAction(style: .normal, title: "Taken") { (contextualAction, view, actionPerformed: @escaping (Bool) -> Void) in
let alert = UIAlertController(title: "Player Taken", message: "Are you sure you want to mark '\(player)' as not available?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { (alertAction) in
actionPerformed(false)
}))
alert.addAction(UIAlertAction(title: "Yes", style: .destructive, handler: { (alertAction) in
}))
self.present(alert, animated: true)
}
taken.backgroundColor = .systemRed
return UISwipeActionsConfiguration(actions: [taken, add])
}
func addPlayer(index: Int) {
let index = top30QuarterbacksTable.indexPathForSelectedRow!
if let cell = top30QuarterbacksTable.cellForRow(at: index) {
let string = objectsArray[0]
cell.textLabel?.text = string
if myRoster!.isEmpty {
myRoster?.insert(string, at: 0)
} else {
myRoster?.append(string)
}
}
}
func markAsTaken() {
}
@IBOutlet weak var top30QuarterbacksTable: UITableView!
@IBOutlet var view1: UIView!
@IBOutlet weak var view2: UIView!
@IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
}
That helped the app not to crash but it is still finding nil. How do I tell the app that the swiped row is the selected row? Thank you
Thank you. Yes it worked.
Yes, it worked by + 1 to indexPath.row in cellForRowAt.So I don't have to run a for loop to add 1 to the row every time?I used numPick() as a function to hold the integer for the number of row.This is what my table view looks like:func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath as IndexPath)
cell.textLabel?.text = "\(numPick())" + (myRoster?[indexPath.row])!
cell.textLabel?.adjustsFontSizeToFitWidth = true
cell.textLabel?.font = UIFont.systemFont(ofSize: 22)
return cell
}
import UIKit
@available(iOS 11.0, *)
class Top_30_Quarterbacks: UIViewController, UITableViewDelegate, UITableViewDataSource {
var objectsArray: [String] = ["Patrick Mahomes, KC", "Deshaun Watson, HOU", "Aaron Rodgers, GB", "Matt Ryan, ATL", "Baker Mayfield, CLE", "Carson Wentz, PHI", "Jared Goff, LAR", "Cam Newton, CAR", "Andrew Luck, IND", "Drew Brees, NO", "Ben Roethlisberger, PIT", "Dak Prescott, DAL", "Russell Wilson, SEA", "Tom Brady, NE", "Lamar Jackson, BAL", "Mitchell Trubisky, CHI", "Jameis Winston, TB", "Philip Rivers, LAC", "Kirk Cousins, MIN", "Derek Carr, OAK", "Sam Darnold, NYJ", "Josh Allen, BUF", "Matthew Stafford, DET", "Marcus Mariota, TEN", "Jimmy Garoppolo, SF", "Andy Dalton, CIN", "Eli Manning, NYG", "Nick Foles, JAC", "Joe Flacco, DEN", "Ryan Fitzpatrick, MIA"
]
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objectsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath as IndexPath)
cell.textLabel?.text = "\(indexPath.row+1). " + objectsArray[indexPath.row]
cell.textLabel?.adjustsFontSizeToFitWidth = true
cell.textLabel?.font = UIFont.systemFont(ofSize: 22)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
top30QuarterbacksTable.deselectRow(at: indexPath, animated: true)
}
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let cell = self.top30QuarterbacksTable.cellForRow(at: indexPath)
let str: String = (cell?.textLabel!.text)!
let player: String = objectsArray[indexPath.row]
if cell?.textLabel?.attributedText == strikeThroughText(str) {
strikeThroughTextBool = true
} else {
strikeThroughTextBool = false
}
if strikeThroughTextBool == false {
let add = UIContextualAction(style: .normal, title: "Add") { (contextualAction, view, actionPerformed: @escaping (Bool) -> Void) in
let alert = UIAlertController(title: "Add Player", message: "Are you sure you want to add '\(player)' to your roster?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { (alertAction) in
actionPerformed(false)
}))
alert.addAction(UIAlertAction(title: "Yes", style: .destructive, handler: { (alertAction) in
self.addPlayer(index: indexPath)
cell?.textLabel?.attributedText = strikeThroughText(str)
let okayAlert = UIAlertController(title: "Player Added!", message: "You added '\(player)' to your roster?", preferredStyle: .alert)
okayAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (alertAction) in
actionPerformed(true)
}))
self.present(okayAlert, animated: true)
cell?.backgroundColor = .systemGray3
}))
self.present(alert, animated: true)
}
add.backgroundColor = .systemGreen
let taken = UIContextualAction(style: .normal, title: "Taken") { (contextualAction, view, actionPerformed: @escaping (Bool) -> Void) in
let alert = UIAlertController(title: "Player Taken", message: "Are you sure you want to mark '\(player)' as taken and not available?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { (alertAction) in
actionPerformed(false)
}))
alert.addAction(UIAlertAction(title: "Yes", style: .destructive, handler: { (alertAction) in
self.markAsTaken(index: indexPath)
cell?.textLabel?.attributedText = strikeThroughText(str)
let okayAlert = UIAlertController(title: "Taken Player", message: "You marked '\(player)' as taken and not available!", preferredStyle: .alert)
okayAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (alertAction) in
actionPerformed(true)
}))
self.present(okayAlert, animated: true)
cell?.backgroundColor = .systemGray2
}))
self.present(alert, animated: true)
}
taken.backgroundColor = .systemRed
let config = UISwipeActionsConfiguration(actions: [taken, add])
config.performsFirstActionWithFullSwipe = false
return config
} else {
let undo = UIContextualAction(style: .normal, title: "Undo") { (contextualAction, view, actionPerformed: @escaping (Bool) -> Void) in
let alert = UIAlertController(title: "Undo", message: "Are you sure you want undo the action for '\(player)'?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { (alertAction) in
actionPerformed(false)
}))
alert.addAction(UIAlertAction(title: "Yes", style: .destructive, handler: { (alertAction) in
self.removePlayer(index: indexPath)
cell?.textLabel?.attributedText = noStrikeThroughText(str)
let okayAlert = UIAlertController(title: "Action Undone", message: "The previous action for '\(player)' has been undone!", preferredStyle: .alert)
okayAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (alertAction) in
actionPerformed(true)
}))
self.present(okayAlert, animated: true)
cell?.backgroundColor = .none
}))
self.present(alert, animated: true)
}
undo.backgroundColor = .systemBlue
let config = UISwipeActionsConfiguration(actions: [undo])
config.performsFirstActionWithFullSwipe = false
return config
}
}
func addPlayer(index: IndexPath) {
let cell = top30QuarterbacksTable.cellForRow(at: index)
let str: String = objectsArray[index.row]
cell?.accessoryType = UITableViewCell.AccessoryType.checkmark
if myRoster == [""] {
myRoster?.insert(str, at: 0)
myRoster?.remove(at: 1)
} else {
myRoster?.append(str)
}
if draftOrder == [""] {
draftOrder?.insert(str, at: 0)
draftOrder?.remove(at: 1)
} else {
draftOrder?.append(str)
}
}
func removePlayer(index: IndexPath) {
let cell = top30QuarterbacksTable.cellForRow(at: index)
let str: [String] = [objectsArray[index.row]]
cell?.accessoryType = UITableViewCell.AccessoryType.none
myRoster?.removeAll(where: { str.contains($0) })
draftOrder?.removeAll(where: { str.contains($0) })
}
func markAsTaken(index: IndexPath) {
_ = top30QuarterbacksTable.cellForRow(at: index)
let str: String = objectsArray[index.row]
if draftOrder == [""] {
draftOrder?.insert(str, at: 0)
draftOrder?.remove(at: 1)
} else {
draftOrder?.append(str)
}
}
@IBOutlet weak var top30QuarterbacksTable: UITableView!
@IBOutlet var view1: UIView!
@IBOutlet weak var view2: UIView!
@IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
}
Also, When I run the app and swipe on a cell and tap the add or taken actions, everything that happens to that cell, happens to every 15th cell as I scroll down. I think this has something to do with reusable cell. How can I fix this?
objectsArray is set. Nothing is appended to it. When I add, it adds the contents to another array named myRoster. In this table view, strikeThroughText adds a line through the cell text at indexPath if the cell data is added to myRoster. It also adds a checkmark to the cell and changes the color of the cell. If the cell text has a line through it, that means that the data in the cell is already added to myRoster or added to draftOrder. In this case, if there is a line through the cell text, when I swipe there is an 'undo' button which deletes that player from myRoster or draftOrder, deletes the line going through the text, and deletes the checkmark. The app is working perfectly except that I cannot save the view if I change views and that when I add the cell data with a swipe and either 'add' button or 'taken' button, when I scroll down the table view, there is a line through the text, a checkmark, and the cell color is changed in every 15th table view row.
Hello,Thank you for your help.I am getting an error though at let player: Quarterback = objectsArray[indexPath.row]Cannot convert value of type 'String' to specified type 'Quarterback'
Hello, The reason why myRoster == [""] and not [] is because the app was crashing and finding nil. I had tomyRoster?.remove(at: 1) to remove the [""] after the string was inserted.For some reason now, I changed it to an emty array and removed line 3 and now it works.I don't use myRoster in this tableView because I want it accessed by other classes. For instance, I have other files that have classes such as:Top_80_RunningBacks and Top_80_WideReceivers.The information isn't lost when I return to the view. All that is lost is:- strikeThroughText- cell?accessoryType- cell?backgroundColorAnother thing, when I scroll down and then scroll back up to the changed cell, the changes are lost.
I have been trying to do what you proposed. I think I almost got it.1. I setup a global structstruct Data {
var num: Int = 0
var name: String = ""
var strikeThrough: Bool = false
var color: Bool = false
var accessory: Bool = false
}
var objectsArray = [Data]()2. I setup a global functionfunc quarterBacks() {
objectsArray = [
Data(num: 1, name: "Patrick Mahomes, KC", strikeThrough: false, color: false, accessory: false),
Data(num: 2, name: "Deshaun Watson, HOU", strikeThrough: false, color: false, accessory: false),
Data(num: 3, name: "Aaron Rodgers, GB", strikeThrough: false, color: false, accessory: false),
Data(num: 4, name: "Matt Ryan, ATL", strikeThrough: false, color: false, accessory: false),
Data(num: 5, name: "Baker Mayfield, CLE", strikeThrough: false, color: false, accessory: false),
Data(num: 6, name: "Carson Wentz, PHI", strikeThrough: false, color: false, accessory: false),
Data(num: 7, name: "Jared Goff, LAR", strikeThrough: false, color: false, accessory: false),
Data(num: 8, name: "Cam Newton, CAR", strikeThrough: false, color: false, accessory: false),
Data(num: 9, name: "Andrew Luck, IND", strikeThrough: false, color: false, accessory: false),
Data(num: 10, name: "Drew Brees, NO", strikeThrough: false, color: false, accessory: false),
Data(num: 11, name: "Ben Roethlisberger, PIT", strikeThrough: false, color: false, accessory: false),
Data(num: 12, name: "Dak Prescott, DAL", strikeThrough: false, color: false, accessory: false),
Data(num: 13, name: "Russell Wilson, SEA", strikeThrough: false, color: false, accessory: false),
Data(num: 14, name: "Tom Brady, NE", strikeThrough: false, color: false, accessory: false),
Data(num: 15, name: "Lamar Jackson, BAL", strikeThrough: false, color: false, accessory: false),
Data(num: 16, name: "Mitchell Trubisky, CHI", strikeThrough: false, color: false, accessory: false),
Data(num: 17, name: "Jameis Winston, TB", strikeThrough: false, color: false, accessory: false),
Data(num: 18, name: "Philip Rivers, LAC", strikeThrough: false, color: false, accessory: false),
Data(num: 19, name: "Kirk Cousins, MIN", strikeThrough: false, color: false, accessory: false),
Data(num: 20, name: "Derek Carr, OAK", strikeThrough: false, color: false, accessory: false),
Data(num: 21, name: "Sam Darnold, NYJ", strikeThrough: false, color: false, accessory: false),
Data(num: 22, name: "Josh Allen, BUF", strikeThrough: false, color: false, accessory: false),
Data(num: 23, name: "Matthew Stafford, DET", strikeThrough: false, color: false, accessory: false),
Data(num: 24, name: "Marcus Mariota, TEN", strikeThrough: false, color: false, accessory: false),
Data(num: 25, name: "Jimmy Garoppolo, SF", strikeThrough: false, color: false, accessory: false),
Data(num: 26, name: "Andy Dalton, CIN", strikeThrough: false, color: false, accessory: false),
Data(num: 27, name: "Eli Manning, NYG", strikeThrough: false, color: false, accessory: false),
Data(num: 28, name: "Nick Foles, JAC", strikeThrough: false, color: false, accessory: false),
Data(num: 29, name: "Joe Flacco, DEN", strikeThrough: false, color: false, accessory: false),
Data(num: 30, name: "Ryan Fitzpatrick, MIA", strikeThrough: false, color: false, accessory: false)
]
}3. I call quarterbacks() in viewDidLoad()override func viewDidLoad() {
super.viewDidLoad()
quarterBacks()
}4. I set the structs data in my three functions addPlayer(index: IndexPath), removePlayer(index: IndexPath), and markAsTaken(index: IndexPath)func addPlayer(index: IndexPath) {
_ = top30QuarterbacksTable.cellForRow(at: index)
let str: String = objectsArray[index.row].name
objectsArray[index.row].strikeThrough = true
objectsArray[index.row].accessory = true
objectsArray[index.row].color = true
if myRoster == [] {
myRoster?.insert(str, at: 0)
} else {
myRoster?.append(str)
}
if draftOrder == [] {
draftOrder?.insert(str, at: 0)
} else {
draftOrder?.append(str)
}
top30QuarterbacksTable.reloadData()
}
func removePlayer(index: IndexPath) {
_ = top30QuarterbacksTable.cellForRow(at: index)
let str: [String] = [objectsArray[index.row].name]
objectsArray[index.row].strikeThrough = false
objectsArray[index.row].color = false
objectsArray[index.row].accessory = false
myRoster?.removeAll(where: { str.contains($0) })
draftOrder?.removeAll(where: { str.contains($0) })
top30QuarterbacksTable.reloadData()
}
func markAsTaken(index: IndexPath) {
_ = top30QuarterbacksTable.cellForRow(at: index)
let str: String = objectsArray[index.row].name
objectsArray[index.row].strikeThrough = true
objectsArray[index.row].color = true
objectsArray[index.row].accessory = false
if draftOrder == [] {
draftOrder?.insert(str, at: 0)
} else {
draftOrder?.append(str)
}
top30QuarterbacksTable.reloadData()
}5. I setup cellForRowAt with if statementsfunc tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let num = objectsArray[indexPath.row].num
let name = objectsArray[indexPath.row].name
cell.textLabel?.text = "\(num). \(name)"
cell.textLabel?.adjustsFontSizeToFitWidth = true
cell.textLabel?.font = UIFont.systemFont(ofSize: 22)
if objectsArray[indexPath.row].strikeThrough == false && objectsArray[indexPath.row].accessory == false && objectsArray[indexPath.row].color == false {
cell.textLabel?.text = "\(num). \(name)"
cell.textLabel?.attributedText = noStrikeThroughText(name)
cell.accessoryType = UITableViewCell.AccessoryType.none
cell.backgroundColor = .none
}
else if objectsArray[indexPath.row].strikeThrough == true && objectsArray[indexPath.row].accessory == true && objectsArray[indexPath.row].color == true {
cell.textLabel?.text = "\(num). \(name)"
cell.textLabel?.attributedText = strikeThroughText(name)
cell.accessoryType = UITableViewCell.AccessoryType.checkmark
cell.backgroundColor = .systemGray3
} else {
cell.textLabel?.text = "\(num). \(name)"
cell.textLabel?.attributedText = strikeThroughText(name)
cell.accessoryType = UITableViewCell.AccessoryType.none
cell.backgroundColor = .systemGray2
}
return cell
}It works perfectly except that when I added the if statements in cellForRowAt it doesn't display the num.I want it to look like this:1. Patrick Mahomes, KC2. Deshaun Watson, HOU3. Aaron Rodgers, GB...but it looks like this:Patrick Mahomes, KCDeshaun Watson, HouAaron Rodgers, GB...All other functionalities work perfect. This is the only thing I cannot figure out why it is doing this. I remove the if statements from cellForRowAt and it displays the way I want it to but the other functionalities do not work right.I am sorry if it seemed like I didn't want to do what you proposed but I am trying.
func strikeThroughText (_ text:String) -> NSAttributedString {
let attributeString: NSMutableAttributedString = NSMutableAttributedString(string: text)
attributeString.addAttribute(NSAttributedString.Key.strikethroughStyle, value: 1, range: NSMakeRange(0, attributeString.length))
return attributeString
}
func noStrikeThroughText (_ text: String) -> NSAttributedString {
let attributeString: NSMutableAttributedString = NSMutableAttributedString(string: text)
attributeString.addAttribute(NSAttributedString.Key.strikethroughStyle, value: 0, range: NSMakeRange(0, attributeString.length))
return attributeString
}
var strikeThroughTextBool: Bool = false
Thank you.I appreciate both of your help. This suggestion makes sense to write less code.
Yes it worked. Thank you. Now, how do I get a smooth animation? When I reload the table in the functions, it just instantly loads. I want a smooth animation.