Custom UITableViewCell data repeating when scrolled - ios

I'm using custom tableview cells. I've 2 sections, first is a custom cell which contains a dynamic height textview and second section custom cell contains labels with firstrow having StartDate and secondrow having EndDate. When a row in second section(DateCell) is selected a date picker is shown which is in a cell of type DatePickerCell.
Both StartDate and EndDate rows use custom cell of type DateCell.
The problem is when tableview is scrolled the same data is appearing in both StartDate and EndDate.
I'm loading the cells from storyboard so the cells will not be nil.
I'm using different reuseidentifiers for StartDate and EndDate but still the problem exists.
Also, can anyone suggest what should be done in prepareForReuse method in this case.
Please find the code below.
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) ->Int{
if section == 1 {
if isDatePickerVisible == true {
return 3
}
return 2
}
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var identifier = ""
switch indexPath.section {
case 0 : identifier = kTextViewCellID
case 1 :
if datePickerIndexPath == indexPath && isDatePickerVisible == true {
identifier = kDatePickerCellID
}
else if indexPath.row == 0 {
identifier = kStartDateCellID
}
else {
identifier = kEndDateCellID
}
default: identifier = ""
}
let cell = tableView.dequeueReusableCellWithIdentifier(identifier, forIndexPath: indexPath)
if let c = cell as? TextCell {
//do something
}
//Both StartDate and EndDate are DateCell type
else if let c = cell as? DateCell {
c.dateLable.text = … //some text
c.dateValue?.text = … // some date in string format
}
else if let c = cell as? DatePickerCell {
//show DatePicker
}
return cell
}
class DateCell : UITableViewCell {
#IBOutlet weak var dateValue: UITextField?
#IBOutlet weak var dateLable: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
func setSomeColor() {
dateValue?.textColor = UIColor.redColor()
}
func clearDateValue (clear: Bool) {
//...
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}

You make changes on "c" variable, but return "cell" variable. You should return "c" variable or make changes on "cell" variable.

Related

TableView cells becomes inactive

I am using a tableView to take some surveys.
Header I use for a question. Footer for «back» and «next» buttons. And tableView cells for answer options.
Now I started to have a problem, with some user interaction: when you simultaneously click on the “next” button and select an answer, the answer options cease to be active, nothing can be selected. Although the buttons remain active.
Tell me in what direction to look for the problem and how you can debug this problem in order to understand what's wrong.
It all started after fixing bugs, when the application crashed when simultaneously (or almost) pressing the "next" button and choosing an answer. Because the didSelectRowAt method worked after I changed the current array of answer options, and the selected index in the previous question turned out to be larger than the size of the array with the answers to the new question.
class AssessmentVC: UIViewController {
#IBOutlet weak var tableView: UITableView!
var footer: FooterTableView?
var header: UIView?
var arrayAssessmnet = [AssessmentDM]()
var assessment: AssessmentDM!
var question: QuestionDM!
var viewSeperationHeader = UIView()
var arrayOptions: [Option]?
var countAssessment = 0
var numberAssessment = 0
var numberQuestion = 0
var countQuestion = 0
var numberQusttionForLabel = 1
var arrayQuestion = [QuestionDM]()
var arrayAnswers = [AnswerDM]()
var arrayEvents = [EventDM]()
override func viewDidLoad() {
super.viewDidLoad()
settingAssessment()
}
//MARK: - settingAssessment()
private func settingAssessment() {
let id = self.assessment.serverId
arrayQuestion = QuestionDM.getQuestions(id: id)
assessmentName.text = assessment.name
countQuestion = arrayQuestion.count
let day = self.assessment.day
arrayAnswers = AnswerDM.getAnswers(idAssessment: id, day: day)
settingQuestion(eventType: .start)
}
//MARK: - settingQuestion()
private func settingQuestion(eventType: EventType? = nil) {
let prevQuestion = question
question = arrayQuestion[numberQuestion]
timeQuestion = 0
footer!.grayNextButton()
//first question
if numberQuestion == 0 && numberAssessment == 0 {
footer!.previousButton.isHidden = true
} else {
footer!.previousButton.isHidden = false
}
arrayOptions = [Option]()
let sortOption = question.options!.sorted {$0.numberOption < $1.numberOption}
for option in sortOption {
arrayOptions?.append(Option(label: option.label, value: option.value))
}
tableView.rowHeight = UITableView.automaticDimension
tableView.reloadData()
heightTableView()
tableView.setContentOffset(.zero, animated: false)
}
//MARK: - heightTableView()
func heightTableView() {
}
//MARK: - UITableViewDataSource
extension AssessmentVC: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
viewSeperationHeader.isHidden = false
footer?.viewSeperationFooter.isHidden = false
tableView.separatorStyle = .singleLine
return question.options?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(forIndexPath: indexPath as IndexPath) as AnswerAssessmentCell
cell.initCell(text: arrayOptions![indexPath.row].label, value: arrayOptions![indexPath.row].value, arrayValue: arrayAnswers[numberQuestion].response, isCheckbox: true)
return cell
}
}
//MARK: - UITableViewDelegate
extension AssessmentVC: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
isChangAnswerInAssessment = true
if question.answerType == "Radio" || question.answerType == "Checkbox"{
selectRadioOrChekbox(indexPath: indexPath)
}
}
}
//MARK: - selectRadioOrChekbox
extension AssessmentVC {
private func selectRadioOrChekbox(indexPath: IndexPath) {
if question.answerType == "Radio" {
let cells = tableView.visibleCells as! Array<AnswerAssessmentCell>
for cell in cells {
cell.select = false
cell.isSelected = false
}
let cell = tableView.cellForRow(at: indexPath) as! AnswerAssessmentCell
cell.select = true
cell.isSelected = true
if arrayOptions?.count ?? 0 > indexPath.row {
arrayAnswers[numberQuestion].response = arrayOptions![indexPath.row].value
footer?.greenNextButton()
}
}
if question.answerType == "Checkbox" {
if arrayOptions?.count ?? 0 > indexPath.row {
//если нажато что-то, что должно сбросить "None"
// question.options![0].isSelect = false
let cells = tableView.visibleCells as! Array<AnswerAssessmentCell>
if cells[0].answerLabel.text == "None" {
cells[0].select = false
cells[0].isSelected = false
}
var array = arrayAnswers[numberQuestion].response?.components(separatedBy: ";")
array?.removeAll { $0 == "0"}
if array?.count == 0 {
arrayAnswers[numberQuestion].response = nil
} else {
arrayAnswers[numberQuestion].response = array?.joined(separator: ";")
}
let cell = tableView.cellForRow(at: indexPath) as! AnswerAssessmentCell
cell.select = !cell.select
cell.isSelected = cell.select
arrayAnswers[numberQuestion].response = array.joined(separator: ";")
if array.count == 0 {
arrayAnswers[numberQuestion].response = nil
footer?.grayNextButton()
} else {
footer?.greenNextButton()
}
}
}
}
}
//MARK: - Navigation between questions
extension AssessmentVC {
func nextQuestion() {
footer!.grayNextButton()
numberQuestion += 1
numberQusttionForLabel += 1
settingQuestion(eventType: .next)
} else {
}
func previousQuestion() {
numberQusttionForLabel -= 1
settingQuestion(eventType: .previous)
}
}
Some snippets that can help you :
// Answer type : use enum . Here the Strong/Codable is if you want to
// save using JSON encoding/decoding
enum AnswerType: String, Codable {
case checkBox = "CheckBox"
case radio = "Radio"
}
Setup of your cell :
class AnswerAssessmentCell: UITableViewCell {
...
// work with Option type
func initCell(option: Option, response: String?, answerType: AnswerType) {
// setup cell contents (labels)
// check for selected status
switch answerType {
case .checkBox:
// check if option is in response
// set isSelected according
break
case .radio:
// check if option is response
// set isSelected according
break
}
}
}
In table view data source :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! AnswerAssessmentCell
// Use the option to init the cell
// this will also set the selected state
let optionNumber = indexPath.row
cell.initCell(option: arrayOptions![optionNumber], response: arrayAnswers[numberQuestion].response, answerType: question.answerType)
return cell
}
In Table view delegate :
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
isChangAnswerInAssessment = true
let optionNumber = indexPath.row
switch question.answerType {
case .radio:
selectRadio(optionNumber: optionNumber)
case .checkBox:
selectCheckBox(optionNumber: optionNumber)
}
// Reload tableview to show changes
tableView.reloadData()
}
// Separate in 2 function for smaller functions
// in this function work only with model data, the reload data will do
// cell update
// only the footer view button cooler may need to be changed
private func selectRadio(optionNumber: Int) {
// Reset current response
// set response to optionNumber
// update footer button cooler if necessary
}
private func selectCheckBox(optionNumber: Int) {
// if option is in response
// remove option from response
// else
// add response to option
// update footer button cooler if necessary
}
Hope this can help you

