I'd like to create a simple fingerpainting feature in my app. Problem is any drawings I do to my UIView subclass view instance are erased after each drawRect call... How do you go about preserving your context drawings in your UIViews?
You have to add your drawings to your model and make sure your model is rendered, as appropriate, when the view is invalidated. You do not want to be drawing on the view directly without a model around to re-render it when necessary.
Jacob
Related
The object is to implement a semi-transparent layer which would slid out to collect user response when needed. The semi-transparent layer would have some icons on it for the user to choose from. Currently I am using a CALayer object which seems ok and it has some build in animation behavior.
But the problem is CALayer does not response to any touch events at all. Now I am thinking that I should be using a UIView instead. UIView inherits from UIResponder, so its objects are naturally capable of responding to users' events.
It's a decision between UIView and CALayer. For the CALayer, I have done quite a bit of work on it and it looks quite ok except about the touch response that has to be added. Or should I use a UIView as subview instead (since it has build-in touch respond) ?
Hope that somebody knowledgable on this could help ...
In order to respond to user interaction, the best way is to use a UIView. You could probably get it to work without one, but I wouldn't recommend it.
As for integrating your existing layer with the UIView, I'd create a subclass of UIView and override its +layerClass method to return the Class of your custom CALayer. Alternatively, if you're not using a custom CALayer subclass (and there generally isn't a real need to create your own), you can do your custom drawing inside the UIView's -drawLayer:inContext: method.
I've understood that I need to subclass a UIView t be able to draw inside it.
The thing I don't understand yet, is the philosophy of the way i must be done...
Let's say I have a view controller, and depending on context, I may want to draw a line into one of the subviews it manages, or a circle, or a rect, or a processed graphic. Or lets say two points that are moving inside a view into a defined rect and that display a bigger point when they are close.
How may I subclass and define the subview to make it able to do this only into its drawRect method ?
How does the controller, that manages more than this simple UIView (let's imagine you have a view controller that manages a view inside which there are many other view, and you want to make some drawings in two of them), and that knows what is needed to be drawn into the correct view (it's a controller, isn't it ?), may interact with the views ? And when the drawing is done, how may the views interact with the controller ?
I've read many doc about drawings (apple, web, forums, tutorials, ...), but I still can't touch the philosophy of the way this must be done.
it's very simple. Make a new class, OliverView, which is a UIView. (ie, it is a subclass of UIView.) In that view, make it draw stuff in a fancy way, inside drawRect.
Now make a UIViewController, called OliverVC. In storyboard put an OliverView inside OliverVC. (beginner explanation of how to do that).
In the OliverView, have properties "hours", "minutes", "seconds".
Now, in OliverView - in the drawRect - have a fancy way to display those values. (Pie chart, glowing letters, animation - whatever you want.)
Now, up in OliverVC, do some calculations to determine the time in Zimbabwe, for example.
Once you want a time displayed, simply set those properties in OliverView - - and you are done.
Your colleague could be programming the OliverView. You need know nothing about how she is going to display the time. Conversely, your colleague need know nothing about your calculations in OliverVC..
So, it's simpleL One part has the job of displaying the data. One part has the job of coming up with the data (doing whatever sort of calculation is relevant in the app).
It's the only architecture possible in a "real time" screen device where the views can and do change at any time.
In answer to your question below: you've forgotten that quite simply, if you have a button that would be a whole separate element. (Perhaps sitting "on top of" the OliverView.) So, it's easy!
The -drawRect method in your UIView subclass defines the onscreen appearance of the view. All drawing is done in -drawRect. Your UIViewController calls methods on its UIView to tell it to draw something differently or to perform some other action.
The UIViewController manages everything to do with the view that is not inherently associated with the drawing of the content. Data associated with the view is often stored in the controller.
I want to create a scroll view with a massive contentSize that will display content inside it. The content will be mostly text (a few small images will be drawn for content-boundaries).
So, think like a tiled map application, but tiling labels instead of tiled images.
Performance is critical in this application, so I think I should avoid using UILabels or any UIViews at all inside the scroll view.
How can I draw these labels as the user scrolls around? Some options I've considered:
override drawRect: in the scroll view and draw everything within the window - this seems like it would be really slow. Sometimes drawRect is called with only a 1 pixel difference.
Same, but keep track of which ones I've already drawn
Draw them from the "outside" somehow, like from the scroll view delegate - I can't figure out how to use [#"mystring" drawInRect:] outside of drawRect: (context problems)
Is there something I haven't thought of? I know the scroll views were designed to be able to handle this kind of problem, but I'm not sure what the designed way is. Thanks!
The standard way to achieve this in an iPhone application is to create a normal UIScrollView that is the size you want it to be, and to populate it either directly with a CATiledLayer if you're feeling brave or with a custom UIView subclass that uses a CATiledLayer and implements - (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context.
CATiledLayer provides the same experience as Safari — it's good for views that are much larger than the screen and which are expensive to render, but which you don't want to ruin the fluidity of the user experience. It'll request tiles as and when it needs them on a background thread, then fade them in (according to a fade of any length, so you can cause them to appear instantly if you desire) when they're ready. If your program really can always keep up with the scrolling and you've requested an instant appearance then there'll be no evidence that there's an asynchronous step involved.
An example people tend to point to is this one, but if you're new to implementing your own UIView subclasses then it might be worth seeing e.g. this tutorial. Then look into the + layerClass property on UIView and the various properties of CATiledLayer (which I think you'll also possibly need to subclass since + fadeDuration is a class method).
I'm pretty sure I saw an example where the graph wasn't filling the whole iPhone screen, but I can't get that to happen in my app, nor in the Core-Plot Test app from Switch On The Code.
I've added a subview to the original CPLayerHostingView in the sample, then changed the classes – original back to UIView, new subview to CPLayerHostingView, and I've reconnected the File's owner's view outlet to the new subview.
When I create a graph with:
graph = [[CPXYGraph alloc] initWithFrame: theSubviewOutlet.bounds];
… and step through the first stages of building up the layers the bounds are accurate (i.e. the same as in the .xib)
however, when all the initialization is done, and the graph shows up, it fills the whole superview.
Am I missing something obvious?
These types of questions are better asked on the core plot mailing list, because we may miss them over here.
There is nothing special about a CPLayerHostingView. You should be able to add it as a subview to your UIView, and resize it as you wish. You should also be able to set springs and struts in interface builder, or via code.
Perhaps you have your parent UIView setup to resize subviews in some way? In any case, Core Plot should not be doing anything to modify the host view frame.
I'm using an IBoutlet to get a reference to a Sub-View I added to the main View in the interface builder but since this won't give me access to drawRect: I won't be able to get a context to draw on. Is there anyway I can still get the graphics context so I can draw on the sub view? How would I go about this?
You can't draw like that; you have to draw in response to a drawRect call, not at any time as some frameworks allow.
The correct way to do it is: create a UIView subclass in Xcode. Switch to Interface Builder, select your subview, and change its "Class Identity" (under "Tools > Identity Inspector") to the name of your new subclass.
Then in your subclass, you can implement drawRect.
Technically there is a means to for one object to become the drawing delegate of another via the view.layer.delegate route. One can construct a delegate to implement
(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
This gives you the ability to reuse drawing instructions should it become necessary. But it can be tedious to read and understand for another programmer. Most would avoid it unless it eliminates code duplication.
You should never be drawing in another view, not even a subview. The subview should draw itself, and it has access to its own graphics context. You cannot get access to another view's context; they are handled by the framework and are set up before calling -drawRect: for the appropriate view.