What is best practice when it comes to reusable UITableViewCell's and delegates? - ios

I'm building an app that presents table views in a majority of the screens. They present a handful of different table view cells, but there is one that is presented in 3/5 of the table views. This cell, which displays a video and provides an interface for users to interact with the video (like, comment, flag, delete, play/pause, etc), has a fairly large delegate with seven methods/functions.
My question is:
Would it be a best practice to set up a separate controller that would be a property of my view controller to be assigned as the delegate to the cell, or to subclass a UITableViewController with the methods already implemented?
The problems I see with the latter is that I would then have to implement a weird way to handle the data source (set up methods to return the models, always ensure that videos are stored in that array) and the former just seems a little odd to the standard MVC practice. Any advice?
Update
I began factoring out a data source to use that implements the cell's protocol. Another issue that I seem to be running into is displaying multiple cells, i.e.:
I have a searchDisplayController that displays UserCell's and VideoCell's, based on the selectedScopeIndex of the search bar. One way I could handle this is to create a dataSource for this tableView that handles both cases, or swap out data sources based on changes to the selectedScopeIndex. Are either of the two options looked down upon? Is swapping a table view's data source valid?

I solved this issue by implementing a UITableViewDataSource controller that would also handle the cell's delegates. I was able to shorten the 7 method delegate to a 3 method delegate on the data source, used to push new controllers, remove objects from the data model, and handle fading/updates.
Granted, I needed to pass reference to the UITableView, the UIView, and the UIStoryboard of the source UIViewController, but the code is much more readable and manageable.

Related

6 separate UIViewControllers or just one with different data sources decided at segue?

I'm building an iOS app in Swift which has a start page with 6 buttons. Each of those buttons will segue to a table view controller whose data is managed with an NSFetchedResultsController (application uses core data). Now, I can see it being easy to create 6 UITableViewControllers however I'm wondering if it would be more sensible to send each button to the same UITableViewController and just change the data loaded/managed by setting some kind of flag in the prepareForSegue method?
What's the best practice here? It seems crazy to have 6 tableViewControllers each backed by it's own NSFetchedResultsController when big portions of the required code for each are reusable and could be subclassed out and used multiple times.
If you can code a single view controller in a way that supports all six with a single code base, reusing a single UITableViewController makes perfect sense. Other approaches include
Composition - make a class for the data source, configure it before opening the table view, and pass it to the table view on segue
Subclassing - make an abstract base view controller, and extend it six times. Override an abstract method or two to reduce the amount of repetition
Helper - make a class that holds all reusable logic shared across the six view controllers, and call its methods from six very slip controllers.
One thing you want to avoid is code like this:
if (viewControllerType == ViewControllerOne) {
doSomethingSpecial()
} else if (viewControllerType == ViewControllerTwo) {
doSomethingElseSpecial()
} ... // and so on
When you have a chain like this, you know that you've missed an opportunity to subcllass.
I don't know if this question will be closed as opinion-based, but here's my thoughts:
Definitely use only one view controller and changed the data source in prepareforsegue()
Imagine having 6 different controllers and then deciding in 6 months that you want to change the entire UI!!! I did that. Wasn't pretty. And that was the point when I reconfigured everything and scaled down my total number of view controllers.
And don't even get me started on the pains of having to do auto layout on multiple screens. You see where I'm going with this.

How to recreate UITableViewController with static cells support?

