I can't draw a NSRect based on where the user clicks - cocoa

I am trying to draw a rectangle and let the user create it based on where he clicks and where he drags the mouse.
The code I am using to draw the NSRect is:
CGFloat width = endPoint.x-startPoint.x; //Width of rectangle.
CGFloat height = endPoint.y-startPoint.y; //Height of rectangle.
CGFloat rectXPoint = startPoint.x; //x component of corner of rect
CGFloat rectYPoint = startPoint.x; //y component of corner of rect
if (width < 0) { //Handle negavive rect size here.
width = startPoint.x-endPoint.x;
rectXPoint = endPoint.x;
}
if (height < 0) { //and here.
height = startPoint.y-endPoint.y;
rectYPoint = endPoint.y;
}
NSRect theRect = NSMakeRect(rectXPoint, rectYPoint, width, height);
[[NSColor blackColor] set];
NSRectFill(theRect);
So I can sort of get the code to work but not exactly. Sometimes it offsets the y of the rectangle from the cursor by some seemingly arbitrary amount (even though I know it isn't arbitrary). It is hard to get a screen shot considering that would involve me moving the mouse from the point it would need to be to demonstrate the point. Just in case you need to see the code for how I am getting the location of the mouse here it is:
- (void)mouseDown:(NSEvent *)event {
NSPoint location = [self convertPointFromBase:[event locationInWindow]];
startPoint = location;
}
- (void)mouseDragged:(NSEvent *)event {
NSPoint location = [self convertPointFromBase:[event locationInWindow]];
endPoint = location;
[self setNeedsDisplay:YES];
}
And isFlipped returns YES.
If you need any more clarification ask what you need clarification.

Probably this line:
CGFloat rectYPoint = startPoint.x; //y component of corner of rect

Related

Double tap to Zoom with UIScrollView

I am using Apple's source code. I am sure there is just a snippet that I have messed up. I have a form that I have made programmatically. When I double tap the recognizer fires and goes through the motions but I it won't actually zoom. Any suggestions? Thanks for the help
imageScrollView.delegate=self;
CGRect fullScreenRect=[[UIScreen mainScreen] applicationFrame];
imageScrollView=[[UIScrollView alloc]initWithFrame:fullScreenRect];
imageScrollView.contentSize=CGSizeMake(750,1250);
imageScrollView.minimumZoomScale=1.0;
imageScrollView.maximumZoomScale=5.0;
[imageScrollView setZoomScale:imageScrollView.minimumZoomScale];
self.view=imageScrollView;
[imageScrollView release];
Added a bunch of UItextfields/labels to the scrollview.
aircraftType = [[UITextField alloc]initWithFrame:CGRectMake(170, 32, 150, 15)];
aircraftType.font=[UIFont systemFontOfSize:10];
aircraftType.borderStyle = UITextBorderStyleRoundedRect;
aircraftType.placeholder = #"Aicraft Type";
aircraftType.delegate = self;
[self.view addSubview:aircraftType];
#pragma mark UIScrollViewDelegate methods
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return [imageScrollView viewWithTag:ZOOM_VIEW_TAG];
}
#pragma mark TapDetectingImageViewDelegate methods
- (void)handleDoubleTap:(UIGestureRecognizer *)gestureRecognizer {
// double tap zooms in
float newScale = [imageScrollView zoomScale] * ZOOM_STEP;
CGRect zoomRect = [self zoomRectForScale:newScale withCenter:[gestureRecognizer locationInView:gestureRecognizer.view]];
[imageScrollView zoomToRect:zoomRect animated:YES];
}
#pragma mark Utility methods
- (CGRect)zoomRectForScale:(float)scale withCenter:(CGPoint)center {
CGRect zoomRect;
// the zoom rect is in the content view's coordinates.
// At a zoom scale of 1.0, it would be the size of the imageScrollView's bounds.
// As the zoom scale decreases, so more content is visible, the size of the rect grows.
zoomRect.size.height = [imageScrollView frame].size.height / scale;
zoomRect.size.width = [imageScrollView frame].size.width / scale;
// choose an origin so as to get the right center.
zoomRect.origin.x = center.x - (zoomRect.size.width / 2.0);
zoomRect.origin.y = center.y - (zoomRect.size.height / 2.0);
return zoomRect;
}

How to calculate correct coordinates for selected text in NSTextView?

