Swift grouped UITableView best practices - ios

I'm setting up the settings page of my first Swift app and can't seem to find a clean way to set up the master-detail relationship between the settings view and a child table view of options.
My settings page is a UITableViewController with a custom layout of grouped cells. I also have a detail UITableViewController that I want to use to present options for various settings. My initial thinking led me to think that I can use a single view controller for this and just change the cells based on the segue. My largest problem is loading all the data into the cells since the grouped tables don't have prototype cells that I can call in
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath) {}
Is there a commonly accepted way to set up these sort of settings table, that have a defined number of rows? I could make a viewcontroller for each setting, but that feels wasteful.

Even though it feels wasteful, it's more intuitive and less convoluted to have (segues to) different view controllers.
Your detail view controller has to
display options
return the selected option
save state
So, there are three points where you'd end up with large blocks of conditional code to determine which group of options you're dealing with, while displaying, unwinding, or preserving/restoring details.
If you added more options, a monolithic approach would only continue to grow more and more complex.
A monolithic approach is also the least flexible, if you ever needed to support a second (non-check mark) type of selection in the future.
Separating responsibilities into different (subclassed) detail view controllers means each controller can clearly and simply handle its own details. Your code will be easier to understand, maintain, and update down the road.

Related

Why UITableViewCell is not accessible (for VoiceOver)

I'm not trying to solve any problem. Of course you can set isAccessibilityEnabled = true and it works. My question is: why is it turned off by default and there is no appropriate section in the interface builder. It seems for me like it's not recommended to make UITableViewCell subclasses accessible. Is there any better ways to make cells accessible? I mean to make a cell as one accessibility element which will contain all the info for VoiceOver.
why is it turned off by default
A UITableViewCell may be seen as a container inside which many elements are embedded (buttons...) and, as is, you can't have simultaneously a parent view (the table view cell) and its child views (label, button...) that are both accessible with VoiceOver: either your cell can be selected or its content.
By default, the content must be seen by VoiceOver: add two buttons in your cell and you'll see the difference by enabling/disabling the accessibility of the cell.
Is there any better ways to make cells accessible? I mean to make a cell as one accessibility element which will contain all the info for VoiceOver.
To reach your goal, the best way is to make your cell accessible while providing an accessibilityLabel and adding custom actions if many actions are planned in this cell (with buttons for instance).
Following this rationale improves the user experience: one unique selection with possible actions.
If you don't want the elements inside each cell to be read out, just define each one of your cells as follows:
override func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier",
for: indexPath)
cell.isAccessibilityElement = true
cell.accessibilityLabel = "APPEND YOUR LABELS HERE"
// Add everything you need to construct your cell here.
return cell
}
That's the simplest configuration but you may decide to reach every elements in the cell and, in this case, it's quite different: see this answer or this one if you need some implementation examples for this.
Personnally, I always subclass the table view cell to define its trait, its array of accessiblity elements and its potential actions so as to control the way information are provided to the VoiceOver user: I find it very flexible even if it seems to be tedious at first sight. ;o)

Paging through different diagrams in a table view cell

I'm looking for a method to page or slide through up to 6 different views in one table view cell. What's the best approach to do that?
I already experiment with a Collection View and a Page View Control. But the diagrams are represented by different classes, so that the standard tutorials setting up a label, are not working for me.
Thank for any feedback on this topic.
Based on what you are doing, I would use a UICollectionView with different UICollectionViewCells for your different diagrams. That should allow you to use different classes for the content of each cell.
So first go and create a UICollectionViewCell for each diagram.
Then you are going to want to go into your UICollectionViewController (or wherever you are implementing your collection view) and in the cellForItemAtIndexPath method tell it which cell to display for each indexPath.

Create nested table view or separate page?

