Rounded WebView on OSX with Cordova? - objective-c

I'm creating a Mac OSX application with Cordova-osx. I've tried to implement all solutions I've seen about having rounded corners for my app but none of them work. Last resource will be removing the shadow and creating the corners with CSS but I don't really like that approach.
So, this is the file in charge of creating the webview and loading it. And this was my approach:
- (void) awakeFromNib
{
_commandDelegate = [[CDVCommandDelegateImpl alloc] initWithViewController:self];
self.webViewDelegate.viewController = self;
NSURL* appURL = nil;
NSString* loadErr = nil;
if ([self.startPage rangeOfString:#"://"].location != NSNotFound) {
appURL = [NSURL URLWithString:self.startPage];
} else if ([self.wwwFolderName rangeOfString:#"://"].location != NSNotFound) {
appURL = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%#", self.wwwFolderName, self.startPage]];
} else {
NSString* startFilePath = [self.commandDelegate pathForResource:self.startPage];
if (startFilePath == nil) {
loadErr = [NSString stringWithFormat:#"ERROR: Start Page at '%#/%#' was not found.", self.wwwFolderName, self.startPage];
NSLog(#"%#", loadErr);
self.loadFromString = YES;
appURL = nil;
} else {
appURL = [NSURL fileURLWithPath:startFilePath];
}
}
if (!loadErr) {
NSURLRequest* appReq = [NSURLRequest requestWithURL:appURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0];
self.webView.layer.cornerRadius = 10;
self.webView.layer.opaque = NO;
[[self.webView mainFrame] loadRequest:appReq];
} else {
NSString* html = [NSString stringWithFormat:#"<html><body> %# </body></html>", loadErr];
[[self.webView mainFrame] loadHTMLString:html baseURL:nil];
}
for (NSString* pluginName in self.startupPluginNames) {
[self getCommandInstance:pluginName];
}
// initialize items based on settings
BOOL enableWebGL = [[self.settings objectForKey:#"EnableWebGL"] boolValue];
WebPreferences* prefs = [self.webView preferences];
// Note that this preference may not be Mac App Store safe
if (enableWebGL && [prefs respondsToSelector:#selector(setWebGLEnabled:)]) {
[prefs performSelector:#selector(setWebGLEnabled:) withObject:[NSNumber numberWithBool:enableWebGL]];
}
}
Which basically is adding this:
self.webView.layer.cornerRadius = 10;
self.webView.layer.opaque = NO;
This is the code I use to show the panel:
- (void) showPanel
{
NSPoint mouseLocation = [NSEvent mouseLocation];
NSEnumerator *screenEnumerator = [[NSScreen screens] objectEnumerator];
NSScreen *screen;
while ((screen = [screenEnumerator nextObject]) && !NSMouseInRect(mouseLocation, screen.frame, NO))
;
NSRect statusFrame = [[self.statusItem valueForKey:#"window"] frame];
NSRect winFrame = [self.window frame];
NSRect screenFrame = [screen frame];
NSPoint p = NSMakePoint(statusFrame.origin.x, screenFrame.size.height + screenFrame.origin.y - 32);
if ((p.x + winFrame.size.width) > (screenFrame.origin.x + screenFrame.size.width)) {
p.x = screenFrame.origin.x + screenFrame.size.width - winFrame.size.width - 30;
}
[self.window setFrameTopLeftPoint:p];
[self.window setIsVisible:YES];
[NSApp activateIgnoringOtherApps:YES];
[self.window makeKeyAndOrderFront:self];
}
But doesn't seem to be showing any rounded corners in the frame. Any ideas?

I still can't make suitable view because of white corners, but I made WebView's corners rounded. I wrote this just in applicationDidFinishLaunching method:
self.webView.wantsLayer = YES;
self.webView.layer.cornerRadius = 50.0f;
self.webView.layer.masksToBounds = YES;
[self.webView.mainFrame.frameView setAllowsScrolling:NO];
and as you can see rounding is works, but there also white places in corners, and I don't know how to deal with them

Related

CustomAnnotationView Changes to MKAnnotation in MapKit

Hello Everyone i am new in developing app for I-Phone I have stuck in the Custom ANNotationView in mapKit. When i load map first Time it shows correctly Custom View , But after scrolling map it become the red colored PIN (default annotation in MapKit). Kindly Help me . Thank You in advance.
I am using the following code in viewForAnnotation delegate
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation: (id<MKAnnotation>)annotation
{
UIImageView *AvatarView=[[UIImageView alloc]initWithFrame:CGRectMake(0,0, 30, 30)];
AvatarView.layer.cornerRadius =AvatarView.frame.size.width / 2;
AvatarView.layer.masksToBounds = YES;
AvatarView.layer.borderColor =(__bridge CGColorRef)([UIColor colorWithRed:34.0/255.0 green:28.0/255.0 blue:36.0/255.0 alpha:1.0]) ;
AvatarView.layer.borderWidth = 1.0;
AvatarView.layer.zPosition=1;
MKPinAnnotationView* pinView =
(MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"CustomPinAnnotationView"];
if (!pinView)
{
// Try to dequeue an existing pin view first.
MKAnnotationView *annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:#"CustomPinAnnotationView"];
// If an existing pin view was not available, create one.
//pinView.animatesDrop = YES;
annotationView.canShowCallout = YES;
if([[responseDataArray objectAtIndex:k] valueForKey:#"thumbPath"]!=nil && [[[responseDataArray objectAtIndex:k] valueForKey:#"thumbPath"] length]>0)
{
NSString *imgPath =[NSString stringWithFormat:#"%#%#",WebSiteUrl,[[responseDataArray objectAtIndex:k] valueForKey:#"thumbPath"]];
k++;
NSString* webStringURL = [imgPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL* url = [NSURL URLWithString:webStringURL];
NSString *imageName = [placeholderImageArray objectAtIndex:(arc4random() %placeholderImageArray.count)];
[AvatarView sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:imageName]];
}
[annotationView addSubview:AvatarView];
return annotationView;
}
else
{
k=0;
pinView.annotation = annotation;
}
return pinView;
}
I am using the following code to add Annotation
MKPointAnnotation *myAnnotation = [[MKPointAnnotation alloc] init];
myAnnotation.coordinate = coord;
myAnnotation.title = titleStr;
myAnnotation.subtitle = #"Best bar in Town";
[myLocation addAnnotation:myAnnotation];
It never goes to else part. I don't know what is the problem. Please help me to solve out this problem. A big thanks to You.

Is there any way to Convert NSView Contain Subviews into NSImage?

Is there any way to Convert NSView Contain many Subviews with background color into NSImage ?
Sample code will be great
NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:[_collageView bounds]];
[_collageView unlockFocus];
// NSData *exportedData = [rep representationUsingType:NSJPEGFileType properties:nil];
NSData *exportedData = [rep representationUsingType:NSPNGFileType properties:nil];
NSSavePanel *savepanel = [NSSavePanel savePanel];
savepanel.title = #"Save chart";
[savepanel setAllowedFileTypes:[NSArray arrayWithObject:#"png"]];
[savepanel beginSheetModalForWindow:self.view.window completionHandler:^(NSInteger result)
{
if (NSFileHandlingPanelOKButton == result)
{
NSURL* fileURL = [savepanel URL];
if ([fileURL.pathExtension isEqualToString:#""])
fileURL = [fileURL URLByAppendingPathExtension:#"png"];
[exportedData writeToURL:fileURL atomically:YES];
}
}];
i also used the following code to
[_collageView lockFocus];
NSBitmapImageRep *bits = [[NSBitmapImageRep alloc]initWithFocusedViewRect: [_collageView bounds]];
[_collageView unlockFocus];
NSImage *image1 = [[NSImage alloc] init];
[image1 addRepresentation: bits];
[self.ImageView setImage:image1];
but not working
Your approach copies the bitmap from the windows server. This is the rendered content of the view. Typically you do not want to do that.
It is a better way to lock the focus on an NSImage, which builds up the graphic contexts and then draw into that image using -drawRect: or whatever drawing methods you have. You can add subviews by simply iterating them.
// perhaps in a category of NSView
- (void)drawRecursively
{
[self drawRect:self.bounds];
for (NSView *subview in self.subviews)
{
[subview drawRecursively];
}
}
To start that
NSImage *image = [[NSImage alloc] initWithSize:theView.bounds.size];
[image lockFocus]; // Set-up the context for the image
[theView drawRecursively];
[image unlockFocus];
Typped in Safari.
Edit: Before going to the subviews, you have to perform a transformation for them. Forgot that.
I have actually implemented the drawing of a view hierarchy offscreen myself.
It is non-trivial, even for a very simple view hierarchy where all views are
subclasses NSControl and have -(BOOL) isFlipped { return YES; }
Here's one small portion of that code, which will give you an idea of the pain of doing this:
- (void)drawRect:(NSRect)iDirtyBigRect
{
#ifdef DISABLE_PSEUDOVIEW
debugRectsBeingDrawn();
return;
#endif
if ( !drawableShape )
{
if ( [[[self window] contentView] wantsLayer] )
{
CGRect cgRect = NSRectToCGRect([self bounds]);
drawableShape = HIShapeCreateMutableWithRect( &cgRect );
}
else
{
DUMP( #"our superview should have set up our drawableShape!" );
[self setNeedsDisplay:YES];
return;
}
}
else if ( iDirtyBigRect.size.width <= 0 )
{
// Our drawable shape is empty.
// This can happen for several valid reasons:
// 1) dirtyShape is fully masked by opaqueShape
// 2) for some reason, we were called to obey needsDisplay when not needed
DLog( #"nothing to draw %#", self );
return; // must return, otherwise we risk drawing outside our bounds
}
//NSArray *hardSubviews = [self subviews];
for ( NSView *pseudoSubview in pseudoSubviews )
{
if ( ![pseudoSubview isHidden] &&
[pseudoSubview window] == offscreenWindow ) // only draw pseudo view if it's in offscreen window
{
NSRect rectToDraw = [pseudoSubview frame];
// if ( rectToDraw.size.width == 0 )
// {
// DLog( #"clipped to superview %#", pseudoSubview );
// }
CGRect cgRect = NSRectToCGRect(rectToDraw);
if ( HIShapeIntersectsRect(drawableShape, &cgRect) )
{
// the magic: transform to subview coordinates
NSAffineTransform* xform = [NSAffineTransform transform];
[xform translateXBy:rectToDraw.origin.x yBy:rectToDraw.origin.y];
if ( [pseudoSubview isFlipped] != [self isFlipped] )
{
if ( ![pseudoSubview isKindOfClass: [NSColorWell class]] )
{
DUMP( #"hmmm flippedness different %d %d", [pseudoSubview isFlipped], [self isFlipped] );
}
[xform translateXBy:0.0 yBy:rectToDraw.size.height];
[xform scaleXBy:1.0 yBy:-1.0];
}
[xform concat];
HIMutableShapeRef newShape = HIShapeCreateMutableWithRect( &cgRect );
HIShapeIntersect( newShape, drawableShape, newShape ); // clip to subview frame
HIShapeOffset( newShape, -cgRect.origin.x, -cgRect.origin.y ); // translate to subview coords
CGRect dirtyRect;
HIShapeGetBounds(newShape, &dirtyRect);
// if ( dirtyRect.size.width <= 0 )
// {
// DUMP( #"why?" );
// }
if ( [pseudoSubview isKindOfClass:[PseudoView class]] )
{
//DLog( #"drawing Pseudo %# dirtyRect %# ", pseudoSubview, NSStringFromRect(NSRectFromCGRect(dirtyRect)));
PseudoView *pView = (PseudoView *)pseudoSubview;
if ( pView->drawableShape )
{
CFRelease( pView->drawableShape );
pView->drawableShape = NULL;
}
// create subview personal drawableShape from my drawable shape
pView->drawableShape = newShape;
if ( [pseudoSubview isOpaque] )
gDrawDirtyPixels += dirtyRect.size.width * dirtyRect.size.height;
if ( dirtyRect.size.width > 0 )
[pseudoSubview drawRect: NSRectFromCGRect(dirtyRect)];
}
else
{
//DLog( #"drawing non-Pseudo %# rectToDraw %# ", pseudoSubview, NSStringFromRect( rectToDraw ));
UInt64 t1 = CpuCycles();
CFRelease( newShape );
// sacrifice efficiency to avoid bugs...
// always draw the entire view.
[pseudoSubview drawRect:[pseudoSubview bounds]]; // NSRectFromCGRect(dirtyRect)]; // [pseudoSubview bounds]];
UnitTestQuartz( [pseudoSubview bounds] );
// NSLog( #"drawRect %# %# sUnitTestQuartzToggle %f\n -- superv %#\n -- self %#", pseudoSubview, NSStringFromRect(rectToDraw), sUnitTestQuartzToggle*100, [pseudoSubview superview], self );
UInt64 t2 = CpuCycles();
int diff = (int)(t2-t1);
gDrawControl += diff;
}
[xform invert]; // restore
[xform concat];
}
}
}
//if ( [self isKindOfClass:[PseudoRootView class]] )
// [offscreenWindow setViewsNeedDisplay:NO];
}
I am able to solve my problem with the following Code
// Setup the image to render
NSRect imgRect = _collageView.frame;
NSSize imgSize = imgRect.size;
NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
pixelsWide:imgSize.width
pixelsHigh:imgSize.height
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSDeviceRGBColorSpace
bitmapFormat:NSAlphaFirstBitmapFormat
bytesPerRow:0
bitsPerPixel:0];
NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithBitmapImageRep:rep];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:context];
// Render
CGContextRef zCgContextRef = (CGContextRef) [context graphicsPort];
[[_collageView layer] renderInContext:zCgContextRef];
// Get the Data for the image
NSData *exportedData = [rep representationUsingType:NSJPEGFileType properties:nil];
// Start the savepanel
NSSavePanel *savepanel = [NSSavePanel savePanel];
savepanel.title = #"Save chart";
[savepanel setAllowedFileTypes:[NSArray arrayWithObject:#"jpg"]];
[savepanel beginSheetModalForWindow:self.view.window completionHandler:^(NSInteger result)
{
if (NSFileHandlingPanelOKButton == result)
{
NSURL* fileURL = [savepanel URL];
if ([fileURL.pathExtension isEqualToString:#""])
fileURL = [fileURL URLByAppendingPathExtension:#"jpg"];
[exportedData writeToURL:fileURL atomically:YES];
}
}];

How do I dismiss a UIView after scanning a barcode?

I have an iPad app that I want to add a barcode reader to... this is the code for the initialization of the barcoder code:
-(void) scanInitializationCode {
_highlightView = [[UIView alloc] init];
_highlightView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin|UIViewAutoresizingFlexibleBottomMargin;
_highlightView.layer.borderColor = [UIColor greenColor].CGColor;
_highlightView.layer.borderWidth = 3;
[self.view addSubview:_highlightView];
// define the label to display the results of the scan
_label = [[UILabel alloc] init];
_label.frame = CGRectMake(0, self.view.bounds.size.height - 40, self.view.bounds.size.width, 40);
_label.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
_label.backgroundColor = [UIColor colorWithWhite:0.15 alpha:0.65];
_label.textColor = [UIColor whiteColor];
_label.textAlignment = NSTextAlignmentCenter;
_label.text = #"(none)";
[self.view addSubview:_label];
// session initialization
_session = [[AVCaptureSession alloc] init];
_device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error = nil;
// define the input device
_input = [AVCaptureDeviceInput deviceInputWithDevice:_device error:&error];
if (_input) {
[_session addInput:_input];
} else {
NSLog(#"Error: %#", error);
}
// and output device
_output = [[AVCaptureMetadataOutput alloc] init];
[_output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
[_session addOutput:_output];
_output.metadataObjectTypes = [_output availableMetadataObjectTypes];
// and preview layer
_prevLayer = [AVCaptureVideoPreviewLayer layerWithSession:_session];
_prevLayer.frame = self.view.bounds;
_prevLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.view.layer addSublayer:_prevLayer];
}
This is the AVCaptureMetadataOutputObjectsDelegate code:
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection {
CGRect highlightViewRect = CGRectZero;
AVMetadataMachineReadableCodeObject *barCodeObject;
NSString *detectionString = nil;
NSArray *barCodeTypes = #[AVMetadataObjectTypeEAN13Code];
for (AVMetadataObject *metadata in metadataObjects) {
for (NSString *type in barCodeTypes) {
if ([metadata.type isEqualToString:type])
{
barCodeObject = (AVMetadataMachineReadableCodeObject *)[_prevLayer transformedMetadataObjectForMetadataObject:(AVMetadataMachineReadableCodeObject *)metadata];
highlightViewRect = barCodeObject.bounds;
detectionString = [(AVMetadataMachineReadableCodeObject *)metadata stringValue];
break;
}
}
if (detectionString != nil) {
_label.text = detectionString;
oISBNField.text = detectionString; // move detectionString to ISBN textbox
[_session stopRunning];
[_highlightView removeFromSuperview];
break;
}
else
_label.text = #"(none)";
}
This is the code that starts the scanning process by having the user tap a UIButton:
- (IBAction)aReadBarcode:(UIButton *)sender {
[self scanInitializationCode];
[_session startRunning];
// display the activity
[self.view bringSubviewToFront:_highlightView];
[self.view bringSubviewToFront:_label];
oISBNField.text = scanResults;
}
The problem is that once the scan has found the barcode, it stays visible; what I want to do is have it return to the UIView that has the button that caused it to start scanning (in other words, I want the _highlightView to disappear). I have tried all kinds of "dismissal" methods, even putting it at the back of the z-order, but none of them work. How can I make the highlightView disappear from the screen?
The answer:
[_prevLayer removeFromSuperlayer]; after [_session stopRunning]

why is - (null) at the end of my object

I have the following class
#import <UIKit/UIKit.h>
#interface UIImageViewInfo : UIImageView
#property(nonatomic,strong) NSString* colorInfo;
#end
when creating an UIImageViewInfo in function1(post below) I get the correct result
"UIImageViewInfo: 0x1d8acd60; baseClass = UIImageView; frame = (0 0;
20 20); opaque = NO; userInteractionEnabled = NO; tag = 2000; layer =
CALayer: 0x1d8a0560>>"
but creating the something in function2(posted below) I get the -(null)
UIImageViewInfo: 0x8372c40; baseClass = UIImageView; frame = (0 0; 20
20); alpha = 0; hidden = YES; opaque = NO; userInteractionEnabled =
NO; tag = 2000; layer = CALayer: 0x8380090>> - (null)
Why is there a - (null) ??
Thanx in advance
P.S> here is more from my code
-(void)function1{
UIImageViewInfo *image;
self.solutionPinSlots = [[NSMutableArray alloc] init];
for (int i=0; i<M; i++) {
image = [[UIImageViewInfo alloc] initWithImage:[UIImage imageNamed:#"solutionPeg#2x.png"]];
image.translatesAutoresizingMaskIntoConstraints=NO;
image.hidden=YES;
image.alpha = 0;
image.colorInfo = #"clear";
image.tag = 2000*(self.brainController.slotsIndex+1) +i;
[self.solutionPinSlots addObject:image];
[self.view addSubview:(UIImageViewInfo *)self.solutionPinSlots[i]];
}
NSLog(#"solutionSlots: %#",self.solutionPinSlots);
__block int counter = 0;
[self.brainController.solution enumerateObjectsUsingBlock:^(NSString *peg, NSUInteger idx, BOOL *stop){
for (int i=0;i<M;i++) {
if ([self.brainController.slots[self.brainController.slotsIndex][i] isEqual:peg]) {
if (i==idx) {
((UIImageViewInfo *) self.solutionPinSlots[counter]).backgroundColor = [UIColor whiteColor];
((UIImageViewInfo *) self.solutionPinSlots[counter]).colorInfo=#"white";
}else{
((UIImageViewInfo *) self.solutionPinSlots[counter]).backgroundColor = [UIColor blackColor];
((UIImageViewInfo *) self.solutionPinSlots[counter]).colorInfo=#"black";}
((UIImageViewInfo *) self.solutionPinSlots[counter]).hidden=NO;
((UIImageViewInfo *) self.solutionPinSlots[counter++]).alpha=1;
}
}
}];
[self.solutionPinSlots shuffle];
[self updateSolutionPinSlots];
}
-(void)function2{
NSString *obj;
for (int idx=0; idx< M;idx++ ) {
UIImageViewInfo *image = [[UIImageViewInfo alloc] initWithImage:[UIImage imageNamed:#"solutionPeg#2x.png"]];
image.translatesAutoresizingMaskIntoConstraints=NO;
obj = (NSString *) [self.solutionPinSlots objectAtIndex:idx];
image.tag=2000*(self.brainController.slotsIndex+1)+idx;
image.hidden=NO;
image.alpha = 1;
if ([obj isEqualToString:#"clear"]) {
image.hidden=YES;
image.alpha = 0;
image.colorInfo=#"clear";
image.backgroundColor=[UIColor clearColor];
}else if ([obj isEqualToString:#"white"]){
image.colorInfo=#"white";
image.backgroundColor=[UIColor whiteColor];
}else if ([obj isEqualToString:#"black"]){
image.colorInfo=#"black";
image.backgroundColor=[UIColor blackColor];
}else{
NSLog(#"Something is Wrong!!!");
}
[self.solutionPinSlots replaceObjectAtIndex:idx withObject:image];
[self.view addSubview:image];
}
}
Never mind. It is coming from the description method of UIView, which is rather complex and dumps a bunch of information about the view. One of the conditionals is being tripped up by your configuration and causing the (null) at the end.
Safe to ignore.
There is no UIImageViewInfo class in UIKit, so the implementation must be in your project. In that implementation, there is likely a description method that is implemented something like:
- (NSString*)description {
....
return [NSString stringWithFormat:#"UIImageViewInfo:%p; %# - %#", self, [super description], someInstanceVariableThatHappensToBeNil]];
}
Only your description is likely more complex due to the sometimes does this sometimes that output.
Not that you should not prefix classes with UI when subclassing the UIKit.
It is because your NSLog call (which you do not show) is asking for a second object, and that second object is null.

NSMatrix with multiple toggle buttons?

I am trying to create an NSMatrix of NSButtonCells where between zero and four buttons can be selected (toggled on). I have tried the following (test) code, but am not sure how I can provide the functionality I require. Perhaps it's not possible with NSMatrix and I need to look at an alternative control, or create my own?
#interface MatrixView : NSView
{
NSScrollView *_scrollView;
NSMatrix *_matrixView;
}
#end
#implementation MatrixView
- (id)initWithFrame:(NSRect)frameRect
{
NSLog(#"initWithFrame. frameRect=%#", NSStringFromRect(frameRect));
self = [super initWithFrame:frameRect];
if (self != nil)
{
_scrollView = [[NSScrollView alloc] initWithFrame:NSMakeRect(0, 0, frameRect.size.width, frameRect.size.height)];
[_scrollView setBorderType:NSNoBorder];
[_scrollView setHasVerticalScroller:YES];
[_scrollView setHasHorizontalScroller:NO];
[_scrollView setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
NSSize contentSize = [_scrollView contentSize];
contentSize.height = 300;
// Make it 3 x however-many-buttons-will-fit-the-height
CGFloat gap = 8.0;
CGFloat width = (contentSize.width / 3.0) - (gap * 2.0);
NSUInteger rows = (contentSize.height / (width + gap));
NSLog(#"width=%f, rows=%lu", width, rows);
NSButtonCell *prototype = [[NSButtonCell alloc] init];
[prototype setTitle:#"Hello"];
[prototype setButtonType:NSToggleButton];
[prototype setShowsStateBy:NSChangeGrayCellMask];
_matrixView = [[NSMatrix alloc] initWithFrame:NSMakeRect(0, 0, contentSize.width, contentSize.height)
mode:NSListModeMatrix
prototype:prototype
numberOfRows:rows
numberOfColumns:3];
[_matrixView setCellSize:NSMakeSize(width, width)];
[_matrixView setIntercellSpacing:NSMakeSize(gap, gap)];
[_matrixView setAllowsEmptySelection:YES];
[_matrixView sizeToCells];
[_scrollView setDocumentView:_matrixView];
[self addSubview:_scrollView];
[self setAutoresizesSubviews:YES];
[prototype release];
}
return self;
}
...
I got this to work with the following subclass of NSMatrix. I added one property, onCount, to keep track of how many buttons were in the on state:
#implementation RDMatrix
#synthesize onCount;
-(id) initWithParentView:(NSView *) cv {
NSButtonCell *theCell = [[NSButtonCell alloc ]init];
theCell.bezelStyle = NSSmallSquareBezelStyle;
theCell.buttonType = NSPushOnPushOffButton;
theCell.title = #"";
if (self = [super initWithFrame:NSMakeRect(200,150,1,1) mode:2 prototype:theCell numberOfRows:4 numberOfColumns:4]){
[self setSelectionByRect:FALSE];
[self setCellSize:NSMakeSize(40,40)];
[self sizeToCells];
self.target = self;
self.action = #selector(buttonClick:);
self.drawsBackground = FALSE;
self.autoresizingMask = 8;
self.allowsEmptySelection = TRUE;
self.mode = NSHighlightModeMatrix;
self.onCount = 0;
[cv addSubview:self];
return self;
}
return nil;
}
-(IBAction)buttonClick:(NSMatrix *)sender {
NSUInteger onOrOff =[sender.selectedCells.lastObject state];
if (onOrOff) {
self.onCount += 1;
}else{
self.onCount -= 1;
}
NSLog(#"%ld",self.onCount);
if (self.onCount == 5) {
[sender.selectedCells.lastObject setState:0];
self.onCount -= 1;
}
}
When you try to select the 5th button it will flash on, but then go off. This could be a problem depending on how you are using the state of these buttons. I just logged them with this method:
-(IBAction)checkMatrix:(id)sender {
NSIndexSet *indxs = [self.mat.cells indexesOfObjectsPassingTest:^BOOL(NSButtonCell *cell, NSUInteger idx, BOOL *stop) {
return cell.state == NSOnState;
}];
NSLog(#"%#",indxs);
}
After Edit: I didn't like the way my first method flashed the button on briefly before turning it off again when you try to click the 5th button. I found what I think is a better solution that involves overriding mouseDown in the matrix subclass (if you want to try this, you should delete the setAction and setTarget statements and delete the buttonClick method):
-(void)mouseDown:(NSEvent *) event {
NSPoint matPoint = [self convertPoint:event.locationInWindow fromView:nil];
NSInteger row;
NSInteger column;
[self getRow:&row column:&column forPoint:matPoint];
NSButtonCell *cell = [self cellAtRow:row column:column];
if (self.onCount < 4 && cell.state == NSOffState) {
cell.state = NSOnState;
self.onCount += 1;
}else if (cell.state == NSOnState) {
cell.state = NSOffState;
self.onCount -= 1;
}
}