What is the proper rationale for subclassing UIView? - iphone

I mean this in compliance with OOP and MVC.
Specifically, I've planned out a UIView XIB file that has a lot of dynamically updated UILabels that change every time the user selects something: two timestamps, two durations, and two names. This is a "header" section of the interface that the user is seeing.
I see a few options
1. Create everything in the view controller, add the UIView as a subview to the main view, and keep track of the UILabel subviews with properties. But this forces me to add a lot of properties to the view controller.
2. Subclass UIView and add the UILabels as properties of the UIView, thus keeping the controller "cleaner."
3. Use the XIB file (but let's assume I want to do this programmatically).
I'm asking this because I keep getting the impression that UIView should be subclassed for "custom drawing," and my rationale is more along the lines of "keep my controller cleaner without 6 UILabel properties."

Don't subclass UIView for this. You should subclass uIView for custom drawing or in some cases custom handling for touches (but there are often better ways to handle touches).
Putting everything in your view controller and subclassing UIView are not the only options. You can create a class to manage the group of views that make up the header. It would conceptually be a view controller, but you don't subclass UIViewController, just inherit from NSObject.

In my personal opinion, I would make theUILabels as properties of the UIView, so option 2. You can then access them from the view easily through the controller.
This also gives you the advantage of reusability from the view to any controller, and since dealloc will get rid of those from the UIView, you only worry about cleanup once!

Related

iPhone - a UIView that behaves like a UIViewController

I need to create a class that will present a UIVIew and has a some code to initialize it before it is ready to show. I need to know when the view is ready, I mean, I need something like viewDidLoad or viewWillAppear, but as it is a UIVIew it lacks these protocols.
I cannot implement it as a UIViewController as I don't want to present it modal. It is really a rectangular view that needs to show on a screen side.
How do I declare this class? If the class is a UIView based I don't have the methods I mentioned.
thanks
Any reason to not do that kind of stuff inside the initWithFrame method on a UIView? Also, you can do additional stuff on layoutSubviews. A view controller has viewDidLoad because the view is lazy loaded (from a nib or otherwise - it also has a loadView). It has viewWillAppear and viewWillDisappear because it is managing the view (btw, even the view controller is managed by another view controller - these methods are called when you have the controller within a UINavigationController or UITabBarController or such classes which mange UIViewControllers. - the view itself is not really managing anything. All it knows about is how to draw itself. For that, you have layoutSubViews, drawRect, etc.
Doing some heavy stuff upon view's load will definitely kill the UI performance. You probably need to implement another kind of design pattern that will asynchrounously assign data values to the instance of your custom view - when that is done, you call layoutSubviews or setNeedsDisplay to update the view.
The scenario you described is no reason for not implementing a UIViewController. Assume you have a container view A and a subview B. Both have their own UIViewController AC and BC. Now on AC you add the View B managed by BC:
[self.view addSubview:BC.view];
You probably want to be using UIViewController.
You don't just have to present them modally. You can get your view controller's view with yourViewController.view, and add that as a subview of whatever view you want.
If you're targeting iOS 5, there are a few new methods (such as addChildViewController:) designed to make doing things like this easier. You can do it on iOS 4 too though, and it'll still work.
Implement a drawRect in youR UIView, plus an initialization flag. Just before the view is to be displayed the drawRect will be called. If the initialization flag isn't yet set, do your initialization and set the flag. This will only look good if your initialization can be done quickly (no long synchronous calls).
I need to create a class that will present a UIVIew and has a some code to initialize it before it is ready to show. I need to know when the view is ready, I mean, I need something like viewDidLoad or viewWillAppear, but as it is a UIVIew it lacks these protocols.
You might want to rethink how your view is being used. It sounds like you're trying to put too much controller-like logic into your view. That's why you're wanting your view to behave like a controller.
More specifically: What exactly are you trying to accomplish? If you're waiting for data to load before display the view, that might actually be something to put in the controller that is calling the view.
To illustrate my point, imagine you're putting some text in a UILabel that you read from disk. The reading from disk isn't really related to the view. The view only cares what text it displays, not how it received the text. Once it's read from disk, you can create a UILabel with that text that you read. This allows the UILabel to be more flexible.
That example might not be at all related to what you're doing, but I use it as an example of the difference between a view and a controller. Anything not related with the display and drawing of the view shouldn't belong there.

how to return a value to the controller from a re-usable view

Specific question based on my implementation is:
Question:
have a UIViewController which has a some views within it (configured in an XIB file)
when something occurs in one of the controls on the screen here (i.e. in one of the views), how can I update a UIViewController parameter at the same time?
Background:
In my case I have a colorPickerController, and within this there are some subview which the user interacts with the select the color
when the user is dragging the mouse over one of the subviews, one of the other others is updated with the color
what I'm not sure of is how to also keep the main colorPickerController parameter updated with the latest color that is being selected
hence the question how to update/access a controller's parameter from within one of the views within a controller
The subview the user drags mouse (or finger?) over can be considered as some kind of a control, so you would just use target/action in that case. or may be a delegation. that way you would keep this subview and viewcontroller loosely coupled, and that would also allow you to reuse that same class (which the subview is instance of) in other places. so, in case of delegation:
define a protocol:
#protocol ColorPickerViewDelegate
(void)colorPickerView:(ColorPickerView *)cpView didUpdateWithColor:(UIColor *)newColor;
#end
add property #property(nonatomic, assign) id<ColorPickerViewDelegate> didUpdateDelegate; to color picking UIView subclass.
Make your viewcontroller adopt the above protocol and define the method from that protocol in viewcontroller's implementation section.
When creating a color picking subview, set your viewcontroller as that subview's didUpdateDelegate.
make calls to didUpdateDelegate at appropriate times in your color picking view touch-handling logic: [didUpdateDelegate colorPickerView:self didUpdateWithColor:someNewColor];
hope that's what you want
I guess there is no any ColorPickerController in iOS Control Library ? Is it custom controller ? Please specify so can answer accordingly.

When to subclass UIViewController for custom subview?

I've seen custom subviews implemented as an UIViewController subclass, but that maybe could have been implemented as an UIView subclass.
When should I subclass UIViewController instead of UIView for a subview? Are there any drawbacks to subclassing UIViewController?
Personally, when I need some significant logic to go on, I do it with a UIViewController subclass. Also, if I am looking for some of the behavior that you get from UIViewController e.g. presenting it modally or in a navigation controller.
If you are doing something fairly simple or lightweight, a UIView subclass is usually enough. I seem to have used them most often when making custom buttons and table view cells.
In my experience I have found myself using more UIViewController subclasses than UIView subclasses, but this might not be the best, it just so happens that I feel a bit more comfortable using view controllers rather than straight-up views.
Take a look at what Apple has to say on Controller Objects and the MVC design pattern
In iOS controller are generally expected to fill at least one the following roles:
Coordinating controllers provide application specific logic. They respond to delegate messages, notifications, and IBActions. Coordinating controllers also setup connections between other objects and often manage the creation and destruction of those objects.
View controllers, specifically UIViewControllers, manage the display of one "screen" worth of content and trigger transitions to the next "screen". They respond to memory warnings and rotation events.
Mediating controllers exist in OS X but their role is usually filled by view controllers in iOS. They act as an intermediary between views and models; updating models when views receive input and updating views when models change.
If the behavior you are implementing fits into one of these categories you probably want to create a controller object. If your logic is only concerned with the display of data (and possibly responding to user input) then perhaps it belongs in the view layer. If your logic is about the data itself then it probably belongs in the model.
If you can't find a good fit for your logic in any of those layers then you probably should model it differently as a combination of different responsibilities which belong on different objects in different layers. ie a view which requests data to display from a mediating view controller.
You would also subclass the UIViewController if you're going to use an AdBannerView in your "view". AdBannerView needs a UIViewController to be able to work.
The thumb rule I follow is, If you are doing custom drawing, subclass UIView. Otherwise, subclass the UIViewController.

How to embed a UIViewController's view from one xib inside a view in another xib?

MyViewController.xib has File's Owner class set to MyViewController (a subclass of UIViewController) and File's Owner view connected to a UIView containing some subviews.
OtherViewController.xib has File's Owner class set to UIViewController and File's Owner view connected to an empty UIView.
Is it possible in Interface Builder to embed MyViewController's view inside the view in OtherViewController.xib?
I tried adding an instance of MyViewController into OtherViewController.xib, but I can't drop it inside the view (because it's not a UIView) and I can't get to the view that was associated with MyViewController in MyViewController.xib (only the view controller itself, and nothing it's connected to, makes it over to OtherViewController.xib).
You probably do not want to do this. Follow the warning in the View Controller Programming Guide:
Note: If you want to divide a view hierarchy into multiple subareas and manage each one separately, use generic controller objects (custom objects descending from NSObject) instead of view controller objects to manage each subarea. Then use a single view controller object to manage the generic controller objects.
A UIViewController subclass whose view does not fill the window will not behave as you might expect. It will not receive view controller lifecycle messages, rotation messages, or have its parentView/navigation/tabBarController properties set correctly.
A UITableViewCell should not be the view for a UIViewController. It might have some controller object responsible for managing its behavior (though I suspect this behavior can probably all be contained within the cell view itself) but that controller should not inherit from UIViewController.
This has changed since some of the other answers were posted - you want to take a look at the latest documentation for UIViewController, particularly the guide section "Presenting View Controllers from Other View Controllers" and the class reference guide section "Implementing a Container View Controller". Also, there's a video from WWDC 2012 covering the topic on iTunes: Session 236 - The Evolution of View Controllers on iOS. (The video is very useful, it's not just a general overview.)
You can put it all in one xib. For example, just put it all in your MainWindow.xib.
This can be done programmaticly by adding a reference in OtherViewController to MyViewController. This is perhaps a bit messy and does in some way lead me to ask why you would want to do this but... I will trust that you know what you're doing.
Warning. Because 'Other' will contain a reference to 'My' you will want retain My inside Other. but Do not, I repeat do not retain 'Other' inside of 'My' this kind of cycle will lead to errors.
Good luck and don't forget to vote
ps if you have a little more detail I may be able to help you sort out a better design so that this sort of thing can be avoided :)

Making an alternative to UITabBarController

I'm making a custom UIViewController, which is similar to a UITabBarController, as there are some buttons which switch between views. However I'm unsure whats the best way to switch the views:
Have a UIView in the nib file, and add/remove the viewController's views as subviews, as they are needed.
Have a UIView in the nib file (as an IBOutlet), and replace the UIView with the viewController's view so that they are subviews of the myTabBarController's view directly.
Don't have a UIView in the nib, and programmatically set the frame of the viewControllers as they are added, so they are subviews of the myTabBarController's view directly
I had to do something similar once, and in my case it was simpler to have my master
"switching" view (for lack of a better term) maintain a list of UIViewControllers. That way, I was able to maintain the state of the child view controllers even when the corresponding view was not visible or had even been destroyed (to save memory, for example), which made it simpler to keep track of the info on each "page". In my approach, I simply programmatically added each UIViewController to the switch view. Basically your approach #3.
That said, there's nothing wrong with your approaches #1 and #2. They'll do the job. The only thing I don't particularly like about #1 is that it doesn't scale as easily, since you've statically set which views are the children of your switcher at compile time, and cannot easily change that at runtime.
I'm using this approach from Red Artisan's Marcus Crafter. It works remarkably well.