I'm trying to create an options page for my iOS app. I have an array of categories like this:
var options = [
"Location",
"Calculation Method",
"Juristic Method",
"Manual Adjustment",
"Daylight Saving Time"
]
Then I am loading them up in my view controller like this:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return options.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .Default, reuseIdentifier: "Cell")
cell.textLabel?.text = options[indexPath.row]
return cell
}
This give me a single table view perfectly! Now I'm trying to handle the click of each row to bring up a list of options for that category. For example, the Location will have a switch to enable GPS or allow them to select their location drop downs or a map. Calculation Method will bring up a table view of check marks. Daylight Saving Time will bring up a single row with a switch.
My question is what is the best approach for all this? Should I create a dictionary of arrays to hold my options and reuse the table, or should I create a separate view for each category of options? I'm finding conflicting or outdated tutorials on this and I'm also having trouble converting examples from Objective-C. Any help or direction would be greatly appreciated!
I'd suggest creating separate views for each category of options. From your example above, you'd end up with 5 more view controllers: Location, Calculation Methods, Juristic Method, Manual Adjustment, and Daylight Saving Time. And then for your Calculation Methods, since it's a table view of check marks, I'd probably have an array that stores each option inside the CalculationMethodsViewController. Same for the others. If they need other data to display, put it in the new view controllers - not in your original view controller.
For your top level of categories, I'd suggest using the sections of a tableview to separate things. Then each row in a section would correspond to the detailed option.
To do this, you could have an array of 'categories' that are to keep the order of sections. This would match the options array you've defined in your question. Alongside this, I would put a Dictionary of sub-category options. For Locations it would look like this:
var options = [
"Location" : ["location-enable-gps", "location-choose-list", "location-choose-map"],
"Calculation Method": ...,
"..."
]
In the above Dictionary, the array values for Location are constants that I've defined. This is because you will be changing the behavior of each cell drastically. Enable GPS can just be a checkbox, Choosing from a list may be better to drilldown/modal (and when selected display selected location), choosing from map may also lend itself to a modal design ("Modal" is when a view controller pops up and then dismisses after an action).
Let me know if this all makes sense, it looks like your options will be very diverse, so unfortunately I don't think there is one method (drill down, collection view) that works best. That being said, its all up to you on how you want your users to experience your app.

When to use UICollectionView instead of UITableView?

