How to make an NSSplitView transparent without having the splitter disappear? - cocoa

I've extended my window so that it has a button content border. The problem is that my NSSplitView covers the border. My thoughts were that I could make the split view transparent (but the controls on top of it opaque).
The following image shows what I am looking for, but with the missing splitter:
See how the bottom of the window has the button on it. This is the effect I am trying to achieve, but without any luck. The code that I am attempting to use is:
CALayer *transparentViewLayer = [CALayer layer];
[viewLayer setBackgroundColor:CGColorCreateGenericRGB(
256.0,
256.0,
256.0,
0)]; //RGB plus Alpha Channel
[splitView setWantsLayer:YES]; // view's backing store is using a Core Animation Layer
[splitView setLayer: transparentViewLayer];
If I do NOT set the transparentViewLayer, then I end up with the following:
Which shows the spitter, but hides the windows bottom bar. I have also tried subclassing NSSplitView and adding the following:
- (void) drawRect: (NSRect) dirtyRect
{
[[NSColor colorWithSRGBRed: 0.0 green: 255.0 blue: 0.0 alpha: 155.0] setFill];
NSRectFill(dirtyRect);
} // End of drawRect
Which left me with a green splitter and no alpha for the windows bottom bar. Any ideas on how I can achieve the effect I am looking for? (I want the bottom bar with my buttons and the splitter visible).

In your subclass, don't override -[NSSplitView drawRect:]. Instead, override -drawDividerInRect: and draw your divider ONLY in that rect. Always consult the documentation first when you want to "bend" a class to your will.

Related

drawWithFrame: draws NSTextFieldCell background out of registration with frame