Sending a signal to collection view within tableview cell from a segment outlet in another tableview cell

I have a segment outlet in a tableview cell in a VC. There are two indexes: 1 and 2.
When I click on 2, I want to tell the collection view within another tableviewcell to reload another view.
And when I click back to 1, I want the same collection view to reload again and display the original content.
Here are my View Controller Functions:
class MyProfileTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource,segment
{
//Variable selection to determine what is selected - 1 by default
var viewSelected = "1"
//Segment Function - viewSelected is used to tell VC what index it's on
func segmentSelected(tag: Int, type: String) {
if type == "1" {
print("1")
viewSelected = "1"
} else if type == "2" {
print("2")
viewSelected = "2"
}
}
//Cell For Row - tells tableviewcell to look at viewSelected
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = AboutTableView.dequeueReusableCell(withIdentifier: "ProfileSegmentTableViewCell", for: indexPath) as! ProfileSegmentTableViewCell
cell.segmentCell = self
return cell
} else {
let cell = AboutTableView.dequeueReusableCell(withIdentifier: "1_2Cell", for: indexPath) as! 1_2Cell
cell.viewSelected = viewSelected
return cell
}
Here is the Segment Control TableviewCell
//protocol used to delegate
protocol segment: UIViewController {
func segmentSelected(tag: Int, type: String)
}
class ProfileSegmentTableViewCell: UITableViewCell {
#IBOutlet weak var profileSegmentControl: UISegmentedControl!
var segmentCell: segment?
#IBAction func segmentPressed(_ sender: Any) {
profileSegmentControl.changeUnderlinePosition()
let Index = self.profileSegmentControl.selectedSegmentIndex
if Index == 0
{
segmentCell?.segmentSelected(tag: (sender as AnyObject).tag, type: "1")
)
} else {
segmentCell?.segmentSelected(tag: (sender as AnyObject).tag, type: "2")
}
}
CollectionView
//variable by default
var viewSelected = "1"
//viewDidLoad
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
cView.delegate = self
cView.dataSource = self
get {
self.cView.reloadData()
self.cView.layoutIfNeeded()
}
}
func get(_ completionHandler: #escaping () -> Void) {
getCount.removeAll()
if viewSelected = "1" {
print("1") } else {
print("2)
}
completionHandler()
}
Here's a very simple example of using a closure so your segmented-control cell can communicate with your table view controller.
Your cell class might look like this:
class ProfileSegmentTableViewCell: UITableViewCell {
#IBOutlet var profileSegmentControl: UISegmentedControl!
var callback: ((Int)->())?
#IBAction func segmentPressed(_ sender: Any) {
guard let segControl = sender as? UISegmentedControl else { return }
// tell the controller that the selected segment changed
callback?(segControl.selectedSegmentIndex)
}
}
When the user changes the selected segment, the cell uses the callback closure to inform the controller that a segment was selected.
Then, in your controller, you could have a var to track the currently selected segment index:
// track selected segment index
var currentIndex: Int = 0
and your cellForRowAt code would look like this:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
// first row - use cell with segemented control
let cell = tableView.dequeueReusableCell(withIdentifier: "ProfileSegmentTableViewCell", for: indexPath) as! ProfileSegmentTableViewCell
// set the segemented control's selected index
cell.profileSegmentControl.selectedSegmentIndex = self.currentIndex
// set the callback closure
cell.callback = { [weak self] idx in
guard let self = self else {
return
}
// update the segment index tracker
self.currentIndex = idx
// reload row containing collection view
self.tableView.reloadRows(at: [IndexPath(row: 1, section: 0)], with: .automatic)
}
return cell
} else if indexPath.row == 1 {
// second row - use cell with collection view
let cell = tableView.dequeueReusableCell(withIdentifier: "1_2Cell", for: indexPath) as! My_1_2Cell
// tell the cell which segment index is selected
cell.setData(currentIndex)
return cell
}
// all other rows - use simple Basic cell
let cell = tableView.dequeueReusableCell(withIdentifier: "PlainCell", for: indexPath) as! PlainCell
cell.textLabel?.text = "Row \(indexPath.row)"
return cell
}
Here is a complete example you can run and examine: https://github.com/DonMag/ClosureExample
You can use NotificationCenter.default.addObserver... method and NotificationCenter.default.post..... Read about them. And don't forget to remove observers in deinit

