views:

43

answers:

2

I have a custom-drawn UIView (i.e. uses drawView to render itself). The data to be displayed will usually be larger than the screen can show, so I want to include scroll view functionality in the view so clients of the class don't have to deal with it, and so the custom view can handle lazy loading of data as it's scrolled.

But how do I design a view like this? UITableView takes the approach of inheriting from UIScrollView. I tried doing the same but the scroll bar no longer had an effect on what was displayed (drawView wasn't being called as the bar moved). If I have my custom view include a scroll view, then I need two separate classes (the outer view class and a custom-drawn view that goes inside the scroll view), which is less than optimal.

+1  A: 

You should definitely put your view in a scrollview, don't over-complicate matters!

(and if you do, try it first and come here with a concrete question when you're stuck!)

mvds
Agreed, I'm planning to do so. Just trying to figure out how I can encapsulate scrolling into my custom class, so external users don't have to create the scroll view themselves and update the content size themselves.
Bill
Then provide them with the complete package, scroll view included. Just subclass uiscrollview. It will be less work than re-inventing the wheel.
mvds
Right, but if I subclass UIScrollView, drawView doesn't get called on scroll.
Bill
But you can subclass UIScrollView so it includes your custom subclass of UIView as content automagically.
mvds
A: 

Basically there are two ways:

-> Make your view a subview of a NSScrollView, this is surely the easiest way.

-> Subclass NSScrollView and toggle a redraw on every change

Somewhere in -init you do:

[self addObserver:self forKeyPath:@"contentOffset" withOptions:0 context:@"redraw"];

Then overwrite this method:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (context == @"redraw") {
        [self setNeedsDisplay];
    } else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

This will require your view to update it's display every time. drawRect: will be called and all you need to do is to draw this portion of the view.

If possible, you should stick to the first method, which is faster because it does not redraw every time, but just moves a prerendered texture (the CALayer of your view) on the graphics card. However, the first method will get slow if your view is MUCH larger than the screen. And there is a maximum size for views. If your content is very large, the app might crash with the first method.

Max Seelemann