I'm failing to have a NSTextFieldCell created and drawn programmatically recreate the same visual appearance equivalently-configured NSTextFieldCells have when built in Interface Builder and drawn automatically by Cocoa.
Context is a custom NSView that displays content that user can select, delete or replace. Though this content is non-textual, I'd like the field to resemble an NSTextField, so I'm trying to press an NSTextFieldCell into service to draw a bezel, background fill, etc., that would match a corresponding NSTextField's.
I setup the cell as follows (during custom control init()):
textFieldCell = [[NSTextFieldCell alloc] init];
[textFieldCell setDrawsBackground: YES];
[textFieldCell setBackgroundColor: NSColor.blueColor];
[textFieldCell setBackgroundStyle: NSBackgroundStyleNormal];
[textFieldCell setBezeled: YES];
[textFieldCell setStringValue: #""];
and then in custom control's drawRect,
- (void) drawRect: (NSRect) rect
{
// Draw an NSTextField-like background
if( isSelectAndDeletable)
{
[textFieldCell drawWithFrame: [self bounds] inView: self];
}
... // more content drawing here
}
This produces on onscreen 1-pixel frame of the appropriate thickness and size, and fills it with blue. But the frame itself is gray, not blue, and the fill touches the frame on top (no white margin) and misses it by two pixels at the bottom (thick white margin). By contrast, when I let Cocoa draw my textFieldCells either as part of an NSTextField or as standalone cells created in Interface Builder, the frame is drawn WITH the cell's background color and the fill is inset with one pixel white margin all around.
This picture demonstrates the problem. It shows three NSTextFieldCells, the second of which is (defectively) drawn by my own call to drawWithFrame:inView:, the other two (correctly) directly by the runtime.
Any ideas what I'm doing wrong? I feel like I am passing the proper rect (the frame is in the correct location), and the single call produces both the correct stroke (frame) and incorrect fill (background). Is this some misconfigured bezel effect? I can hand draw the effect I'm after but would rather stick with NSTextFieldCell if I can fix my approach here.

Xcode How do you make circular hitboxes on buttons?

I'm using Xcode 5 to make an UIButton. I made the button in storyboard and set the background image to a .png of a circle with transparency. I set up a simple action for the button to display how many times it was pressed in a label.
When I press the corners of the circle on the screen, it still adds to the score. So the button is still keeping its square hitbox, even though it has a round image as its background. I searched everywhere for a way to make a circular hitbox, but I can't find anything. Is this even possible? Is there an alternative way to do this?
The button responds to touch on corners because the bounding box of the button is a rectangle no matter what image you set for background.
To achieve a circular UIButton you must make a circular bounding box by simply using this code:
- (UIView *)setRoundedView:(UIView *)roundedView toDiameter:(float)newSize {
CGPoint saveCenter = roundedView.center;
CGRect newFrame = CGRectMake(roundedView.frame.origin.x, roundedView.frame.origin.y, newSize, newSize);
roundedView.frame = newFrame;
roundedView.layer.cornerRadius = newSize / 2.0;
roundedView.center = saveCenter;
roundedView.layer.masksToBounds = YES;
return roundedView;
}
In this method you can pass any UIView or subclass of UIView in your case UIButton. So in code you must create an IBOutlet of you button and in your -viewDidLoad method simply put this line of code:
self.btnMyButton = (UIButton *)[self setRoundedView:self.profilePic toDiameter:34];
Assuming you UIButton outlet is btnMyButton and set the diameter as per your requirement.
Hope this helps.
Don't forget to import QuartzCore Framework.

Draw NSView background partially, with a gradient

I have a NSView, subclassed, with custom background drawing, filling it with a gradient.
In IB, I've put a checkbox on it, somewhere in the middle.
This is the drawRect method.
-(void) drawRect:(NSRect)dirtyRect {
CGFloat sc = 0.9f;
CGFloat ec = 0.6f;
NSColor* startingColor = [NSColor colorWithDeviceRed:sc green:sc blue:sc alpha:1];
NSColor* endingColor = [NSColor colorWithDeviceRed:ec green:ec blue:ec alpha:1];
NSGradient *grad = [[NSGradient alloc] initWithStartingColor:startingColor endingColor:endingColor];
[grad drawInRect:dirtyRect angle:270];
}
What happens is, this same method gets called to draw the whole view area first and then for the part, where NSButton (checkbox) lies on top of it. OF course the checkbox background is drawn with a complete gradient and it is not right, since the portion is much smaller. The same happens with other controls I put on the said NSView.
What is the suggested approach on such thing?
One option is to make controls height the same as the views' but this will result in problems in the future.
The answer is, always draw the WHOLE area of the view, not just the dirtyRect
[grad drawInRect:[self bounds] angle:270];

UINavigationBar with image and default gradient background with iOS5

I'm attempting to use the new [UINavigationBar appearance] functionality in iOS5 to add a logo image to the UINavigationBars in my application. Primarily, I'd like to keep the default gradient, but center a transparent png in the NavBar. The logo image is roughly 120 pixels wide (240 pixels#2x).
I have first attempted this by setting the background image. The default behavior for setBackgroundImage:forBarMetrics: appears to be to tile the image, and all transparent parts show the default navbar background color, black. I can also set the background color via the appearance modifier, and get a flat color background, but I'd really like to get the original gradient behavior without maintaining a separate image resource for it. It also makes it easier to adjust in code, since I can adjust the tint there, rather than re-generating a new image if I decide to change it.
What I'm trying to use:
UIImage *logoImage = [UIImage imageNamed:#"logoImage"];
[[UINavigationBar appearance] setBackgroundImage:logoImage forBarMetrics:UIBarMetricsDefault];
You can do this two ways. If you want to always have the image in the navigation bar, then create an image view and set it as a subview of the navigation bar:
[self setLogoImageView:[[UIImageView alloc] init]];
[logoImageView setImage:[UIImage imageNamed:#"logo.png"]];
[logoImageView setContentMode:UIViewContentModeScaleAspectFit];
CGRect navFrame = [[navController navigationBar] frame];
float imageViewHeight = navFrame.size.height - 9;
float x_pos = navFrame.origin.x + navFrame.size.width/2 - 111/2;
float y_pos = navFrame.size.height/2 - imageViewHeight/2.0;
CGRect logoFrame = CGRectMake(x_pos, y_pos, 111, imageViewHeight);
[logoImageView setFrame:logoFrame];
[[[self navigationController] navigationBar] addSubview:logoImageView];
If you only want to display the logo in a certain view, then set the view's navigation item:
[logoImageView setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
[[self navigationItem] setTitleView:logoImageView];

Reposition an NSBox in the top pane of a horizontal NSSplitView

I the issue I'm having has to do with the coordinate system in Cocoa but I really don't know. This is all happening in the top pane of a horizontal NSSplitView.
Very simply, I'm trying to position one NSBox right below a second one (I load custom views into the boxes - that all works fine). The top box's top-left corner is at the top-left corner of the pane and never changes. If the height of the top NSBox shrinks I want the top of the second NSBox to slide right up below it. Conversely, if the top NSBox's height increases I want the bottom NSBox to slide down.
This code gets called twice. Box is correct (first time top box, second time bottom box) and v is correct (this is the view I'm loading into the box - this works fine and it is what is causing the height to change in the top box).
NSSize destBoxSize = [[box contentView] frame].size; //the size of the box in the view to load the view into
NSSize newViewSize = [v frame].size; // the size of the view to be loaded
float deltaWidth = [horizSplitView frame].size.width - destBoxSize.width;
float deltaHeight = newViewSize.height - destBoxSize.height;
NSRect boxFrame = [box frame];
boxFrame.size.height += deltaHeight;
boxFrame.size.width += deltaWidth;
boxFrame.origin.y -= deltaHeight;
NSLog(#"vc=%# boxFrame x%f y%f h%f w%f", nibName, boxFrame.origin.x, boxFrame.origin.y, boxFrame.size.height, boxFrame.size.width);
// Clear the box for resizing
[box setContentView:nil];
[box setContentView:v];
[box setFrame:boxFrame];
What you want to do is not so hard, but it will need some subclassing. First of all, you need to subclass NSSplitView and either and override either -(void)init or -(void)awakeFromNib to add this line:
[self setAutoresizesSubviews:YES]; //
Then you need to subclass the two boxes and set their auto resizing masks, either in -(void)init or in - (void)viewWillMoveToSuperview:(NSView *)newSuperView.
For the first box you'll probably want:
[newInstance setAutoresizingMask:NSViewNotSizable];
For the second bbox you'll probably want:
[newInstance setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin];
See also NSView. It takes a bit of experimenting to get the right combination, but then it works quite nicely.

Resources