iOS controllers for custom views - ios

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!

Related

Storyboard design pattern (to share or not to share)

This is probably largely preferential but I'd like to know if there are any reasons to decide one way or another on this.
When designing with storyboards you invariably end up with a number of view controllers. I'm looking at the overhead of a strict MVC approach where each controller is implemented in its own UIViewController subclass with corresponding UIView subclass (and even view model class for MVVM), and that seems to get out of hand quickly – it takes no time to add dozens of files to the project (many with little function). An alternative approach would be to link all the views to a common controller representative of all of the storyboard functionality.
My inclination is that if you don't have substantial controller code for any individual view controller, then the combination of all of them into one shouldn't be of harm to the readability of the code (and may enhance it over adding a large number of source files). On the other hand, if you have significant functionality to implement for any particular view controller, then it should be encapsulated within it's own controller.
In most situations I'd build all controllers to be as reusable as possible (encapsulated in their own custom UIViewController subclasses). Storyboards put this in an interesting light though since they seem to be geared towards sequences of views that typically have few entry points.
Your thinking is correct
If you don't have much functionality in each VC (ViewController) them combine all your code into one VC. The only drawback to this approach is that you won't be able to implement view specific code and every view where you will use this common VC will execute the same code whether its needed there or not. e.g. code in viewWillAppear etc.
Similarly if you have a lot of functionality for a particular view then its better to put it under its own VC
This is just a suggestion, if you need to use some common code logic between multiple VCs then instead of doing copy and paste in each VC of the same code, make it into a method of type Category and then call it where ever its needed. So change the code only at 1 place.
More VCs doesn't necessarily mean bad design. In my opinion its easier to maintain that way. My two cents. :-)
Each scene in your storyboard should have its own UIViewController subclass. Even doing it like this it's way too easy to get huge unmaintainable view controllers (MVC = Massive View Controller). Putting all the code for multiple scenes in the same view controller would create even bigger ones and also violate the Single Responsibility Principle. Each class should do only a single thing.
That also means you shouldn't duplicate the common functionality into all your UIViewController subclasses. Then they again would do multiple things - the common stuff and their actual purpose. Instead you can put your common code in other controller objects (which are not descendants of UIViewController) and use them in your view controllers.
Depending on the usecase a common base class would work as well, but it's always preferable to use composition instead of inheritance.
Another nice thing about other controller objects is that you also can add them directly in Interface Builder and connect actions and outlets to them. Your main view controller class often doesn't even have to know they exist.

What is the need to subclass UIView instead of writing everything in ViewController

I went through lot of similar questions but still have not clearly understood this.
In terms of better design - what is right way - creating all the UIButtons, UILabels etc in the view controller itself and then add them as subviews, or should I create a custom view (#interface MyView : UIView) with all the required buttons/labels etc and then assign that view to the view property of View controller? I am not using interface builder.
Is there any real need/advantage of creating a custom view like this or adding everything in view controller itself should be okay/good idea? Sorry I am very new to iOS app development :-)
If someone could explain it to me - would be really helpful.
Some advantages of subclassing a view are:
Code separation. If you have a complex view and want to keep your viewcontroller clean, subclass and separate it out.
Reuse. If you reuse the view anywhere else it is possible with minimal effort when the view is it's own class.
You can choose what setup and methods to expose for setup
Some disadvantages of subclassing a view are:
View no longer will have access to view controller ivars.
View controller will no longer have direct manipulation on the view
You are constrained to what the view publicly exposes in terms of setup and config (may be a good thing).
Overall, there is no single best answer, it all depends on your setup and how you like to keep your project organized.
My non-answer : the answer lies in between these 2 opposite sides :
putting verything in the view Controller, using just basic views
using very elaborate views that abstract away some tedious graphic work.
To get an idea of what I'm talking about : one might implement a UITableView behaviour directly into a UIViewController with just a UIScrollView, and handle all the indexPath computation (depending on the amount of scrolling), views recycling, etc... in this very viewController.
But, as lists are a common way to visually display information, all this 'recycling view', 'setting content for currently displayed cells' has been moved into a custom view class : UITableView, and delegate and datasource patterns have been use to make this class behavior easily customizable.
Creating abstractions has its advantages and disadvantages.
I try to do things incrementally :
hold the most you can in viewController
when code becomes too complex, or if you see yourself needind the same kind of 'component' elsewhere : the create a custom view, and try to define its API (what properties it exposes, what implementation it hides)
View controllers are a nice way of separating the things that a view must do in the strict sense (handle input, render output) from the larger hierarchy, manipulation, and information routing of views around it, which the view controller coordinates.
Striking the right balance depends on a number of localized factors. A view which contains a label and an image (like a cell) could certainly "own" its subviews and appear to the controller like one container unit. But note in this case that the subviews are also supporting the input/rendering of the "cell" unit.
In that sense, if you have a large canvas with a set of actual controls that are "on top" of that canvas but not necessarily "owned" by it, the view controller would probably want to create and attach these into the view hierarchy.

How can I clone a UITableView from one controller to another so it stays in synch?