I need to highlight line with caret in NSTextView using CALayer overlay and grab rect for line with this code in my subclassed NSTextView...:
- (NSRect)overlayRectForRange:(NSRange)aRange
{
NSScreen *currentScreen = [NSScreen currentScreenForMouseLocation];
NSRect rect = [self firstRectForCharacterRange:aRange];
rect = [self convertRectToLayer:rect];
rect.origin = [currentScreen flipPoint:rect.origin];
rect = [self.window convertRectToScreen:rect];
return rect;
}
...and put overlay:
- (void)focusOnLine
{
NSInteger caretLocation = [aTextView selectedRange].location;
NSRange neededRange;
(void)[layoutMgr lineFragmentRectForGlyphAtIndex:caretLocation effectiveRange:&neededRange];
CALayer *aLayer = [CALayer layer];
[aLayer setFrame:NSRectToCGRect([aTextView overlayRectForRange:neededRange])];
[aLayer setBackgroundColor:[[NSColor redColor] coreGraphicsColorWithAlfa:0.5]];
[[aTextView layer] addSublayer:aLayer];
}
As a result, the selection overlay coincides with width of desired line, but absolutely not matches by Y axis (X axis is ok).
What am I missing?
Okay, I completely rewrote method overlayRectForRange: and now all works fine. There is fixed code:
- (NSRect)overlayRectForRange:(NSRange)aRange
{
NSRange activeRange = [[self layoutManager] glyphRangeForCharacterRange:aRange actualCharacterRange:NULL];
NSRect neededRect = [[self layoutManager] boundingRectForGlyphRange:activeRange inTextContainer:[self textContainer]];
NSPoint containerOrigin = [self textContainerOrigin];
neededRect.origin.x += containerOrigin.x;
neededRect.origin.y += containerOrigin.y;
neededRect = [self convertRectToLayer:neededRect];
return neededRect;
}

CoreGraphics drawRect in a cocos2d project EXEC BAD ACCESS