I found that UICollectionView is like an upgraded version of UITableView introduced in iOS6, but when should I choose UICollectionView instead of UITableView?
There are still Apps using UITableView, if UICollectionView can do anything UITableView can do , why people still use UITableView? Is there a difference as far as performance is concerned?
Thanks!
That depends on the requirements. How the application flows determines which type of UI to integrate into the application.
People mainly use the UICollectionview for creating types of UIs with multiple images shown in a grid. This would have complex logic using UITableView, but with UICollectionview, it would be easy.
When using UICollectionview, you don't need to set buttons with tags or other things by getting selected items values. You can simply get -(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath and in UITableViewDelegate:
`-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath`
You get the selected row instead of the item, so for creating grid or modified items, using UICollectionview is best.
For the listing details of each item, people use UITableView because it shows more info on each item.
Apple Docs:
UICollectionView Class Reference
The UICollectionView class manages an ordered collection of data items and presents them using customizable layouts. Collection views provide the same general function as table views except that a collection view is able to support more than just single-column layouts. Collection views support customizable layouts that can be used to implement multi-column grids, tiled layouts, circular layouts, and many more. You can even change the layout of a collection view dynamically if you want.
UITableView Class Reference
A table view displays a list of items in a single column. UITableView is a subclass of UIScrollView, which allows users to scroll through the table, although UITableView allows vertical scrolling only. The cells comprising the individual items of the table are UITableViewCell objects; UITableView uses these objects to draw the visible rows of the table. Cells have content—titles and images—and can have, near the right edge, accessory views. Standard accessory views are disclosure indicators or detail disclosure buttons; the former leads to the next level in a data hierarchy and the latter leads to a detailed view of a selected item. Accessory views can also be framework controls, such as switches and sliders, or can be custom views. Table views can enter an editing mode where users can insert, delete, and reorder rows of the table.
Here's my criteria:
If a UITableView can do it, use it
If a UITableView needs lots of code to do it or can't do it at all, use UICollectionView.
You have to consider the restrictions on UITableView before making a decision: It's a single column. And you can only customize the cells, but not section backgrounds and such. So if you have a straight-up list of things with no extra frills - that looks like a bog standard iOS view, basically - then use UITableview. If you have custom insets, or a border around each section, use UICollectionView.
I'm actually considering UICollectionView for all things simply because it's very expensive when you start developing your view as a table view, then later find out it can't do that one thing that you need it to do. 1st hand experience ;)
Edit after even more experience with the two: Disregard that last paragraph. UICollectionView requires a lot of boilerplate code to make it work like a UITableView. Use UICollectionView only when really needed. ;)
For simple lists and forwards/backwards navigtaion, use UITableView.
If you need a high degree of customisability, use UICollectionView.
Generally speaking, in software development, it's best to choose the approach which represents "The Simplest Possible Thing".
EDIT: As of iOS 14, UICollectionView can now do lists as well and is now the recommended approach. See this session from WWDC20 for more information and implementation details: https://developer.apple.com/videos/play/wwdc2020/10026/
According to my point of view main difference between collectionView and tableView is that
TABLEVIEW --> show list of items in only one column.
COLLECTION-VIEW -->show list of items in multiple column.
Hope it will help you.
If you choose UITableView for iPhone, make sure you have considered your iPad strategy first. If you want an iPad-specific layout, you may want that single-column layout to become a grid.
Although it's not required, I always use a collectionview. That way I can easily adapt how my collections are presented for differing resolutions. A plus is that it's ready to quickly add new types of cells when refactoring in the future.
I see no point of tableviews. It's very simple to use a collection view to represent a table. IMO.
From my personal experience the two elements should only be compared loosly.
TableView
A TableView is a UI element designed for showing data in a list format. There is certain functionality that comes as standard with a UITableView, such as:
Accessory View
Cell Selection Style
Editting Style (Delete and edit buttons).
The above elements enhance the usability of data when displaying and interacting in a list format. Such as viewing emails.
CollectionView
A CollectionView is a UI element designed for showing content using a custom layout (usually anything that isn't a list). CollectionViews improve functionality of displaying data in completely bespoke layout styles and also dynamically changing layouts on the fly. Some examples are:
Horizonal Lists
Photo Galleries
Thumbnail views
Carousels
Dials
Laying out elements on a map
etc.
CollectionViews also allow for multiple selections.
Conclusion
As you can see from the above, both have completely different use cases and are designed for enhancing the development and usability of their own specific data sets.
If you are looking at displaying anything in a list style with the followin interactions:
- Adding
- Deleting
- Re-ordering
Then a UITableView will simplify this process by providing the support straight out of the box.
Anything else, you should leverage the benefits of CollectionView as you have more flexibility.
Its totally dependent on how your data to be shown.
As mentioned by many above, if you require only single set of data and that too not complex, go for UITableView else use UICollectionView.
UICollectionView is customization friendly.
If you are dealing with multiple cell heights or so, then go for UICollectionView.
Both are depends on the requirements. Table Views also have support for a variety of editing scenarios. This support has not been implemented in the Collection View classes.
If you are converting from a Table View that relies on these methods, expect to do a little extra heavy lifting in the Collection View.
Collection View section headers can be placed anywhere within the view.
and UITableView don't need to set buttons with tags or other things by getting selected items values.
In practice, everyone uses UICollectionView that I've come across, when they only need a UITableView. "It's one-dimensional. It goes up and down. Why are you adding unnecessary delegate methods for layout AND data?". I once spent an extra 2 hours helping a startup find out why their UICollectionViewCell got squished because the owner, who didn't read the Animations manual, nor HIG, nor the UICollectionView guide, decided to use it and add variable heights and anims. Needless to say, he gave himself a headache and much lost time on a non-business-critical issue he could have avoided by simply using a table cell, since there's no extra layout delegate + Nib.
Let me get this straight, I am all for UICollectionView's when your data and display need it. They're very powerful. But in practice, most people I've seen have been using them on lists.
This brings up another flaw. They're also used on short, constant lists that won't change, ever. In this case, just make a Xib. Or write a custom view that stacks them. Why? Because you don't need the memory management for 5 sets of labels with a button or switch. If they might change, then yes, use a list. If you want physics, then UICollectionView works well with a some cool effects. But do you really need to add 5 delegate methods and a layout system for 5 labels that will never move?
Also, I'm not forgetting that iOS has a native stacking view now too. I can never get it to deform how I want, even though I'm quite adept at the 2D and animation systems, so I never use the built-in one.
All I'm saying is, define your requirements. Maybe you don't need either of these, if your UI isn't adding/removing items and refreshing itself. Or maybe you want to write a Card Game and throw them out virtually on a table, then use UICollectionView with a physics system for its layout guide.
Personally I think the UICollectionView can do most of the work which UITableview can do. well, at the same time, it's more complex to use.
I suggest you use UICollectionView as TableView just in case your manager change requirements in the future.
Based on our need we are choosing TableView or CollectionView.
Example:
For phone contacts tableView is best option.
For photo gallery, collection view will be best option.
I had this issue in my current project. Which to use. In my case it was simple really. I needed both. I needed my view to look like UITableView and also to change its change / layout. So, UICollectionView was used. I also use UITableView everywhere I don't need any extra customisation. Since UiTableView comes with a default layout that includes images and text - I use it for simplicity.
Based on our requirement we choose UITableView or UICollection view.
If we want to display images or items in grid type or if we need more customisability we use UICollectionview.
For listing each item with details and subdetails we use UITableView.
UICollectionView:
The UICollectionView class manages an ordered collection of data items and presents them using customizable layouts. Collection views provide the same general function as table views except that a collection view is able to support more than just single-column layouts.
UITableView: A table view displays a list of items in a single column. UITableView is a subclass of UIScrollView, which allows users to scroll through the table, although UITableView allows vertical scrolling only.
As per my view for Grid View display use UI Collection View.All other list view use UITable View

Force UITableView to cache reusable cells

I use a table view with relatively 'heavy' custom table view cells - say there's is around 100 subviews in a single cell (full hierarchy). Despite of that I've been able to reach very smooth scrolling - using standart practices like working only with opaque views, hiding unused subviews (or even removing from the view hierarchy), pre-loading cells height etc.
The problem is that it's still relatively costly to create a single cell instance. Because of how the standard UITableView's cell reusage logic works this problem is only noticeable in specific conditions.
Usually when table view is loaded (and intitial content is displayed) it creates almost all required cells instances - so when you start scrolling it rarely needs to create any more reusable cells. But if for some reason it only displays a few cells at the beginning it would obviously need to create more when scrolling is initiated. In my case this is noticeable if there's a wide table view header or if first cells are big enough. When that happens once you start scrolling there's a noticeable lag for a few moments which is clearly caused by new cell instances being created. Nothing really ugly but still noticeable especially compared to absolutely smooth scrolling after that.
Now the obvious solution for that would be to make cells 'lighter' - like break them into different more specific types so each can contain less subviews. But because of the way my content is organized that would be a very complex solution.
So my question is - is there any good way to 'trick' table view logic to force loading and caching of specific number of reusable cell instances right away - despite of the number of cells actually visible?
One of the options I've been thinking of is to have own explicit cache of cells and pre-populate it with cells when needed (say in -viewDidLoad). The problem I have with this approach is that you have to known exact type of cells you will need beforehand - doesn't work well for me. The improvement would be to build cache when the initial data is loaded (so at least the exact type of content is known) but I've been wondering if there are any simple options.
You can do it by adding your own "secondary cache" layer to your data source.
When you initialize your data source, pre-create as many cells as needed, and put them into an array. When your dequeueReusableCellWithIdentifier fails, go for that array first, and take a cell from there. Once the array is exhausted, start creating new cells (if you create enough cells upfront, you shouldn't need to create new ones on the fly: dequeuing will eventually stop failing when you have enough cells rotating in and out of visibility).
I too had a similar situation wherein I wanted to load the several websites in different cells and loadrequest must be triggered only once. Idea is to avoid duplicate calls when the cell is made visible/invisible by scrolling up or down. say I have 5 cells in my tableview and each cell has a webview which loads the different websites. loadrequest will be called once and cell will be cached. here goes the simplest implementation.
Variable declaration
let weblinks:[String] = ["http://www.google.com",
"http://www.facebook.com",
"http://www.yahoo.com",
"http://www.puthiyathalaimurai.com/",
"http://www.github.com"]
var mycells:[MyTableViewCell] = [MyTableViewCell(),
MyTableViewCell(),
MyTableViewCell(),
MyTableViewCell(),
MyTableViewCell()]
CellForRow. I hope you know what it does,
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = String(describing: MyTableViewCell.self)
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as? MyTableViewCell
if cell?.isNewCell == true { //return from cache
return mycells[indexPath.row]
}
cell?.title.text = weblinks[indexPath.row].uppercased()
cell?.web.loadRequest(URLRequest(url: URL(string: weblinks[indexPath.row])!))
cell?.isNewCell = true //mark it for cache
mycells[indexPath.row] = cell!
return cell!
}

Resources