using rect argument for drawRect method in custom UIView - objective-c

How can I possibly use the rect argument passed in drawRect calls, am sure whenever setNeedsDisplayInRect is passed, drawRect method is called for particular region. for now I redraw the text and shapes for entire frame and not constraining to the rect part that needs to be redrawn and I have no idea on how effectively can I use the rect param.
Any insights on this will be helpful.

The rect is the region to redraw. In some cases UIKit will redraw only a "dirty" region of your view. You can optimize your code to detect that some of the drawing would occur outside this rect by using CGRectContainsPoint() and therefore not do it.

Related

Redraw outside drawRect function

I have a function that draws a rectangle. If called within drawRect image is drawn perfectly. If I call the function directly it doesn't draw the rect. When I searched I found that we need to setNeedsDisplay to Yes. But still it doesn't work.
You can only draw to your window when the system invokes your drawRect: method. You should not call it directly. The system will activate the graphics context before calling drawRect: and perform various housekeeping functions before and after its invocation.
To explicitly request the entire window to be redrawn, you call:
[view setNeedsDisplay:YES];
This will cause your drawRect: method to be called in the next event loop.
The documentation covers all this in detail:
Cocoa Drawing Guide
In such questions the best method is to use a boolean flag. Then call the function inside drawRect: only when the flag is set.
Now when you need the image to be drawn set the flag value to true and then call the [view setNeedsDisplay:YES];
If need be change the flag to false within the if statement once the task has been done.

setNeedsDisplay does not trigger drawRect in subviews as expected

I'm struggling with setNeedsDisplay. I thought it was supposed to trigger calls of drawRect: for the view for which it is called and the hierarchy below that if it's within the view's bounds, but I'm not finding that to be the case. Here is my setup:
From the application delegate, I create a view whose size is a square that covers essentially the whole screen real estate. This view is called TrollCalendarView. There is not much that happens with TrollCalendarView except for a rotation triggered by the compass.
There are 7 subviews of TrollCalendarView called PlatformView intended to contain 2D draw objects arranged around the center of TrollCalendarView in a 7-sided arrangement. So when the iPad is rotated, these 7 views rotate such that they are always oriented with the cardinal directions.
Each of the PlatformView subviews contains 3 subviews called Tower. Each tower contains 2D draw objects implemented in drawRect:.
So, in summary, I have TrollCalendarView with empty drawRect:, and subviews PlatformView and Platformview -> Tower that each have drawRect implementations. Additionally, Tower lies within the bounds of Platform, and Platform lies within the bounds of TrollCalendarView.
In TrollCalendarView I've added a swipe recognizer. When I swipe happens, a property is updated, and I call [self setNeedsDisplay] but nothing seems to happen. I added NSLog entries to drawRect: method in each of these views, and only the TrollCalendarView drawRect: method is called. Ironically, that is the one view whose drawRect method will be empty.
There is no xib file.
What do I need to do to ensure the drawRect method in the other subviews is called? Is there documentation somewhere that describes all the nuances that could affect this?
I'm struggling with setNeedsDisplay. I thought it was supposed to trigger calls of drawRect for the view for which it is called and the hierarchy below that if it's within the view's bounds
No, that is not the case. Where did you get that idea?
-setNeedsDisplay: applies only to the view to which it is sent. If you need to invalidate other views, you need to add some code to send -setNeedsDisplay: to them, too. That's all there is to it.
I think this is an optimization in the framework; if your subviews don't need to draw again, then this is a major performance improvement. Realize that almost anything animatable does not require drawrect (moving, scaling, etc).
If you know that all of your subviews should be redrawn (and not simply moved), then override setNeedsDisplay in your main view and do like this:
-(void) setNeedsDisplay {
[self.subviews makeObjectsPerformSelector:#selector(setNeedsDisplay)];
[super setNeedsDisplay];
}
I have tested this, and it causes all subviews to be redrawn as well. Please note that you will earn efficiency karma points if you somehow filter your subviews and make sure you only send that to subviews which actually need redrawn... and even more if you can figure out how not to need to redraw them. :-)

How to implement a custom Focus Ring in drawRect for NSTextField or NSTextVew

