How to add multiple instances of custom subviews in UIViewController - ios

Often, when I'm making my apps, I'm in this situation : I have a UINavigationController, handling the view stack, some UIViewControllers, controlling their respective views...
But when I want to add several custom UIViews in my mainView, I don't know how to manage my code.
Each UIViewController needs to handle one and only one view (wich normally occupy all the screen size), and a view should not control their content (update it a the extrême limit).
You can't neither do this :
[myViewController1.view addSubview:childViewController.view];
So if I want to achieve something like this, what should I do ?
The orange parts have to be 3 instances of the same UIView(Controller?), but with a content depending of a NSObject (User, obviously).
I think this very important to segment your content, this should be an easy problem, but I found a lot of contradictory answers so, what's the best practice to handle this common issue?
Theses orange views should be instances of UIViewControllers in order for it to handle their UITableViewDatasource? Is addChildViewController relevant in this case?
I already found a lot of things which work, but I don't know what should I do...
Also, I'm using xibs.
Thanks in advance if you can help me (and other people I think).

You can do it either way (view or view controller) depending on how you want to handle things. Certainly, you can have one object be the data source for multiple tables, so in that case, you would just add multiple views. If, however, you want to keep your code more compartmentalized, then add view controllers, and have each control its own view -- to do this, you do need to use addChildViewController, and use the methods that Apple describes for creating custom container controllers. Alternatively, you can use container views in a storyboard which makes the process of creating custom container controllers simpler.

You're on the right path... Create separate instances of your subviews, and add them to your view. If you will have more than 3 (for instance, imagine coverview for your music, and you could scroll indefinitely left and right), I'd take a look at UICollectionViewController ... That will help manage cell re-use.
But, if it's just 3, just create three instances with different frames and add them to your view.

Here's how I'd do it:
each orange box will be a custom view (inherits from UIView)
the view will have the label, image and the tableview.
since you are not sure of the number of instances of these views you'd be using, its better to use some kind of tagging, so that you can have one place for the datasource and delegate methods of the tables in these orange views.
in the datasource and the delegate methods, you can make use of the tableView.tag (same as the orangeView.tag property).
I personally dislike having more than one viewController in a view (except the splitVC), probably because I haven't had a such requirement.
I dont see how a uiviewcontroller for orange box would help, over a uiview.
as #James Boutcher mentioned in his answer, UICollectionViews will simplify this issue further.

Why not creating a UIView class and overriding the drawRect method and then adding subView for this class in your myViewController1.view

Related

Best Practice: UIViewController or UIView

I am gonna design a component in IOS, in which we will have Table view and Grid View; means user can see his data in table or grid. So what i am doing is i added a UIViewController and added two child view controller one for table and another one for collection view. I am handling everything using UIViewController only. Its means when user wants to use my component he has to add as a child view controller only. My question is that "Is it the best practice to use UIViewController like i am doing or Should I convert everything to UIView because UIView is light weight." I am gonna write only presentation logic in my component. We will get data from outside using delegate. And if i should use UIView then when should i use UIViewController?
Thanks
Your current approach is correct.
UIViews should do one and only one thing: paint a view when asked to.
UIViewControllers are more complex. They are are responsible for setting up views with any data they need to do their job, compositing multiple views (or view controllers) together if needed, responding to events from view components or from the device in general, and communicating with the rest of the code/system.
Because your code wants to show two different kinds of information, it fits the UIViewController much better than the plain UIView style.
As you've pointed out, Apple themselves have many examples of UIView subclasses that have delegates and do complex things. However, if you're writing a new component and it is complex (as yours is), I seriously recommend going for the UIViewController approach.
A note on history: Apple used to be of the advice that UIViewControllers should only show/composit UIViews and not other UIViewControllers (even though, confusingly, they didn't always follow their own advice!). This would align with their making of some quite complex UIView subclasses. However, they changed this stance a while back, and added support to UIViewControllers for compositing other UIViewControllers' views inside them. If Apple's stance had always been as it is now, you might find that some of their UIView subclasses were implemented as UIViewControllers!
As an experiment, it would probably be quite educational to try and implement your component both ways. I predict that you'd find UIView approach more unwieldy, with you having manually re-plumb some of the 'wiring' that you'd get 'for free' with the UIViewController approach.
Avoid putting logic in UIView subclass.
As I think you can make grid view by using table view just simply add multiple views on each cells
at time of switching grid to list or list to grid simply check your type and reload tableview

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.

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.

How could I clone/duplicate some sort of UIView for iOS?

(screenshot below helps explain what I am trying to do)
The idea behind this is that I have a UIView, with various different UI elements inside, for example, let's say I have a UIView, and inside there we have a UILabel.
Now I'm wanting to duplicate the UIView (with the label inside) BUT somehow after that I need to perhaps make a change to the label, e.g. change the text property.
The reason I need something like this is so I can structure the UIView with everything I need in it looking nice, but to actually have different data with different copies of it.
I'm not certain this is the best approach, but it's the only one I could come up with. If anyone has any ideas on how to do this, or any ideas on a better approach I'd really appreciate it. A lot!
I personally think the best answer is to create each view separately and configure it as needed. You can make a method that just configures new UIViews to look the same, and pass each view through it.
However, if you really need to copy a UIView, you can archive it, and then unarchive it:
id copyOfView =
[NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:originalView]];
UIView *myView = (UIView *)copyOfView;
[self.view addSubview:myView];
If you have a bunch of these, make sure you're using the Instruments time profiler to check your drawing efficiency.
This is a very natural and useful thing to do. What you're looking for is a container view controller. Put your reusable "cell" into its own view controller and its own nib file. Then, in your parent view controller, use addChildViewController: to add as many of these as you'd like and configure each of them. They can each have their own IBOutlets that you can use to modify the contents.
This is very similar to the pattern used by UITableView and UITableViewCell (it doesn't use "child view controllers" specifically, but it does use this pattern).
For full details, see "Creating Custom Container View Controllers" in the View Controller Programming Guide for iOS.
Note that Storyboard includes a "Container View" as an option in the object templates to make this even easier.
If you want lower-level access, you can also do this by hand using UINib to load the nib file and wire its outlets (and this is how we used to do it before iOS 5), but today I would use child view controllers.
If you have only one label inside it the obvious solution is to have a custom UIView subclass with that label added as a subview. Everytime you need a new view you make an instance of your custom subclass and set the label text. If you have multiple things to set, some of which are common to all your custom subclass views you can use the prototype design pattern, it's pretty straight forward to implement.

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