I have a lot of common logic, therefore all my viewControllers inherit from BaseViewController, BaseDetailsViewController or BaseWebViewController. The last two inherit from BaseViewController which inherits from UIViewController. The problem is I cannot use tableViews with static cells because my BaseDetailsViewController is not inheriting from UITableViewController.
I'v never used and inherited UITableViewController because the same could be achieved by inheriting from UIViewController, wiring it with an added UITableView and having implemented datasource and delegate methods. Therefore for the form type screens (SignUp or other data input screens), I use my own BaseDetailsViewController which adds nice and handy methods (input validation, custom styling, scrolling keyboard, handles input field navigation and etc.)
Using UITableViewController has two real benefits: 1) keyboard scrolling (if you use text fields) and 2) easy creation of your form UI elements inside on screen. The first benefit is irrelevant for me, because I'v already have my own implementation of this stuff inside BaseDetailsViewController. Regarding the second benefit, I'm creating my form inside scrollView which gives a lot of flexibility but it's a little bit more effort (especially when need to update the screen). Therefore I heard some guys implemented their own UITableViewController and then made it to inherit from their BaseViewController. Therefore I started to dig into this approach.
So far I'v came upon these two approaches:
Recreate your own tableVC. Not clear how to make it work for static cells. Currently my demo app crashes if my ReplaceTableViewController is not implementing numberOfRows and cellForRow, and not shows static cells if I implement them with dummy content.
method-swizzling. Not clear how to change class inheritance by injecting baseViewController inheritance for UITableViewController, thought still not clear which methods are needed to be added.
Anybody tried and want to share?
UPDATE I'm not using storyboards as I promote clean MVC - every screen as component should have its own Model, View (Xib), and controller, all stored in separate files to eliminate merge conflicts of multiple devs in large app projects (30+ screens). And therefore container view with embedded segue to tableViewController is also not an option.
UPDATE2 In case anybody would like to take a look why and how I'm doing this, here's a link to my open source template based new project generator framework which I started to publish recently.
Static cells need a UITableViewController. You can't changed this right now.
But to use static cells in a UITableView outside of a UITableViewController you can use a ContainerView in your Non-Table-UIViewController, where you place a real, separate created UITableViewController working with those static cells.
To share common code between multiple view controllers, inheritance is not that best solution as you found out by yourself while subclassing UIViewController vs. UITableViewController. You can use some sort of composition for sharing code or - especially for objective-c - categories.
Categories are not allowed to have its own properties, but there are workarounds possible with objc_setAssociatedObject.
Another approach would be to not use static cells, but dynamic cells in a UITableView with DataSource-Delegate :)
As you see in my screenshot, to reuse a special TableView with static cells, i place it in other ViewControllers in a ContainerView. Since you are not using storyboards, i am pretty sure that this can also be done by code without storyboard.

iOS controllers for custom views

I'm all for trying to create lightweight view controllers (testability, separation of concern, etc. etc.), however, I've been unable to find a reasonable solution or pattern when it comes to certain cases.
A very common case (with plenty of examples out there) is separating the view controller from a tableview's delegate & datasource; I get this, it makes complete sense. But what about cases where a view controller may contain multiple custom views of varying complexity? What should be responsible for controlling each of those views? Surely not just the parent view controller.
I tend to think of a 'UIViewController' as more of a screen controller that is heavily coupled with the UI framework and its events; it does not have a single responsibility of controlling one particular view. To further illustrate my point, imagine a tableview with a couple of different prototype cells - some of which are fairly complicated and may require network access for instance - how should this be managed? Surely no single view controller, datasource or delegate should act as the "controller" for all of these cells? And a lot of that logic/responsibility does not belong in the cell views themselves, so it needs to be delegated somewhere.
One option I've thought of is to just create controller objects (subclasses of NSObject) that act as "view controllers" for the custom views I create, such as a controller object for a complex tableview cell - its single responsibility is to manage that one particular view. The tableview cell then delegates to the controller object, which then (if needed) delegates back to the parent UIViewController. Whilst this will work and helps to separate concerns, it starts to feel a bit awkward with all the layers of delegation going on.
Does anybody have any good suggestions on handling these scenarios or know of good code examples out there that demonstrates this?
Thanks!

Best way to implement UICollectionViewDataSource protocol?

