My UITableView has two sections, so I created an enum for them:
private enum TableSections {
HorizontalSection,
VerticalSection
}
How do I switch with the "section" var passed in numberOfRowsInSection delegate method? It seems that I need to cast "section" to my enum type? Or is there a better way to accomplish this?
The error is "Enum case "HorizontalSection" not found in type 'int'.
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case .HorizontalSection:
return firstArray.count
case .VerticalSection:
return secondArray.count
default
return 0
}
}
In order to do this, you need to give your enum a type (Int in this case):
private enum TableSection: Int {
horizontalSection,
verticalSection
}
This makes it so that 'horizontalSection' will be assigned the value 0 and 'verticalSection' will be assigned the value 1.
Now in your numberOfRowsInSection method you need to use .rawValue on the enum properties in order to access their integer values:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case TableSection.horizontalSection.rawValue:
return firstArray.count
case TableSection.verticalSection.rawValue:
return secondArray.count
default:
return 0
}
}
Jeff Lewis did it right, to elaborate on that and give the code litlle bit of more readiness -> my way of handling these things is to:
Instantiate enum with the raw value -> section index
guard let sectionType = TableSections(rawValue: section) else {
return 0
}
Use switch with section type
switch sectionType {
case .horizontalSection:
return firstArray.count
case .verticalSection:
return secondArray.count
}
Ok, I figured it out, thanks #tktsubota for pointing me in the right direction. I'm pretty new to Swift. I looked into .rawValue and made some changes:
private enum TableSections: Int {
case HorizontalSection = 0
case VerticalSection = 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case TableSections.HorizontalSection.rawValue:
return firstArray.count
case TableSections.VerticalSection.rawValue:
return secondArray.count
default
return 0
}
}
Related
I got following code but it gives error "Missing return in a function expected to return 'Int'"
so what is the right way to write this code to give different return value depends on different if condition?
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if category == "followings"{
return usersRealNameFollowing.count
}
if category == "followers"{
return usersRealNameFollower.count
}
}
Thanks!
When a function has a return type then there should be a must return statement, mean function should return something always. Now in your case you are using if statement for both return statements which means that if both statements are wrong then there will be no return value.
Use if-else here that would be best option or
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count: Int()
if category == "followings"{
count = usersRealNameFollowing.count
}else if category == "followers"{
count = usersRealNameFollower.count
}
return count
}
Add another return in the very end outside of any if statement.
The reason it's giving you the error is that it's possible that both if statements are false and therefore there is nothing to return!
Or maybe you could try switch
switch(catagory)
{
case "following":
return usersRealNameFollowing.count;
case "followers":
return usersRealNameFollower.count;
default:
return -1; //This will catch the situations that non of the conditions are met!
}
Good luck!!!
You have no return statement in he case where neither if statement is true.
If there are only two options then you can just use an else statement. It would probably be better if category was an enum rather than a string since then you can be sure you have an exhaustive test using a switch
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if category == "followings"{
return usersRealNameFollowing.count
} else {
return usersRealNameFollower.count
}
}
let’s setup the methods for rendering the table view row height
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if category == "followings"{
return usersRealNameFollowing.count
}else if category == "followers"{
return usersRealNameFollower.count
}else{
return 0
}
}
My UITableView has two sections, so I created an enum for them:
private enum TableSections {
HorizontalSection,
VerticalSection
}
How do I switch with the "section" var passed in numberOfRowsInSection delegate method? It seems that I need to cast "section" to my enum type? Or is there a better way to accomplish this?
The error is "Enum case "HorizontalSection" not found in type 'int'.
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case .HorizontalSection:
return firstArray.count
case .VerticalSection:
return secondArray.count
default
return 0
}
}
In order to do this, you need to give your enum a type (Int in this case):
private enum TableSection: Int {
horizontalSection,
verticalSection
}
This makes it so that 'horizontalSection' will be assigned the value 0 and 'verticalSection' will be assigned the value 1.
Now in your numberOfRowsInSection method you need to use .rawValue on the enum properties in order to access their integer values:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case TableSection.horizontalSection.rawValue:
return firstArray.count
case TableSection.verticalSection.rawValue:
return secondArray.count
default:
return 0
}
}
Jeff Lewis did it right, to elaborate on that and give the code litlle bit of more readiness -> my way of handling these things is to:
Instantiate enum with the raw value -> section index
guard let sectionType = TableSections(rawValue: section) else {
return 0
}
Use switch with section type
switch sectionType {
case .horizontalSection:
return firstArray.count
case .verticalSection:
return secondArray.count
}
Ok, I figured it out, thanks #tktsubota for pointing me in the right direction. I'm pretty new to Swift. I looked into .rawValue and made some changes:
private enum TableSections: Int {
case HorizontalSection = 0
case VerticalSection = 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case TableSections.HorizontalSection.rawValue:
return firstArray.count
case TableSections.VerticalSection.rawValue:
return secondArray.count
default
return 0
}
}
I use enum for building my UITableView cells:
enum VAXSections: Int
{
case Segmented = 0
case Scrollable = 1
case ScheduledMode = 2
case ScheduledFooter = 3
case SilentHours = 4
case SilentFooter = 5
}
here how I use it:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
guard let unwrappedSection = VAXSections(rawValue: indexPath.section) else {
showFatalError()
return nil
}
Few problem here I want to guard my section value if it's out of max case in my enum. For example if indexPath.section will be bigger then 5 then app should fall back. But if you see we can't return nil here, as cellForRowAtIndexPath has to return cell in any case.
I can solve problem by providing more readability with replacing showFatalError() fiction with:
guard let unwrappedSection = VAXSections(rawValue: indexPath.section) else {
fatalError("Check \(String(VAXUnitDashboardViewController.self)) UITableView datasource or check \(String(VAXSections.self)) enum.")
}
then I don't need to return any value. But then I turned in another problem. As I need to specify at least 3 datasource functions of my UITableView I need to duplicate fatal error which I wish to replace with one function that do the same all the time:
fatalError("Check \(String(VAXUnitDashboardViewController.self)) UITableView datasource or check \(String(VAXSections.self)) enum.")
enum VAXItems: String {
case Item1
case Item2
case Item3
}
enum VAXSections: String {
case Segmented
case Scrollable
case ScheduledMode
case ScheduledFooter
case SilentHours
case SilentFooter
}
struct VAXModel {
var type: VAXSections
var items: [VAXItems]
}
Then on your UIViewController you can have:
class ViewController: UIViewController {
private var model: [VAXModel] = []
override func viewDidLoad() {
super.viewDidLoad()
model = [
VAXModel(type: .ScheduledMode, items: [.Item1, .Item2, .Item3]),
VAXModel(type: .SilentFooter, items: [.Item1])
]
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return model.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return model[section].items.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(String(UITableViewCell), forIndexPath: indexPath)
let item = model[indexPath.section].items[indexPath.row]
switch item {
case .Item1: cell.textLabel?.text = item.rawValue
case .Item2: // Config
case .Item3: // Config
}
return cell
}
}
I don't think you need to have it in 3 places actually. Assuming that the 3 data source methods you are talking about are :
func numberOfSectionsInTableView(_ tableView: UITableView) -> Int
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
You actually need to have your guard in only one of them.
numberOfSectionsInTableView is the first method that will be called, so if you fail here, the other two methods won't be called. If the number of sections is based on some calculations, you can also cut off the value, something like this : if calculatedNumberOfSections > 6 { return 6 } else { return calculatedNumberOfSections } (remember that section numbering is 0 based)
numberOfRowsInSection - if you guard here you have two options - either fail with fatalError or (better in my opinion) - return 0 if a section number higher than 5 gets passed. Returning 0 will result in cellForRowAtIndexPath not being called with that section.
cellForRowAtIndexPath - you already got this :)
Back in the old Objective-C days I would often use enums for things like tables with constant contents, segmented controls, etc - in situations where there was an enforced incremented list of integers starting at zero. I would also often add a ...count member at the end to give a count of the entries, useful for table section & rows. Trying to do this with swift enums is proving troublesome - lots of conversions to & from raw values and extra default clauses in switches to allow for the 'count' entry. Can anyone suggest a graceful method of dealing with these sorts of situations?
Automatic increment is still available in Swift.
enum Section: Int {
case A = 0
case B
case C
}
Section.C.rawValue // -> 2
As for count, you should implement it manually (as How do I get the count of a Swift enum?):
enum Section: Int {
case A = 0
case B
case C
static let count = C.rawValue + 1
}
As for "conversions to & from raw values and extra default clauses" problem, compare with enum instead of its rawValue.
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return Section.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch Section(rawValue: section)! {
case .A: return 1
case .B: return 2
case .C: return 5
}
}
If you want to do something like array[Section.A], you can easily implement it.
extension Array {
subscript(section: Section) -> T {
return self[section.rawValue]
}
}
extension NSIndexPath {
convenience init(forRow row: Int, inSection section: Section) {
self.init(forRow: row, inSection: section.rawValue)
}
}
let array = ["foo", "bar", "baz"]
array[.B] // -> "bar"
let indexPath = NSIndexPath(forRow: 20, inSection: .C)
indexPath.section // -> 2
indexPath.row // -> 20
And so on.. :)
Add a function "count" to each enum. For example
static func count() -> Int { return 3 }
Integer -> enum conversion is done by the init method.
I am looking for a "good" way to solve some special requirements:
I have an UITableView with different sections, for example:
Base Data
About me
Interests
Images
Base Data contains always values (but there is still an variable row count) - and all other "Categories" could contain rows, or still could be empty. If there is no data, the category should be not shown.
No my first idea to solve that is:
Create all possible categories (but that could be 20 or more) - and do something like that:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count:Int = 0
switch (section) {
case 0:
count = baseHeaders.count
case 1:
if(mapItem.courses?.count > 0) {
count = mapItem.courses!.count
}
break;
default:
count = 0
}
return count
}
And ill check also with: titleForHeaderInSection if the count is null, and return then no header for the section.
BUT: is that a good way? My concern is about creating 20 sections and just 2 are used. Is there another, better way? Can i create sections manually? If yes, should i? So that only the base category is visible, and append everything else if there is data available.
This looks like my way of approaching such problems. I'm using enums (Obj-C & especially Swift) to handle and identify my Sections and I always return the full amount of potential sections:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return FormSection.count // enum function
}
In func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int, however, I switch the unused sections off by returning 0 rows.
The benefit I saw after struggling with your type of dynamic tables was that all sections are always at the same index which made cell management relatively easy:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let section:FormSection = FormSection(rawValue:indexPath.section)!
switch section {
case .Header:
//…
default:
//…
}
}
The same goes for section headers/footers:
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
switch section {
case FormSection.Header.rawValue:
return nil
case FormSection.RoomSetup.rawValue where foo == false:
return nil
default:
// return header with title = FormSection(rawValue: section)?.headerTitle()
// Swift enums ftw ;)
}
And the number of rows is calculated/fetched at runtime:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let section:FormSection = FormSection(rawValue:section)!
switch section {
case .Section1:
return fooExpanded ? (numberOfFoo) : 0
case .Section2:
return model.barCount()
default:
return 1
}
}