How to display NSText to NSWindow? - objective-c

I have the following code:
#include <Cocoa/Cocoa.h>
NSColor *createColor(float r, float g, float b, float a) {
return [NSColor colorWithCalibratedRed:r green:g blue:b alpha:a];
}
NSRect createRect(float startX, float startY, float width, float height) {
return NSMakeRect(startX, startY, width, height);
}
int main() { #autoreleasepool {
NSWindow *window = [[NSWindow alloc] init];
window.title = #"Title";
window.subtitle = #"Subtitle";
NSText *label = [[NSText alloc] initWithFrame: createRect(10, 10, 20, 20)];
t.string = #"test";
[window setFrame:createRect(0, 0, 300, 300) display:YES animate:YES];
[window setBackgroundColor: createColor(0.5, 1, 0.1, 1)];
[window makeKeyAndOrderFront:nil];
while (1) {
NSEvent *event = [window nextEventMatchingMask:NSEventMaskAny];
}
}
}
Is there an simple way to display to NSWindow such as:
[window *command*: label];
I have looked at this post but it does not address my issue as it uses NSApplication rather than NSWindow. Also, I would prefer to use NSText instead of NSTextField.

Related

Printing NSView to PDF not working as expected

I converted in PDF an NSView (PDFContentView:NSView) with the method NSPrintOperation. This NSView contains 2 subviews (Drawing:NSView). This subviews are drawn using the drawRect method.
The expected result is correct when I display my view in a NSWindow :
But in the PDF file, the second subview seem to be included in the first :
It seems to be a problem of layers, but when I change setWantsLayer: to NO, it's the same result.
Nota : I found answers that recommend to convert the subviews, which are drawn by drawRect method, into NSImage and then add it to the View. But This solution don't fit my problem, because I need to keep the possibility of zooming in the pdf without pixelising the subviews.
Thank you for your help.
Here is my code :
The method to convert my pdfContentView in PDF :
- (void)createPDFWithPages
{
NSPrintInfo *printInfo;
NSPrintInfo *sharedInfo;
NSPrintOperation *printOp;
NSMutableDictionary *printInfoDict;
NSMutableDictionary *sharedDict;
sharedInfo = [NSPrintInfo sharedPrintInfo];
sharedDict = [sharedInfo dictionary];
printInfoDict = [NSMutableDictionary dictionaryWithDictionary:sharedDict];
[printInfoDict setObject:NSPrintSaveJob forKey:NSPrintJobDisposition];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *desktopURLArray = [fileManager URLsForDirectory:NSDesktopDirectory inDomains:NSUserDomainMask];
NSURL *desktopURL = [desktopURLArray objectAtIndex:0];
NSString *fileName = [NSString stringWithFormat:#"/%#", #"myPDF.pdf"];
NSURL *fileURL = [NSURL fileURLWithPath:[[desktopURL path] stringByAppendingString:fileName]];
[printInfoDict setObject:[fileURL path] forKey:NSPrintSavePath];
printInfo = [[NSPrintInfo alloc] initWithDictionary: printInfoDict];
[printInfo setTopMargin:30.0];
[printInfo setBottomMargin:30.0];
[printInfo setLeftMargin:30.0];
[printInfo setRightMargin:30.0];
[printInfo setHorizontalPagination: NSAutoPagination];
[printInfo setVerticalPagination: NSAutoPagination];
[printInfo setVerticallyCentered:NO];
printOp = [NSPrintOperation printOperationWithView:pdfContentView printInfo:printInfo];
[printOp setShowsPrintPanel:NO];
[printOp runOperation];
}
PDFContentView.m :
#import "PDFContentView.h"
#import "Drawing.h"
#interface PDFContentView ()
{
Drawing *drawing1;
Drawing *drawing2;
}
#end
#implementation PDFContentView
- (id)init
{
if(self = [super init])
{
self.frame = NSMakeRect(10, 10, 520, 780); //page size = (520, 780)
[self setWantsLayer:YES];
self.layer.backgroundColor = [NSColor whiteColor].CGColor;
drawing1 = [[Drawing alloc] initWithFrame:NSMakeRect(0, 20, 50, 50)];
[drawing1 setBackgroundColor:[NSColor greenColor]];
[self addSubview:drawing1];
drawing2 = [[Drawing alloc] initWithFrame:NSMakeRect(30, 0, 50, 50)];
[drawing2 setBackgroundColor:[NSColor yellowColor]];
[self addSubview:drawing2];
}
return self;
}
- (BOOL)isFlipped
{
return YES;
}
#end
Drawing.m :
#import "Drawing.h"
#interface Drawing ()
{
NSColor *backgrounColor;
}
#end
#implementation Drawing
- (id)initWithFrame:(NSRect)contentRect
{
if(self = [super initWithFrame:(NSRect)contentRect])
{
self.frame = contentRect;
[self setWantsLayer:YES];
backgrounColor = [[NSColor alloc] init];
backgrounColor = [NSColor colorWithCalibratedRed:255.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:255.0/255.0];
}
return self;
}
- (BOOL)isFlipped
{
return YES;
}
- (void)setBackgroundColor:(NSColor*)color;
{
backgrounColor = color;
[self setNeedsDisplay:YES];
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
CGMutablePathRef path00 = CGPathCreateMutable();
CGPathMoveToPoint(path00, NULL, 0, 0);
CGPathAddLineToPoint(path00, NULL, self.frame.size.width, 0);
CGPathAddLineToPoint(path00, NULL, self.frame.size.width, self.frame.size.height);
CGPathAddLineToPoint(path00, NULL, 0, self.frame.size.height);
CGPathCloseSubpath(path00);
CGContextSaveGState(context);
CGContextAddPath(context, path00);
CGContextSetFillColorWithColor(context, backgrounColor.CGColor);
CGContextFillPath(context);
CGContextRestoreGState(context);
CGRect borderRect = NSMakeRect(0.5, 0.5, 24, 24);
CGMutablePathRef borderPath = CGPathCreateMutable();
CGPathAddRect(borderPath, NULL, borderRect);
CGPathCloseSubpath(borderPath);
CGContextSaveGState(context);
CGContextAddPath(context, borderPath);
CGContextSetStrokeColorWithColor(context, [NSColor redColor].CGColor);
CGContextSetLineWidth(context, 1);
CGContextStrokePath(context);
CGContextRestoreGState(context); // => Missing funtion
CGMutablePathRef path2 = CGPathCreateMutable();
CGRect aRect = NSMakeRect(10, 11, 4, 4);
CGPathAddRect(path2, NULL, aRect);
CGPathCloseSubpath(path2);
CGContextSaveGState(context);
CGContextAddPath(context, path2);
CGContextSetShouldAntialias(context, NO);
CGContextSetStrokeColorWithColor(context, [NSColor blueColor].CGColor);
CGContextSetLineWidth(context, 1);
CGContextStrokePath(context);
CGContextRestoreGState(context); // => Missing funtion
}
#end

need help for speeding up custom UIView's subclass

I've started to write a UIView subclass. It will be a control that will let the user scroll blocks and press any of them, very similar to UITableView.
Please check the links to images I posted below.
http://i.piccy.info/i7/0577902c1bdba79ac3a26a5d3ce322f9/4-50-285/17627834/2012_12_12_14_06_11.jpg
http://i.piccy.info/i7/91a0a1eec95d74e7c13a789fc69a5582/4-50-285/20953097/2012_12_12_14_06_39.jpg
Here's the source code:
(.h file)
#import <UIKit/UIKit.h>
#import "Quartzcore/Quartzcore.h"
#interface OSBlockView : UIView
{
CGFloat scrollPosition; // from 0 to 1
CGFloat zoneForFlexion;
CGSize blockSize;
NSUInteger blockQuantity;
CGFloat projection;
NSDictionary *anglesForHeights;
CGFloat oldTranslatedY;
UIGestureRecognizer *panGesture;
NSArray *blocks;
}
#property (nonatomic, readwrite) CGFloat scrollPosition;
#property (nonatomic, readwrite) CGFloat zoneForFlexion;
#property (nonatomic, readwrite) CGSize blockSize;
#property (nonatomic, readwrite) NSUInteger blockQuantity;
#property (nonatomic, readwrite) CGFloat projection;
#property (nonatomic, retain) NSDictionary *anglesForHeights;
#property (nonatomic, readwrite) CGFloat oldTranslatedY;
#property (nonatomic, retain) UIGestureRecognizer *panGesture;
#property (nonatomic, retain) NSArray *blocks;
- (id)initWithFrame:(CGRect)frame blockHeight:(CGFloat)bHeight projection:(CGFloat)proj andBlocks:(NSArray *)blcks;
+(NSDictionary *)calculateAllHeightsForAnglesWithBlockHeight:(CGFloat)blockHeight andProjection:(CGFloat)proj;
-(NSNumber *)getBlockTopWithBlockNumber:(NSNumber *)bn;
-(NSNumber *)angleValueForBlockHeight:(NSNumber *)h;
+(NSArray *)multicoloredViewsWithSize:(CGSize)size;
#end
and here's the .m file:
#import "OSBlockView.h"
#implementation OSBlockView
#synthesize scrollPosition, zoneForFlexion;
#synthesize blockSize, blockQuantity, projection;
#synthesize anglesForHeights;
#synthesize oldTranslatedY;
#synthesize panGesture;
#synthesize blocks;
int logForTimingIndex=0;
+(void)logFotTimingWithString:(NSString *)str
{
NSLog(#"time log: %08i %#", logForTimingIndex, str);
logForTimingIndex++;
}
- (id)initWithFrame:(CGRect)frame blockHeight:(CGFloat)bHeight projection:(CGFloat)proj andBlocks:(NSArray *)blcks;
{
[[self class] logFotTimingWithString:#"begin init"];
self = [super initWithFrame:frame];
if (self)
{
self.blockSize=CGSizeMake(frame.size.width, bHeight);
self.blockQuantity=[blcks count];
self.blocks=blcks;
self.projection=proj;
self.anglesForHeights=[OSBlockView calculateAllHeightsForAnglesWithBlockHeight:bHeight andProjection:proj];
self.scrollPosition=0.2;
self.zoneForFlexion=frame.size.height*0.9;
//adding pan gesture
self.panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanGesture:)];
[self addGestureRecognizer:panGesture];
};
[[self class] logFotTimingWithString:#"end init"];
return self;
}
+(NSArray *)multicoloredViewsWithSize:(CGSize)size
{
[[self class] logFotTimingWithString:#"multicoloredViewsWithSize begin"];
//generating multicoloured blocks
NSMutableArray *blocks=[[NSMutableArray alloc] init];
for (int i=0; i<13; i++)
{
UIColor *blockColor;
switch (i)
{
case 0:
{
blockColor=[UIColor colorWithRed:0.75 green:0.75 blue:0.75 alpha:1.0];
break;
}
case 1:
{
blockColor=[UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1.0];
break;
}
case 2:
{
blockColor=[UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0];
break;
}
case 3:
{
blockColor=[UIColor colorWithRed:0.5 green:0.0 blue:0.0 alpha:1.0];
break;
}
case 4:
{
blockColor=[UIColor colorWithRed:1.0 green:1.0 blue:0.0 alpha:1.0];
break;
}
case 5:
{
blockColor=[UIColor colorWithRed:0.5 green:0.5 blue:0.0 alpha:1.0];
break;
}
case 6:
{
blockColor=[UIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:1.0];
break;
}
case 7:
{
blockColor=[UIColor colorWithRed:0.0 green:0.5 blue:0.0 alpha:1.0];
break;
}
case 8:
{
blockColor=[UIColor colorWithRed:0.0 green:1.0 blue:1.0 alpha:1.0];
break;
}
case 9:
{
blockColor=[UIColor colorWithRed:0.0 green:0.5 blue:0.5 alpha:1.0];
break;
}
case 10:
{
blockColor=[UIColor colorWithRed:0.0 green:0.0 blue:1.0 alpha:1.0];
break;
}
case 11:
{
blockColor=[UIColor colorWithRed:0.0 green:0.0 blue:0.5 alpha:1.0];
break;
}
case 12:
{
blockColor=[UIColor colorWithRed:1.0 green:0.0 blue:1.0 alpha:1.0];
break;
}
case 13:
{
blockColor=[UIColor colorWithRed:0.5 green:0.0 blue:0.5 alpha:1.0];
break;
}
default:
{
NSLog(#"PANIC: unknown block index");
blockColor=[UIColor blackColor];
break;
}
}
UIView *bView=[[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, size.width, size.height)];
[bView setBackgroundColor:blockColor];
[bView.layer setBorderWidth:3];
[bView.layer setBorderColor:[[UIColor whiteColor] CGColor]];
UILabel *blockLabel=[[UILabel alloc] initWithFrame:CGRectMake(2.5, 2.5, bView.frame.size.width-5, bView.frame.size.height-5)];
[blockLabel setBackgroundColor:[UIColor clearColor]];
[blockLabel setText:[NSString stringWithFormat:#"block number %i", i]];
[blockLabel setFont:[UIFont boldSystemFontOfSize:20]];
[blockLabel setTextColor:[UIColor whiteColor]];
[blockLabel setTextAlignment:UITextAlignmentCenter];
[bView addSubview:blockLabel];
[blocks addObject:bView];
};
[[self class] logFotTimingWithString:#"multicoloredViewsWithSize end"];
return blocks;
}
- (void)drawRect:(CGRect)rect
{
//NSLog(#"\n\n\n\n\n\n\n\n\n");
[[self class] logFotTimingWithString:#"drawRect begin"];
//NSLog(#"drawRect called");
//NSLog(#"self.subview.count: %i", [self.subviews count]);
//[[self class] logFotTimingWithString:#"subviews remove begin"];
for (UIView* subview in self.subviews)
{
//NSLog(#"subview removed");
[subview removeFromSuperview];
};
//[[self class] logFotTimingWithString:#"subviews remove end"];
//calculating top coordinates of blocks
//NSLog(#"calculating block tops...");
//[[self class] logFotTimingWithString:#"calculating block tops begin"];
NSMutableArray *blockTops=[[NSMutableArray alloc] init];
for (unsigned int blockCounter=0; blockCounter<blockQuantity; blockCounter++)
{
[blockTops addObject:[self getBlockTopWithBlockNumber:[NSNumber numberWithInt:blockCounter]]];
};
//[[self class] logFotTimingWithString:#"calculating block tops end"];
/*
for (unsigned int index=0; index!=[blockTops count]; index++)
{
NSLog(#"top %i at %i", index, [[blockTops objectAtIndex:index] intValue]);
};
*/
//calculating block heights
//NSLog(#"calculating block heights...");
//[[self class] logFotTimingWithString:#"calculating block heights begin"];
NSMutableArray *blockHeights=[[NSMutableArray alloc] init];
for (unsigned int index=0; index<[blockTops count]; index++)
{
if (([blockTops count]-1)!=index)
{
[blockHeights addObject:[NSNumber numberWithInt:([[blockTops objectAtIndex:(index+1)] intValue]-[[blockTops objectAtIndex:index] intValue])]];
}
else
{
if ((self.frame.size.height-[[blockTops objectAtIndex:index] intValue])<blockSize.height)
{
[blockHeights addObject:[NSNumber numberWithInt:(self.frame.size.height-[[blockTops objectAtIndex:index] intValue])]];
}
else
{
[blockHeights addObject:[NSNumber numberWithInt:blockSize.height]];
};
};
};
//[[self class] logFotTimingWithString:#"calculating block heights end"];
for (unsigned int blockCounter=0; blockCounter<blockQuantity; blockCounter++)
{
//NSLog(#"\n\n");
//[[self class] logFotTimingWithString:#"draw cycle begin"];
//[self preLoadBlockAtY:[blockTops objectAtIndex:blockCounter] withHeight:[blockHeights objectAtIndex:blockCounter] andIndex:[NSNumber numberWithUnsignedInt:blockCounter]];
//NSLog(#" preLoadingSampleBlockAtY: %g withHeight: %g andIndex: %i", [y doubleValue], [height doubleValue], [index unsignedIntValue]);
NSNumber *y=[blockTops objectAtIndex:blockCounter];
NSNumber *height=[blockHeights objectAtIndex:blockCounter];
//NSNumber *index=[NSNumber numberWithUnsignedInt:blockCounter];
//NSLog(#"loading block %02u y:%g h:%g", [index unsignedIntValue], [y doubleValue], [height doubleValue]);
if ([height intValue]>2)
{
BOOL up;
if ([y doubleValue]>self.frame.size.height/2.0)
{
up=true;
}
else
{
up=false;
};
//[[self class] logFotTimingWithString:#"bview init begin"];
UIView *bView=[NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:[blocks objectAtIndex:blockCounter]]];
[bView setFrame:CGRectMake(0.0, [y doubleValue], self.frame.size.width, blockSize.height)];
//[bView.layer setMasksToBounds:YES];
//[[self class] logFotTimingWithString:#"bview init end"];
//NSLog(#" blockImage frame y: %g", blockImage.frame.origin.y);
//NSLog(#" blockImage frame height: %g", blockImage.frame.size.height);
double oldHeight=bView.frame.size.height;
//NSLog(#" preanchor: %#", NSStringFromCGRect(bView.frame));
//[[self class] logFotTimingWithString:#"bview anchor begin"];
//setting anchor point
if (up)
{
[bView.layer setAnchorPoint:CGPointMake(0.5, 1.0)];
[bView setFrame:CGRectMake(bView.frame.origin.x, bView.frame.origin.y+bView.frame.size.height/2.0, bView.frame.size.width, bView.frame.size.height)];
}
else
{
[bView.layer setAnchorPoint:CGPointMake(0.5, 0.0)];
[bView setFrame:CGRectMake(bView.frame.origin.x, bView.frame.origin.y-bView.frame.size.height/2.0, bView.frame.size.width, bView.frame.size.height)];
};
//[[self class] logFotTimingWithString:#"bview anchor end"];
//NSLog(#" postanchor: %#", NSStringFromCGRect(bView.frame));
//[[self class] logFotTimingWithString:#"bview transform begin"];
if ([height doubleValue]!=blockSize.height)
{
//preparing transform
CATransform3D basicTrans = CATransform3DIdentity;
basicTrans.m34 =1.0/-projection;
//calculating angle
double angle= [[self angleValueForBlockHeight:height] doubleValue];
double rangle;
if (up)
{
rangle=angle/360*(2.0*M_PI);
}
else
{
rangle=(360.0-angle)/360*(2.0*M_PI);
};
//NSLog(#" angle: %g", angle);
//transforming
bView.layer.transform = CATransform3DRotate(basicTrans, rangle, 1.0f, 0.0f, 0.0f);
//NSLog(#" blockImage frame y: %g", blockImage.frame.origin.y);
//NSLog(#" blockImage frame height: %g", blockImage.frame.size.height);
};
//[[self class] logFotTimingWithString:#"bview transform end"];
//[[self class] logFotTimingWithString:#"bview post transform begin"];
double newHeight=bView.frame.size.height;
if (up)
{
[bView setCenter:CGPointMake(bView.center.x, bView.center.y-(oldHeight-newHeight))];
};
//NSLog(#" blockImage frame y: %g", bView.frame.origin.y);
//NSLog(#" blockImage frame height: %g", bView.frame.size.height);
//adding subview
[self addSubview:bView];
//[[self class] logFotTimingWithString:#"bview post transform end"];
}
else
{
//do not need to draw blocks with very low height
};
//[[self class] logFotTimingWithString:#"draw cycle end"];
//NSLog(#"\n\n");
};
[[self class] logFotTimingWithString:#"drawRect end"];
//NSLog(#"\n\n\n\n\n\n\n\n\n\n");
}
-(NSNumber *)angleValueForBlockHeight:(NSNumber *)h
{
//[[self class] logFotTimingWithString:#" angleValueForBlockHeight begin"];
//NSLog(#"angleValueForBlockHeight: %g called", [h doubleValue]);
//searching for closest key
double minDistance=blockSize.height;
double distance;
NSString *minKey=#"";
for(NSString *key in anglesForHeights)
{
if ([[anglesForHeights objectForKey:key] doubleValue]==[h doubleValue])
{
//match found
//NSLog(#"returned: %g", [key doubleValue]);
return [NSNumber numberWithDouble:[key doubleValue]];
};
distance=fabs([[anglesForHeights objectForKey:key] doubleValue]-[h doubleValue]);
if (distance<minDistance)
{
minDistance=distance;
minKey=key;
};
};
//NSLog(#"returned: %g", [blockSizesForAngles objectForKey:minKey]);
//[[self class] logFotTimingWithString:#" angleValueForBlockHeight end"];
return [NSNumber numberWithDouble:[minKey doubleValue]];
}
+(NSDictionary *)calculateAllHeightsForAnglesWithBlockHeight:(CGFloat)blockHeight andProjection:(CGFloat)proj
{
//NSLog(#"calculateAllHeightsForAnglesWithBlockHeight:%g andProjection:%g called", blockHeight, proj);
//[[self class] logFotTimingWithString:#" calculateAllHeightsForAnglesWithBlockHeight:andProjection begin"];
NSMutableDictionary *res=[[NSMutableDictionary alloc] init];
for (double i=0.0; i<=90.0; i=i+1.0)
{
//NSLog(#"i: %g", i);
UIView *block=[[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, blockHeight)];
[block.layer setAnchorPoint:CGPointMake(0.5, 1.0)];
CATransform3D basicTrans = CATransform3DIdentity;
double rangle;
basicTrans.m34 =1.0/-proj;
rangle=i/360*(2.0*M_PI);
//NSLog(#"rangle: %g Pi", rangle/M_PI);
block.layer.transform = CATransform3DRotate(basicTrans, rangle, 1.0f, 0.0f, 0.0f);
//NSLog(#"calculated block height: %g for angle: %g", block.frame.size.height, i);
if ([res objectForKey:[NSString stringWithFormat:#"%i", (int)roundf(i)]]==nil)
{
[res setObject:[NSString stringWithFormat:#"%i", (int)floor(block.frame.size.height)] forKey:[NSNumber numberWithDouble:i]];
};
};
//NSLog(#" result (size: %i): %#", [res count], [res debugDescription]);
//[[self class] logFotTimingWithString:#" calculateAllHeightsForAnglesWithBlockHeight:andProjection end"];
return [NSDictionary dictionaryWithDictionary:res];
}
-(NSNumber *)getBlockTopWithBlockNumber:(NSNumber *)bn
{
//NSLog(#"getBlockTopWithBlockNumber: %i", [bn intValue]);
//[[self class] logFotTimingWithString:#" getBlockTopWithBlockNumber begin"];
int t=[bn intValue];
CGFloat h=self.blockSize.height;
float v = self.blockQuantity * h - self.zoneForFlexion;
float vp = v * self.scrollPosition;
float z = t * h;
float alpha = (self.frame.size.height-self.zoneForFlexion) / (self.scrollPosition*self.scrollPosition + (1-self.scrollPosition) * (1-self.scrollPosition));
float f;
if (z < vp)
{
f = z / v;
//NSLog(#" %i", (int)(alpha * (f * f)));
//[[self class] logFotTimingWithString:#" getBlockTopWithBlockNumber end"];
return [NSNumber numberWithInt:(int)(alpha * (f * f))];
};
if (z <= vp + self.zoneForFlexion)
{
//NSLog(#" %i", (int)(z + alpha * (self.scrollPosition * self.scrollPosition) - vp));
//[[self class] logFotTimingWithString:#" getBlockTopWithBlockNumber end"];
return [NSNumber numberWithInt:(int)(z + alpha * (self.scrollPosition * self.scrollPosition) - vp)];
}
else
{
f = (blockQuantity*h-z) / v;
//NSLog(#" %i", (int)(self.frame.size.height - alpha * (f * f)));
//[[self class] logFotTimingWithString:#" getBlockTopWithBlockNumber end"];
return [NSNumber numberWithInt:(int)(self.frame.size.height - alpha * (f * f))];
};
}
double oldTranslatedY=0.0;
-(IBAction)handlePanGesture:(UIPanGestureRecognizer *)sender
{
//NSLog(#"handlePanGesture: called");
//[[self class] logFotTimingWithString:#" handlePanGesture begin"];
double translatedY = [sender translationInView:self].y;
double delta;
if (fabs(translatedY)<fabs(oldTranslatedY))
{
[sender setTranslation:CGPointZero inView:self];
oldTranslatedY=0.0;
delta=0.0;
}
else
{
delta=translatedY-oldTranslatedY;
oldTranslatedY=translatedY;
};
//NSLog(#"translatedY: %g", translatedY);
double pOffset=delta/((blockQuantity*blockSize.height)-self.frame.size.height);
self.scrollPosition=self.scrollPosition-pOffset;
if (self.scrollPosition<0.0)
{
self.scrollPosition=0.0;
}
else if(self.scrollPosition>1.0)
{
self.scrollPosition=1.0;
};
[[self class] logFotTimingWithString:#" handlePanGesture end"];
[self setNeedsDisplay];
}
Everything is working fine on simulator. But it is too slow on the real device.
I need help in 2 things:
In the moment of drawing the blocks: If I know what height value the block should have, how can I calculate the angle which I will use to rotate the block to fit height? It also depends on projection value. Now it is working with an ugly solution that precalculates heights for all angles (from 0 to 90) after the view is initialized and saves to dictionary, and when we need to know the specific angle for block rotation to fit height, we just search for the closest value of height from this hardcoded dictionary and use the appropriate angle.
It is toooo ugly. I tried to avoid this by calculating the needed value during drawing, but I had no luck (need formula).
Optimization in another parts.
Help, me please, I've stuck on this problem.
Put calculations on another thread. Avoid working with GUI in background threads! :)
[self performSelectorInBackground:<#(SEL)#> withObject:<#(id)#>];

Multiple images/buttons staying relative to their position on a zoomable image

I've made a map with UIScrolView, I want to place other small images or buttons onto the map and have them be in a relative position on the map when you zoom in and be able to click on them whenever. So when zoomed out, a button on country A, will still be on Country A when zoomed in, and disappear out of the screen when you scroll out of the countries view whilst zoomed in. How could I go about doing this?
As i can understand, you want to place custom views on your own custom map. And you need to keep the same sizes for views, but they should move when you scroll or zoom imageView.
You have to place views to scrollView's superview and recalculate positions when you zoom or scroll:
CustomMapViewController.h:
#interface CustomMapViewController : UIViewController <UIScrollViewDelegate>
{
UIScrollView *_scrollView;
UIImageView *_mapImageView;
NSArray *_customViews;
}
CustomMapViewController.m:
#import "CustomMapViewController.h"
enum {
kAddContactButton = 1,
kInfoDarkButton,
kInfoLightButton,
kLogoImage,
};
#implementation CustomMapViewController
- (void)dealloc
{
[_scrollView release]; _scrollView = nil;
[_mapImageView release]; _mapImageView = nil;
[_customViews release]; _customViews = nil;
[super dealloc];
}
- (void) loadView
{
[super loadView];
UIImageView *mapImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"map.png"]];
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
scrollView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
scrollView.delegate = self;
scrollView.minimumZoomScale = 0.2;
scrollView.maximumZoomScale = 2.0;
[scrollView addSubview:mapImageView];
scrollView.contentSize = mapImageView.frame.size;
[self.view addSubview:scrollView];
_scrollView = scrollView;
_mapImageView = mapImageView;
// Add custom views
UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeContactAdd];
btn1.tag = kAddContactButton;
[self.view addSubview:btn1];
UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeInfoDark];
btn2.tag = kInfoDarkButton;
[self.view addSubview:btn2];
UIButton *btn3 = [UIButton buttonWithType:UIButtonTypeInfoLight];
btn3.tag = kInfoLightButton;
[self.view addSubview:btn3];
UIImageView *image = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"logo.png"]] autorelease];
image.tag = kLogoImage;
[self.view addSubview:image];
_customViews = [[NSArray alloc] initWithObjects:btn1, btn2, btn3, image, nil];
[self _zoomToFit];
}
- (void) _zoomToFit
{
UIScrollView *scrollView = _scrollView;
CGFloat contentWidth = scrollView.contentSize.width;
CGFloat contentHeigth = scrollView.contentSize.height;
CGFloat viewWidth = scrollView.frame.size.width;
CGFloat viewHeight = scrollView.frame.size.height;
CGFloat width = viewWidth / contentWidth;
CGFloat heigth = viewHeight / contentHeigth;
CGFloat scale = MIN(width, heigth); // to fit
// CGFloat scale = MAX(width, heigth); // to fill
// May be should add something like this
if ( scale < _scrollView.minimumZoomScale ) {
_scrollView.minimumZoomScale = scale;
} else if ( scale > _scrollView.maximumZoomScale ) {
_scrollView.maximumZoomScale = scale;
}
_scrollView.zoomScale = scale;
}
////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - Positions
- (void) _updatePositionForViews:(NSArray *)views
{
CGFloat scale = _scrollView.zoomScale;
CGPoint contentOffset = _scrollView.contentOffset;
for ( UIView *view in views ) {
CGPoint basePosition = [self _basePositionForView:view];
[self _updatePositionForView:view scale:scale basePosition:basePosition offset:contentOffset];
}
}
- (CGPoint) _basePositionForView:(UIView *)view
{
switch (view.tag) {
case kAddContactButton:
return CGPointMake(50.0, 50.0);
case kInfoDarkButton:
return CGPointMake(250.0, 250.0);
case kInfoLightButton:
return CGPointMake(450.0, 250.0);
case kLogoImage:
return CGPointMake(650.0, 450.0);
default:
return CGPointZero;
}
}
- (void) _updatePositionForView:(UIView *)view scale:(CGFloat)scale basePosition:(CGPoint)basePosition offset:(CGPoint)offset;
{
CGPoint position;
position.x = (basePosition.x * scale) - offset.x;
position.y = (basePosition.y * scale) - offset.y;
CGRect frame = view.frame;
frame.origin = position;
view.frame = frame;
}
//////////////////////////////////////////////////////////////////////////////////////
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
[self _updatePositionForViews:_customViews];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
[self _updatePositionForViews:_customViews];
}
- (UIView *) viewForZoomingInScrollView:(UIScrollView *)scrollView;
{
return _mapImageView;
}
#end

Resizeable Custom NSPanel

OK, here's my situation :
I'm trying a HUD-like custom-controls collection, SNRHUDKit.
I'm specifically using SNRHUDWindow as my main window class
No matter what, although it actually works, I can't get the NSWindow (or NSPanel - doesn't make much difference) to resize, when the user drags its lower-right corner.
The code for SNRHUDWindow is :
//
// SNRHUDWindow.m
// SNRHUDKit
//
// Created by Indragie Karunaratne on 12-01-22.
// Copyright (c) 2012 indragie.com. All rights reserved.
//
#import "SNRHUDWindow.h"
#import "NSBezierPath+MCAdditions.h"
#define SNRWindowTitlebarHeight 22.f
#define SNRWindowBorderColor [NSColor blackColor]
#define SNRWindowTopColor [NSColor colorWithDeviceWhite:0.240 alpha:0.960]
#define SNRWindowBottomColor [NSColor colorWithDeviceWhite:0.150 alpha:0.960]
#define SNRWindowHighlightColor [NSColor colorWithDeviceWhite:1.000 alpha:0.200]
#define SNRWindowCornerRadius 5.f
#define SNRWindowTitleFont [NSFont systemFontOfSize:11.f]
#define SNRWindowTitleColor [NSColor colorWithDeviceWhite:0.700 alpha:1.000]
#define SNRWindowTitleShadowOffset NSMakeSize(0.f, 1.f)
#define SNRWindowTitleShadowBlurRadius 1.f
#define SNRWindowTitleShadowColor [NSColor blackColor]
#define SNRWindowButtonSize NSMakeSize(18.f, 18.f)
#define SNRWindowButtonEdgeMargin 5.f
#define SNRWindowButtonBorderColor [NSColor colorWithDeviceWhite:0.040 alpha:1.000]
#define SNRWindowButtonGradientBottomColor [NSColor colorWithDeviceWhite:0.070 alpha:1.000]
#define SNRWindowButtonGradientTopColor [NSColor colorWithDeviceWhite:0.220 alpha:1.000]
#define SNRWindowButtonDropShadowColor [NSColor colorWithDeviceWhite:1.000 alpha:0.100]
#define SNRWindowButtonCrossColor [NSColor colorWithDeviceWhite:0.450 alpha:1.000]
#define SNRWindowButtonCrossInset 1.f
#define SNRWindowButtonHighlightOverlayColor [NSColor colorWithDeviceWhite:0.000 alpha:0.300]
#define SNRWindowButtonInnerShadowColor [NSColor colorWithDeviceWhite:1.000 alpha:0.100]
#define SNRWindowButtonInnerShadowOffset NSMakeSize(0.f, 0.f)
#define SNRWindowButtonInnerShadowBlurRadius 1.f
#interface SNRHUDWindowButtonCell : NSButtonCell
#end
#interface SNRHUDWindowFrameView : NSView
- (void)snr_drawTitleInRect:(NSRect)rect;
#end
#implementation SNRHUDWindow {
NSView *__customContentView;
}
- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation
{
if ((self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:bufferingType defer:deferCreation])) {
[self setOpaque:NO];
[self setBackgroundColor:[NSColor clearColor]];
[self setMovableByWindowBackground:YES];
[self setLevel:NSFloatingWindowLevel];
}
return self;
}
- (NSRect)contentRectForFrameRect:(NSRect)windowFrame
{
windowFrame.origin = NSZeroPoint;
windowFrame.size.height -= SNRWindowTitlebarHeight;
return windowFrame;
}
+ (NSRect)frameRectForContentRect:(NSRect)windowContentRect
styleMask:(NSUInteger)windowStyle
{
windowContentRect.size.height += SNRWindowTitlebarHeight;
return windowContentRect;
}
- (NSRect)frameRectForContentRect:(NSRect)windowContent
{
windowContent.size.height += SNRWindowTitlebarHeight;
return windowContent;
}
- (void)setContentView:(NSView *)aView
{
if ([__customContentView isEqualTo:aView]) {
return;
}
NSRect bounds = [self frame];
bounds.origin = NSZeroPoint;
SNRHUDWindowFrameView *frameView = [super contentView];
if (!frameView) {
frameView = [[SNRHUDWindowFrameView alloc] initWithFrame:bounds];
NSSize buttonSize = SNRWindowButtonSize;
NSRect buttonRect = NSMakeRect(SNRWindowButtonEdgeMargin, NSMaxY(frameView.bounds) -(SNRWindowButtonEdgeMargin + buttonSize.height), buttonSize.width, buttonSize.height);
NSButton *closeButton = [[NSButton alloc] initWithFrame:buttonRect];
[closeButton setCell:[[SNRHUDWindowButtonCell alloc] init]];
[closeButton setButtonType:NSMomentaryChangeButton];
[closeButton setTarget:self];
[closeButton setAction:#selector(close)];
[closeButton setAutoresizingMask:NSViewMaxXMargin | NSViewMinYMargin];
[frameView addSubview:closeButton];
[super setContentView:frameView];
}
if (__customContentView) {
[__customContentView removeFromSuperview];
}
__customContentView = aView;
[__customContentView setFrame:[self contentRectForFrameRect:bounds]];
[__customContentView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[frameView addSubview:__customContentView];
}
- (NSView *)contentView
{
return __customContentView;
}
- (void)setTitle:(NSString *)aString
{
[super setTitle:aString];
[[super contentView] setNeedsDisplay:YES];
}
- (BOOL)canBecomeKeyWindow
{
return YES;
}
#end
#implementation SNRHUDWindowFrameView
- (void)drawRect:(NSRect)dirtyRect
{
NSRect drawingRect = NSInsetRect(self.bounds, 0.5f, 0.5f);
NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:drawingRect xRadius:SNRWindowCornerRadius yRadius:SNRWindowCornerRadius];
[NSGraphicsContext saveGraphicsState];
[path addClip];
// Fill in the title bar with a gradient background
NSRect titleBarRect = NSMakeRect(0.f, NSMaxY(self.bounds) - SNRWindowTitlebarHeight, self.bounds.size.width, SNRWindowTitlebarHeight);
NSGradient *titlebarGradient = [[NSGradient alloc] initWithStartingColor:SNRWindowBottomColor endingColor:SNRWindowTopColor];
[titlebarGradient drawInRect:titleBarRect angle:90.f];
// Draw the window title
[self snr_drawTitleInRect:titleBarRect];
// Rest of the window has a solid fill
NSRect bottomRect = NSMakeRect(0.f, 0.f, self.bounds.size.width, self.bounds.size.height - SNRWindowTitlebarHeight);
[SNRWindowBottomColor set];
[NSBezierPath fillRect:bottomRect];
// Draw the highlight line around the top edge of the window
// Outset the width of the rectangle by 0.5px so that the highlight "bleeds" around the rounded corners
// Outset the height by 1px so that the line is drawn right below the border
NSRect highlightRect = NSInsetRect(drawingRect, 0.f, 0.5f);
// Make the height of the highlight rect something bigger than the bounds so that it won't show up on the bottom
highlightRect.size.height += 50.f;
highlightRect.origin.y -= 50.f;
NSBezierPath *highlightPath = [NSBezierPath bezierPathWithRoundedRect:highlightRect xRadius:SNRWindowCornerRadius yRadius:SNRWindowCornerRadius];
[SNRWindowHighlightColor set];
[highlightPath stroke];
[NSGraphicsContext restoreGraphicsState];
[SNRWindowBorderColor set];
[path stroke];
}
- (void)snr_drawTitleInRect:(NSRect)titleBarRect
{
NSString *title = [[self window] title];
if (!title) { return; }
NSShadow *shadow = [NSShadow new];
[shadow setShadowColor:SNRWindowTitleShadowColor];
[shadow setShadowOffset:SNRWindowTitleShadowOffset];
[shadow setShadowBlurRadius:SNRWindowTitleShadowBlurRadius];
NSMutableParagraphStyle *style = [NSMutableParagraphStyle new];
[style setAlignment:NSCenterTextAlignment];
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:SNRWindowTitleColor, NSForegroundColorAttributeName, SNRWindowTitleFont, NSFontAttributeName, shadow, NSShadowAttributeName, style, NSParagraphStyleAttributeName, nil];
NSAttributedString *attrTitle = [[NSAttributedString alloc] initWithString:title attributes:attributes];
NSSize titleSize = attrTitle.size;
NSRect titleRect = NSMakeRect(0.f, NSMidY(titleBarRect) - (titleSize.height / 2.f), titleBarRect.size.width, titleSize.height);
[attrTitle drawInRect:NSIntegralRect(titleRect)];
}
#end
#implementation SNRHUDWindowButtonCell
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
NSRect drawingRect = NSInsetRect(cellFrame, 1.5f, 1.5f);
drawingRect.origin.y = 0.5f;
NSRect dropShadowRect = drawingRect;
dropShadowRect.origin.y += 1.f;
// Draw the drop shadow so that the bottom edge peeks through
NSBezierPath *dropShadow = [NSBezierPath bezierPathWithOvalInRect:dropShadowRect];
[SNRWindowButtonDropShadowColor set];
[dropShadow stroke];
// Draw the main circle w/ gradient & border on top of it
NSBezierPath *circle = [NSBezierPath bezierPathWithOvalInRect:drawingRect];
NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:SNRWindowButtonGradientBottomColor endingColor:SNRWindowButtonGradientTopColor];
[gradient drawInBezierPath:circle angle:270.f];
[SNRWindowButtonBorderColor set];
[circle stroke];
// Draw the cross
NSBezierPath *cross = [NSBezierPath bezierPath];
CGFloat boxDimension = floor(drawingRect.size.width * cos(45.f)) - SNRWindowButtonCrossInset;
CGFloat origin = round((drawingRect.size.width - boxDimension) / 2.f);
NSRect boxRect = NSMakeRect(1.f + origin, origin, boxDimension, boxDimension);
NSPoint bottomLeft = NSMakePoint(boxRect.origin.x, NSMaxY(boxRect));
NSPoint topRight = NSMakePoint(NSMaxX(boxRect), boxRect.origin.y);
NSPoint bottomRight = NSMakePoint(topRight.x, bottomLeft.y);
NSPoint topLeft = NSMakePoint(bottomLeft.x, topRight.y);
[cross moveToPoint:bottomLeft];
[cross lineToPoint:topRight];
[cross moveToPoint:bottomRight];
[cross lineToPoint:topLeft];
[SNRWindowButtonCrossColor set];
[cross setLineWidth:2.f];
[cross stroke];
// Draw the inner shadow
NSShadow *shadow = [[NSShadow alloc] init];
[shadow setShadowColor:SNRWindowButtonInnerShadowColor];
[shadow setShadowBlurRadius:SNRWindowButtonInnerShadowBlurRadius];
[shadow setShadowOffset:SNRWindowButtonInnerShadowOffset];
NSRect shadowRect = drawingRect;
shadowRect.size.height = origin;
[NSGraphicsContext saveGraphicsState];
[NSBezierPath clipRect:shadowRect];
[circle fillWithInnerShadow:shadow];
[NSGraphicsContext restoreGraphicsState];
if ([self isHighlighted]) {
[SNRWindowButtonHighlightOverlayColor set];
[circle fill];
}
}
#end
Any ideas what could be responsible for the NSPanel losing its resizing ability?
I'm using this framework as well, and the reason that resizing doesn't work by default is this line in the initWithContentRect method:
if ((self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:bufferingType defer:deferCreation])) {
As you can see, instead of passing the windowStyle bitmask provided to super's init method, it passes through just NSBorderlessWindowMask. A bit of sniffing around shows that for resizing to be possible at all, the styleMask must have NSResizableWindowMask included in the bitmask.
So, changing the line to
if ((self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask|NSResizableWindowMask backing:bufferingType defer:deferCreation])) {
should solve your problem.

Connect a UITapGestureRecognizer to a UIView

I'm trying to connect a gesture to a UIView so I can tap on the object, but it is not working. What am I doing wrong?
Shape.h
#import <UIKit/UIKit.h>
#interface Shape : UIView;
- (id) initWithX: (int)xVal andY: (int)yVal;
#end
Shape.m
#import "Shape.h"
#implementation Shape
- (id) initWithX:(int )xVal andY:(int)yVal {
self = [super init];
UIView *shape = [[UIView alloc] initWithFrame:CGRectMake(xVal, yVal, 10, 10)];
shape.backgroundColor = [UIColor redColor];
shape.userInteractionEnabled = YES;
[self addSubview:shape];
return self;
}
#end
MODIFIED CODE: The following code is in the main ViewController. I have removed the UITapGestureRecognizer from the Shape class. The code works if I make the following change, but then it is 'box' that responds to the tap gesture, not 'shape':
[shape addGestureRecognizer:tap];
to
[box addGestureRecognizer:tap];
- (void)handlerTap:(UITapGestureRecognizer *)recognizer {
//CGPoint location = [recognizer locationInView:[recognizer.view superview]];
NSLog(#"Success");
}
-(void)drawShapes{
NSLog(#"Draw");
if(!box){
box = [[UIView alloc] initWithFrame:CGRectMake(0, 0, screenWidth, screenHeight-100)];
box.backgroundColor = [UIColor colorWithRed: 0.8 green: 0.8 blue: 0.0 alpha:0.2];
[self.view addSubview:box];
}
for (int i = 0; i<5; i++) {
int x = arc4random() % screenWidth;
int y = arc4random() % screenHeight;
Shape * shape =[[Shape alloc] initWithX:x andY:y ];
[box addSubview:shape];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
[tap setNumberOfTapsRequired:1];
[tap addTarget:self action:#selector(handlerTap:)];
[box addGestureRecognizer:tap];
}
}
SOLUTION: I have learned that
self = [super init];
needs to be changed to include a CGRECT that defines the boundaries of the view that *shape is placed into.
self = [super initWithFrame:CGRectMake(xVal, yVal, 10, 10)];
Also, *shape needs to be placed at 0,0 to ensure its correct placement within its parent.
UIView *shape = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
#import "Shape.h"
#implementation Shape
- (id) initWithX:(int )xVal andY:(int)yVal {
self = [super initWithFrame:CGRectMake(xVal, yVal, 10, 10)];
UIView *shape = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
shape.backgroundColor = [UIColor redColor];
shape.userInteractionEnabled = YES;
[self addSubview:shape];
return self;
}
#end
You should set the target of the gesture recognizer to self, not the view, because you implemented the handlerTap: method in the Shape class.