Increase/Decrease a value and display results in a Label inside a TableViewCell Swift Xcode

I have a ViewController with a TableView and a TableViewCell containing multiple sections and rows.
I have 2 button "plus" and "minus" and a label "totalLabel" in each row.
How can I get the value displayed in the label for each specific row when the user presses the + or - button?
for now when I run the app and press the + or - buttons only the totalLabel of the section 0/row 0 is working while random values just appear and disappear in the other sections/rows
my tableViewCell code :
import UIKit
protocol CommandeCellDelegate: class {
}
class CommandeCell: UITableViewCell {
weak var delegate : CommandeCellDelegate!
#IBOutlet weak var drinksLabel: UILabel!
#IBOutlet weak var priceLabel: UILabel!
#IBOutlet weak var totalLabel: UILabel!
#IBOutlet weak var plusButton: UIButton!
#IBOutlet weak var minusButton: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
here is my code for cellForRowAt :
class MenuViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, CommandeCellDelegate {
var count : Int = 0
var countValue : String!
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CommandeCell", for: indexPath) as! CommandeCell
cell.plusButton.tag = indexPath.section
cell.plusButton.tag = indexPath.row
cell.plusButton.addTarget(self, action: #selector(self.increaseValue), for: .touchUpInside)
cell.minusButton.tag = indexPath.section
cell.minusButton.tag = indexPath.row
cell.minusButton.addTarget(self, action: #selector(self.decreaseValue), for: .touchUpInside)
if indexPath.section == 0 {
let softInfo = softs[indexPath.row]
cell.drinksLabel?.text = softInfo.drinkName
cell.totalLabel?.text = // how to display countValue here?
let HappyHourStatus = partner!.barHHStatus
if case "0" = HappyHourStatus {
cell.priceLabel?.text = softInfo.drinkHHPrice
} else
if case "1" = HappyHourStatus {
cell.priceLabel?.text = softInfo.drinkPrice
}
}
else if indexPath.section == 1 {
let cocktailInfo = cocktails[indexPath.row]
cell.drinksLabel?.text = cocktailInfo.drinkName
cell.totalLabel?.text = // how to display countValue here?
let HappyHourStatus = partner!.barHHStatus
if case "0" = HappyHourStatus {
cell.priceLabel?.text = cocktailInfo.drinkHHPrice
} else
if case "1" = HappyHourStatus {
cell.priceLabel?.text = cocktailInfo.drinkPrice
}
}
return cell
}
and my funcs to increase or decrease the value :
func increaseValue(_ sender: UIButton) -> Int {
count = 1 + count
print(count)
countValue = "\(count)"
let rowToReload = IndexPath(row: sender.tag, section: sender.tag)
let rowsToReload: [Any] = [rowToReload]
tableView.reloadRows(at: rowsToReload as! [IndexPath], with: .automatic)
return count
}
func decreaseValue(_ sender: UIButton) -> Int {
if count == 0 {
print("Count zero")
} else {
count = count - 1
}
countValue = "\(count)"
let rowToReload = IndexPath(row: sender.tag, section: sender.tag)
let rowsToReload: [Any] = [rowToReload]
tableView.reloadRows(at: rowsToReload as! [IndexPath], with: .automatic)
return count
}
I have tried countless solutions but so far none is working - thank you for your help!
So your problem is this code
cell.plusButton.tag = indexPath.section
cell.plusButton.tag = indexPath.row
A tag can only store one value. So you are overriding the section with the row. So it is going to cause all sorts of weirdness. The better solution is to determine what cell you are targeting based on the button itself. Since you know what button was clicked you can convert the location of this button to a point on the table view. And then that point to a a particular index path.
So using your example code you can do something like below:
var softsCount: [Int] = []
var cocktailsCount: [Int] = []
override func viewDidLoad() {
super.viewDidLoad()
softsCount = Array(repeating: 0, count: softs.count) // Fill an array with 0
cocktailsCount = Array(repeating: 0, count: cocktails.count) // Fill an array with 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
if indexPath.section == 0 {
...
cell.totalLabel?.text = "\(softsCount[indexPath.row])"
...
} else if indexPath.section == 1 {
...
cell.totalLabel?.text = "\(cocktailsCount[indexPath.row])"
...
}
...
}
func increaseValue(_ sender: UIButton) {
let pointInTable = sender.convert(sender.bounds.origin, to: tableView)
if let indexPath = self.tableView.indexPathForRow(at: pointInTable), let cell = tableView.cellForRow(at: indexPath) {
if indexPath.section == 0 {
softsCount[indexPath.row] += 1
cell.totalLabel?.text = "\(softsCount[indexPath.row])"
} else if indexPath.section == 1 {
cocktailsCount[indexPath.row] += 1
cell.totalLabel?.text = "\(cocktailsCount[indexPath.row])"
}
}
}
No sure why you are returning count. I am sure this is just a partial implementation. But the button should take care of the entire action including updating the label with the new count. You don't normally return values from button presses.
So updated the example to update the label with the current count. Since I am unable to see what your drinks object I made an assumption that the drinks class has a count parameter that starts at 0. This way each individual drink has a count assigned to it.

Remove the UIlabel text from table view cells

i Have a table view and use custom cells for this. now i have set a clear button in my Bar. now on click of that UIBarButton i want to clear all the text inside the text field in the cells. How can i do this..??
var DataSource = [NewAssessmentModel]()
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.DataSource.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let model = self.DataSource[indexPath.row]
switch(model.assessmentControlType)
{
case .text:
let cell = (tableView.dequeueReusableCellWithIdentifier("QuestionWithTextField", forIndexPath: indexPath) as? QuestionWithTextField)!
cell.model = model
cell.indexPath = indexPath
cell.txtAnswer.delegate = self
cell.lblQuestion.text = model.labelText
cell.indexPath = indexPath
return cell
}
}
now cell contains a txtAnswer as a UITextField. How can i clear the text fields of txtAnswer.
for clearing the fields:
func clearView(sender:UIButton)
{
print("Clear Button clicked")
}
The above code applies only for the cells that are visible.The cell values won't be cleared if it is not visible in the phone.
For this you need to loop through every table view cells. I think this one is the one of the good option for you.
func clearView(sender:UIButton)
{
print("Clear Button clicked")
for view: UIView in tableView.subviews {
for subview: Any in view.subviews {
if (subview is UITableViewCell) {
let cell = subview as? UITableViewCell
// do something with your cell
if let questioncell = cell as? QuestionWithTextField
{
questioncell.txtField.text = ""
}
// you can access any cells
}
}
}
}
You can get all visible cell of tableView.
#IBAction func deleteText(_ sender: Any) {
for cell in tableView.visibleCells {
if let questionCell = cell as? QuestionWithTextField {
// Hide your label here.
// questionCell.lblQuestion.hidden = true
}
}
}

How do use different cell styles in a UITableView

I'm creating a view for accessing settings in an app which will be presented in a UITableView. Some cells will have a UITableViewCellAccessoryDisclosureIndicator, I need one with a UISwitch, and another with a UISegmentedControl sort of like this:
In other words, I think I need custom cells. Through a lot of research, I've gotten pretty close but I can't wire up everything I've learned to work together. Here's what I've done:
Created three UITableViewCell classes:
class DisclosureCell: UITableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
class func defaultIdentifier() -> String {
return NSStringFromClass(self)
}
}
class SegmentedControlCell: UITableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
class func defaultIdentifier() -> String {
return NSStringFromClass(self)
}
}
class SwitchCell: UITableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
class func defaultIdentifier() -> String {
return NSStringFromClass(self)
}
}
I have my Sections class and Section struct that will provide the table view sections and each choice available:
class SettingsData {
func getSectionsFromData() -> [Section] {
var settings = [Section]()
let preferences = Section(title: "OPTIONS", objects: ["Formulas", "Lifts", "Units", "Allow 15 reps"])
let patronFeatures = Section(title: "PATRON FEATURES", objects: ["Support OneRepMax"])
let feedback = Section(title: "FEEDBACK", objects: ["Send Feedback", "Please Rate OneRepMax"])
settings.append(preferences)
settings.append(patronFeatures)
settings.append(feedback)
return settings
}
}
struct Section {
var sectionHeading: String
var items: [String]
init(title: String, objects: [String]) {
sectionHeading = title
items = objects
}
}
Finally, my UITableViewController:
class SettingsViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.topItem?.title = "Settings"
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Done", style: .Plain, target: self, action: #selector(self.dismissSettings(_:)))
}
func dismissSettings(sender: AnyObject?) {
self.dismissViewControllerAnimated(true, completion: nil)
}
// MARK: - Table view data source
var sections: [Section] = SettingsData().getSectionsFromData()
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sections.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].items.count
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section].sectionHeading
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("DisclosureCell", forIndexPath: indexPath)
cell.textLabel?.text = sections[indexPath.section].items[indexPath.row]
return cell
}
I think what I need help with is:
As I create each cell, how do I determine which of my three types each cell is so I can display them based on type as well as dequeue each type properly
I created the func defaultIdentifier() -> String in each custom class AND specified the reuse identifiers in the Storyboard for each cell type. Do I need to do both?
do I need to register these cells or is that unnecessary because I'm using a storyboard?
I've found a lot of threads about this subject but either they're in Objective-C and/or they don't speak to the particular parts I seem to be stuck on.
Thanks!
UPDATE:
I've updated my override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell as follows:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = tableView.cellForRowAtIndexPath(indexPath)?.reuseIdentifier
if cellIdentifier == "DisclosureCell" {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "DisclosureCell")
cell.textLabel?.text = sections[indexPath.section].items[indexPath.row]
return cell
} else if cellIdentifier == "SegmentedControlCell" {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "SegmentedControlCell")
cell.textLabel?.text = sections[indexPath.section].items[indexPath.row]
return cell
}
else {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "SwitchCell")
cell.textLabel?.text = sections[indexPath.section].items[indexPath.row]
return cell
}
}
I hope this is a step in the right direction but I'm still missing something because I get this error:
2016-07-28 07:15:35.894 One Rep Max[982:88043] Terminating app due to uncaught exception 'NSRangeException', reason: ' -[__NSArrayI objectAtIndex:]: index 2 beyond bounds [0 .. 0]’
And I need to figure out a way for each cell to know which of the three types it SHOULD be, then based on which type it IS, I can set it up in the code above.
UPDATE 2
I've now got it kind of working by using the indexPath.row:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CustomTableViewCell", forIndexPath: indexPath) as! CustomTableViewCell
if indexPath.row == 0 {
cell.selectionStyle = .None
cell.textLabel!.text = sections[indexPath.section].items[indexPath.row]
cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
return cell
} else if indexPath.row == 1 {
cell.selectionStyle = .None
cell.textLabel!.text = sections[indexPath.section].items[indexPath.row]
//cell.accessoryView = useAuthenticationSwitch
let switchView: UISwitch = UISwitch()
cell.accessoryView = switchView
switchView.setOn(false, animated: false)
return cell
} else {
cell.selectionStyle = .None
cell.textLabel!.text = sections[indexPath.section].items[indexPath.row]
let segmentedControl: UISegmentedControl = UISegmentedControl(items: ["lbs", "kg"])
segmentedControl.selectedSegmentIndex = 0
segmentedControl.addTarget(self, action: nil, forControlEvents: .ValueChanged)
cell.accessoryView = segmentedControl
return cell
}
}
The problem is that by using the indexPath.row, not all of the rows have the correct accessoryView. In the example above, I want the first two rows to have disclosureIndicators, the third one to have the UISegmentedControl, and the fourth to have a UISwitch. Now, I completely understand why my code produces this - it's because the accessoryView is being determined by the cell's position. Instead, I want to be able to tell each cell as it's being created, "You're the cell for this setting, so you get a [fill in the blank] accessoryView". I have custom cell classes created for each type and tried to do what I'm trying to do with those but couldn't figure it out. You may notice that in the code above I went to one custom cell class and reuse identifier.
I suppose I could do things like if indexPath.row == 0 | 1 { blah, blah } but that approach won't hold up through each section. If I try to deal with each section like this, then the code will get messier than it already is. There must be a smarter way.
Is there?
I'd suggest you to use static cells (not prototype) for such screens like settings.
You can change type of cells in storyboard.
After you can assign IBOutlets to all controls and make them interact with your model.

Resources