I have theoretical question.
Currently my app is using UICollectionView as a way to display objects list. UIViewController, that contains UICollectionView as subview, implements UICollectionViewDelegate protocol and acts as delegate and datasource. Datasource uses NSFetchedResultsController to provide data;
In my opinion this is not the best way to implement datasource, and implementing it in separate class looks way better idea. But the issue it that datasource depends on search parameters in UITextField, and some other buttons selections, so every time when user types text into search field or press the any of "sorting" buttons I should update datasource (in particular fetchRequest in NSFetchedResultsController).
So, finally, my question: Is there any "best practices" of implementing datasources that depends on external parameters? Should I create separate class for datasource of leave it the way it is now? If implementing datasource as separate class - should I create datasourcedelegate for calling self-made delegate methods on delegate when datasource was updated or there is some other workarounds for this problem (I'm not considering using notifications on datasource update because as for me notifications mechanism is more global solution then I need here)?
I'm not looking for the fastest way, I just want to find out the rightest theoretical way of implementation.
Thank you all in advance :)
I personally implemented a concrete NSObject derived class, that implements UICollectionViewDataSource as well as NSFetchedResultsControllerDelegate that practically translates the fetched results controller events (object inserted, updated, deleted) to collection view events (insert, update or delete cells). You can find examples on how to do this, I took mine from here but I implemented it as a separate class instead of a category over collection view. I found my class highly reusable, in practice I use it in all of my projects where there is a need to visualize managed objects in a collection view. A similar class can be implemented also for UITableViewDataSource.
If you need to update the fetch request with the search predicate, I would subclass your newly created DataSource class, and add the logic to update the fetch request right there. Say, you add a -(void)updateSearchFilterWithText:(NSString*)text method where you add the logic to update the fetch request of the fetched results controller. Don't forget to perform fetch again afterwards and call a reloadData on the collection view!
With this architecture the view controller owns this dataSource object. Every time the user updates one of your filtering text field (or other widget), the view controller calls the updateSearchFilterWithText: of your data source object and the rest of the work is done by this later.
What you currently have is the standard approach. While there is no defined 'best' approach, what you describe is certainly a better approach.
Your view controller would own an instance of your new data source class, and would itself most likely handle the delegate methods (because these are actions to take rather than data to provide), so when anything changes in the UI the view controller should be 'pushing' these changes to the data source. No additional delegation should be required.
You shouldn't be creating your data source with the idea that text fields and buttons are directly driving changes in. Your data source should be presenting a generic interface where you can update the fetch request to execute (which covers the predicate and sorting) and change how the cell is configured (perhaps with a block). This way you keep your business logic in the view controller and the reusable data source code in another class that is reusable for other collection views / projects.

iOS -- When to create a child ViewController vs. a UIView Subclass?

Maybe this is a silly question, but I've bumped into it a number of times during iOS Development.
Sometimes I'll develop a view component that I want to use on multiple screens, so I'll decide to subclass UIView and make it something I can use in multiple places.
Then, I start adding functionality to it. Maybe it needs to respond to an NSNotification, or it is supposed to respond to user touches.
At a certain point, I start wondering if I should really be making a UIViewController subclass, and add it to my UI as a child ViewController.
Is there any consensus on where to draw the line between adding some behaviors to a UIView, and when to create a full UIViewController?
I can't tell you about the consensus, but here's my opinion:
Subclass UIView only when...
You want to do custom drawing
You need to customize some behaviour of an already existing UIView subclass
You have special needs for layouting subviews. Most layouting can be done by UIViewController, though.
Maybe for special touch handling that you can't be done with gesture recognizers
Subclass UIViewController in all other cases. You almost always need a controller anyway, for writing glue code that ties together views and models, or for handling user interaction. Consequently, Apple has made it easy in UIKit to let controllers do all the work and to keep views as "stupid" as possible. For instance, it is very simple to nest controllers to create complex view hierarchies, without the need to have a single view subclass.
An indicator that subclassing UIView is not the first thing one should do is the section titled "Alternatives to Subclassing" in the UIView class reference. An indicator that subclassing UIViewController is the preferred thing to do is that there is no such section in the UIViewController class reference :-)
You should use a controller anytime that you need to handle or control data. Views are supposed to be as stupid as possible, not knowing what they are displaying but rather where. You can easily subclass and reuse ViewControllers. A good example, say you need to retrieve a string (or text) from the user throughout your app via a popover controller and a modal. Create a generic subclass of UIViewController that has a view with a textfield and a button. You can then use this view and it's controller in any capacity you need. Reusing it in the popover, modal or anywhere else (and generally passing the data back through delegation). Since you are dealing with data you should not being using a sole subclass of UIView.
From my experience I subclass UIViewControllers more often then UIViews. It is a little difficult for me to understand if you are solely talking about Containers or reuse of views in general application workflow. Either way though it should be the same.
I've used the embedded view controllers to load reusable table views from time to time. I've found that it's useful sometimes but not always. Communication between the two can be cumbersome, like if you want the embedded controller to communicate back up to the container. Delegation makes it easier but still cumbersome. It also limits you to iOS 6, if I remember right iOS 5 and lower don't support embedded controllers.
If it's just adding methods you can use a category to store some extra methods. I do that a lot on NSManagedObjects that I don't want to subclass and if I regenerate the NSManagedObject from the datamodel I don't lose the code in my categories. Gives me added functionality like calculated fields or conversion methods without having to subclass. If you don't need those methods for a particular instance just exclude the reference to the category.
Subclassing never is bad though IMO.

Resources