开发者

How can drawRect request/rcv a image freshly computed by the model?

开发者 https://www.devze.com 2023-02-06 13:23 出处:网络
I have my first app running on iphone/ipad that uses Core Graphics and gesture recognizers (NOT OpenGL or touch events).The App, view, and controller were set up using IB.It\'s simply a View within a

I have my first app running on iphone/ipad that uses Core Graphics and gesture recognizers (NOT OpenGL or touch events). The App, view, and controller were set up using IB. It's simply a View within a ViewController. I added CG calls to draw into a CG Context. The gestures are added to the view, but handled in the view controller. The view controller uses accessors to change variables that determine what is drawn in the view. The computation (ie model) is embedded in the view. The app works quite well, but I would like to add features, such as emailing and saving what has been drawn. (I know I'll need other graphics contexts).

Conventional wisdom tells me I need to use MVC paradigm, meaning to factor the model out of the view into its own object. So, I re-factored the code. I manually added an NSObject for the model, which nicely contains the data and needed methods. No compile errors or warnings.

Now, when the view's drawRect is triggered, I want to get an updated image from the model via the controller. At the key place where I expect the image to be returned, NULL comes back. I probably just don't have my object reference(s) correct, but I'm too confused to cut though the fog, and I'm working in a vaccum, so I am enlisting your help. Thanks for reading.

SUMMARY and QUESTION

The View Controller always has the current parameters that it can pass to the model. The model can compute a bitmapped image given those parameters. I'm pretty sure I have that much down.

My question is (phrased various different ways):

What is the best way to get the image FROM the model TO the view (via the controller :^) How should the View send a message to the view controller to initiate the request?

How can the model return the image to the controller? I assume I'd just pass a CGContextRef back through whatever chain you recommend.

About me: I am new to OOP, Objective-C, Xcode, and IB. I've watched most of the Stanford lectures several times, studied G开发者_开发百科oldstein's Dummies books, scoured Apple docs. It is hard for a noob to glean appropriate level answers from these sources. I just need more "experience". I'm so green, I'm not even sure how to refer to the view controller, other than to include "myViewController *viewController" in the view's interface definition! I have been googling and reading the answers to similar questions here on Stackoverflow. I can't beleeve I haven't found anything appropos. I hoping for a practical principle or two to serve as my guide.

Please advise.

This is similar to reference-to-model-data-in-a-view-instance-drawrect, which was not answered satifactorily.


One thing you can try if it suits you is to use NSNotifications.

The View gets the gesture information and sends high level events to the Controller (through direct messaging). The Controller takes those high level events and makes changes to the Model. The Model consumes those changes and then sends out NSNotifications to anyone concerned about the changes.

The View already knows about the Model, since that's how it gets its data to display, so the View will be able to subscribe to those changes readily. The Model doesn't really know about the View, so should have to send messages directly to it. However, something, somewhere, is interested in the Model changes, so it uses NSNotifications to broadcast and publish that information. This way, particularly with a complex model and/or simpler View, the Views subscribe only to what they are interested in. You can also easily have more than one View (or whatever) listening for NSNotifications at a time.

Another option is Key Value Coding/Observing, which iPhone supports, but it's not as ingrained as it is in Cocoa on the Mac. This handles much of Notification stuff semi-automatically for you, so you may want to consider that as well.


If I understand you correctly the view needs to get a new image when the image in the model changes. As Will said you can use notifications or key-value observing. Since your model shouldn't have to know about the view and the view shouldn't have a strong reference to the model. to do so add this to the viewDidLoad method in your view controller:

[model addObserver:self.view forKeyPath:@"<replace this with the name of the image property in the model>" options:NSKeyValueChangeNewKey context:NULL];

then in your uiview subclass make this method:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([keyPath isEqual:@"<replace this with the name of the image property in the model>"] {
         // set an image property that has been added to the uiview
         // the image property should be a weak reference ie. (nonatomic, assign)
         self.image = [change valueForKey:NSKeyValueChangeNewKey];
         [self setNeedsDisplay];
    }
}

and finally in your drawRect: method you should draw the image.


HOW I SET UP LINKAGE BETWEEN VIEW | CONTROLLER | MODEL:

view|controller - From the view controller's loadView method I sent a message "hello:" to the view, containing a reference to 'self'. In the hello method (of the view object), I stashed that pointer to the viewcontroller in a global variable for later use.

controller|model - I had the view controller alloc/init the instance of the model object and save the pointer to that in a global variable in the view controller.

I was able to use these two pointers as a bridge from the view to the model by going up and over through the vew controller. I don't know what I am doing well enough to know whther I could have accomplished this linkage with IB or not.

RECAP OF HOW IT WORKS

drawRect sends a request/message for an updated image to the viewcontroller.

The view controller can act however it wants, but since it has the parameters as adjusted by the gesture handlers, it can pass a specific request to the model with those parameters.

The model then gets busy with CG, uses CGBitmapContextCreateImage (from the bitmap context in the model) and returns a CGImageRef (to the view controller). Then the view controller just returns the same CGImageRef as the result of the original request. drawRect then does [[UIImage imageWithCGImage:modelImage] drawInRect:rect] to place the image in the view.

POSSIBLE DOWNSIDES, FINAL THOUGHTS

It /might/ slower but that may just be paranoia. The model typically draws 8K lines or more to make the bitmap image, but the view doesn't have to do that much work to copy the resulting pixels. I should use Instruments to see what's up.

Also I'm concerned about image quality. Seems OK. I don't think I lose anything by first drawing into a CGbitmap context, then going through CGBitmapContextCreateImage, then finally the UIImage imageWithCGImage as above. All same exact size.


First off you will probably need to redraw whenever your parameters would cause a change in the data. So instead of requesting new data when the view needs to redraw, have the view or view controller monitor the data so that if it changes the view will redraw. drawRect REALLY shoudn't be requesting data, especially not indirectly from the view controller, this breaks the whole MVC design because the view needs to know about it's view controller (so it can request the data). Secondly for speed use the cgcontext to draw the image directly: CGContextDrawImage(GContextRef c, CGRect rect, CGImageRef image);.

0

精彩评论

暂无评论...
验证码 换一张
取 消