I want to draw a custom focus ring for my NSTextView subclass (which doesn't have a focus ring by default). I managed to implement it by overriding the parent NSScrollView drawRect and adding this code:
- (void)drawRect:(NSRect)dirtyRect {
if (focused) {
NSSetFocusRingStyle(NSFocusRingOnly);
NSRectFill(dirtyRect);
}
[super drawRect:dirtyRect];
}
However, I want to draw my own, custom focus ring. I have searched and searched for examples of this, and tried messing around and writing it myself, to no avail. The biggest issue I have is the fact that it will get cropped to the NSScrollView/NSTextView frame, no matter how I do it.
Thanks.
Updating this answer for 10.7+:
Now you should override drawFocusRingMask to render (simply drawing a shape; the system will take care of color/style), and override focusRingMaskBounds to hint at its boundaries. Also, call noteFocusRingMaskChanged if you change the shape in some way that the system could not figure out on its own.
(Below is the previous answer, requiring older APIs:)
In the Carbon framework there are HIThemeBeginFocus() and HIThemeEndFocus(), which allow you to cause any series of drawings (such as a rectangle or shape) to have an automatic "focused" appearance. Requires Mac OS X 10.5 or later.
This uses Core Graphics directly. To find the CG context from a drawRect: method in Cocoa, you'd do something like:
NSGraphicsContext* contextMgr = [NSGraphicsContext currentContext];
CGContextRef drawingContext = (CGContextRef)[contextMgr graphicsPort];
As far as avoiding clipping, one option is to use a parent view (such as an NSBox that has no border) to give extra padding. Perform the custom drawing at an inset location in the parent view that won't be clipped; in other words, give the illusion that the view is a bit smaller than its actual rectangle.

drawRect's aRect argument in relation to UIView's bounds

I'm working on creating my first custom UIView and learning about the semantics of the drawRect method.
I have encountered advice (advice that makes sense to me) to only draw within the scope of the CGRect that is passed in as an argument, rather than always drawing everything within the UIView's bounds:
- (void)drawRect:(CGRect)aRect {
// draw, draw, draw...
}
What I'm trying to find out now is whether there are any constraints about what relationship there is between bounds and the aRect that gets passed in. In particular, I'm wondering if aRect is guaranteed to be entirely inside bounds, or whether it may extends outside of bounds.
If some code outside of my UIView passes, for example, a very large CGRect into setNeedsDisplayInRect:(NSRect)invalidRect, will the underlying code just blindly pass the same CGRect into my drawRect method, or does it do some sort of intersection and always pass in a sensible rectangle?
I haven't found an answer to this in any documentation. Is this something I shouldn't even worry about? Or should I always intersect bounds and aRect on my own?
From the View Programming Guide:
Before calling your view’s drawRect: method, UIKit configures the basic drawing environment for your view. Specifically, it creates a graphics context and adjusts the coordinate system and clipping region to match the coordinate system and visible bounds of your view.
You won't be asked to draw outside your view. Even if you were, the clip rect should be set such that the drawing would have no effect.

UIView. How do I redisplay a "data driven" UIView?

I am doing a scientific visualization app for iPhone. Periodically a UIView subclass will receive some data from the network that it must render via its overridden drawRect: method.
My challenge is that there is no way to indicate to Cocoa-Touch that this data has arrived - or changed - other then [myView setNeedsDisplay] which is ignored except for changes to the bounds of the UIView. I have tried hiding and unhiding. Nuthin'.
Maddeningly, since all I did was alter some internal state of the UIView [myView setNeedsDisplay]. This change is completely invisible to Cocoa-Touch. This change is not one of the criteria that warrents a redraw - according to Cocoa-Touch - thus my UIView sits there, unchanged.
This is very, very, very frustrating. I have hit a wall here.
Could someone please suggest a technique, a trick, a hack, that will prompt my UIView to re-render.
Cheers,
Doug
[myView setNeedsDisplay] should cause drawRect: to be sent to myView the next time you're back in the run loop and myView is visible. If changing the bounds is causing the view to redraw (myView.contentMode is UIViewContentModeRedraw), then setNeedsDisplay must be working, since that's how the redraw is signalled by the bounds change. See the UIView class reference for details.
Is your drawRect: being invoked the first time the view is shown? If not, it may be misspelled, overridden in a subclass, or even on the wrong object.
Is the view visible and on screen? It won't be drawn if it is off screen.
Is control returning to the event loop? drawRect: won't be invoked if your application is busy.