I'm trying to build some reusable UI elements with functionality (kind of like the slider or switch), and I'm wondering if there is a way to get a view's controller. I feel like this is either really easy and I will kick myself, or Apple doesn't want you to do it.
What I'd like to be able to do is have a UIView subclass that can present a modal view controller, set itself as the delegate, and take care of any necessary details. It will hand relevant info back to its view controller after everything is done.
My current solution is to declare a
UIViewController *controller;
and initialize with a custom method:
initWithFrame: andController:
Then I can present the modal view controller thus:
[self.controller presentModalViewController:stuff animated:YUPPEROONIE];
While I understand MVC, I think there are occasions to use more "grassroots" code (decentralized? I'm self-taught and don't know the right lingo). I'd also be very interested in your opinions of whether this is a good or bad idea, because maybe there's something I'm not thinking of.
Your reusable objects should have a "myDelegate" property (of type id), or something similar. When you initialize the object you would also need to set the delegate
Your reusable objects should have a "myDelegate" property (of type id), or something similar. When you initialize the object you would also need to set the delegate
Then in your object(s), you can use the following method to determine the type of object it is...
Code:
if ([myDelegate isKindOfClass:[UIViewController class]]){
//take appropriate steps
} else if ([myDelegate isKindOfClass:[WeirdCustomObject class]]){
//take appropriate steps
}
Hi Dutch,
You are Da Man on these forums! Always with the quick, clear, helpful replies.
I've used delegation, but only in the context of sending back messages. I wasn't sure you could call a regular method (non-delegate method) on the delegate...but then again, it's just a pointer to an object, so why not?
You are Da Man on these forums! Always with the quick, clear, helpful replies.
I've used delegation, but only in the context of sending back messages. I wasn't sure you could call a regular method (non-delegate method) on the delegate...but then again, it's just a pointer to an object, so why not?
That's a great idea, I'm going to try it.
Thanks!
I rarely use the delegate method model, unless my object is really complex. Essentially you are accomplishing the same thing. There may be actual differences, but I have no idea what they are.
Actually, now that I think it through a little bit more you might not even need to check the type of object myDelegate is... you can probably just do...
Code:
if ([myDelegate respondsToSelector:@selector(methodName:)])
[myDelegate methodName:object];
if ([myDelegate respondsToSelector:@selector(methodName2:)])
[myDelegate methodName2:object];
I rarely use the delegate method model, unless my object is really complex. Essentially you are accomplishing the same thing. There may be actual differences, but I have no idea what they are.
Thanks for the kind words.
This approach worked like a charm.
Thanks!
(oo, how I love cool reusable decentralized code! woo!)
After using this for a bit, I ended up going back to my original idea (mostly). Using the delegate when you're not looking for any delegate protocol callbacks is a bit tricky -- you get lots of warnings.
My real goal here was to be able to access the current/owning view controller to present a modal view controller. So what I did was declare a UIViewController object that I called viewController (I could have also declared my subclass) as a property in the widget (using assign). When I created the widget from my view controller, I simply passed self into that property like this:
Nope, that's actually exactly what I do. Considering that the framework kind of enforces separation of concerns (which is fine), I find it a bit annoying that UIViewController *controller isn't automatically a property on UIView inherently, and thusly I have to declare it every time. Especially considering that controllers have a UIView *view property.
Anyway, I also use this method anytime I have interactions, so the controller handles the behaviors - all selectors happen on the self.controller and then the view is only handling it's appearance.
I have a different point of vue, i dont think that using :
Code:
if ([myDelegate isKindOfClass:[UIViewController class]]){
//take appropriate steps
} else if ([myDelegate isKindOfClass:[WeirdCustomObject class]]){
//take appropriate steps
}
is the appropriate method... Delegation is made to avoid that. (What if you rename, add or remove a controller ? you will have to edit your view, which is the opposite of the separation of concern principle : your component should not need to know which controller implement it.
Imagine that the UITableView component was supposed to check which controller is calling it to know what to do : each time you create a new app you would have to edit the UITableView code, it would be absurd)
You have to declare a protocol in your view; any controller which needs to receive messages from your view will set itself as delegate of your view,
and when when you need to display a modal view, call your delegate.
-(void) presentModalView {
//present the appropriate modal view according to the controller
}
Your view should never directly present a modal view controller, it's the controller job.
The view is only here to display stuff and send message when user do something.
...I find it a bit annoying that UIViewController *controller isn't automatically a property on UIView inherently, and thusly I have to declare it every time. Especially considering that controllers have a UIView *view property....
That's because every view controller must have a view, but not every view must have a controller.
I am fascinated by this discussion, because I've been thinking a lot about architecture lately. (Perhaps I should start a separate post?)
The more apps I build, the more I want two levels of controller objects: "page" controllers (UIViewController subclasses) and "widget" controllers (UIView, UIImageView, or other UI element subclasses).
I now agree with colionel that the viewController should be in charge of presenting a modal view, because that is obviously on the page level. (Thanks for the code specifying the delegate protocol.) Although I don't see why subclasses of UIView couldn't have a *controller property that refers to their current view controller -- it only has one at a time, and that property should only be invoked in the context of some view controller, no?
But I'm also having lots of fun creating "widget" controllers that do a lot of work, including interacting with Core Data. For example, I'm populating a form by passing Core Data objects to my widget objects, which then save their input directly to Core Data (without informing the view controller).
Well I'm not going to say this is the best approach, but I can tell you some of the things I typically do.
1. Controlled views have a controller property. You could either do this generically or specifically ( IE UIViewController *controller or RootViewController *controller ). There is a pro/con to both approaches. The first is more generic and will fill most circumstances for passing interaction delegation back to the controller (IE, setTarget, performSelector, etc ). If you need to access specific method calls on the controller however it will require a tiny bit more typing if you want to be proper about it and cast it.
2. UIViews that are controlled ( IE base views ) don't handle any interaction themselves - anytime something happens it isn't even seen by the view. For example if I have a MenuView that creates some buttons, then when I set them up their target for the event is self.controller. It keeps things cleaner IMO, and means I have less work to do if I refactor because I didn't like the method name I chose - because then I don't also have a sub method in the view.
3. Controls / Widgets typically don't have an associated controller. For example if you're making custom table cell or button implementation, or a game sprite actor. They're just for display and they should be able to be controlled by anything that wants to use them.
4. For advanced controls that have very specific interactions and callbacks that need to happen I like to use delegates. It's how Apple has done things, it works well and it's pretty simple after you've done it a couple times. Plus it's nice to be able to see in the header what protocols are being implemented - as Colionel pointed out very correctly, it shouldn't matter *what* is using your control, just that it conforms to the expected interfaces.
To communicate between the view and the controller you can either use delegation, either explicitly declare a UIviewController* myController property in your view.
Delegation is a loose relation between your view and your controller, most of the time this save you a lot of problems.
Explicite declaration is a tight relation between the controller and your view. I think you should avoid this as much as possible.
In fact you dont usually need to subclass UIview. There is only 2 case where i actually subclass UIView :
-When you need to create a widget or re-implement the init method, this needs to be re-usable so you must use delegation
-When you need to re-implement the drawRect to do some custom drawing. In this case you could use an explicite declaration since this view is maybe not meant to be re-used and is very specific to your controller context.
I dont see any other use case where you need to subclass UIView (maybe they are). Anyway you can never be wrong using delegation whereas with explicite declaration you can only be right in some cases (and maybe a proper architect would simply tell you that explicite delegation is an anti-pattern)
Quote:
Originally Posted by jazztpt
But I'm also having lots of fun creating "widget" controllers that do a lot of work, including interacting with Core Data. For example, I'm populating a form by passing Core Data objects to my widget objects, which then save their input directly to Core Data (without informing the view controller).
No this is definitly wrong. A widget writing in core data is a big big mistake. A view should never interact with your model !
If you want to follow the MVC pattern, this is how you would do that :
The view needs data to populate the form.
It just sends message to its delegate saying "hey i need this and that"
A controller that set itself as delegate of your views receives theses messages.
it then calls a specific class (a service) saying hey get me this data.
The service knows where to find the data (in core Data for example, or in an xml or whatever)
then the service returns the data
the controller formats the data
the view displays the data.
If you follow that pattern you only have very loose relation in your application:
- you can replace core data with a webservice your controller and views stays exactly the same, 0 lines of code.
- you can meet a designer, he gives you amazing idea about the way your apps look, you can replace all your views and your services and data will stay the same.
- you can create a new apps which uses the same widget, plug it to your new controller and services, the widget will stay unchanged, etc, etc, etc
No this is definitly wrong. A widget writing in core data is a big big mistake. A view should never interact with your model !
If you want to follow the MVC pattern....
I am following the MVC pattern. I don't think my approach is so wrong, and I think I may be confusing the issue by saying that I am subclassing UIView. I'm only subclassing UIView or UIImageView because these widgets have a skin, but really it should be an NSObject that contains a UIView/UIImageView. (Each of these widgets has subviews, so I felt it was simpler to subclass UIView rather than creating one extra level of objects...I don't think that really matters. The MVC views are created with nibs.)
In my MVC architecture, I simply have an extra controller level: the widget controller. So in my architecture, sometimes the "view controller" (a controller that controls an entire screen) interacts with Core Data, and other times a "widget controller" (a controller that controls only a small functional object on the screen) interacts with Core Data.
The view level is changed by nibs and images, and is still separate from the control level. I just don't see why it is important to pass everything that interacts with a functional object on the screen through the UIViewController subclass. Some functionality affects only a small widget object (and can get info from/set info in Core Data); other functionality impacts the entire screen.
These two levels should be separate. Otherwise your UIViewController subclasses become bloated simply passing data back and forth that has nothing to do with them. They should create the objects that are on the screen, passing them any data they need, and then receive only messages that affect the screen: navigation, modal views, table updates, etc.
I'm only subclassing UIView or UIImageView because these widgets have a skin, but really it should be an NSObject that contains a UIView/UIImageView.
But why not simply have a skin property in your controller ? that's the normal way to do this.. in your viewDidLoad you set your skin to your view.. No need to subclass UIView.
Quote:
Originally Posted by jazztpt
So in my architecture, sometimes the "view controller" (a controller that controls an entire screen) interacts with Core Data, and other times a "widget controller" (a controller that controls only a small functional object on the screen) interacts with Core Data.
What if tomorrow you use a webservice instead of core data ? you have to rewrite everything.
The question is not do you need core data, the question is can i handle changes easily.
Quote:
Originally Posted by jazztpt
I just don't see why it is important to pass everything that interacts with a functional object on the screen through the UIViewController subclass. Some functionality affects only a small widget object (and can get info from/set info in Core Data); other functionality impacts the entire screen.
Imagine that the UITextField widget was reading and writing from core data. When you want to use it you need to edit the framework. That would be a nightmare. Your widget must not decide how your system is built, it's your system which decides how to use a widget...
colionel: I'm intrigued by your use of a service. I haven't seen this before, so I'm imagining how this would work. In the Apple sample code I've seen, the UIViewController subclasses access Core Data directly...I assume that each UIViewController subclass would import the service, then use it each time it wanted to read/write data.
If this is the basic paradigm, why couldn't a widget controller also access the service?
Quote:
Originally Posted by colionel
But why not simply have a skin property in your controller ? that's the normal way to do this.. in your viewDidLoad you set your skin to your view.. No need to subclass UIView.
As I said, these widget controller objects should be NSObjects with a view property and subviews, mirroring the UIViewController paradigm.
Quote:
Originally Posted by colionel
Imagine that the UITextField widget was reading and writing from core data. When you want to use it you need to edit the framework. That would be a nightmare. Your widget must not decide how your system is built, it's your system which decides how to use a widget...
I'm not building framework-reusable widgets, I'm building single-app reusable widgets. I have n screens, each of which is dynamically populated with any number of five types of widgets. Since the widgets function identically and are populated dynamically, I think reuse is served when they are separate objects.
Is there a reason not to have them access the service?