I got a interesting riddle here. My cocos2d project uses a UIView to display nicer popups than the regular alert view. To be more flexible in the size of the popup, I draw the background in the drawRect method.
But first the hierarchy how I implement cocos2d and UIKit:
Every UIKit element is added to the RootViewController. Every CCNode to the EAGLView. (If someone has a better solution to mix those world, don't wait to tell me!) Unfortunately is every UIKit view in front of cocos nodes.
Everything works fine when I add the first popup to the RootViewController. But if I remove the first popup and add a new one to the RootViewController occurs a bad access.
It crashes only in combination with cocos2d.
I use the code from Ray Wenderlichs CoreGraphics 101 tutorial.
context and strokeColor are not nil.
Another important infos: I use ARC and am supporting iOS 4.2 and above.
The complete code can be found at raywenderlich.com or below
void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor, CGColorRef endColor)
{
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = { 0.0, 1.0 };
NSArray *colors = [NSArray arrayWithObjects:(__bridge id)startColor, (__bridge id)endColor, nil];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace,
(__bridge CFArrayRef) colors, locations);
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
CGContextSaveGState(context);
CGContextAddRect(context, rect);
CGContextClip(context);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGContextRestoreGState(context);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
}
CGRect rectFor1PxStroke(CGRect rect)
{
return CGRectMake(rect.origin.x + 0.5, rect.origin.y + 0.5, rect.size.width - 1, rect.size.height -1);
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect frame = CGRectInset(self.bounds, 2, 2);
CGContextSetShadowWithColor(context, CGSizeMake(0, 0), 3.0, shadowColor);
CGRect strokeRect = CGRectInset(frame, -2.0, -2.0);
CGContextSetStrokeColorWithColor(context, strokeColor);
CGContextSetLineWidth(context, 2.0);
CGContextStrokeRect(context, rectFor1PxStroke(strokeRect));
drawLinearGradient(context, frame, gradient1, gradient2);
}
ARC releases the CGColorRef named strokeColor. One fix is to replace it with an CGFloat array and use CGContextSetStrokeColor instead CGContextSetStrokeColorWithColor
This answer solves this issue:
App crashes when using __bridge for CoreGraphics gradient on ARC
I agree with zeiteisen ,ARC releases CGColorRef and the easiest way to solve this by
replacing CGContextSetStrokeColorWithColor with
CGContextSetStrokeColor(context, components)
Change UIColor of strokeColor to 'RGB CGFloat components array' as following:
static CGFloat red, green, blue, alpha;
- (void)getRGBComponents:(CGFloat [4])components forColor:(UIColor *)color {
[color getRed:&red green:&green blue:&blue alpha:&alpha];//To fetch CGFloat RGB components
for (int component = 0; component < 4; component++) {
switch (component) {
case 0:
components[component] = red;
break;
case 1:
components[component] = green;
break;
case 2:
components[component] = blue;
break;
case 3:
components[component] = alpha;
break;
default:
break;
}
}
}
Use this method like this:
CGFloat components[4];
UIColor *strokeColor=[UIColor greyColor];//My color
[self getRGBComponents:components forColor:strokeColor];//'components' will be substituted in CGContextSetStrokeColor
NSLog(#"%f %f %f %f", components[0], components[1], components[2],components[3]);

reset image in tableView imageCell is blurry

i'm trying to show an 'info' icon during a cursor 'rollover' on an NSTableView cell. i'm getting a copy of the cell's image, drawing the 'info' icon on it, and telling the cell to setImage with this copy. just drawing the icon will scale it and it won't be the same size in every one as the images in the table are different sizes. i'm not having a problem with the scaling or positioning the correct size icon.
my problem is that the replaced image is slightly blurry and it's edges are not crisp on close examination. the lack of edge makes it appear to be 'moving' slightly when the mouseEntered happens and the image is replaced.
i've tried a number of drawing techniques that doen't use lockFocus on an NSImage (drawing in CGBitmapContext, or using CIFilter compositing), and they produce the same results.
i'm using NSTableView's preparedCellAtColumn as it seems that drawing in willDisplayCell is unpredictable -- i read somewhere but can't remember where.
here is my preparedCellAtColumn method:
- (NSCell *)preparedCellAtColumn:(NSInteger)column row:(NSInteger)row
{
NSCell *cell = [super preparedCellAtColumn:column row:row];
if ((self.mouseOverRow == row) && column == 0) {
NSCell * imageCell = [super preparedCellAtColumn:0 row:row];
NSImage *sourceImage = [[imageCell image] copy];
NSRect cellRect = [self frameOfCellAtColumn:0 row:row];
NSSize cellSize = cellRect.size;
NSSize scaledSize = [sourceImage proportionalSizeForTargetSize:cellSize];
NSImage *outputImage = [[NSImage alloc] initWithSize:cellSize];
[outputImage setBackgroundColor:[NSColor clearColor]];
NSPoint drawPoint = NSZeroPoint;
drawPoint.x = (cellSize.width - scaledSize.width) * 0.5;
drawPoint.y = (cellSize.height - scaledSize.height) * 0.5;
NSRect drawRect = NSMakeRect(drawPoint.x, drawPoint.y, scaledSize.width, scaledSize.height);
NSPoint infoPoint = drawPoint;
infoPoint.x += NSWidth(drawRect) - self.infoSize.width;
[outputImage lockFocus];
[sourceImage drawInRect:drawRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
[self.infoImage drawAtPoint:infoPoint fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
[outputImage unlockFocus];
[cell setImage:outputImage];
}
return cell;
}
[this is the enclosed scaling method from scott stevenson]
- (NSSize)proportionalSizeForTargetSize:(NSSize)targetSize
{
NSSize imageSize = [self size];
CGFloat width = imageSize.width;
CGFloat height = imageSize.height;
CGFloat targetWidth = targetSize.width;
CGFloat targetHeight = targetSize.height;
CGFloat scaleFactor = 0.0;
CGFloat scaledWidth = targetWidth;
CGFloat scaledHeight = targetHeight;
if ( NSEqualSizes( imageSize, targetSize ) == NO )
{
CGFloat widthFactor;
CGFloat heightFactor;
widthFactor = targetWidth / width;
heightFactor = targetHeight / height;
if ( widthFactor < heightFactor )
scaleFactor = widthFactor;
else
scaleFactor = heightFactor;
scaledWidth = width * scaleFactor;
scaledHeight = height * scaleFactor;
}
return NSMakeSize(scaledWidth,scaledHeight);
}
i need to support 10.6, so can't use new groovy lion methods.
thanks for your consideration...
Your code is setting the origin of the image to non-integral coordinates. That means that the edge of the image will not be pixel-aligned, producing a fuzzy result.
You just need to do this:
NSPoint drawPoint = NSZeroPoint;
drawPoint.x = round((cellSize.width - scaledSize.width) * 0.5);
drawPoint.y = round((cellSize.height - scaledSize.height) * 0.5);
That will ensure the image origin has no fractional component.

How to stop NSScrollView from scrolling to top when horizontally resizing contained NSTextView?

I have a NSTextView that I want to display a horizontal scroll bar. Following some leads on the internet, I have most of it working, except that I am having problems with the vertical scroll bar.
What I have done is to find the width of the longest line (in pixels with the given font) and then I resize the NSTextContainer and the NSTextView appropriately. This way the horizontal scroll bar is representative of the width and scrolling to the right will scroll to the end of the longest line of text.
After doing this work, I noticed that my NSScrollView would show and hide the vertical scroll bar as I typed. I've 'fixed' this problem by setting autohidesScrollers to NO before the resize and then YES afterwards. However, there still remains another problem where, as I type, the vertical scrollbar thumb jumps to the top of the scrollbar and back to the proper place as I type. I type 'a' <space>, it jumps to the top, I press the <space> again and it jumps back to the proper location.
Any thoughts?
Here is some sample code:
- (CGFloat)longestLineOfText
{
CGFloat longestLineOfText = 0.0;
NSRange lineRange;
NSString* theScriptText = [myTextView string];
NSDictionary* attributesDict = [NSDictionary dictionaryWithObject:scriptFont forKey:NSFontAttributeName]; //scriptFont is a instance variable
NSUInteger characterIndex = 0;
NSUInteger stringLength = [theScriptText length];
while (characterIndex < stringLength) {
lineRange = [theScriptText lineRangeForRange:NSMakeRange(characterIndex, 0)];
NSSize lineSize = [[theScriptText substringWithRange:lineRange] sizeWithAttributes:attributesDict];
longestLineOfText = max(longestLineOfText, lineSize.width);
characterIndex = NSMaxRange(lineRange);
}
return longestLineOfText;
}
// ----------------------------------------------------------------------------
- (void)updateMyTextViewWidth
{
static CGFloat previousLongestLineOfText = 0.0;
CGFloat currentLongestLineOfText = [self longestLineOfText];
if (currentLongestLineOfText != previousLongestLineOfText) {
BOOL shouldStopBlinkingScrollBar = (previousLongestLineOfText < currentLongestLineOfText);
previousLongestLineOfText = currentLongestLineOfText;
NSTextContainer* container = [myTextView textContainer];
NSScrollView* scrollView = [myTextView enclosingScrollView];
if (shouldStopBlinkingScrollBar) {
[scrollView setAutohidesScrollers:NO];
}
CGFloat padding = [container lineFragmentPadding];
NSSize size = [container containerSize];
size.width = currentLongestLineOfText + padding * 2;
[container setContainerSize:size];
NSRect frame = [myTextView frame];
frame.size.width = currentLongestLineOfText + padding * 2;
[myTextView setFrame:frame];
if (shouldStopBlinkingScrollBar) {
[scrollView setAutohidesScrollers:YES];
}
}
}
Thanks to Ross Carter's post on the Cocoa-Dev list, I resolved this issue.
A. You have to set up your text view to support horizontal scrolling:
- (void)awakeFromNib {
[myTextView setHorizontallyResizable:YES];
NSSize tcSize = [[myTextView textContainer] containerSize];
tcSize.width = FLT_MAX;
[[myTextView textContainer] setContainerSize:tcSize];
[[myTextView textContainer] setWidthTracksTextView:NO];
}
B. You have to update the width of the text view as it changes, otherwise the horizontal scroll-bar doesn't update properly:
- (void)textDidChange:(NSNotification *)notification
{
[self updateTextViewWidth];
}
- (CGFloat)longestLineOfText
{
CGFloat longestLineOfText = 0.0;
NSLayoutManager* layoutManager = [myTextView layoutManager];
NSRange lineRange;
NSUInteger glyphIndex = 0;
NSUInteger glyphCount = [layoutManager numberOfGlyphs];
while (glyphIndex < glyphCount) {
NSRect lineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphIndex
effectiveRange:&lineRange
withoutAdditionalLayout:YES];
longestLineOfText = max(longestLineOfText, lineRect.size.width);
glyphIndex = NSMaxRange(lineRange);
}
return longestLineOfText;
}
// ----------------------------------------------------------------------------
- (void)updateTextViewWidth
{
static CGFloat previousLongestLineOfText = 0.0;
CGFloat currentLongestLineOfText = [self longestLineOfText];
if (currentLongestLineOfText != previousLongestLineOfText) {
previousLongestLineOfText = currentLongestLineOfText;
NSTextContainer* container = [myTextView textContainer];
CGFloat padding = [container lineFragmentPadding];
NSRect frame = [myTextView frame];
frame.size.width = currentLongestLineOfText + padding * 2;
[myTextView setFrame:frame];
}
}

Resources