I found this in SO; it doesn't exactly answer my question, which is: is there a way to clone a UITableView from one controller to another while using Storyboards and maintain synchronization?
You can clone them in the sense that their initial property values remain the same, like position, layout etc. For this, just copy the UITableView from storyboard, go to destination view controller and paste it there.
If you share same UITableView object between two view controllers, it is still possible, but you must estimate how much work you would have to do yourself. When a view controller solely handles a table view, much of the work is done under the hood and table is handed over to you. In case of your UITableView shared between two view controllers, you would have to play with it quite carefully. You need to take care of entire life cycle of the view - see the usual methods like viewDidLoad, viewDidAppear and so on. How would you take care of them when your view exists in two scenes? Maybe you would be inventing a unique design pattern - if at all you find the most optimistic and programmatically correct way to accomplish it.
With storyboards, you cannot achieve cloning up to the level wherein data changes will reflect seamlessly between the two. Storyboard is, as the name suggest, just a board, where you can draw things to know how would they look initially, before your code takes over.
In order to achieve what you want, you have to create a shared model that updates two table views through proper delegate methods. Most likely such a model (NSArray, or any such collections as per your requirement) can reside inside a shared class (app delegate isn't a wrong choice), from where both your view controllers can refer to it. That is neat practice, it not only is right from programming perspective but also extensible and helpful to anyone who deals with the code any time.
For details about how to update UI from your model, there is nothing better than starting from scratch, going through the books I mean.
I am not aware of such possibilities.
I would pass the tableview source object amongst different controllers and let the view controller handle their own table view.
I think the best approach would be to use a framework such as the freely available Sensible TableView, then use the same model for both table views. This should be really straight forward.

iOS, Pattern for adjusting content properties in custom subviews

What's the established way to handle a situation where you have some sort of model object, and various properties on it get modified by multiple views and (custom) subviews?
Should the subview have a reference to its container (I don't know if iOS keeps such a reference, but I could set one if necessary), which has the content, and modifies it such?
Should as few views as possible have pointers to the content, and subviews can send messagea to their containers to modify the content?
something else? I know I can hack something together, but I want a proper design pattern for this.
The usual - MVC way - is to have a controller object to mediate between the views and models.
You would have one viewController to manage each screenful-of-views view hierarchy and a separate model object which each of the various viewControllers updates.
None of the views or subviews have pointers to the model. Common ways to get data out of a view or subview is through delegation or - if your view is a UIControl subclass - target/action. The delegate (or the location of the action method) would be the view controller. The views should not know about the model, and vice-versa.
if you are going to want one persisting model object through the life of the app, being updated from various view controllers, you will most likely want to use a singleton pattern - apple docs here - see also numerous Q&A's here on singletons (globally-accessible objects), and this link.

Connecting a UIView to a UIViewController

I have a UIView subclass which contains some labels, a tableview, and a few other bits an pieces. It is currently the delegate and datasource for the tableview, and also manages many UI actions, none of which rely on data. For example, when an 'edit' button is pressed, it knows how to update its controls, but it won't do anything when 'save' is pressed besides switch the controls back to the previous state. All of this is done in code, I'm not using IB at all in this application.
I now want to plumb in all the data model changes that it can provoke. But I would like to put these in a new controller class, which I guess is the MVC compliant way to do things. I'm not sure how to get there.
First, I need to create a custom controller class. Should I be exposing from the UIView subclass a few of the controls so that the view controller can access them? For example, I will need to read and write to a textfield in the view, so should I provide a getter/setter for this?
Secondly, the tableview - instead of the UIView being the delegate, should I expose this also, and make the view controller the delegate? I.e. view.tableView.delegate = self from the UIViewController?
And finally, how do I launch the view from another view? Specifically, this is a paged scrollview application similar to the weather app, so I have a mainView UIView that specifies the single paged scrollview and then adds multiple custom UIViews, one for each page. How do I replace [scrollView addSubview:myCustomView] and instead add the viewController? And how do I connect the view to it's controller and vice versa?
I've not tried all of this without IB before so thanks for helping.
Your question is very broad, and part of the answer would depend on how you code your solution. I will try replying with a few hints of what works for me:
View: As a general rule of thumb, keep in mind that a view object should be something very generic, that knows nothing about other views outside of its own hierarchy. As such, a view should not rely on any specific interaction with other views, but rather communicate back to its delegate / owner / creator through callbacks, protocols, blocks, etc, when needed.
View Controller: Any time you need to make two views in different hierarchies interact with one another, my suggestion is that you stick to handling that interaction through the view controller. By doing so, you'd be making sure your view is not contaminated by code that would be useless in a different screen.
Also please keep in mind that UIViewController for iOS is intended to be a class that you use for handling the complete visible view hierarchy, as opposed to acting as the controller for a single view. As such, I recommend that you don't attempt have a controller for each view, but rather a single one to handle them all.
Publishing view elements: How much a UIView exposes as public in its header file is up to your implementation. Any method that handles the way the view looks, that looks to be generic and reusable, and that doesn't need anything from outside the scope of the view's tree, is surely something you want to include in it's implementation and publish in the header file.
The same goes for any property that you feel is highly probable that someone from the outside will need to access.
Suggestion: publish only what's really needed. Usually publishing new methods and properties is easier than removing them later on.

Resources